• Welcome to Powerbasic Museum 2020-B.
 

ProgEx13 - Implicit Dynamic Linking; C++ Dll -- PowerBASIC Host

Started by Frederick J. Harris, November 06, 2009, 03:45:00 AM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

Frederick J. Harris


/*
   ProgEx13  In this project lets create a C++ Dll and see how to access it from
   PowerBASIC code.  First follow my earlier directions on creating a C++ Dll
   Project using whatever development environment you are using.  Make sure you
   choose C++ as the Dll type and save the file with a .cpp extension. 
   Actually, so everything works correctly while you are learning this you may
   be ahead to just name the files as I have here and use the same build
   directory for both the C++ Dlls and the PowerBASIC host programs.  This Dll
   and directory should then be named ProgEx13.dll and \ProgEx13.  I named this
   cpp file dllMain.cpp and if you named the project ProgEx13 then it will
   compile to ProgEx13.dll.

   If your IDE (Dev-C++, CodeBlocks, Visual Studio, etc.) creates any wizard
   code or files in the project, you can just delete the files or code and make
   sure you save the code below as dllMain.cpp in the ProgEx13 project and it
   should compile OK.  Have any problems reply here or email me.
   
   The following short code simply declares and implements an exported Print
   function that takes a char pointer as a parameter and uses printf to write it
   to the console.  While its not terribly useful in itself, it will serve to
   learn about dlls.  What's noteworthy in it is the declare showing one good
   way to export a function from a C or C++ dll.  The extern "C" part tells the
   compiler not to mangle the exported function name as C++ compilers are wont
   to do to support function overloading.  So with extern "C" and the
   declspec(dllexport) attribute the function will be exported and visible to
   other compilers as 'Print'.  Please compile this to a Dll now.
   
   My narritive will now jump to dllHost.bas
*/

#include <stdio.h>
extern "C" __declspec(dllexport) void Print(char*); //Exported Function Declare

void Print(char* msg)
{
printf("%s\n",msg);
}


And here is the code for the PowerBASIC Console Compiler program that can load ProgEx13.dll and call the C or C++ Print exported function...


'dllHost.bas
'
'Continuing from our discussion of ProgEx13 and dllMain.cpp, you should have
'ProgEx13.dll in your ProgEx13 directory, and this program - dllHost.bas, also
'there.  One way to successfully load and use this dll is to declare the
'exported function here in this file so that this program can load it at start
'up. This is referred to as implicit dynamic linking because if a declare
'references a dll, it is assummed the program will use that function somewhere
'in the code.  This is in contrast to explicit dynamic linking where you
'explicitly call LoadLibrary() (an Api function) to load a specific dll.
'
'In the PowerBASIC Help under 'Declare' you'll find all kinds of good info on
'the Declare statement, including what to do if an external function name
'clashes with a PowerBASIC reserved word such as the case here with 'Print'.
'Recall we named our exported dll function 'Print'.  Here we can easily drop a
'vowel and just call it by 'Prnt'.  With the Alias clause we inform the loader
'that 'Print' is the exported symbol it should look for.  When the dll was
'compiled a module definition file was created (libProgEx13.def) by the
'compile system and the exports are listed.  Here is the pertinent part of
'the above file when compiled with Dev-C++ and the GNU compiler...
'
'EXPORTS
'  Print @ 1
'
'This then becomes the symbol the loader will look for when this program is
'loaded.  Next program couple we'll look at what happens when the extern "C"
'specifier is not used in a C++ compiled dll.

#Compile Exe
#Dim All
Declare Sub Prnt CDecl Lib "ProgEx13.dll" Alias "Print" (Byref szMsg As Asciiz)

Function PBMain() As Long
  Call Prnt("Hello, World!")
  Waitkey$

  PBMain=0
End Function

'Output
'=============
'Hello, World!

Todd Wasson

THANK YOU!!! THANK YOU!!! THANK YOU!!! THANK YOU!!! THANK YOU!!! THANK YOU!!!

I've been messing around trying to do something like this for a few hours now.  My PB calling program couldn't find the entry point to any of the functions.

I was trying to do this:

http://msdn.microsoft.com/en-us/library/ms235636.aspx

I figured maybe it was the fact that there was some classing and namespace stuff going on.  Still not sure really, but for me it doesn't matter at the moment.  I can call a C++ dll function now!  Thanks again!

Edwin Knoppert


Frederick J. Harris

I'm afraid you're right Edwin.  Thanks for catching that.  It should be...

Declare Sub Prnt CDecl Lib "ProgEx13.dll" Alias "Print" (Byref szMsg As Asciiz) 

I guess it doesn't matter if parameters are pushed right to left or left to right if there is only one!
     

Edwin Knoppert

>I'm afraid you're right Edwin.  Thanks for catching that.  It should be...

Hmm, maybe my karma will grow now :)
You #12, me only #2.... (sigh)

José Roca

Quote
I guess it doesn't matter if parameters are pushed right to left or left to right if there is only one!

It matters a lot!

Quoting Mr. Zale...

Quote
In a way, STDCALL and CDECL are very similar. Just different enough to eventually nuke your program.

With these two calling conventions, the parameters are expressed the same, and the parameters are in the same physical location. Good so far. The only difference is the way in which the parameters are removed from the stack when a function ends. With CDECL, the caller removes them, while with STDCALL, the called function removes them. So, if you execute a CDECL function with STDCALL conventions, nobody removes them! The stack will grow and grow and grow... until POOF... a GPF when the stack space is finally consumed.

Frederick J. Harris

Noted.  I believe I need to fix the next one too (ProgEx14).  In ProgEx15 in calling a PowerBASIC dll from C++ it looks like I remembered to specify __stdcall (SDECL) in the function pointer declaration.     

Todd Wasson

Thanks for the info on the calling convention.  Just to be sure I don't get bit later, this is to be done only on the PB side, correct?  The C++ version is ok? 

For the first time ever, after years and years of wanting to do it, I've got my force feedback steering wheel working from a PowerBasic program.  After getting fed up trying to work with DirectX in PB I finally decided to just stick the stuff in a C++ dll and see if that would work.  It does and I'm really stoked about it.  Thanks again :)

Todd Wasson

Now I just need to find out why the FFB only works if I open a message box during the DirectInput initialization. 

Peter Weis

Hello Todd Wasson
FFB is what?
Windows usually has only STDCALL and C only moderately CDECL the default order is the same the only difference in CDECL the caller cleans up the stack

regards Peter


Frederick J. Harris

Quote
Thanks for the info on the calling convention.  Just to be sure I don't get bit later, this is to be done only on the PB side, correct?  The C++ version is ok?

I'm at home right now getting ready to head for work, but as soon as I get there I'll double check everything again.  I believe I caught all the missing CDecls though.  Basically, unless specified otherwise, C and C++ use CDECL as the standard, and PowerBASIC uses SDECL.  Therefore, if you are calling a C function from PB you'll need to specify CDECL, and if you are calling a PowerBASIC function from C, you need to specify SDECL.  Many Windows interfaces use __stdcall though (SDECL), even though the C calling convention is used elsewhere in the C standard libraries.  For example, this...

LRESULT CALLBACK is really a macro substitution for this...

long __stdcall

and this...

int WINAPI WinMain(......

is really this...

int __stdcall. 

In x86 you can freely substitute these (not so in x64 though, because there the calling conventions are different). 

Frederick J. Harris

It looks OK to me Todd.  However, I haven't fixed the attachments yet.  I need to do that.  Its wrong in them yet.