EGO DLL interface

Skip to main content (skip navigation menu)
Letterhead logo






EGO DLL interface

 

Even with its extensive command set, EGO cannot be everything for all users. Sometimes you need to extend the EGO interpreter with custom libraries or controls. EGO supports two interfaces to do this: it supports ActiveX controls and it has a DLL interface.

Actually, the DLL interface in EGO is remarkable in that it has allowed the use of legacy 16-bit DLLs in 32-bit versions of EGO in Windows 9x. When Microsoft came out with Windows 95 (back in 1995), the company drew an artificial barrier between 16-bit mode and 32-bit mode. Rumours have it that Microsoft wished to mask the fact that most of the code that made Windows 95 was still 16-bit. Personally, I could not care less how many bits a processor register carries, but at the time Microsoft made a big point of Windows 95 being 32-bit software —and thereby supposedly vastly better than 16-bit predecessors.
    Anyway, a 32-bit application cannot call a 16-bit DLL directly, according to Microsoft, it needs the help of a pair of "thunking DLLs", one 16-bit and one 32-bit, which are built in a special way with the aid of a "thunk compiler" and a version of the Macro Assembler that was hard to come by in the early days of Windows 95. (I recall a hectic weekend driving through Paris with the head of the EGO development team, Thibault Peuchot, visiting every computer shop that we could find and approaching them with the single question: "do you have MASM 6.10d?". The reply was always negative; at best it came with a sigh and the comment that we were not the first to ask for that special "DDK" version of MASM.)
    The development team for EGO, of which I was part, was quick to assert that GDI32.DLL (32-bit) thunks down to the 16-bit GDI.EXE (a DLL, regardless of its extension) without the use of intermediate thunking DLLs. So we traced through a few calls from GDI32.DLL to GDI.EXE and back, and found out all the hidden, undocumented, machinery to "thunk without thunk DLLs". Apart from doing away with thunk DLLs and the need for special software like the thunk compiler and MASM 6.10d, the direct thunking method also turned out to be simpler to use (and probably faster as well).
    As an aside, there is no such simple trick to call 16-bit DLLs from a 32-bit program in Windows NT, Windows 2000 or Windows XP. Probably, it isn't possible at all, without rewriting part of the operating system.

So you can build a special purpose DLL or ActiveX control and call into it from an EGO module. Occasionally your code needs a tighter interface with EGO. If your DLL does some graphic effect, for instance, you need access not only to the EGO window(s), but also to the background layer or other layers; otherwise your graphic will not refresh when part of the screen needs updating. Also, you probably need to query the scroll positions of the EGO window(s) to position your graphic effect correctly.

Now,... how do you do that?

If you look at the binaries in the EGO distribution, you will notice that the run-time interpreter, ELEVE32.EXE, is just over 100 KiB. Surely such a tiny package cannot contain all the hundreds of high-level commands that EGO provides. Indeed, ELEVE32.EXE depends on a number of DLLs and the most notable (by its size) of these is EGO32.DLL: over 1 MiB.

If you are a programmer (this paper assumes that you are), you know that a DLL exports an API, and if one program (such as ELEVE32.EXE) can call into a DLL, another program, or a support DLL can do that too. Now, knowing that the bulk of the EGO interpreter is inside a DLL, opens the door to let your "plug-in" call back into EGO for any support work.

EGO32.DLL exports many, many functions. By far most of these are intended to only be called ELEVE32.EXE or other core components of the EGO interpreter. For the purpose of plug-ins, there are also a few functions that the plug-in may call to get information or to send information from the plug-in to EGO. These functions are still officially undocumented and unsupported. There is no guarantee that these functions will operate in a compatible way in a next release of EGO. There are no warranties (of any kind) either.


CAUTION: If you call directly into a the EGO DLL, you are out in the cold and on your own. When you ask assistance on a problem, even if it seems a genuine bug in the EGO product that is unrelated to your calling into the DLL, the EGO development team will always ask you to reproduce the problem in an EGO module that does not use any kind of DLL interfacing trickery.

That behind us, below are a lists of functions from the EGO DLL that can be fairly safely called from the "outside".

Interface functions

One useful set of functions, created more or less with plug-ins in mind, starts with the prefix "ego_". These are the "interface" functions. A few of these functions were originally implemented to support EGO Assistant.

ego_NotifyIdle() Tell EGO that your thread is currently "idle".
ego_SetBackground() Set the background bitmap of a EGO window and return the old background bitmap.
ego_GetDCs() Get the HDCs for the foreground and backup images, or for the layer and the mask.
ego_GetLayer() Get a pointer to the active layer.
ego_InvalidateLayer() Mark the indicated layer "dirty" so that it will be repainted.
ego_SelectPopup() Set the active window and optionally display it.
ego_GetAxesState() Check whether "axes" were set up, and how.
ego_GetLayerInfo() Get layer size and position when the EGO Assistant is running. This function is not discussed here further, because it was made specifically to support EGO Assistant.

