_SHELL_CallDll


include shell.inc
VxDcall _SHELL_CallDll, <<OFFSET32 lpszDll>, <OFFSET32 lpszProcName>,\
    cbArgs, <OFFSET32 lpvArgs>>
mov [ReturnValue], eax

Loads the given 16-bit library, creates a dynamic link to the given function, thunks the function arguments, and calls the function at ring 3. After return from the function, this service frees the library.

lpszDll

Address of a null-terminated string specifying the filename of the DLL to load, or zero to indicate that lpszProcName is a 16:16 raw address.

lpszProcName

Address of a null-terminated string specifying the name of the function to call, or the ordinal of the function to call, or a raw 16:16 address to call. See the following comments for additional information .

cbArgs

Number of bytes of arguments to pass.

lpvArgs

Address of buffer that contains the function arguments. The arguments must be placed in the buffer in the same order as they are pushed on the ring 3 stack. This order depends on whether the function being called uses PASCAL or C calling conventions. The size, in bytes, of each argument must be as required by the function being called.

This service may be called only during application time. Failure to observe this restriction may crash the system. This service is capable of calling only 16-bit DLLs.

The procedure being called must conform to the C or PASCAL calling conventions. Parameters must be accepted on the stack, not in registers; register contents on entry to the procedure are undefined. The procedure must preserve the SI, DI, and BP registers.

If the lpszDll parameter is zero, the lpszProcName is treated as a raw 16:16 pointer with the selector in the high word and the offset in the low word. The procedure at the indicated address is called, with the parameters pushed as in the normal case. This is the only way 16-bit code should be called at application time. Do not attempt to use Begin_Nest_Exec at application time, because it will crash the machine if a timer tick comes in at the wrong time. Do not use the _CallRing3 service because it may crash the machine if the Windows 16-bit memory manager has performed a GlobalCompact.

The following simple example illustrates the basic idea of the _SHELL_CallDll service.


/* PASCAL calling convention passes arguments backwards */
struct tagEXITWINDOWARGS {
    WORD  wReserved;
    DWORD dwReturnCode;
} ewa = { 0, EW_REBOOTWINDOWS };
SHELL_CallDll("USER", "EXITWINDOWS", sizeof(ewa), &ewa);

The following example illustrates how a string parameter can be passed, as well as calling the target DLL by ordinal rather than by name:


#define ordWinExec  166
/* PASCAL calling convention passes arguments backwards */
struct tagWINEXECARGS {
....WORD  nCmdShow;
....DWORD lpszCmdLine;
} wea = { SW_NORMAL, 0 };

wea.lpszCmdLine = SHELL_LocalAllocEx(LPTR + LMEM_STRING, 0, 
    "MyProg.exe");
SHELL_CallDll("KERNEL", ordWinExec, sizeof(wea), &wea);
SHELL_LocalFree(wea.lpszCmdLine);

The following example illustrates how raw 16:16 addresses can be called. This is handy if the address didn't come from a DLL, but was registered by an application. It is also handy if you will be calling into your DLL many times in quick succession, because the normal _SHELL_CallDll loads the library, calls the function, and then frees it. Each LoadLibrary loads the DLL from the disk anew, wasting time with disk I/O. Instead, load it once, do all the calls, and then free it when done.


HINSTANCE hinst;
FARPROC lpfnMyFunction;

struct tagMYFUNCTIONARG {
    WORD wParam;
} mfa = { 0 };

hinst = SHELL_LoadLibrary("MYDLL");
if (hinst < 32) return ERROR;
lpfnMyFunction = SHELL_GetProcAddress(hinst, "MYFUNCTION");
if (lpfnMyFunction == 0) {
    SHELL_FreeLibrary(hinst); return ERROR;
}

/* Now call MyFunction three times with arg 0, 1, and 2. */
mfa.wParam = 0;
SHELL_CallDll(0, lpfnMyFunction, sizeof(mfa), &mfa);

mfa.wParam = 1;
SHELL_CallDll(0, lpfnMyFunction, sizeof(mfa), &mfa);

mfa.wParam = 2;
SHELL_CallDll(0, lpfnMyFunction, sizeof(mfa), &mfa);

/* Finished. Free the library. */
SHELL_FreeLibrary(hinst);
return OK;

There is no way to distinguish between a DLL function that happened to return a value in the range 0 through 31 from the inability to load the DLL. If such fine control is necessary, you should use the method described in the third example.