Author Topic: dynamically calling DLLs  (Read 17206 times)

Steve A.

  • Guest
Re: dynamically calling DLLs
« Reply #30 on: October 11, 2010, 06:56:00 AM »
Sorry Steve I know nothing of interpreters and am still a novice c coder. It just popped into my mind that va_args might be useful when not knowing ahead of time the number of parameters.

Not a problem James.
I had seen something last week, while google-ing this subject, that also mentioned va_args.
I'm open for any and all suggestions, in case I miss something obvious (or hidden).
There's not a lot written, on the web, about this subject.

So far, John, AIR and Peter (Pjot) have given me more information than all last weeks google searches.

Steve

Steve A.

  • Guest
Re: dynamically calling DLLs
« Reply #31 on: October 11, 2010, 07:03:54 AM »
So then I guess you have to stick with those external libraries I mentioned. For GTK-server I have created a Windows version of FFI using MinGW, you can get it from here.

Thanks for the link Peter.
I downloaded it and I'll have a look.

And, thanks a lot for those other 4 links.
Wow, those never came up in any of my searches last week.
From what I read, all 4 seem to be right on target, as well.

Steve

Pjot

  • Guest
Re: dynamically calling DLLs
« Reply #32 on: October 11, 2010, 10:53:38 PM »
Quote
From what I read, all 4 seem to be right on target, as well.

OK. In GTK-server I have used all four of them. The source is a little bit messy, but in there you can see how to apply the different API's, if you need an example. From my experience FFI is the most versatile library, and it is almost always available on Linux/Unix and BSD platforms (including MacOSX).

BR,
Peter

Steve A.

  • Guest
Re: dynamically calling DLLs
« Reply #33 on: October 21, 2010, 10:26:19 AM »
Okay, it's time to post some results, here.
First, I want to thank everyone who participated in this discussion, because for me this was a real learning experience.
I took to heart and mind every bit of input everyone had to contribute.
I knew what I wanted to do, I just didn't know how to go about doing it, successfully.
As I had illustrated previously, I did have a working model that just didn't fit most situations and it was mostly a hack anyway.

I downloaded the sources from the links provided by John, AIR and Peter.
I went through the documentation provided and studied the source code for each.
In all cases, a portion of the work of dynamically calling DLL functions at run-time is done using inline assembly language.
That is an important point to make note of.

The reason for that, is that at run-time, when calling a DLL function "on the fly", you have to bypass the regular system call and directly manipulate the STACK.
By that I mean you have to manually "PUSH" the parameters, that the function will be expecting, onto the stack before you call the DLL function.

Now with that said, the DLL tool I found to my likeing was Dyncall.
Not that there was anything wrong with the others, Dyncall just seemed more "turn-key", if you get my meaning.
Now, the documentation (if they had any) for all of these tools was among the worst.
I'm big on "good" documentation.
After exchanging a dozen or so emails with the developers of Dyncall, I finally got it working.
Unfortunately, their lengthy documentation doesn't actually show you how to use it.
It spends more time on describing all the systems they have tested it on and technical info about the build process.

Anyway...,
I have an example here that illustrates, using C source code, just how simple Dyncall makes the process of calling DLL functions:

Code: [Select]
/* This routine dynamically calls MessageBox */
/* (or any API function) at run-time.        */

/* LINK with these LIBs: libdyncall_s.lib, libdynload_s.lib */


#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <dyncall.h>
#include <dynload.h>


  char dllname[32];
  char functionname[32];


int main()
{   int xhwnd = 0, xtype = 1, retVal;
    char text[20] = {"hello world"};
    char capt[10] = {"hello"};
    void* funcptr;
    void* libhandle;

            /* name of DLL to open */
    strcpy(dllname, "user32.dll");

            /* name of function to call */
    strcpy(functionname, "MessageBoxA");

            /* open DLL */
    libhandle = dlLoadLibrary(dllname);
            /* here: check for NULL return: failure */

            /* locate function */
    funcptr = dlFindSymbol(libhandle,functionname);
            /* here: check for NULL return: failure */

            /* create a callvm (to be shared within your app) */
    DCCallVM * pvm = dcNewCallVM(4096);

            /* configure the callvm for call conv..*/
    dcMode(pvm, DC_CALL_C_X86_WIN32_STD);

            /* reset the argument buffer */
    dcReset(pvm);

            /* now load arguments (from left to right order) */
    dcArgInt(pvm, xhwnd);
    dcArgPointer(pvm, text);
    dcArgPointer(pvm, capt);
    dcArgInt(pvm, xtype);

            /* now call by specifying return type */
    retVal = dcCallInt(pvm,funcptr);

            /* free library handle */
    dlFreeLibrary(libhandle);

            /* free VM */
    dcFree(pvm);

    system("pause");

    return 0;
}
/*--------- end main ----------*/