Variable functions

EGO stores the status of mouse, keyboard, buttons and its environment into system variables (system variables are the predefined variables, which start with ew). EGO has quite a few functions that manipulate variables, but only a subset can be called fairly safely from the "outside". The limitations of using this subset are:

VarGetType() Get the type (numeric/string/file) of a variable. You also use this function to check whether a variable exists.
VarGetNum() Get the value of a numeric variable.
VarSetNum() Set the value of a (new) variable; this also sets the "type" of the variable to "numeric".
VarGetString() Get the string of a string variable.
VarSetString() Set the string of a (new) variable; this also sets the "type" of the variable to "string".

Command executive

The ·dll() EGO command allows you to call any DLL from the EGO side of the fence. An inverse function, where a DLL calls into EGO to execute EGO commands, exists under the name of Dispatcheur(). This function executes a string with EGO commands or expressions. EGO executes this string in the context of the currently scheduled module, which means that any command that causes a reschedule is potentially fatal. Obviously, commands like ·Fin() ·Suite(), ·CoMod() or ·Tache() cause a reschedule (because a module is added to or removed from the process list), but also all functions that enter a "wait" state, like ·Pause(), ·Inter() and ·AttenteEvt(), cause a reschedule. Next to rescheduling, any EGO command that causes a change in the graphic context, like ·Pinceau() and ·PStyle(), can also be catastrophic.

As a result, neat as Dispatcheur() may be, I strongly advise against its use (remember that I told you that you are on your own when calling any functions from the EGO DLL). If you need such functionality, file a feature request to the EGO development group.

Reference

Below is an alphabetic reference. The syntax descriptions is in the C/C++ language. Knowledge of Microsoft Windows programming (at the API level) is assumed.

 
ego_GetAxesState Get the EGO axes and metric

ego_GetAxesState() returns the state of the "axes" in EGO. If defined, the axis system in EGO determines the pixel position of the origin and the measurement unit (metric). If not defined, the origin is the upper left corner of the window and the measurement unit is a pixel.

Syntax:
void ego_GetAxesState(BOOL FAR *pbAxeExist, BOOL FAR *pbAxeActif, int FAR *piAxeOrgX, int FAR *piAxeOrgY, double FAR *pdblAxeUniteX, double FAR *pdblAxeUniteY)
pbAxeExist
Upon return: holds the state whether axes were defined for the current context.
pbAxeActif
Upon return: holds whether axes are currently active.
piAxeOrgX
Upon return: holds the horizontal pixel position of the origin.
piAxeOrgY
Upon return: holds the vertical pixel position of the origin.
pdblAxeUniteX
Upon return: holds the horizontal measurement unit (in pixels).
pdblAxeUniteY
Upon return: holds the vertical measurement unit (in pixels).
 
ego_GetDCs Get the DCs for a window

ego_GetDCs() returns one or two Display Contexts (DCs) that a plug-in can use to draw in the EGO screens.

Syntax:
BOOL ego_GetDCs(HDC FAR *hdc, HDC FAR * hdcVirt, int vri, int fen, int layer)
hdc
Will hold the DC for the display, the virtual screen or for the layer upon return.
hdcVirt
Will hold the DC for the window background or for the layer's mask upon return.
vri
The virtual screen number to get the DC for, or 0 for none.
fen
The EGO window number to get the DCs for (ignored when vri is greater than 0). This value is 0 for the main window.
layer
The layer number to get the DCs for (ignored when vri is greater than 0). If an EGO window does not have any layers, this parameter should be set to 0.
Returns:
TRUE on success, FALSE on failure.
Notes:
A virtual screen has only a foreground DC. If the DCs for a virtual screen are requested, hdcVirt will be NULL upon return. The hdc parameter point to a memory DC for the virtual screen.

An EGO window has a "display" DC for the window and a "background" DC which contains a copy of the window contents (see also ego_SetBackground). The hdc parameter will contain the display DC and hdcVirt will contain the memory DC to the background bitmap.

A layer consists of an image (the "face") and a mask. Upon return hdc will contain the DC to the face and hdcVirt will contain the DC to the mask. Both are memory DCs.

Note that if an EGO window has layers, "layer 0" is a layer too. A plug-in that wants to draw something in an EGO window must draw it to both DCs if the window does not have layers, and only to the DC referred to by hdc if the window has layers. Use ego_GetLayer() to find out whether an EGO window has layers.

 
ego_GetLayer Return a pointer to a layer

ego_GetLayer() returns a pointer to the requested layer, if such a layer is present.

Syntax:
LPVOID ego_GetLayer(int fen, int layer)
fen
The index of the EGO window (0 for the main window).
layer
The layer number.
Returns:
A pointer to the layer, or NULL if no such layer is present. This pointer is actually a pointer to a "sprite" from the AniSprite library (an "ASPRITE" type).
Notes:
To find out whether an EGO window has any layers at all, call this function with layer set to 0.
 
