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!