Main forum > FAQ

How to wrap a DLL

(1/4) > >>

Kitty Hello:
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.

--- End quote ---
- 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.
--- End quote ---
? 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) ---extern "C"{ DELCARE... }
--- End code ---
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) ---// 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

--- End code ---
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) ---FUNCTION Win32MessageBox: text$, caption$

ENDFUNCTION

--- End code ---
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) ---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

--- End code ---
Whoa! Waitaminute!
This is a lot of new stuff for you, so we see ein detail:

--- Code: (glbasic) ---INLINE
--- End code ---
well, GLBasic does not know about "exportMessageBoxA", but the INLINE C++ part does, so we must go there.

--- Code: (glbasic) ---if(exportMessageBoxA)
--- End code ---
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) ---exportMessageBoxA(
--- End code ---
Last step is to provide the arguments.


--- Code: (glbasic) ---text_Str.c_str()
--- End code ---
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) ---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!

--- End code ---

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) ---void* hwnd = GLBASIC_HWND();
  exportMessageBoxA(hwnd, "test", "test", 0);

--- End code ---

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) ---Win32MessageBox("Test\nText", "Yippieh!")

--- End code ---

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.







bigsofty:
Excellent little tutorial... as a non C++ programmer I find this area the most confusing.

Many thanks,


Ian

Kitty Hello:
DLL search order:
-program directory
-system directory
-Windows directory
-current directory (SETCURRENTDIR)
-PATH environment variable (left to right)

Leos:
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) ---;//its Purebasic code

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

--- End code ---

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

MrTAToad:
If I remember correctly, with PureBasic you use ProcedureCDLL

Navigation

[0] Message Index

[#] Next page

Go to full version