Deprecated: Array and string offset access syntax with curly braces is deprecated in /homepages/21/d38531796/htdocs/jose/smfforum/Sources/Subs.php on line 3825
c++ client PowerBASIC Server

Author Topic: c++ client PowerBASIC Server  (Read 27790 times)

0 Members and 1 Guest are viewing this topic.

Offline James C. Fuller

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 597
  • User-Rate: +11/-8
c++ client PowerBASIC Server
« on: January 26, 2009, 12:43:17 AM »
Fred,
  I decided to move to discussion as it really has no bearing on your COM Memory exploration.
I'm just experimenting and have a very poor knowledge of c++ so I thought I'd put this code here for you to critique.
I too have been reading (and reading and reading and....)Inside COM. I thought your examples looked a bit familiar. ;D
I can't believe all the c++ code needed to create a server!
I did get the client to compile using Code::Blocks and gcc 3.4.5. I had to add liboleaut32.a and libuuid.a to the Build Options
I read that gcc's COM support is not that hot??
I much prefer RadASM with BCC++ 5.5.
Here's another example of a c++ client PowerBASIC server. Note the use of my PROPERTY Macros.

James

Edit: Had to change a couple UNIT to DWORD so gcc would compile.

c++ Client
Code: [Select]
#include <windows.h>
#include <oleauto.h>     
#include <stdio.h>

static const CLSID CLSID_cDisplayOpenFileGuid = {0x1b0f486f, 0x9cca, 0x47ba,{ 0xa3, 0x38, 0x8f, 0x71, 0x46, 0xb7, 0x3c, 0x26}};
static const IID IID_iDisplayOpenFileGuid = {0xb72cdd37, 0x5349, 0x46b7,{ 0x8a, 0x27, 0x59, 0x2d, 0x23, 0xb3, 0x4f, 0xe3}};

HRESULT (__stdcall* ptrDllGetClassObject) (const CLSID&, const IID&, void**);

interface iDisplayOpenFile : IUnknown
{
virtual void __stdcall put_Folder(BSTR);
virtual void __stdcall put_Filter(BSTR);
virtual void __stdcall put_hParent(DWORD);
virtual void __stdcall put_DflExt(BSTR);
virtual void __stdcall put_Start(BSTR);
virtual void __stdcall put_Title(BSTR);
virtual void __stdcall put_Flags(DWORD);
virtual BSTR __stdcall GetName();
};

int main(void)
{
  iDisplayOpenFile* pDof=NULL;
  HRESULT hr;
  IClassFactory*  pCF=NULL;
  HMODULE         hDll=NULL;
  BSTR str1;
// change here for a starting Folder 
  char stemp[]="D:\\Borland\\BCC55\\Include\\Sys";
 
//Change Path Here

  hDll=LoadLibrary("D:\\ComTutorial1A\\Server\\PbSource\\PBCTEST03.DLL");
 
  ptrDllGetClassObject=(HRESULT (__stdcall*)(REFCLSID, REFIID, void**))GetProcAddress(hDll,"DllGetClassObject");

  hr=ptrDllGetClassObject(CLSID_cDisplayOpenFileGuid,IID_IClassFactory,(void**)&pCF);
  if(SUCCEEDED(hr))
  {

    pCF->CreateInstance(NULL,IID_iDisplayOpenFileGuid,(void**)&pDof);
    // Release Class Factory
    pCF->Release();
    // Create a BSTR using char stemp
    str1 = SysAllocStringByteLen(stemp,strlen(stemp));
    // Set the starting Folder
    pDof->put_Folder(str1);
    // Free up the string
    SysFreeString(str1);
    //Show OpenFile Dialog and retrieve Selected Files
    str1 = pDof->GetName();
    printf("str1 = %s\n",str1);
    // Release pDof
    hr=pDof->Release();
  }
FreeLibrary(hDll);
 
  getchar();
  return 0;

}   



PowerBASIC Server
Code: [Select]
'SED_PBWIN
#COMPILE DLL "PBCTEST03.DLL"
#DIM ALL
#COM TLIB ON
#INCLUDE ONCE "WIN32API.INC"
MACRO PropGet(PropName,PropType)=PROPERTY GET PropName() AS PropType:PROPERTY=PropName:END PROPERTY
MACRO PropSet(PropName,PropType)=PROPERTY SET PropName(BYVAL param AS PropType):PropName=param:END PROPERTY


