Author Topic: How to wrap a DLL  (Read 12057 times)

Offline Kitty Hello

  • code monkey
  • Administrator
  • Prof. Inline
  • *******
  • Posts: 10649
  • here on my island the sea says 'hello'
    • View Profile
    • http://www.glbasic.com
How to wrap a DLL
« on: 2008-Jul-24 »
keywords:
DLL, wrapper, INLINE, DECLARE

Hi, and wellcome to something veeery sophisticated, which will turn out to be easy as sending an email, later: Wrapping an external DLL library to be used with GLBasic.

First, we must descide which DLL we might use. As I don't have any other one here, I suggest we take a look at some built in Windows(R)(tm) dlls, and in order to see something quickly we wrap the MessageBoxA function.

OK, first we need to know a few things:
- which dll contains that function
For our case, that's easy. Just look here:
http://msdn.microsoft.com/en-us/library/ms645505.aspx
Which says:
Quote
MessageBox Function

Displays a modal dialog box that contains a system icon, a set of buttons, and a brief application-specific message, such as status or error information. The message box returns an integer value that indicates which button the user clicked.

Syntax

    int MessageBox(     
        HWND hWnd,
        LPCTSTR lpText,
        LPCTSTR lpCaption,
        UINT uType
    );
...
Function Information
    Minimum DLL Version:   user32.dll
    Header:   Declared in Winuser.h, include Windows.h
    Import library:   User32.lib
    Minimum operating systems:   Windows 95, Windows NT 3.1
    Unicode:   Implemented as ANSI and Unicode versions.