I have attached a Windows executable, called shelldcall.zip for the above code.

Now, that's not the end of it.
I have done a considerable amount of experimentation on my own, as well.
I first experimented using Masm, doing the whole thing in straight assembly language and it worked quite well.
And, much to my surprise, was quite simple to accomplish.

But, since Bxbasic is written in C, using the LccWin32 compiler, I wanted to be able to do this using "inline" assembly language.
For those not familiar with the concept, "inline assembly" is assembly language embeded into the C source code and compiled with the main program.
To illustrate this, I have as an example, the exact same program as the above, written in C, using "inline assembly":

Code: [Select]
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>


   void * (__stdcall *stdcall_func)();

    char dllname[20]="user32.dll";
    char functionname[20]="MessageBoxA";

    HWND hwnd=0;
    UINT utype=1;
    char text[] = "Hello Windows!";
    char strng[] = "Winasm";
    HMODULE hLib=0;


int main()
{
        _asm("pushl  $_dllname");
        _asm("call   %LoadLibraryA");
        _asm("movl   %eax, _hLib");

        _asm("pushl  $_functionname");
        _asm("pushl  %hLib");
        _asm("call   %GetProcAddress");
        _asm("movl   %eax, _stdcall_func");

        _asm("pushl  _utype");
        _asm("pushl  $_strng");
        _asm("pushl  $_text");
        _asm("pushl  _hwnd");
        _asm("call   *_stdcall_func");


    FreeLibrary(hLib);

    system("pause");
    return 0;
}
/*--------- end main ----------*/

Sweet !

I have attached a Windows executable, called shellacall.zip for the above code.


I presently have Bxbasic setup for Dyncall and have been testing it and it works quite well.
Shortly, I will fully develop my own inline version and will replace Dyncall with it.

If anyone has any questions, I will be only too happy to answer them.
I couldn't have done this without your help.

Thanks
Steve

JRS

  • Guest
Re: dynamically calling DLLs
« Reply #34 on: October 21, 2010, 10:44:23 AM »
That's really good news Steve that you got this working to your satisfaction.

I think this will help others as well. (QB64, ...)


Pjot

  • Guest
Re: dynamically calling DLLs
« Reply #35 on: October 21, 2010, 11:33:17 AM »
Quote
Shortly, I will fully develop my own inline version and will replace Dyncall with it.

That's really brave! The inline ASM code look like a complete nightmare to me...  :-\

But good news that it works, and presumably it also will for Unix-based platforms, as Dyncall is a multiplatform library!

Regards
Peter

Steve A.

  • Guest
Re: dynamically calling DLLs
« Reply #36 on: October 21, 2010, 02:23:38 PM »
That's really brave! The inline ASM code look like a complete nightmare to me...  :-\

But good news that it works, and presumably it also will for Unix-based platforms, as Dyncall is a multiplatform library!

Yes, multiplatform is an important consideration in what I do to Bxbasic these days.
As to the ASM code (nightmare)..., yes and no.

I have to admit when I asked Jacob Navia (developer of LccWin32) for assistance in explaining Lcc's assembly code, he tried to discourage me from using it. Not that there is any problem in using inline asm code, it's just that Lcc's inline code is un-documented. Nowhere in the LccWin32 docs does it explain it's usage. Fortunately, John Findlay, (practically famous for his Lcc tutorials) stepped up and explained what I needed to know about Lcc inline.

I'm quite familiar with assembly language. You could say it's my "first language", when it comes to programming languages.
Additionally, Lcc's inline assembly is based on what is called "the AT&T syntax".
Even though the code is being run on an x86 CPU, it's not Intel style code. It's a style of syntax originally developed at AT&T Bell Labs for Unix machines. Hence, most Linux compilers also use AT&T syntax, (multiplatform).