$cDisplayOpenFileGuid = GUID$("{1B0F486F-9CCA-47ba-A338-8F7146B73C26}")
$iDisplayOpenFileGuid = GUID$("{B72CDD37-5349-46b7-8A27-592D23B34FE3}")
'GUID's create with GUIDGEN
'// {1B0F486F-9CCA-47ba-A338-8F7146B73C26}
'DEFINE_GUID(<<name>>,
'0x1b0f486f, 0x9cca, 0x47ba, 0xa3, 0x38, 0x8f, 0x71, 0x46, 0xb7, 0x3c, 0x26);
'// {B72CDD37-5349-46b7-8A27-592D23B34FE3}
'DEFINE_GUID(<<name>>,
'0xb72cdd37, 0x5349, 0x46b7, 0x8a, 0x27, 0x59, 0x2d, 0x23, 0xb3, 0x4f, 0xe3);

CLASS cDisplayOpenFile $cDisplayOpenFileGuid AS COM
    INSTANCE sFileName,Folder,Filter,Start,DflExt,Title AS STRING
    INSTANCE hParent,Flags AS LONG

    CLASS METHOD CREATE
        Filter = CHR$("All Files",0,"*.*",0)
        Flags = %OFN_EXPLORER OR %OFN_FILEMUSTEXIST
        Title = "Select File"
        Start = ""
        Folder = CURDIR$
STDOUT "CREATE"
    END METHOD

    INTERFACE iDisplayOpenFile $iDisplayOpenFileGuid : INHERIT IUNKNOWN

PropSet(Folder,STRING)
        PropSet(Filter,STRING)
        PropSet(hParent,DWORD)
        PropSet(DflExt,STRING)
        PropSet(Start,STRING)
        PropSet(Title,STRING)
        PropSet(Flags,DWORD)
        METHOD GetName() AS STRING
            DISPLAY  OPENFILE hParent,,,Title,Folder,Filter,Start,DflExt,Flags TO sFileName
            METHOD = sFileName
        END METHOD
    END INTERFACE
END CLASS


'==============================================================================
'MCM code
'------------------------------------------------------------------------------
 FUNCTION STDOUT (Z AS STRING) AS LONG
 ' returns TRUE (non-zero) on success

   LOCAL hStdOut AS LONG, nCharsWritten AS LONG
   LOCAL w AS STRING


   hStdOut      = GetStdHandle (%STD_OUTPUT_HANDLE)
   IF hSTdOut   = -1&  or hStdOut = 0&  THEN     ' invalid handle value, coded in line to avoid
                                                 ' casting differences in Win32API.INC
                                                 ' %NULL test added for Win/XP
     AllocConsole
     hStdOut  = GetStdHandle (%STD_OUTPUT_HANDLE)
   END IF
   w = Z & $CRLF
   FUNCTION = WriteFile(hStdOut, BYVAL STRPTR(W), LEN(W),  nCharsWritten, BYVAL %NULL)


 END FUNCTION
'==============================================================================
« Last Edit: January 26, 2009, 12:59:12 AM by James C. Fuller »

Offline Frederick J. Harris

  • Hero Member
  • *****
  • Posts: 914
  • User-Rate: +16/-0
    • Frederick J. Harris
Re: c++ client PowerBASIC Server
« Reply #1 on: January 26, 2009, 05:17:43 PM »
Hi James!

     I'm stuck on the top command in the dll - '#COM TLIB ON'.  My compiler doesn't seem to like that and I don't understand it either even though I've looked through help.  What does that command do and how should I set up my environment to get that to compile?

     Oh, by the way.  Here is the thought or observation that just more or less forced itself upon me on Friday night.  Actually, I had noted it before but tried to forget it do to its unpleasantness.  Take a look at any of the tabular outputs from the many programs I presented that dumps memory as it iterates through the VTables printing function addresses, and take note of the fact that the addresses of QueryInterface(), AddRef() and Release() in each VTable (either I_X or I_Y) don't point to the single implementation of these functions in class CA!  Indeed, when any of these functions get called through the addresses in the respective VTable, we get an output message that the single implementation of that function was called, even though the numbers in the VTable don't match!

     Do you have a plausible explanation for this?  I really believe its a C++ issue rather than a PowerBASIC or COM issue.  Dale Rogerson kind or skirts around the issue in chapter 3 of his book Inside COM when he discusses QueryInterface() and casting with reference to Multiple Inheritance. 

     So what I'm saying is that I think the whole issue concerning memory layout would be 'tied neatly with a red bow' in my mind, in other words would set a good bit better with me, if in the function pointer slots of each VTable for the IUnknown functions the numbers would point to a single implementation of these functions, because, after all, even though many VTables are possible, IUnknown only has one implementation.

