• Welcome to Jose's Read Only Forum 2023.
 

ProgEx15 - Explicit Dynamic Linking; PowerBASIC Dll - C++ Host

Started by Frederick J. Harris, November 07, 2009, 03:03:52 AM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

Frederick J. Harris

This ProgEx15 consists of a PowerBASIC created Dll and a C++ created host to load it.  First, here is the PowerBASIC Dll code.


'ProgEx15 -- PowerBASIC Dll Using PBWin 9.02 Accessed By A C++ Host Doing
'            Explicit Dynamic Linking
'
'            This dll function will write a character string to a console
'            window of its parent or host process.  It takes as input a
'            pointer to a character string.  Remember you'll need one of
'            the PowerBASIC Windows compilers for this as its a Dll.
#Compile Dll
#Dim All
#include "Win32Api.inc"

Sub Prnt(Byref szMsg As Asciiz) Export
 Local hStdOut,iLen As Dword
 hStdOut=GetStdHandle(%STD_OUTPUT_HANDLE)
 WriteConsole(hStdOut,szMsg,Len(szMsg),iLen,%NULL)
End Sub


And here is the continuing discussion and C++ host.


/*
  ProgEx15
 
  In this project we'll do explicit dynamic linking with our PowerBASIC Dll -
  DllPwrBasic.dll.
 
  I consider this a very tricky, difficult subject.  Both C/C++ and PowerBASIC
  allow procedures to be called through their address.  Keep in mind that not
  only variables but procedures too have addresses.  To call a procedure
  through its address instead of through its name one must know the full
  signature of the procedure and its return value if any.  For example, we have
  been calling Prnt in DllPwrBasic.dll, and that procedure does not return
  anything, i.e., in 'C speak' it has a void return value; it is a standard
  call procedure because we didn't specify otherwise and standard call is the
  PowerBASIC default; and it takes one character string pointer parameter, i.e,
  a char* in 'C speak'.  Therefore, the way a C function pointer to such a
  function would be declared in C/C++ is as follows if we name the function
  pointer pFn...
 
  void (__stdcall* pFn)(char*);
 
  In C/C++ C Declension is the standard function stack setup protacol, so if
  this function was C Declension ( __cdecl ) instead of __stdcall it would look
  like this..
 
  void (*pFn)(char*);
 
  You know you are looking at a function pointer declaration when you see what
  looks like a return value (or void), followed by a set of parentheses
  containing the '*' symbol followed by a label (*pFn), and afterwards followed
  by what looks like a function parameter list.  Its really the middle set of
  parentheses containing the '*' symbol that's the dead givaway.  The set of
  parentheses are really important too because if they were removed the meaning
  of the entire entity would be completely changed.  If we remove the
  parentheses we would have this...
 
  void *pFn(char*);
 
  ...which actually associates like this to the compiler...
 
  void* pFn(char*);
 
  ...and that is a function (not a function pointer) named pFn that returns a
  void*, i.e., void pointer - which can point to any quantity with an address
  and takes a char* as its single parameter.
 
  You might be wondering at this point what the utility of such a strange
  beaste as a function pointer might be!  Believe it or not, there are some
  really cool things in advanced programming that become possible with them.
  For example, my standard setup for mapping windows messages to the message
  handling procedure that will handle a message is an array of function
  pointers.  In my COM tutorials I used arrays of function pointers to help
  elucidate the memory structure of COM objects.  The list goes on and on.
 
  In the program below we declare the function pointer pFn.  GetProcAddress()
  returns an address, but the C or C++ compiler won't under any circumstances
  allow this address to be assigned to pFn unless we 'cast' it to a __stdcall
  function pointer that returns void, i.e., nothing, and takes a single char*
  as a parameter.  This whole term then...
 
  void (__stdcall*)(char*)
 
  within the containing parentheses (...) in front of GetProcAddress()
  represents a rather complicated 'cast'.
 
  In any case, when you run the program - if DllPwrBasic is in your path, the
  Prnt Sub will be called through its address and the "Hello, World!" message
  printed.  In the next example we'll show another and more common way of doing
  this with C's typedef statement.  
*/
#include <windows.h>
#include <stdio.h>

int main(void)
{
HINSTANCE hInstance;  
void (__stdcall* pFn)(char*);             //Declaration of function pointer
                                          //pFn that can be used to call any
hInstance=LoadLibrary("DllPwrBasic.dll"); //function for which you can obtain
if(hInstance)                             //its address if the function returns
{                                         //void and takes a single char* as a
   pFn=(void (__stdcall*)(char*))GetProcAddress(hInstance,"PRNT");//parameter
   if(pFn)                          //In other words, assign to pFn the address
      pFn("Hello, World!\n");       //of "PRNT" in DllPwrBasic.dll cast to a
   else                             //standard call function that returns void,
      puts("Couldn't Find pFn!");   //i.e., nothing, and takes a char* as its
   FreeLibrary(hInstance);          //sole parameter.
}
else
   puts("Couldn't Load Library!");
getchar();
 
return 0;  
}


I'm having fun!