It is possible for an external library to detect asynchronous external events, i.e.
asynchronous events like for example: a new e-mail, double click mouse message by the
user or an error message from a machine that is being controlled. These asynchronous
events can happen any time but it is possible to have these events handled by
Trinc-Prolog.
There are two ways to send events to Trinc-Prolog, the first one described works only if
the extension library and Trinc-Prolog are always inside the same operating system process
and the second one described works even if the extension library and Trinc-Prolog are in
different processes.
For both methods the first step in handling an asynchronous event is to create a string with a valid Prolog goal. This Prolog goal string will be treated as a normal Prolog goal and an attempt to prove it will be started after it has been correctly compiled. It doesn't matter if Trinc-Prolog is already proving another goal or another external event, the current goal being proven is interrupted and after proving an external event has finished the previous goal is activated again. An example of a valid Prolog goal is:
| do_my_event(mouse_click, point(12, 45)). |
The TPEXTEVENT2 structure
Create a local instance of a TPEXTEVENT2 structure. A memory buffer must be allocated for the Prolog goal and then the Prolog goal to prove must be copied to the _GoalPtr attribute of a TPEXTEVENT2 structure. The memory buffer will NOT be deleted by Trinc-Prolog, after it has been used it can be deleted or re-used by the extension library.
| //Fill a structure with information to transmit to
Trinc-Prolog, the second version of the //external-event structure because it is not necessary to cross process boundaries! TPEXTEVENT2 ExtEvent; ExtEvent._EngineId = EngineID; //Trinc-Prolog engine identifier ExtEvent._TPId = Call->_TPId; //Trinc-Prolog instance identifier //Allocate a block of memory for the Prolog goal, this piece of memory will NOT be //deleted by Trinc-Prolog after the "SendMessage" function returns!! size_t StrLength = strlen(Buf); ExtEvent._GoalPtr = TP_MEMALLOC( StrLength+1 ); //Copy the goal to prove the memory block strcpy(ExtEvent._GoalPtr, Buf); //Make the string zero terminated ExtEvent._GoalPtr[StrLength] = '\x0'; |
It is possible to send an event to a specific instance of a Prolog class. The Trinc-Prolog instance identifier must be assigned to the attribute _TPId, else the value 0 must be assigned to it.
Sending the event
After a TPEXTEVENT2 structure has been filled it must be send to Trinc-Prolog. This is done by sending a WM_EVENT_TRINC_PROLOG message to a hidden window inside Trinc-Prolog. This message can only be used if the extension library and Trinc-Prolog are inside the same operating system process!
The handle of the hidden window inside Trinc-Prolog, to which the event can be send, can be retrieved in two ways. First, the TPINIT structure of the TPExtensionStart function contains this window handle, but this value can only be used if the extension library is inside the same process as Trinc-Prolog. If the extension library is executed from inside another process than the process of Trinc-Prolog the macro TP_DESTINATION_WINDOW must be used to retrieve the window handle.
Sending the message to Trinc-Prolog must be done with the Windows function SendMessage. This function must be used because it waits for the processing of the message to finish, posting the WM_EVENT_TRINC_PROLOG message with PostMessage will generate protection failures because the TPEXTEVENT2 structure will be destroyed after the function that contains the PostMessage call returns.
| SendMessage( DestWin, WM_EVENT_TRINC_PROLOG, (WPARAM)GetActiveWindow(), (LPARAM)&ExtEvent ) |
The last parameter of the SendMessage function is the location of the TPEXTEVENT2 instance.
The TPEXTEVENT structure
Create a local instance of a TPEXTEVENT structure. The Prolog goal to prove must be copied to the _Goal attribute of a TPEXTEVENT structure.
| //Some event was detected.. For instance: a click on the
right button of the mouse //Fill a TPEXTEVENT structure with information about the event //Copy a valid Prolog goal to the buffer of the structure |
It is necessary to create a zero-terminted string of the Prolog goal. It is possible to send an event to a specific instance of a Prolog class. The Trinc-Prolog instance identifier must be assigned to the attribute _TPId, else the value 0 must be assigned to it.
Sending the event
After a TPEXTEVENT structure has been filled it must be send to Trinc-Prolog. This is done by sending a WM_COPYDATA message to a hidden window inside Trinc-Prolog. The WM_COPYDATA message is used because information associated with this message can cross process boundaries in Win32. A COPYDATASTRUCT structure with the data to transmit must be filled before the WM_COPYDATA message can be send.
| //Fill a COPYDATASTRUCT structure, the attribute lpData must
point to the //location of a TPEXTEVENT structure. The attribute cbData must contain the size of the //data to transmit. The attribute dwData is not used. COPYDATASTRUCT CDStruct; CDStruct.dwData = 0; CDStruct.cbData = sizeof(TPEXTEVENT); CDStruct.lpData = &ExtEvent; Delphi example |
After both these structures have been filled it is possible to send them to Trinc-Prolog. The handle of the hidden window inside Trinc-Prolog, to which the event can be send, can be retrieved in two ways. First, the TPINIT structure of the TPExtensionStart function contains this window handle, but this value can only be used if the extension library is inside the same process as Trinc-Prolog. If the extension library is executed from inside another process than the process of Trinc-Prolog the macro TP_DESTINATION_WINDOW must be used to retrieve the window handle.
Sending the message to Trinc-Prolog must be done with the Windows function SendMessage. This function must be used because it waits for the processing of the message to finish, posting the WM_COPYDATA message with PostMessage will generate protection failures because the COPYDATASTRUCT and TPEXTEVENT structures will not exist after the function that contains the PostMessage call returns.
| SendMessage( DestWin, WM_COPYDATA, (WPARAM)GetActiveWindow(),
(LPARAM)&CDStruct ) or if the message must pass process boundaries: SendMessage( TP_DESTINATION_WINDOW, WM_COPYDATA, (WPARAM)(Msg->hwnd),
(LPARAM)&CDStruct ) |