Offline Frederick J. Harris

  • Hero Member
  • *****
  • Posts: 914
  • User-Rate: +16/-0
    • Frederick J. Harris
Re: c++ client PowerBASIC Server
« Reply #2 on: January 26, 2009, 06:29:27 PM »
Hi Again James!

     I've been a bit remiss in following up on your posts/code so I decided to start at the top.  The 1st one worked fine, but I'm getting crashes on the one where you created the dll with PowerBASIC and set your name 'James' to the s1 variable in the PB Constructor.  Its this one...

#COMPILE DLL "PBCTEST01.DLL"   

     When I run the C++ program I get as far as

     hr=piTest01->S1_Get(&str1);

     ...and then a crash.  Here is the last output I get...

Code: [Select]
ptrDllGetClassObject = 9983009
Constructor Called #1
Called Fx1 iNum = 25
Called Fx2 iNum = 50
Called iNum2_Get = 17
iNum2 = 17
Called iNum2_Set = 22
Called iNum2_Get = 22
iNum2 = 22

     So I expect there may be something wrong there.  Is that running to a successful completion for you?

     Also, I noted in the PB Dll code that you were passing the String Method parameters Byref...

Code: [Select]
METHOD S1_Get(Param AS STRING)
      Param = S1
END METHOD
METHOD S1_Set(Param AS STRING)
      S1 = Param
      STDOUT "Called S1_Set -> "+ S1
END METHOD

     I thought I read somewhere in the PB Docs that string parameters to methods should be Byval, or am I wrong there or doesn't it matter?  I don't know that's why I'm asking?  I remember ruminating on the issue for a bit when the compiler 1st came out, and ended up thinking Byval would be the way to do it because the instance of a class needs to have its own copy of the whole string, not a reference to one somewhere else? 

     I really need to learn more about the BSTR type.  I used it some on the work I was doing a month or two ago with Typelibs, but that was my 1st use of it.       

Offline James C. Fuller

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 597
  • User-Rate: +11/-8
Re: c++ client PowerBASIC Server
« Reply #3 on: January 26, 2009, 07:21:45 PM »
Fred,
  I confess I have not examined your memory tests too close. That goes a little deeper than I care to venture.
I'm having a hard enough time getting a superficial grasp on COM to the point so it works  :)


Ok, to make sure we are on the same page as regard to my examples. The ones in the other thread were experiments and I may have changed them since.

Re: #COM TLIB ON
  It should compile with 9.00? It's not needed.

I changed to BYVAL on all Property Sets and will continue to use it from now on.

I used code::Blocks with gcc(g++) 3.4.5 from the mingw setup to compile. I noticed the above OpenFile code was only
a 6k exe with gcc where the bc++ was 53k ??? There may be debug code in the bcc one. I'm not all that familiar with RadASM.


I think the void should probably be HRESULT now that I'm using properties in the server??

I am not really using BSTR as a true BSTR just as a receptacle that will compile and work with PB strings.
True BSTR are unicode.


The thing I like is using PB with my PropGet/Set's and having the code accessable rather easily from c++.
I did try to get it to work with Peles c but very little luck in that department.

James


c++ client

Code: [Select]
// Compiled with g++ 3.4.5
#include <windows.h>
#include <oleauto.h>
#include <stdio.h>
static const CLSID CLSID_cTest01   = {0xeedfbd9e, 0x8cd7, 0x43ae,{0x8b, 0x58, 0x66, 0x76, 0xee, 0x2b, 0x1e, 0x72}};
static const IID   IID_iTest01    = {0x7e58490e, 0x8445, 0x4b61,{0x9c, 0xed, 0x61, 0x18, 0x46, 0xf8, 0x35, 0xd7}};



HRESULT (__stdcall* ptrDllGetClassObject) (const CLSID&, const IID&, void**);

interface iTest1 : IUnknown
{
 virtual HRESULT __stdcall Fx1(int)=0;
 virtual HRESULT __stdcall Fx2(int)=0;
 virtual long __stdcall get_iNum2();
 virtual void __stdcall put_iNum2(int);
 virtual BSTR __stdcall get_S1();
 virtual void __stdcall put_S1(BSTR);
};