- what are the parameters and the return value
We have a set of parameters here like:
HWND , LPCTSTR and UINT.
You can google for them (search for "typedef HWND"), and will find that all parameters can be limited to a few basic types:
strings: char* or const char*. "const char*" means a string, that's contents will not be changed inside the dll function.
integers: 8 bit(char), 16 bit(short), 32 bit(long or int - it's the same), [optional: unsigned]
floating points: 32bit (float), 64 bit(double)
pointer to something: mostly (void*)
We will learn more about pointers, later. No worries, just easy basics.

So we can reduce this parameter list to the basic types:
void*, const char*, const char*, unsigned int
and the return value is an "int".

Aces.

- The function name in the DLL
Did you notice
Quote
Unicode   Implemented as ANSI and Unicode versions.
? Microsoft names their functions with "A" at the end for ANSI and "W" for Unicode. We need the ansi version. Sometimes functions can look totally different, though. So in order to be safe, we download the DependencyWalker from here:
http://www.dependencywalker.com/
and open the "user32.dll" from the system32 directory.


At the left side, they cyan box informs us, that this function has a "C" linking (as oposed to C++ linking)., thus we should wrap our "DECLARE" statement in an
Code: GLBasic [Select]
extern "C"{ DELCARE... }
later.

It's not obvious if the calling convention is "cdecl" or "stdcall" from the dependency walker. I _know_ that all Microsoft DLLs are stdcall, and if you can load a DLL from VisualBasic, it _must_ be stdcall, so try this first. If it crashes, try with a cdecl convention later.

So, fine. We now have everything we need to get started!

Loading that function
Open your GLBasic project, and insert a new source file in the file tabs. Make sure you use a separate file for each dll you wrap, to make things a bit more modular.
Now type this code in the new, 2nd file of your project:
Code: GLBasic [Select]
// A MessageBox wrapper
INLINE
extern "C" { // it's a C linking, as dependency walker told us
DECLARE_ALIAS(exportMessageBoxA, "user32.dll", "MessageBoxA", (void*, const char*, const char*, unsigned int), int);
} // close the extern "C"
ENDINLINE
 
The DECLARE_ALIAS statement (notice to write it all capital letters) takes the name of the function to be used in the GLBasic context, then the dll name, then the name of the function in the dll. This can be something complicated like "_kungfu@12" for other calls - just take the name from the dependency walker.

The next argument is the parameter list in braces!. Last parameter is the return value.
For beginners, please use DELCARE_ALIAS instead of DECLARE.

Wrapping it
Now that we loaded the function, we should urge to call it. Therefore, we need a GLBasic function to conveniently call it from our program. Thus we make the wrapper function:

Code: GLBasic [Select]
FUNCTION Win32MessageBox: text$, caption$

ENDFUNCTION
 
Make sure, that the function name does not equal the first parameter of the DECLARE instruction, or you will have quite some fun with the compiler.

Now we must fill that function with our wrapped dll.
Code: GLBasic [Select]
INLINE // of course, GLBAsic does not know about "exportMessageBoxA" - only INLINE does
   if(exportMessageBoxA)
      return exportMessageBoxA(0, text_Str.c_str(), caption_Str.c_str(), 0);
ENDINLINE
 
Whoa! Waitaminute!
This is a lot of new stuff for you, so we see ein detail:
Code: GLBasic [Select]
INLINE
well, GLBasic does not know about "exportMessageBoxA", but the INLINE C++ part does, so we must go there.
Code: GLBasic [Select]
if(exportMessageBoxA)
exportMessageBoxA is a pointer to a function that you created with the DECLARE statement above. GLBasic does the implementation and loading automatically for you. However, if you have a typo, the function cannot be loaded. Then the pointer has the value "0", which means, if you call the function this pointer is pointing at your program will sort of "inform" you about it. :noggin:

So, to be sure that the function was loaded correctly, we check if it's a valid pointer and then call the function with
Code: GLBasic [Select]
exportMessageBoxA(
Last step is to provide the arguments.

Code: GLBasic [Select]
text_Str.c_str()
OK, this is a bit hard to explain now. The GLBasic string "text$" is in INLINE called "text_Str". The $ gets "_Str". If you have a number, there's no such problem. Next, the GLBasic string is of type "DGStr", which is a class needed to have you write things like:
a$= "test" + 5 + "foo:" +foo().
Very convenient for you, but in order to get a const char* to the first character, you need to call the "c_str()" function of that class. Just do it, K?
Another thing is, that if you need the char* to this string, you should do:
Code: GLBasic [Select]
my_Str.Alloc(1024); // make sure that string has space for 1024 bytes
GetTempPathA(my_Str.GetStrData() );
my_Str.CalcLen(); // have GLBasic find the '\0' character and internally set the string length -> if you manipulate with GetStrData, this must be called!
 

The MessageBoxA function takes a HWND as the first parameter, which is a pointer to the parent window of that message box. You can use 0 here to say the desktop is the parent. If you want to refer to the GLBasic window, the HWND pointer is gained by calling:
Code: GLBasic [Select]
void* hwnd = GLBASIC_HWND();
  exportMessageBoxA(hwnd, "test", "test", 0);
 

The last parameter is the MessageBox appearance. You need a constant integer for this, which you can get my googling for: "#define MB_OK", which turns out to be 0.
Make sure you end the call with a ';', but don't do a ';' after the "if" statement.

So, now we can call that wrapper from our program:
Code: GLBasic [Select]
Win32MessageBox("Test\nText", "Yippieh!")
 

I think that should be enough to get you started. When you experience trouble, the forum's full of people willing to help you out.

If someone might translate this into the German forum, I'd be very thankful.







« Last Edit: 2008-Aug-01 by Kitty Hello »

Offline bigsofty

  • Community Developer
  • Prof. Inline
  • ******
  • Posts: 2457
    • View Profile
Re: How to wrap a DLL
« Reply #1 on: 2008-Aug-01 »
Excellent little tutorial... as a non C++ programmer I find this area the most confusing.

Many thanks,


Ian
Cheers,

Ian.

“It is practically impossible to teach good programming style to students that have had prior exposure to BASIC.  As potential programmers, they are mentally mutilated beyond hope of regeneration.”
(E. W. Dijkstra)

Offline Kitty Hello

  • code monkey
  • Administrator
  • Prof. Inline
  • *******
  • Posts: 10649
  • here on my island the sea says 'hello'
    • View Profile
    • http://www.glbasic.com
Re: How to wrap a DLL
« Reply #2 on: 2008-Oct-14 »
DLL search order:
-program directory
-system directory
-Windows directory
-current directory (SETCURRENTDIR)
-PATH environment variable (left to right)


Offline Leos

  • Mc. Print
  • *
  • Posts: 22
  • Wrecking Everything
    • View Profile
Re: How to wrap a DLL
« Reply #3 on: 2010-Sep-30 »
Sorry, but I couldn't make it work :/ could you give me a code example for this DLL for instance?
(I'm trying to follow this tutorial but still doesn't work, I'm sure I'm missing something...)
Code: GLBasic [Select]
;//its Purebasic code

ProcedureDLL MyTest(arg$)
   MessageRequester("MyTitle", arg$)  
EndProcedure
 

I'ts not decorated inside the dll, I think... it's just "MyTest"
Leo.

GLBasic fan!

MrTAToad

  • Guest
Re: How to wrap a DLL
« Reply #4 on: 2010-Oct-07 »
If I remember correctly, with PureBasic you use ProcedureCDLL

Offline Leos

  • Mc. Print
  • *
  • Posts: 22
  • Wrecking Everything
    • View Profile
Re: How to wrap a DLL
« Reply #5 on: 2010-Oct-07 »
It's working, thank you!
Leo.

GLBasic fan!

Offline Gary

  • Dr. Type
  • ****
  • Posts: 313
    • View Profile
Re: How to wrap a DLL
« Reply #6 on: 2015-Jul-27 »
Could someone tell me where I am going wrong in this code? I thought I had followed all the example properly

Code: GLBasic [Select]
// --------------------------------- //
// Project: test
// Start: Tuesday, May 26, 2015
// IDE Version: 12.308


// SETCURRENTDIR("Media") // go to media files

INLINE
// A MessageBox wrapper

extern "C" { // it's a C linking, as dependency walker told us
DECLARE_ALIAS(exportMessageBoxA, "user32.dll", "MessageBoxA", (void*, const char*, const char*, unsigned int), int);
} // close the extern "C"

ENDINLINE

main:


FUNCTION Win32MessageBox: text$, caption$
INLINE // of course, GLBAsic does not know about "exportMessageBoxA" - only INLINE does
   if(exportMessageBoxA)
      return exportMessageBoxA(0, text_Str.c_str(), caption_Str.c_str(), 0);
ENDINLINE
ENDFUNCTION

FUNCTION main:
Win32MessageBox("Test\nText","Hello")
ENDFUNCTION

but it compiles with these errors

C:\Users\Gary\AppData\Local\Temp\glbasic\gpc_temp0.cpp: In function `int __GLBASIC__::__MainGameSub_()':
C:\Users\Gary\AppData\Local\Temp\glbasic\gpc_temp0.cpp:65: error: expected unqualified-id before string constant
C:\Users\Gary\AppData\Local\Temp\glbasic\gpc_temp0.cpp: In function `DGInt __GLBASIC__::Win32MessageBox(__GLBASIC__::DGStr, __GLBASIC__::DGStr)':
C:\Users\Gary\AppData\Local\Temp\glbasic\gpc_temp0.cpp:175: error: `exportMessageBoxA' was not declared in this scope
*** FATAL ERROR - Please post this output in the forum

Offline kanonet

  • Administrator
  • Prof. Inline
  • *******
  • Posts: 1142
    • View Profile
    • My GLBasic code archiv
Re: How to wrap a DLL
« Reply #7 on: 2015-Jul-27 »
You either need to place this into a 2ndary gbas file, or you need to close GLBasic's main function. To do so, add a dummy GLBasic function before your INLINE code:
Code: GLBasic [Select]
FUNCTION Foo:
ENDFUNCTION

INLINE
// your stuff below here
This rule applies to any INLINE code. Now try again. ;)
Lenovo Thinkpad T430u: Intel i5-3317U, 8GB DDR3, NVidia GeForce 620M, Micron RealSSD C400 @Win7 x64

MrTAToad

  • Guest
Re: How to wrap a DLL
« Reply #8 on: 2015-Jul-27 »
The code needs to be in a source file and not the main program.  The main function however, should be in the main program.

Offline Gary

  • Dr. Type
  • ****
  • Posts: 313
    • View Profile
Re: How to wrap a DLL
« Reply #9 on: 2015-Jul-28 »
cheers guys, thats got the example code running.

Im trying to wrap a 3rd party dll and have a few functions to add

the first one has no parameters passed to it but returns an int. I thought the code would be as follows

Code: GLBasic [Select]
INLINE


extern "C" { // it's a C linking, as dependency walker told us
DECLARE_ALIAS(BPConnect, "GameSocket.dll", "BP_Connect", (void*), int);
} // close the extern "C"
ENDINLINE

FUNCTION Connect:
INLINE
if(BPConnect)
return BPConnect();
ENDINLINE
ENDFUNCTION

but this fails with the following error
compiling:
CMD: >>"C:\Program Files\GLBasic_v11\Compiler\platform\Win32\Bin\g++.exe" -B"C:\Program Files\GLBasic_v11\Compiler\platform\Win32\Bin" -pipe -O3 -w -c -x c++ -mwindows -I"C:\Program Files\GLBasic_v11\Compiler\platform\Include" -I"C:\Documents and Settings\Owner\Desktop\GL_DLL" -D_WINDOWS_ -DNDEBUG -DHAVE_OPENGL "C:\DOCUME~1\Owner\LOCALS~1\Temp\glbasic\gpc_tempg.cpp" "C:\DOCUME~1\Owner\LOCALS~1\Temp\glbasic\gpc_temp0.cpp" "C:\DOCUME~1\Owner\LOCALS~1\Temp\glbasic\gpc_temp1.cpp"    -DWIN32=1<<
C:\DOCUME~1\Owner\LOCALS~1\Temp\glbasic\gpc_temp1.cpp: In function `DGInt __GLBASIC__::Connect()':
C:\DOCUME~1\Owner\LOCALS~1\Temp\glbasic\gpc_temp1.cpp:42: error: too few arguments to function
*** FATAL ERROR - Please post this output in the forum

Any more tips?

I was given the following C code to connect the main functions

bool UnlimitedConnect(void)
{
   bool RetVal;
   bool connected;
   connected = false;
   DllModule = LoadLibrary( TEXT( "GameSocketDll.dll" ) );
   if (DllModule)
   {
       // Find all of the addresses
BP_StartDLL =(bp_bool_hwnd) GetProcAddress( DllModule, "BP_StartDLL" );
BP_EndDLL =   (bp_bool_void) GetProcAddress( DllModule, "BP_EndDLL" );
BP_Connect =   (bp_bool_void) GetProcAddress( DllModule, "BP_Connect" );
BP_Disconnect =   (bp_bool_void) GetProcAddress( DllModule, "BP_Disconnect" );
BP_EndGame =  (bp_bool_void) GetProcAddress( DllModule, "BP_EndGame" );
BP_UseCredit =   (bp_int_int) GetProcAddress( DllModule, "BP_UseCredit" );
BP_GetCredit =   (bp_int_bool) GetProcAddress( DllModule, "BP_GetCredit" );
BP_AddBank =   (bp_int_int) GetProcAddress( DllModule, "BP_AddBank" );
BP_GetBank =   (bp_int_bool) GetProcAddress( DllModule, "BP_GetBank" );
BP_PayoutAtLimit =   (bp_void_void) GetProcAddress( DllModule, "BP_PayoutAtLimit" );
BP_Payout =    (bp_bool_int) GetProcAddress( DllModule, "BP_Payout" );
BP_IsButtonDown =   (bp_bool_int) GetProcAddress( DllModule, "BP_IsButtonDown" );
BP_SetLamp =   (bp_void_int_bool) GetProcAddress( DllModule, "BP_SetLamp" );

       if (BP_StartDLL && BP_Connect)
       {
          RetVal = (*BP_StartDLL)(hWndThread);
          if (RetVal)
          {
             RetVal = (*BP_Connect)();
             if (RetVal)
             {
                 connected = true;
}
}
}
}
   return (connected);
}

MrTAToad

  • Guest
Re: How to wrap a DLL
« Reply #10 on: 2015-Jul-28 »
Try using just (void) and not (void *) - as the latter means it expects a value, it just doesn't care what it is...

Offline Gary

  • Dr. Type
  • ****
  • Posts: 313
    • View Profile
Re: How to wrap a DLL
« Reply #11 on: 2015-Jul-28 »
that seems to have worked, strange thing is that it does not seem to be loading the dll file though, getting a DECLARE module not found : game socket.dll error in debug. Ive put the dll in the system 32 folder, program folder, and media folder and still the same error. Very strange

MrTAToad

  • Guest
Re: How to wrap a DLL
« Reply #12 on: 2015-Jul-28 »
Is it a 64bit DLL and your OS is 32bit?
It may also require other DLLs too.


Offline Gary

  • Dr. Type
  • ****
  • Posts: 313
    • View Profile
Re: How to wrap a DLL
« Reply #13 on: 2015-Jul-28 »
Just tried it on a 32 bit and 64 bit system and same result on both. I was told it does not need another DLL but dependancy walker says otherwise, think I have it running now, cheers
« Last Edit: 2015-Jul-28 by Gary »

MrTAToad

  • Guest
Re: How to wrap a DLL
« Reply #14 on: 2015-Jul-29 »
Good to hear its working  :happy: