GLBasic forum

Main forum => FAQ => Topic started by: Kitty Hello on 2008-Jul-24

Title: How to wrap a DLL
Post by: Kitty Hello 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 (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
QuoteUnicode   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/ (http://www.dependencywalker.com/)
and open the "user32.dll" from the system32 directory.
(http://img77.imageshack.us/img77/1864/snippingtool1rp2.th.png) (http://img77.imageshack.us/my.php?image=snippingtool1rp2.png)

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" (http://www.google.com/search?q=%23define+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.







Title: Re: How to wrap a DLL
Post by: bigsofty on 2008-Aug-01
Excellent little tutorial... as a non C++ programmer I find this area the most confusing.

Many thanks,


Ian
Title: Re: How to wrap a DLL
Post by: Kitty Hello on 2008-Oct-14
DLL search order:
-program directory
-system directory
-Windows directory
-current directory (SETCURRENTDIR)
-PATH environment variable (left to right)

Title: Re: How to wrap a DLL
Post by: Leos 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"
Title: Re: How to wrap a DLL
Post by: MrTAToad on 2010-Oct-07
If I remember correctly, with PureBasic you use ProcedureCDLL
Title: Re: How to wrap a DLL
Post by: Leos on 2010-Oct-07
It's working, thank you!
Title: Re: How to wrap a DLL
Post by: Gary 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
Title: Re: How to wrap a DLL
Post by: kanonet 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. ;)
Title: Re: How to wrap a DLL
Post by: MrTAToad 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.
Title: Re: How to wrap a DLL
Post by: Gary 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);
}
Title: Re: How to wrap a DLL
Post by: MrTAToad 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...
Title: Re: How to wrap a DLL
Post by: Gary 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
Title: Re: How to wrap a DLL
Post by: MrTAToad on 2015-Jul-28
Is it a 64bit DLL and your OS is 32bit?
It may also require other DLLs too.

Title: Re: How to wrap a DLL
Post by: Gary 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
Title: Re: How to wrap a DLL
Post by: MrTAToad on 2015-Jul-29
Good to hear its working  :happy:
Title: Re: How to wrap a DLL
Post by: Szantai on 2015-Nov-06
Hi,

How can we receive MIDI signal from the keyboard?
I would like Piano Lesson programming.

Thanks
Title: Re: How to wrap a DLL
Post by: Abrexxes on 2015-Nov-07
Hi, you need the winmm.dll , if this is not allready installed.

Some stuff to read:

https://msdn.microsoft.com/en-us/library/windows/desktop/dd798458(v=vs.85).aspx

http://www.bass.radio42.com/help/html/3d449780-2092-9175-51d8-5404d755641a.htm

Cu
Title: Re: How to wrap a DLL
Post by: Szantai on 2015-Nov-08
Thank you!
Please write a minimal sample code in glbasic. (only midi in send to variable)

Big big THX :)