int main(void)
{
  iTest1* piTest01=NULL;
  HRESULT hr;
  IClassFactory*  pCF=NULL;
  HMODULE         hDll=NULL;

  int iNum2,iNum3=9;
  BSTR str1;
  char stemp[]="Fuller";

//Change Path Here

  hDll=LoadLibrary("D:\\ComTutorial1A\\Server\\PbSource\\PBCTEST01.DLL");

  ptrDllGetClassObject=(HRESULT (__stdcall*)(REFCLSID, REFIID, void**))GetProcAddress(hDll,"DllGetClassObject");

  hr=ptrDllGetClassObject(CLSID_cTest01,IID_IClassFactory,(void**)&pCF);
  if(SUCCEEDED(hr))
  {
    pCF->CreateInstance(NULL,IID_iTest01,(void**)&piTest01);
    // Release Class Factory
    pCF->Release();

    hr=piTest01->Fx1(25);
    hr=piTest01->Fx2(50);
    iNum2 = piTest01->get_iNum2();
    printf("iNum2 = %u\n",iNum2);
    piTest01->put_iNum2(iNum3);
    iNum2 = piTest01->get_iNum2();
    printf("iNum2 = %u\n",iNum2);
    str1 = piTest01->get_S1();
    printf("str1 = %s\n",str1);
    str1 = SysAllocStringByteLen(stemp,strlen(stemp));
    piTest01->put_S1(str1);
    SysFreeString(str1);
    str1 = piTest01->get_S1();
    printf("str1 = %s\n",str1);

// Added 01-27-09
// Need to free all string memory
    SysFreeString(str1);
    hr=piTest01->Release();

  }


 FreeLibrary(hDll);

 getchar();
 return 0;

}




PbServer
Code: [Select]
'SED_PBWIN
#COMPILE DLL "PBCTEST01.DLL"
#DIM ALL
#INCLUDE ONCE "WIN32API.INC"
$CLSID_cTest01 =GUID$("{EEDFBD9E-8CD7-43ae-8B58-6676EE2B1E72}")
$IID_iTest1 = GUID$("{7E58490E-8445-4b61-9CED-611846F835D7}")
MACRO PropGet(PropName,PropType)=PROPERTY GET PropName() AS PropType:PROPERTY=PropName:END PROPERTY
MACRO PropSet(PropName,PropType)=PROPERTY SET PropName(BYVAL param AS PropType):PropName=param:END PROPERTY


CLASS cTest01 $CLSID_cTest01 AS COM
INSTANCE S1 AS STRING, iNum2 AS LONG
CLASS METHOD CREATE
STDOUT "Constructor Called #1"
iNum2 = 17
S1 = "James"
END METHOD
CLASS METHOD DESTROY
STDOUT "Destructor Called"
END METHOD
INTERFACE iTest1 $IID_iTest1 : INHERIT IUNKNOWN
METHOD Fx1(BYVAL iNum AS LONG)
STDOUT "Called Fx1 iNum = " + FORMAT$(iNum)
END METHOD

METHOD Fx2(BYVAL iNum AS LONG)
STDOUT "Called Fx2 iNum = " + FORMAT$(iNum)
END METHOD

PropGet(iNum2,LONG)
PropSet(iNum2,LONG)


PropGet(S1,STRING)
PropSet(S1,STRING)


END INTERFACE
END CLASS



'==============================================================================
'MCM code
'------------------------------------------------------------------------------
 FUNCTION STDOUT (Z AS STRING) AS LONG
 ' returns TRUE (non-zero) on success

   LOCAL hStdOut AS LONG, nCharsWritten AS LONG
   LOCAL w AS STRING


   hStdOut      = GetStdHandle (%STD_OUTPUT_HANDLE)
   IF hSTdOut   = -1&  or hStdOut = 0&  THEN     ' invalid handle value, coded in line to avoid
                                                 ' casting differences in Win32API.INC
                                                 ' %NULL test added for Win/XP
     AllocConsole
     hStdOut  = GetStdHandle (%STD_OUTPUT_HANDLE)
   END IF
   w = Z & $CRLF
   FUNCTION = WriteFile(hStdOut, BYVAL STRPTR(W), LEN(W),  nCharsWritten, BYVAL %NULL)


 END FUNCTION
'==============================================================================
« Last Edit: January 27, 2009, 02:35:09 PM by James C. Fuller »

Offline Frederick J. Harris

  • Hero Member
  • *****
  • Posts: 914
  • User-Rate: +16/-0
    • Frederick J. Harris
Re: c++ client PowerBASIC Server
« Reply #4 on: January 26, 2009, 08:07:57 PM »
Good job James!  I got it to work and will study your code.  I'm just really looking close at the whole BSTR, OLECHAR thing now.  Certainly strings are important as it would be hard for me to write a program without them!

The thing with the memory is one of those things I'm going to have to put aside until I gather more knowledge on C++ because I suspect the issue goes right to the heart of what the C++ compiler does to make multiple inheritance work.  I've a book on it by Stanley Lippmann named 'The C++ Object Model' but its unfortunately over my head as of yet.  I skimmed through one of the chapters yesterday that seems to be discussing the issue I'm raising, and its unbelievably complex.  It seems there are more tables in memory containing other information beyond what I found, and I'm rather sure within those other tables the final resolution is made that causes the single implementation of the IUnknown functions to be called.

I watch the sizes of executables very close too, and was astounded to see how small the CodeBlocks generated executables were.  That is the 1st instance where I actually found a C or C++ compiler to generate exes smaller than PowerBASIC's.  However, just a few weeks ago I downloaded and installed Microsoft's VC++ 9 (Visual Studio 2008) and its executables are about like CodeBlocks (or perhaps I should say the other way around).  So its my guess there is system code not being statically linked into the newer exes.  I'd be interested if anyone knew the details on this.

You had mentioned that you were astounded by the quantity of C++ code necessary to create a COM server.  If you are basing your comment on the fairly voluminous code bulk of my little test class CA, I can assure you it gets considerably worse than that as that example - while a valid COM server - lacked the code to deal with creating a type lib.  In C++ one does that by 1sr creating a *.idl file and compiling that with midl (Microsoft Interface Definition Language Compiler).  That generates several more files that have to be compiled and linked into the dll.  And if you want to see some nasty looking C code you'll find it in those files as they contain  a lot of proxy-stubb marshalling code to allow the COM server to function transparently across a network.

The reason I know as much as I do about that end of it is I spent a lot of time in Guy and Henry Eddon's book "Inside Distributed COM' (which I found three or four years ago in a used bookstore for about $2.00 - CD and all ), and its a pretty good book.  They start out in that book with IDL, as opposed to Dale Rogerson's book which doesn't introduce it to near the end.

Right at the moment I'm trying to work through "Developer's Workshop to COM and ATL 3.0" by Andrew W. Troelson.  Its pretty much consuming my time.

Offline Frederick J. Harris

  • Hero Member
  • *****
  • Posts: 914
  • User-Rate: +16/-0
    • Frederick J. Harris
Re: c++ client PowerBASIC Server
« Reply #5 on: January 26, 2009, 08:33:56 PM »
Hi Jim!

     With the removal of that #COM metastatement I compiled PBCTest03.bas to the dll, made the C++ project, and ran it OK!  You're making fast progress.  I'm getting to like that business of using GetProcAddress on the dll instead of registering it.  Saves cluttering up the registry -  cuz I know I'll never find all the junk I'd end up putting in there with my tests.

     One thing about that CodeBlocks.  There is a neat Linux version of it I installed on all my Laptops (I set up all my laptops as dual boots with Linux/Windows).  Unfortunately, I hadn't gotten beyond Hello, World! type stuff in testing out CodeBlocks on Linux, but the IDE looks and works exactly the same as on Windows.  There is even a template in there to do GTK+ programs but I hadn't gotten to that yet - COM is eating me alive.  I had been working with a lot of Linux programming for some time, but always doing command line compiling.  CodeBlocks was the first IDE I tried.

Offline James C. Fuller

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 597
  • User-Rate: +11/-8
Re: c++ client PowerBASIC Server
« Reply #6 on: January 27, 2009, 02:36:32 PM »
I was missing a  SysFreeString in the c++ client code.
Hay your the c++ expert here and are supposed to catch these things ;D

James

Offline Frederick J. Harris

  • Hero Member
  • *****
  • Posts: 914
  • User-Rate: +16/-0
    • Frederick J. Harris
Re: c++ client PowerBASIC Server
« Reply #7 on: January 28, 2009, 06:31:29 PM »
Hello James!

     I've been 'dark' for the past 48 hours or so as a result of a slight medical emergency.  That's why I havn't been responding.  I had a bad 'boil' or facial absess that I had to get taken care of.  It seems better now so hopefully I can get some work done!

Fred

Offline James C. Fuller

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 597
  • User-Rate: +11/-8
Re: c++ client PowerBASIC Server
« Reply #8 on: January 29, 2009, 12:11:22 PM »
Fred,
  Sorry to hear about your malady. I went through a few myself and know the distraction the pain can cause.
Mine were never facial though and one of the worst was at the other end. Had trouble sitting for quite awhile.

I know you are investigating the memory aspects of COM components where my focus is on developing different programming
 language code to access MY created PowerBASIC COM servers. This way I can supply the access code in the native code.
For starters I am attacking c and c++ (of which my knowledge is limited) but there is a wealth of info out there and here.
All code will use dynamic loading and instantiation.

Is there another way to code this:
Code: [Select]

HRESULT (__stdcall* ptrDllGetClassObject) (const CLSID&, const IID&, void**);

ptrDllGetClassObject=(HRESULT (__stdcall*)(REFCLSID, REFIID, void**))GetProcAddress(hDll,"DllGetClassObject");
hr=ptrDllGetClassObject(CLSID_cTest01,IID_IClassFactory,(void**)&pCF);


If this is the way it's normally done then ok, but I have a hard time following exactly what it means??

I don't want to use MFC,STL,ATL..... or any of the other ABCDEFG's

possibly with a typedef ???

My c++ is VERY weak. I am learning, but now basically just throwing things together I find in my surfing.

James


Offline James C. Fuller

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 597
  • User-Rate: +11/-8
Re: c++ client PowerBASIC Server
« Reply #9 on: January 29, 2009, 12:55:24 PM »
I did change this so it would compile under pelles and it also works with bcc and g++.

HRESULT (__stdcall* ptrDllGetClassObject) (const CLSID&, const IID&, void**);
To
HRESULT (__stdcall* ptrDllGetClassObject) (REFCLSID,REFIID, void**);

James


Offline Frederick J. Harris

  • Hero Member
  • *****
  • Posts: 914
  • User-Rate: +16/-0
    • Frederick J. Harris
Re: c++ client PowerBASIC Server
« Reply #10 on: January 29, 2009, 05:56:57 PM »
Yes James, there is another way to do it with typedefs.  I had thought of posting it several days back and even explaining in brutal detail how it works, but I know your knowledge of macro expansion & substitution is very advanced, and as for others this is afterall a PowerBASIC oriented forum, so I refrained from going into the details.

I have a little demo that describes typedefs pretty good.  Give me a few minutes to dig it up.

Offline Edwin Knoppert

  • Sr. Member
  • ****
  • Posts: 254
  • User-Rate: +11/-4
    • Hellobasic.com
Re: c++ client PowerBASIC Server
« Reply #11 on: January 29, 2009, 07:18:08 PM »
>True BSTR are unicode.
Can be both

Offline Frederick J. Harris

  • Hero Member
  • *****
  • Posts: 914
  • User-Rate: +16/-0
    • Frederick J. Harris
Re: c++ client PowerBASIC Server
« Reply #12 on: January 29, 2009, 07:25:31 PM »
Jim,

     Here is a little demonstration that should explain the use of typedef in helping with C's ugly function pointer syntax.  Believe me, it confused me for years until I finally 'got it'.  The funny thing about typedefs is that nowhere can one find a good explanation of how they work with regard to function pointer syntax.  So I believe the little explanation I will give below is something that most folks don't really understand.

     I understand you have CodeBlocks.  This demo should work with whatever you have - I expect Pelles C too because I don't believe there is anything intrinsic to C++ in it.

     Start by creating a dll project in whatever environment/compiler you are using.  We'll make a simple server dll that just uses the printf function to write to the console window some string passed in to the dll.  We'll call the single function Prt like this...

Code: [Select]
//dllServer.cpp
#include <windows.h>
#include <stdio.h>

extern "C" __declspec(dllexport) void Prt(char* msg)
{
 printf("%s\n",msg);
}


BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
 switch (fdwReason)
 {
  case DLL_PROCESS_ATTACH:
    // attach to process
    // return FALSE to fail DLL load
    break;
  case DLL_PROCESS_DETACH:
    // detach from process
    break;
  case DLL_THREAD_ATTACH:
    // attach to thread
    break;
  case DLL_THREAD_DETACH:
    // detach from thread
    break;
 }

 return TRUE; // succesful
}

Compile that into a dll.  Note that all it does is contain one function named "Prt" that returns nothing (void), and takes a char pointer as a single parameter.  The calling convention in C or C++ is __cdecl unless changed to something else such as __stdcall.

Now, lets create a client program named perhaps dllHost.cpp where we'll load the dll with GetProcAddress and call the function both with and without using typedefs to clean it up.

Here is a program without using typedefs.  Note that the __cdecl isn't really necessary (because its standard), but I thought it worthwhile to include it for the reason that the function pointer syntax necessary to call the IUnknown functions needs __stdcall, and with this simple demo the placement of __cdecl would hopefully show how the substitution is done.  Here is a Hello World program that calls the dll we just made...

Code: [Select]
#include <windows.h>                //How to get a grasp of function pointer casting
#include <stdlib.h>                 //in C/C++ with and without typedefs.  It actually
                                    //isn't magic, however, its pretty close!
int main(void)
{
 char szMsg[]="Hello, World!";      //array of characters assigned to szMsg
 void (*pFn)(char*);                //C/C++ function pointer syntax for function
 HINSTANCE hIns;                    //that returns void and takes 1 char pointer param

 hIns=LoadLibrary("dllServer.dll");
 if(hIns)
 {
    pFn=(void (__cdecl*)(char*))GetProcAddress(hIns,"Prt");  //In other words,
    pFn(szMsg);                                              //void (__cdecl*)(char*) within
    FreeLibrary(hIns);                                       //an enclosing pair of braces and
 }                                                           //right before the GetProcAddress()
 system("PAUSE");       //call, tells the C compiler to cast the return from GetProcAddress to
                        //a pointer to a function with no return (void) that takes one
 return 0;              //character pointer parameter, and assign this address to pFn.  Note
}                       //that pFn has to match this mess, i.e., void (__cdecl*)(char*).
*/

/*
Hello, World!
Press any key to continue . . .

Process returned 0 (0x0)   execution time : 2.875 s
Press any key to continue.

*/

Now, here is the same program using typedef for a __cdecl function that returns void and takes a char * parameter...

Code: [Select]
#include <windows.h>                   //Here is an example using the
#include <stdlib.h>                    //same dll but calling the function
typedef void (__cdecl* FNPTR)(char*);  //address with the help of typedef

int main(void)                         //to clean up the messy C function
{                                      //pointer syntax.  Note that all the
 char szMsg[]="Hello, World!";         //C/C++ keywords/symbols such as 'void',
 HINSTANCE hIns;                       //'*', 'char', '__cdecl, etc, are
 FNPTR pFn;                            //substituted wherever the FNPTR is
 
 hIns=LoadLibrary("dllServer.dll");    //found in the code.  That way,
 if(hIns)                              //(FNPTR)GetProcAddress(hIns,"Prt")
 {                                         //becomes... pFn=(void (__cdecl*)(char*))GetProcAddress(hIns,"Prt")
    pFn=(FNPTR)GetProcAddress(hIns,"Prt"); //In still other words, pFn is a pointer to a function that
    pFn(szMsg);                        //returns nothing, uses standard C declension call stack setup, and takes
    FreeLibrary(hIns);                 //one char pointer parameter.  See below.
 }
 system("PAUSE");

 return 0;
}


/*=====Output========================================
Hello, World!
Press any key to continue . . .

Process returned 0 (0x0)   execution time : 3.344 s
Press any key to continue.
*/

/*
Now, here's an explanation of how to rationalize it or think about it.

(FNPTR)
(void (__cdecl* )(char*))

typedef void (__cdecl* FNPTR)(char*);  //typedef statement
void (__cdecl*)(char*)                 //remove everything from typedef statement that isn't C/C++ symbol or keyword
(FNPTR)                                //from right in front of GetProcAddress().  will replace 'FNPTR' with text 'void (__cdecl*)(char*)'
()                                     //remove term 'FNPTR'
(  void (__cdecl*)(char*)   )          //insert text string from typedef inside remaining quotes
(void (__cdecl*)(char*))               //Walah, Shizamm, Ausgezeichnet, Whatever!

(              void                         (__cdecl*)                              (char*)             )

                ^                                ^                                     ^

     Function returns nothing     Standard C Function Pointer Syntax     Function Takes a char* parameter

*/



Offline James C. Fuller

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 597
  • User-Rate: +11/-8
Re: c++ client PowerBASIC Server
« Reply #13 on: January 29, 2009, 07:51:28 PM »
Thanks Fred that really helped.

James

Offline Frederick J. Harris

  • Hero Member
  • *****
  • Posts: 914
  • User-Rate: +16/-0
    • Frederick J. Harris
Re: c++ client PowerBASIC Server
« Reply #14 on: January 29, 2009, 07:54:36 PM »
And below is a version of the program I posted several days back when you had 1st asked about using LoadLibrary() and
GetProcAddress() on the COM dll.  In it I made a typedef for the ugly function pointer syntax.  With the help of the little sample programs above you should be able to figure it out.  In fact, it would be a good exercise to create typedefs for the QueryInterface(), AddRef() and Release() function calls too.  If you did that you would have a better understanding of all those crazy numbers in the tables and where they came from...

Code: [Select]
//CAClient8.cpp
#include <windows.h> //CAClient
#include <stdio.h>   //IID_IClassFactory={00000001-0000-0000-C000-000000000046};
const CLSID CLSID_CA ={0x20000000,0x0000,0x0000,{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04}}; //Class ID of Class CA, ie., Class A
const IID   IID_I_X  ={0x20000000,0x0000,0x0000,{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05}}; //Interface X
const IID   IID_I_Y  ={0x20000000,0x0000,0x0000,{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06}}; //Interface Y
const IID   IID_Junk ={0x12345678,0x9876,0x5432,{0x12,0x34,0x56,0x78,0x98,0x76,0x54,0x32}}; //Junk Number So QueryInterface() Fails