ego_InvalidateLayer Mark a layer for redraw

ego_InvalidateLayer() tells the EGO drawing system that a layer needs to be partially or fully refreshed. When drawing into a layer, the result is not immediately displayed (this is in contrast to drawing to a "display DC"). So when the drawing is completed, the layer must put on the "refresh list". The action of ego_InvalidateLayer() is not immediate; the layer refresh occurs on a timer.

Syntax:
BOOL ego_InvalidateLayer(LPVOID Layer, LPRECT lprc)
Layer
A pointer to the layer to refresh. One can obtain this pointer with ego_GetLayer().
lprc
A pointer to the bounding rectangle to refresh, or NULL to refresh the entire layer.
Returns:
TRUE on success, FALSE on failure.
 
ego_NotifyIdle Notify EGO of a waiting state

ego_NotifyIdle() lets a plug-in notify that it is waiting for an event to occur.

Syntax:
void ego_NotifyIdle(void)
Returns:
Nothing.
Notes:
EGO verifies the system load for determining the relative priority of the screen refresh (in the case that layers are present). When the system load is high, the refresh rate is dropped in order to free up CPU resources. However, EGO is unable to determine the CPU load if a thread sits in some tight loop (for example, waiting for an event). This call lets such a thread tell EGO that it should not drop the refresh rate.
 
ego_SelectPopup Set the current EGO window

ego_SelectPopup() sets the specified EGO window as the "current window" and optionally displays it.

Syntax:
BOOL ego_SelectPopup(int fen, BOOL activate, int visible)
fen
The EGO window number to make "current".
activate
Whether to give the indicated window the "focus"; i.e., whether to make it the "active" window.
visible
The visibility state: 0 to hide the window, 1 to show it and -1 to keep the current visibility state.
Returns:
TRUE on success, FALSE on failure.
 
ego_SetBackground Swap the background bitmap

ego_SetBackground() sets a new background bitmap for an EGO window and returns the original background bitmap.

Syntax:
HBITMAP ego_SetBackground(int fen, HBITMAP hbmp)
fen
The index of the EGO window (0 for the main window).
hbmp
The new bitmap to set.
Returns:
The previous bitmap.
Notes:
EGO keeps a copy of what it displays at the screen for every window in the "background bitmap". (Foreground objects are layers, buttons, treeviews, etc.) This call lets you save the background and restore it, or dump the content into a graphic file, or send it over the network.

Note that there is no equivalent ego_GetBackground(); to get the current background, you must set a dummy background and then restore the original background. The ego_SetBackground() selects the hbmp parameter immediately in a "DC", and Windows does not allow you to process a HBITMAP when it is selected. As a result, you should process the EGO background bitmap only while a different (dummy) HBITMAP is set.

See also ego_GetDCs().

 
VarGetNum Read a numeric variable

VarGetNum() returns the value of a numeric variable.

Syntax:
double VarGetNum(LPSTR VarName)
VarName
The name of the variable.
Returns:
The value of the variable, or 0.0 if the variable does not exist or is is not a numeric type.
Notes:
See also VarGetType() to verify the existance and the type of a variable.
 
VarGetString Read a string variable

VarGetString() reads the value of a string variable. The string is set to an empty string if the variable does not exist or if it does not have the correct type.

Syntax:
void VarGetString(LPSTR VarName, LPSTR Result)
VarName
The name of the variable.
Result
A pointer to a buffer that will hold the value of the variable upon return. This buffer should be big enough to hold the longest string.
Notes:
This function can also read strings from string files ("fichiers chaîne"), by appending an index between braces to the filename.

In EGO 4, 32-bit, the maximum string length is 65500 characters, excluding the zero-terminator for a C string. This means that the buffer should be 65501 bytes to accommodate the largest possible string. Earlier versions of EGO and 16-bit versions of EGO have a maximum string length of 1024.

See also VarGetType() to verify the existance and the type of a variable.

 
VarGetType Get the type of a variable

VarGetType() returns the type (numeric/string) of a variable

Syntax:
int VarGetType(LPSTR VarName)
VarName
The name of the variable.
Returns:
262 for a numeric type, 264 for a string type, 265 for a string file and 266 for tables. Other values are for internal and reserved types. When the variable does not exist, this function returns 0.
 
VarSetNum Set a variable to a value

VarSetNum() sets a variable to a value. It changes the type of the variable to "numeric" if necessary and it creates the variable if it does not exist.

Syntax:
void VarSetNum(LPSTR VarName, double Value)
VarName
The name of the variable.
Value
Notes:
 
VarSetString Set a variable to a string

VarSetString() sets a variable to a string. It changes the type of the variable to "string" if necessary and it creates the variable if it does not exist.

Syntax:
void VarGetString(LPSTR VarName, LPSTR Value)
VarName
The name of the variable.
Value
The string to set. This string should not exceed the maximum string length that EGO supports.
Notes: