Runtime 'Dynamic' DLL calling
Environment:Visual C++ (SP3), Windows NT4 (SP3)
The Problem:
Call a function in a DLL passing some arguments. The DLL, function and arguments are not known until run-time. The argument types are limited to standard types (i.e. char, short, long, float, char*) but not, for the sake of simplicity, doubles (due to their 8 bytes). Further types could be added, I think, but for now we'll leave them alone. I'm still not sure about return types and the solution that I propose can only cope with return types that fit into 4 bytes.
Notes on the problem
- I have posted this article not so much as to give a solution but, as an idea that I expect to get ripped to shreds in the hope that what comes out is neat, tidy and useable.
- The solution should fix the problem as it stands i.e. we can't say, "just use COM" or "only have functions that take some complex object that can work out its own parameter list".
void func(...)style functions are for the callee not the caller.
Possible Solution
- We start by going against rule two and limiting the maximum number of
arguments to 10. We do this by creating a structure which contains an array
of ten DWORDS and pass the struct by value to the DLL. The DLL will get all
the data it needs on the stack (and probably a bit to spare).
typedef struct { DWORD dwARG[10]; }STACK; typedef DWORD (*PFNDLL)(STACK); void main() { HINSTANCE hDll = LoadLibrary("MyDll.dll") PFDLL pfFunc = (PFDLL)GetProcAddress(hDll,"MyFunc"); STACK args; char szBuf[] = "hello world"; DWORD dwRet; args[0] = 10; // a dword args[1] = (DWORD)szBuf; dwRet = (pfFunc)(args); }We sort of solved the problem but had to impose extra limits, so no points there. - This leads to a better solution, put our data on the stack ourselves and
then we can have as much or as little as we want, and potentially deal with
types other than the simple ones (once we know that the idea is sound). The
problem is that this uses two lines of assembler code which is pushing my
assembler knowledge by two lines. As far as I can tell this works for the
types supported in the demo.
// see the detail in the RuntimeDll.cpp file, all error handling // has been removed for brevity // pointer to function in dll, caller of CallDll typedef __declspec(dllimport)DWORD (*pfFunc)(void); void main() { HINSTANCE hDll = ::LoadLibrary("MyDll.dll"); pfFunc pFun = NULL; DWORD dwTemp; DWORD dwRet = 0; float fRet = 0.0f; int i; bool bRetIsFloat = false; nArgCount = 4; int nArg[4]; long nA = 123456; char cB = 20; float fC = 5.678f; char* szD = "test string"; // memcpy not cast as a cast will loose data // in the case of a float memcpy(nArg+0, &nA ,sizeof(long)); memcpy(nArg+1, &cB ,sizeof(long)); memcpy(nArg+2, &fC ,sizeof(long)); memcpy(nArg+3, &szD,sizeof(long)); pFun = (pfFunc)::GetProcAddress(hDll, "MyFunc"); // load the stack (in reverse order) for( i=nArgCount-1; i>=0; i--) { // copy the data to a temporary holder // (casts can lose data) memcpy(&dwTemp, &(Arg[i].bVal), sizeof(DWORD)); // chuck it on the stack _asm push dwTemp } // call the function, data already on the stack if (bRetIsFloat) { // float returns are not passed via the EAX register but, // it seem, are on the stack so ... // ok I didn't know this bit of asm I compiled some // code and copied it once I had some idea of what // the instructions do. // call, calls a function // fstp, does a store & pop _asm { call dword ptr [pFun] fstp dword ptr [fRet] } } else { dwRet = (pFun)(); } // unload the stack for (i = 0; i<nArgCount; i++) { _asm pop dwTemp // copy the data back in case of altered values memcpy(&(Arg[i].bVal), &dwTemp, sizeof(DWORD)); } if (bRetIsFloat) printf("returned %f\n",fRet); else printf("returned %d\n",dwRet); }I have tested this code against a test DLL using various argument lists and return types and everything seems to work nicely, but as I don't know much about assembler I'm not sure what problems this code may cause. If you have any comments I'd be pleased to hear them. The Demo Source code contains this basic idea wrapped up in a class CRuntimeDll and uses CRuntimeDllException. - Some other, better, less hacky, solution?
Notes on the Demo project
The demo allows you to call a function in a DLL. Follow the example below for an explanation of how the demo app can be used.
To call the WINAPI GetWindowText(HWND hwnd,char* szBuffer,long nBufferSize) function:
- Set the DLL name to "user32.dll" and press "Load", all being well the "Add" , "Call" and "Set Type" buttons should be enabled and the "Load" button should now read "Unload".
- Enter "GetWindowTextA" (for non-unicode version) in the "Function" edit box.
- Set the "Value" edit box to that of any hwnd, get the value using Spy++, the value must be in decimal not hex!
- Select "unsigned long" on the "Types" list box then press "Add"
- Set the "Value" to "12345678901234567890" then add an "unsigned char*".
- Finally for nBufferSize set "Value" to 20 and add an "unsigned long".
- Press "Call".
- Click on the "unsigned char*" on the Args list and the window text (the first 19 bytes) for the window you entered should appear in the "Value" box.
- Press "Clear" to call another function in this DLL.
The return type is always caught in a DWORD and can be converted after the function has been called. This is done by selecting a "Type" and pressing "Set Type", the value and type are displayed at the bottom of the dialog box. The Exception to this is functions that return type float, they must be declared before the function is called, the return type will remain float until it is changed to an integer or pointer type.
Closing notes
- The string to value and value to string functionality should be moved into the CRuntimeDll object as should return type setting and access.
- Calling some DLL's with the wrong arguments can cause the demo to crash. I have yet to find a way to stop or catch this. Bear in mind though, that applications with this kind of functionality would normally be used by some sort of developer who expect this sort of thing ;-)
Downloads
Download demo application - 7 Kb
Download demo source - 15 KbHistory

Comments
hiding dll exported functions
Posted by Dan_Henderson on 09/30/2004 09:42pmis there any way to export dll functions only when a certain condition exists? here's the scenario: 1. program calls an exported dll function, passing a "code". 2. dll checks the value of the code, and if it is ok, other exported functions will become available, and if the code is incorrect those functions would remain unavailable. Any ideas would be appreciated.
ReplyWindows NT/XP
Posted by Legacy on 09/10/2003 12:00amOriginally posted by: albert drent
There is a similar Delphi implementation by James Mistry. It is based on the C program. However, in NT and XP the function simply doesn't work. Does anybody know if this is also the case with the C function?
Albert Drent
ReplySaving a passed argument in DLL
Posted by Legacy on 08/04/2003 12:00amOriginally posted by: Nikhil Khedkar
Hi,
ReplyI have a MFC application. It loads a DLL and invokes a method in it. In this method few arguments are passed of type long, LPSTR, COM interface pointer. I can access these arguments in the method in which I have passed them. I store them in some DLL variables. But when I try to access them in some other DLL method, these variables are set to the default value i.e. to NULL. How do you save the state of the variables, is anything special is needed for COM interface pointer?
- Nikhil
Help
Posted by Legacy on 07/28/2003 12:00amOriginally posted by: Erum Raza
I use to call function from my activex dll from VB application, i use createobject to create the dll object, the tASK MANAGER shows that application increase in memory size, thats obivious but when i use set object=nothing then why that memory isn't returned to window system, it is showing same memory and some times 2-3 bytes less.
ReplyPlz help me friends.
Calling a dll function
Posted by Legacy on 06/09/2003 12:00amOriginally posted by: anjali
-
ReplyCalling convention
Posted by Rakeshsoni on 04/28/2005 03:30amThere may be a problem with the calling convention. Make sure you are calling the function the same way you declared it.
ReplyThe value of ESP was not saved across a function call
Posted by Legacy on 05/30/2003 12:00amOriginally posted by: bing yang
Dear sir:
When i run c++ dll, i got error message below. Is there anyone can help me?
Thanks
bing yang
Debug error:
Moudle
File: i386\chkesp.c
The value of ESP was not saved across a function call .This is usually a result of calling a function declared with one calling convention with a function pointer declared with a different calling convention.
-
Replycalling convention
Posted by Rakeshsoni on 04/28/2005 04:25amThe message shown by Windows is quite clear and correct. This is happening because the way the function was defined is not the same as the way you are calling for example WINAPI is missing or the return type is incorrect or something of that sort.
Replydll promblem
Posted by Legacy on 05/22/2003 12:00amOriginally posted by: Joey Bate
I am trying to download this game for Windows 2000. Its saying that i dont have thedinput8.dll file or the dsound.dll file and its keep on doing that when i download them. Is there something i can buy to fix this promblem?�?
sincerly
ReplyJoey Bate
call c++ dll from oracle form
Posted by Legacy on 05/15/2003 12:00amOriginally posted by: bing yang
ReplyCalling a DLL from C++ without lib file or sources = HOWTO?
Posted by Legacy on 04/06/2003 12:00amOriginally posted by: thenext_1
The problem is as simple as water.
I have a dll (precisely ZLIB.DLL)
How I can call its exports (which I know) and pass them parameters?
Do I have to mess around with getprocaddress....?
Is there a visualbasic-like way to declare a dll function and call it?
thanks
Reply
How to get the export function address of a DLL by Ordinal??
Posted by Legacy on 01/14/2003 12:00amOriginally posted by: Eric.Wu
Normally,use a function name for get the address of a export-function in DLL Library.
ReplyBut,If you don't know the name of the function,can you get it's address by IT'S Ordinal Number???SOS??!
Loading, Please Wait ...