typedef HRESULT (__stdcall*LPDLLGETCLASSOBJECT)(const CLSID&, const IID&, void**);
        HRESULT (__stdcall* ptrQueryInterface)    (int, const IID&, void**);
        ULONG   (__stdcall* ptrAddRef)            (int);
        ULONG   (__stdcall* ptrRelease)           (int);
        void    (__stdcall* pIFn)                 (int,int);


interface I_X : IUnknown
{
 virtual HRESULT __stdcall Fx1(int)=0;
 virtual HRESULT __stdcall Fx2(int)=0;
};


interface I_Y : IUnknown
{
 virtual HRESULT __stdcall Fy1(int)=0;
 virtual HRESULT __stdcall Fy2(int)=0;
};


int main(void)
{
 LPDLLGETCLASSOBJECT  ptrDllGetClassObject=NULL;
 IClassFactory*       pCF=NULL;
 HMODULE              hDll=NULL;
 BOOL blnFree         =FALSE;
 IUnknown*            pIUnk=NULL;
 unsigned int*        pVTbl=0;
 unsigned int*        VTbl=0;
 HRESULT              hr;

 hDll=LoadLibrary("C:\\Code\\VStudio\\VC++6\\Projects\\COM\\ComObjects\\CA\\Release\\CA.dll");
 if(hDll)
 {
    //ptrDllGetClassObject=(HRESULT (__stdcall*)(REFCLSID, REFIID, void**))GetProcAddress(hDll,"DllGetClassObject");
    ptrDllGetClassObject=(LPDLLGETCLASSOBJECT)GetProcAddress(hDll,"DllGetClassObject");   //<<<<< here's what you want
    printf("ptrDllGetClassObject = %u\n",ptrDllGetClassObject);
    hr=ptrDllGetClassObject(CLSID_CA,IID_IClassFactory,(void**)&pCF);
    if(SUCCEEDED(hr))
    {
       puts("Worked!  Miracles Never Cease!");
       pCF->CreateInstance(NULL,IID_IUnknown,(void**)&pVTbl);
       pCF->Release();
       printf("pVTbl   = %u\n",pVTbl);
       printf("\n");
       printf("&pVTbl[i]\t&VTbl[i]\tVTbl[i]\t\tFunction Call Through Pointer\n");
       printf("=============================================================================\n");
       for(unsigned int i=0;i<=1;i++)
       {
           VTbl=(unsigned int*)pVTbl[i];                                             //Call...
           printf("%u\t%u\t%u\t",&pVTbl[i],&VTbl[0],VTbl[0]);  
           ptrQueryInterface=(HRESULT(__stdcall*)(int, const IID&, void**)) VTbl[0];
           ptrQueryInterface((int)&pVTbl[i],IID_Junk,(void**)&pIUnk);                //QueryInterface()
           printf("%u\t%u\t%u\t",&pVTbl[i],&VTbl[1],VTbl[1]);                        
           ptrAddRef=(ULONG(__stdcall*)(int)) VTbl[1];
           ptrAddRef((int)&pVTbl[i]);                                                //AddRef()
           printf("%u\t%u\t%u\t",&pVTbl[i],&VTbl[2],VTbl[2]);                        
           ptrRelease=(ULONG(__stdcall*)(int)) VTbl[2];
           ptrRelease((int)&pVTbl[i]);                                               //Release()
           printf("%u\t%u\t%u\t",&pVTbl[i],&VTbl[3],VTbl[3]);                        
           pIFn=(void(__stdcall*)(int,int)) VTbl[3];
           pIFn((int)&pVTbl[i],i);                                                   //Fx1() / Fy1()    
           printf("%u\t%u\t%u\t",&pVTbl[i],&VTbl[4],VTbl[4]);                        
           pIFn=(void(__stdcall*)(int,int)) VTbl[4];
           pIFn((int)&pVTbl[i],i);                                                   //Fx2() / Fy2()  
           printf("\n");
       }
       ptrRelease=(ULONG(__stdcall*)(int)) VTbl[2];
       ptrRelease((int)&pVTbl[0]);
       getchar();
    }  
    blnFree=FreeLibrary(hDll);
    printf("blnFree=%u\n",blnFree);
 }
 
 return 0;
}