Begin Conversion Of PowerBASIC x86 ActiveX Grid To C++ x64

Started by Frederick J. Harris, November 23, 2013, 04:43:45 PM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

Frederick J. Harris

     In this post I'll present the progress I've made so far on converting my PowerBASIC ActiveX Grid Control to x64 C++.  Basically, there isn't any grid yet; all I've accomplished so far is the creation of what I'd term the COM infrastructure code, plus an increase in my understanding of x64 specific issues and use of the x64 command line tool chain.  I've just been doing command line compiling so far with Microsoft's x64 midl.exe, rc.exe, cvtres.exe,  and cl.exe.  I'm using Visual Studio 2008 Professional.  By the way, I decided to use C++ instead of C because of the need for the Parse member of my String Class. 

     This code can be compiled to either a 32 bit dll or a 64 bit one.  It can also be compiled ansi or wide character.  Be careful to comment/uncomment #defines in all the necessary *.cpp files.  Another #define I had to create was X64_GRID.  You'll see that in Server.cpp.  The issue there is that the exported function DllUnregisterServer() makes a call to UnRegisterTypeLib(),  and the last parameter of that call is an enumeration with several members such as SYS_WIN32 and SYS_WIN64. 


#if defined X64_GRID
    hr=UnRegisterTypeLib(LIBID_FHGrid,1,0,LANG_NEUTRAL,SYS_WIN64);
#else
    hr=UnRegisterTypeLib(LIBID_FHGrid,1,0,LANG_NEUTRAL,SYS_WIN32);
#endif


Also, there is a #define MYDEBUG that controls the output of voluminous file and console debug output.  The files are as follows ...


Server.cpp         //Some global data and the exported functions DllRegisterServer, DllUnregisterServer,                                       
                          //DllGetClassObject, and DllCanUnloadNow().

Registry.h         //Header for Registry functions         
Registry.cpp     //Registry routines

Interfaces.h      //Guids and interface declarations

Grid.h              //Header for grid code.  Contains C++ Class headers
Grid.cpp          //Actual grid code


Here is the Idl file on which everything is based.  This is very similiar to what I used for my PowerBASIC FHGrid.idl file.  I believe the only thing I changed is I used HWND parameters where needed instead of ints ...

// FHGrid.idl
import "unknwn.idl";

[object, uuid(30000000-0000-0000-0000-000000000001), oleautomation] interface IGrid : IUnknown
{
HRESULT CreateGrid
(
  [in] HWND   hParent,
  [in] BSTR   strSetup,
  [in] int    x,
  [in] int    y,
  [in] int    cx,
  [in] int    cy,
  [in] int    iRows,
  [in] int    iCols,
  [in] int    iRowHt,
  [in] int    iSelectionBackColor,
  [in] int    iSelectionTextColor,
  [in] BSTR   strFontName,
  [in] int    iFontSize,
  [in] int    iFontWeight
);
HRESULT SetRowCount([in] int iRowCount, [in] int blnForce);
HRESULT GetRowCount([out, retval] int* iRowCount);
HRESULT SetData([in] int iRow, [in] int iCol, [in] BSTR strData);
HRESULT GetData([in] int iRow, [in] int iCol, [out, retval] BSTR* strData);
HRESULT FlushData();
HRESULT Refresh();
HRESULT GetVisibleRows([out, retval] int* iVisibleRows);
HRESULT GethGrid([out, retval] HWND* hWnd);
HRESULT GethCell([in] int iRow, [in] int iCol, [out, retval] HWND* hCell);
HRESULT GethComboBox([in] int iCol, [out, retval] HWND* hCombo);
HRESULT SetCellAttributes([in] int iRow, [in] int iCol, [in] int iBackColor, [in] int iTextColor);
HRESULT DeleteRow([in] int iRow);
};

[object, uuid(30000000-0000-0000-0000-000000000002), oleautomation] interface IGridEvents : IUnknown
{
HRESULT Grid_OnKeyPress([in] int iKeyCode, [in] int iKeyData, [in] int iRow, [in] int iCol, [out] int* blnCancel);
HRESULT Grid_OnKeyDown([in] int KeyCode, [in] int iKeyData, [in] int iCellRow, [in] int iGridRow, [in] int iCol, [out] int* blnCancel);
HRESULT Grid_OnLButtonDown([in] int iCellRow, [in] int iGridRow, [in] int iCol);
HRESULT Grid_OnLButtonDblClk([in] int iCellRow, [in] int iGridRow, [in] int iCol);
HRESULT Grid_OnPaste([in] int iCellRow, [in] int iGridRow, [in] int iCol);
HRESULT Grid_OnRowSelection([in] int iRow, [in] int iAction);
HRESULT Grid_OnDelete([in] int iRow);
};

[uuid(30000000-0000-0000-0000-000000000003), helpstring("FHGrid TypeLib"), version(1.0)] library FHGridLibrary
{
importlib("stdole32.tlb");
interface IGrid;
interface IGridEvents;
[uuid(30000000-0000-0000-0000-000000000000)] coclass FHGrid
{
           interface IGrid;
  [source] interface IGridEvents;
}
};


Next is FHGrid.rc ...


1 TYPELIB "FHGrid.tlb"


Here's FHGrid.def ...


;//FHGrid.def
LIBRARY      "FHGrid"
EXPORTS
    DllGetClassObject     PRIVATE 
    DllCanUnloadNow       PRIVATE
    DllRegisterServer     PRIVATE
    DllUnregisterServer   PRIVATE


Now Server.cpp ...


// Server.cpp
// CD C:\Code\VStudio\VC++9\64_Bit\FHGrid
// cl Server.cpp Grid.cpp Registry.cpp FHGridRes.obj UUID.lib Advapi32.lib Ole32.lib OleAut32.lib FHGrid.def /O1 /Os /FeFHGrid.dll /LD             
#define       UNICODE
#define       _UNICODE
#define       X64_GRID            // This code can be compiled to create either a 32 bit or 64 bit COM object.  The X64_GRID define at
#define       MYDEBUG             // left is the only equate I've found which was made necessary by the 32/64 bit issue.  The define is
#include      <windows.h>         // used in DllUnregisterServer() in the code that Unregisters the Type Library, i.e., UnRegisterTypeLib().
#include      <tchar.h>           // The 5th parameter of that function is an object of type SYSKIND, which is an enumeration with members
#include      <initguid.h>        // SYS_WIN32 and SYS_WIN64.  The creation of my X64_GRID #define seemed like a reasonable way to handle
#include      <ocidl.h>           // this somewhat unfortunate situation.
#include      <cstdio>         
#include      "Grid.h"            // Note that if you have registered both a 32 bit version and a 64 bit version of this COM dll, and you
#include      "Registry.h"        // choose to Unregister one of those, only the win32 or win64 TypeLib subkey will be removed. 

#if defined   MYDEBUG   
FILE*         fp=NULL;
#endif

//Globals
HINSTANCE     g_hModule           = NULL;             
const TCHAR   g_szFriendlyName[]  = _T("Fred Harris Grid Control v1"); 
const TCHAR   g_szVerIndProgID[]  = _T("FHGrid.Grid");     
const TCHAR   g_szProgID[]        = _T("FHGrid.Grid.1");   
long          g_lObjs             = 0;
long          g_lLocks            = 0;
long          g_CtrlId            = 1500;


STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, void** ppv) 
{                                   
GridClassFactory* pCF=NULL;           
HRESULT hr; 
                       
#if defined MYDEBUG
printf("  Entering DllGetClassObject()\n");
#endif                                     
if(rclsid!=CLSID_FHGrid)               
    return E_FAIL;                   
pCF=new GridClassFactory;             
if(pCF==0)                           
    return E_OUTOFMEMORY;
#if defined MYDEBUG
printf("    pCF = %d\n",pCF);
#endif           
hr=pCF->QueryInterface(riid,ppv);   
if(FAILED(hr))                     
    delete pCF;
#if defined MYDEBUG
printf("  Leaving DllGetClassObject()\n");
#endif                       
                                     
return hr;                         
}                                   


STDAPI DllCanUnloadNow()             

#if defined MYDEBUG
printf("Entering DllCanUnloadNow()\n");
printf("  g_lObjs  = %d\n",g_lObjs);
printf("  g_lLocks = %d\n",g_lLocks);
#endif                                   
if(g_lObjs||g_lLocks)             

    #if defined MYDEBUG
    printf("  Gonna Hang Around A Bit Yet Wheather You Like It Or Not!\n");
    printf("Leaving DllCanUnloadNow()\n");
    #endif   
    return S_FALSE;
}                   
else

    #if defined MYDEBUG
    printf("  I'm Outta Here!\n");
    printf("Leaving DllCanUnloadNow()\n");
    #endif                                 
    return S_OK;
}                     
}                                   


STDAPI DllRegisterServer()           
{
ITypeLib* pTypeLib=NULL;
TCHAR szBuffer[512];
wchar_t szWide[512];
HRESULT hr=E_FAIL;

#if defined MYDEBUG
GetCurrentDirectory(512,szBuffer);
_tcscat(szBuffer,_T("\\Output.txt"));
fp=_tfopen(szBuffer,_T("w"));
if(!fp)
    return E_FAIL;
fprintf(fp,"Entering DLLRegisterServer\n");
_ftprintf(fp,_T("  szBuffer = %s\n"),szBuffer);
#endif
if(GetModuleFileName(g_hModule,szBuffer,512)>=512)
{
    #if defined MYDEBUG
    _ftprintf(fp,_T("  GetModuleFileName() Failed!\n"));
    _ftprintf(fp,_T("Leaving DllRegisterServer()\n"));
    fclose(fp);
    #endif
    return E_FAIL;
}   
#if defined UNICODE
     _tcscpy(szWide,szBuffer);
#else
     mbstowcs(szWide,szBuffer,511);
#endif
#if defined MYDEBUG
_ftprintf(fp,_T("  szBuffer = %s\n"),szBuffer);
fwprintf(fp,L"  szWide   = %s\n",szWide);
#endif
hr=LoadTypeLibEx(szWide, REGKIND_REGISTER, &pTypeLib);
if(SUCCEEDED(hr))
{
    #if defined MYDEBUG
    fprintf(fp,"  LoadTypeLibEx() Succeeded!\n");
    #endif
    pTypeLib->Release();
}
else
{
    #if defined MYDEBUG
    _ftprintf(fp,_T("  LoadTypeLib() Failed!\n"));
    _ftprintf(fp,_T("Leaving DllRegisterServer()\n"));
    fclose(fp);
    #endif
    return E_FAIL;
}   
hr=RegisterServer(szBuffer,CLSID_FHGrid,g_szFriendlyName,g_szVerIndProgID,g_szProgID);
if(SUCCEEDED(hr))
{
    #if defined MYDEBUG
    fprintf(fp,"  RegisterServer() Succeeded!\n");
    #endif
}
else
{
    #if defined MYDEBUG
    fprintf(fp,"  RegisterServer() Failed!\n");
    #endif
}
#if defined MYDEBUG
_ftprintf(fp,_T("Leaving DllRegisterServer()\n"));
fclose(fp);
#endif
                                           
return hr;                     
}


STDAPI DllUnregisterServer()         
{
TCHAR szBuffer[512];
HRESULT hr=E_FAIL;

#if defined MYDEBUG
GetCurrentDirectory(512,szBuffer);
_tcscat(szBuffer,_T("\\Output.txt"));
fp=_tfopen(szBuffer,_T("w"));
if(!fp)
    return E_FAIL;
_ftprintf(fp,_T("Entering DllUnregisterServer()\n"));
#endif
hr=UnregisterServer(CLSID_FHGrid,g_szVerIndProgID,g_szProgID);
#if defined MYDEBUG
_ftprintf(fp,_T("  hr = %d\n"),hr);
#endif
if(SUCCEEDED(hr))
{
    #if defined MYDEBUG
    _ftprintf(fp,_T("  UnregisterServer() Succeeded!\n"));
    #endif
    #if defined X64_GRID
    hr=UnRegisterTypeLib(LIBID_FHGrid,1,0,LANG_NEUTRAL,SYS_WIN64);
    #else
    hr=UnRegisterTypeLib(LIBID_FHGrid,1,0,LANG_NEUTRAL,SYS_WIN32);
    #endif
    if(SUCCEEDED(hr))
    {
       #if defined MYDEBUG
       _ftprintf(fp,_T("  UnRegisterTypeLib() Succeeded!\n"));
       #endif
    }
    else
    {
       #if defined MYDEBUG
       _ftprintf(fp,_T("  UnRegisterTypeLib() Failed!\n"));
       #endif
    }
}
#if defined MYDEBUG
_ftprintf(fp,_T("Leaving DllUnregisterServer()\n"));
fclose(fp);
#endif

return hr;



BOOL APIENTRY DllMain(HINSTANCE hInst, DWORD reason, LPVOID reserved)
{
switch (reason)                     
{                                   
  case DLL_PROCESS_ATTACH:           
    g_hModule=hInst;                 
    break;                           
  case DLL_PROCESS_DETACH:
    break;
}

return TRUE;                       
}


continued ...

Frederick J. Harris

#1
Here is Registry.h


BOOL SetKeyAndValue(const TCHAR* szKey, const TCHAR* szSubkey, const TCHAR* szValue);
void CLSIDToChar(const CLSID& clsid, TCHAR* szCLSID, int length);
LONG RecursiveDeleteKey(HKEY hKeyParent, const TCHAR* lpszKeyChild);
HRESULT RegisterServer(TCHAR* szModule, const CLSID& clsid, const TCHAR* szFriendlyName, const TCHAR* szVerIndProgID, const TCHAR* szProgID);
HRESULT UnregisterServer(const CLSID& clsid, const TCHAR* szVerIndProgID, const TCHAR* szProgID);


And Registry.cpp


// Registry.cpp
#define   UNICODE
#define  _UNICODE
#define  MYDEBUG
#include <objbase.h>
#include <tchar.h>
#include <cstdio>
#include "Registry.h"
const int CLSID_STRING_BUFFER_LENGTH = 39; //A CLSID Converted Needs 38 chars, i.e., {30000000-0000-0000-0000-000000000000}. The 39 number is for the terminating NULL.
#if defined MYDEBUG
extern FILE* fp;
#endif


BOOL SetKeyAndValue(const TCHAR* szKey, const TCHAR* szSubkey, const TCHAR* szValue)
{
TCHAR szKeyBuf[1024];
long lResult;
HKEY hKey;

#if defined MYDEBUG
_ftprintf(fp,_T("    Entering SetKeyAndValue()\n"));
_ftprintf(fp,_T("      szKey    = %s\n"),szKey);
_ftprintf(fp,_T("      szSubkey = %s\n"),szSubkey);
_ftprintf(fp,_T("      szValue  = %s\n"),szValue);
#endif
_tcscpy(szKeyBuf,szKey);
if(szSubkey!=NULL)          // Add subkey name to buffer.
{
     _tcscat(szKeyBuf, _T("\\"));
     _tcscat(szKeyBuf, szSubkey);
     #if defined MYDEBUG
     _ftprintf(fp,_T("      szKeyBuf = %s\n"),szKeyBuf);
     #endif
}
//Create and open key and subkey.
lResult=RegCreateKeyEx(HKEY_CLASSES_ROOT,szKeyBuf,0,NULL,REG_OPTION_NON_VOLATILE,KEY_ALL_ACCESS,NULL,&hKey,NULL);
if(lResult!=ERROR_SUCCESS)
    return FALSE ;
if(szValue!=NULL)         //Set the Value.
    RegSetValueEx(hKey,NULL,0,REG_SZ,(BYTE*)szValue,(_tcslen(szValue)*sizeof(TCHAR))+sizeof(TCHAR));
RegCloseKey(hKey);
#if defined MYDEBUG
_ftprintf(fp,_T("    Leaving SetKeyAndValue()\n"));
#endif

return TRUE;
}


void CLSIDToTChar(const CLSID& clsid, TCHAR* szCLSID, int iStrlen)    // Convert a CLSID to a TCHAR string.
{
LPOLESTR wszCLSID=NULL;
size_t pRet=0;
HRESULT hr;

#if defined MYDEBUG
_ftprintf(fp,_T("    Entering CLSIDToTChar()\n"));
#endif
hr=StringFromCLSID(clsid,&wszCLSID);      // Get CLSID
#if defined MYDEBUG
_ftprintf(fp,_T("      iStrLen  = %d\n"),iStrlen);
fwprintf(fp,   L"      wszCLSID = %s\n",wszCLSID);
#endif
if(SUCCEEDED(hr))
{
    #if defined UNICODE
        _tcscpy_s(szCLSID,iStrlen,wszCLSID);
        #if defined MYDEBUG
        _ftprintf(fp,_T("      szCLSID  = %s\n"),szCLSID);
        #endif
    #else
        wcstombs_s(&pRet,szCLSID,iStrlen,wszCLSID,iStrlen); // Covert from wide characters to non-wide.
        #if defined MYDEBUG
        _ftprintf(fp,  _T("      szCLSID  = %s\n"),szCLSID);
        _ftprintf(fp,  _T("      pRet     = %u\n"),pRet);
        #endif
    #endif
    CoTaskMemFree(wszCLSID);               // Free memory.
}
#if defined MYDEBUG
_ftprintf(fp,_T("    Leaving CLSIDToTChar()\n"));
#endif
}


LONG RecursiveDeleteKey(HKEY hKeyParent, const TCHAR* lpszKeyChild)       // Key to delete
{
TCHAR szBuffer[256];
DWORD dwSize=256 ;
HKEY hKeyChild;
FILETIME time;
LONG lRes;

lRes=RegOpenKeyEx(hKeyParent,lpszKeyChild,0,KEY_ALL_ACCESS,&hKeyChild); //Open the child.
if(lRes!=ERROR_SUCCESS)
    return lRes;
while(RegEnumKeyEx(hKeyChild,0,szBuffer,&dwSize,NULL,NULL,NULL,&time)==S_OK) //Enumerate all of the decendents of this child.
{
  lRes=RecursiveDeleteKey(hKeyChild,szBuffer);  //Delete the decendents of this child.
  if(lRes!=ERROR_SUCCESS)
  {
     RegCloseKey(hKeyChild);  //Cleanup before exiting.
     return lRes;
  }
  dwSize=256;
}
RegCloseKey(hKeyChild);      // Close the child.

return RegDeleteKey(hKeyParent,lpszKeyChild);  //Delete this child.
}


HRESULT RegisterServer(TCHAR* szModule, const CLSID& clsid, const TCHAR* szFriendlyName, const TCHAR* szVerIndProgID, const TCHAR* szProgID)
{
TCHAR szCLSID[CLSID_STRING_BUFFER_LENGTH];   // GetModuleFileName(hModule,szModule,sizeof(szModule)/sizeof(TCHAR))
TCHAR szKey[64];

#if defined MYDEBUG
_ftprintf(fp,_T("  Entering RegisterServer()\n"));
_ftprintf(fp,_T("    szFriendlyName = %s\n"),szFriendlyName);
_ftprintf(fp,_T("    szVerIndProgID = %s\n"),szVerIndProgID);
_ftprintf(fp,_T("    szProgID       = %s\n"),szProgID);
_ftprintf(fp,_T("    szModule       = %s\n"),szModule);
#endif
CLSIDToTChar(clsid, szCLSID, CLSID_STRING_BUFFER_LENGTH);            //Get server location &Convert the CLSID into a char.
_tcscpy_s(szKey, _T("CLSID\\"));                                     //Build the key CLSID\\{...}
_tcscat_s(szKey,szCLSID);
#if defined MYDEBUG
_ftprintf(fp,_T("    szCLSID        = %s\n"),szCLSID);
_ftprintf(fp,_T("    szKey   = %s\n"),szCLSID);
#endif
SetKeyAndValue(szKey,NULL,szFriendlyName);                           //Add the CLSID to the registry.
SetKeyAndValue(szKey, _T("InprocServer32"), szModule);               //Add the server filename subkey under the CLSID key.
SetKeyAndValue(szKey, _T("ProgID"), szProgID);                       //Add the ProgID subkey under the CLSID key.
SetKeyAndValue(szKey,_T("VersionIndependentProgID"),szVerIndProgID); //Add the version-independent ProgID subkey under CLSID key.
SetKeyAndValue(szVerIndProgID, NULL, szFriendlyName);                //Add the version-independent ProgID subkey under HKEY_CLASSES_ROOT.
SetKeyAndValue(szVerIndProgID, _T("CLSID"), szCLSID);
SetKeyAndValue(szVerIndProgID, _T("CurVer"), szProgID);
SetKeyAndValue(szProgID, NULL, szFriendlyName);                      //Add the versioned ProgID subkey under HKEY_CLASSES_ROOT.
SetKeyAndValue(szProgID, _T("CLSID"), szCLSID);
#if defined MYDEBUG
_ftprintf(fp,_T("  Leaving RegisterServer()\n"));
#endif

return S_OK ;
}


HRESULT UnregisterServer(const CLSID& clsid, const TCHAR* szVerIndProgID, const TCHAR* szProgID)
{
TCHAR szCLSID[CLSID_STRING_BUFFER_LENGTH];
TCHAR szKey[64];
LONG lResult;

#if defined MYDEBUG
_ftprintf(fp,  _T("  Entering UnregisterServer()\n"));
#endif
CLSIDToTChar(clsid, szCLSID,CLSID_STRING_BUFFER_LENGTH);                //Convert the CLSID into a char.
_tcscpy_s(szKey, _T("CLSID\\"));                                        //Build the key CLSID\\{...}
_tcscat_s(szKey, szCLSID) ;
lResult=RecursiveDeleteKey(HKEY_CLASSES_ROOT, szKey);                   //Delete the CLSID Key - CLSID\{...}
lResult=RecursiveDeleteKey(HKEY_CLASSES_ROOT, szVerIndProgID);          //Delete the version-independent ProgID Key.
lResult=RecursiveDeleteKey(HKEY_CLASSES_ROOT, szProgID) ;               //Delete the ProgID key.
#if defined MYDEBUG
_ftprintf(fp,  _T("  Leaving UnregisterServer()\n"));
#endif

return S_OK ;
}


Now Interfaces.h, Grid.h, and Grid.cpp ...


//IFunctions.h
const CLSID CLSID_FHGrid           =                    {0x30000000, 0x0000, 0x0000, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}          };
const IID   IID_IFHGrid            =                    {0x30000000, 0x0000, 0x0000, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01}          };
const IID   IID_IFHGrid_Events     =                    {0x30000000, 0x0000, 0x0000, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02}          };
const IID   LIBID_FHGrid           =                    {0x30000000, 0x0000, 0x0000, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03}          };

interface IGrid : IUnknown
{
virtual HRESULT STDMETHODCALLTYPE CreateGrid           (HWND, BSTR, int, int, int, int, int, int, int, int, int, BSTR, int, int        ) = 0;
virtual HRESULT STDMETHODCALLTYPE SetRowCount          (int, int                                                                       ) = 0;
virtual HRESULT STDMETHODCALLTYPE GetRowCount          (int*                                                                           ) = 0;
virtual HRESULT STDMETHODCALLTYPE SetData              (int, int, BSTR                                                                 ) = 0;
virtual HRESULT STDMETHODCALLTYPE GetData              (int, int, BSTR*                                                                ) = 0;
virtual HRESULT STDMETHODCALLTYPE FlushData            (void                                                                           ) = 0;
virtual HRESULT STDMETHODCALLTYPE Refresh              (void                                                                           ) = 0;
virtual HRESULT STDMETHODCALLTYPE GetVisibleRows       (int *iVisibleRows                                                              ) = 0;
virtual HRESULT STDMETHODCALLTYPE GethGrid             (HWND*                                                                          ) = 0;
virtual HRESULT STDMETHODCALLTYPE GethCell             (int, int, HWND*                                                                ) = 0;
virtual HRESULT STDMETHODCALLTYPE GethComboBox         (int, HWND*                                                                     ) = 0;
virtual HRESULT STDMETHODCALLTYPE SetCellAttributes    (int, int, int, int                                                             ) = 0;
virtual HRESULT STDMETHODCALLTYPE DeleteRow            (int                                                                            ) = 0;
};

interface IGridEvents : IUnknown
{
virtual HRESULT STDMETHODCALLTYPE Grid_OnKeyPress      (int iKeyCode, int iKeyData, int iRow, int iCol, int* blnCancel                 ) = 0;
virtual HRESULT STDMETHODCALLTYPE Grid_OnKeyDown       (int KeyCode, int iKeyData, int iCellRow, int iGridRow, int iCol, int* blnCancel) = 0;
virtual HRESULT STDMETHODCALLTYPE Grid_OnLButtonDown   (int iCellRow, int iGridRow, int iCol                                           ) = 0;
virtual HRESULT STDMETHODCALLTYPE Grid_OnLButtonDblClk (int iCellRow, int iGridRow, int iCol                                           ) = 0;
virtual HRESULT STDMETHODCALLTYPE Grid_OnPaste         (int iCellRow, int iGridRow, int iCol                                           ) = 0;
virtual HRESULT STDMETHODCALLTYPE Grid_OnRowSelection  (int iRow, int iAction                                                          ) = 0;
virtual HRESULT STDMETHODCALLTYPE Grid_OnDelete        (int iRow                                                                       ) = 0;
};



//Grid.h                                     
#include "Interfaces.h"                     
extern long g_lObjs;                         
extern long g_lLocks;                     

class FHGrid : public IGrid, public IConnectionPointContainer, public IConnectionPoint         
{                                         
public:                                 
FHGrid();            //Constructor           
virtual ~FHGrid();   //Destructor             

//Iunknown Functions                     
virtual HRESULT STDMETHODCALLTYPE QueryInterface(const IID& iid, void** ppv);   
virtual ULONG   STDMETHODCALLTYPE AddRef();         
virtual ULONG   STDMETHODCALLTYPE Release();     
                   
//IGrid Interface Methods/Functions         
virtual HRESULT STDMETHODCALLTYPE CreateGrid(HWND, BSTR, int, int, int, int, int, int, int, int, int, BSTR, int, int);
virtual HRESULT STDMETHODCALLTYPE SetRowCount(int, int);
virtual HRESULT STDMETHODCALLTYPE GetRowCount(int*);
virtual HRESULT STDMETHODCALLTYPE SetData(int, int, BSTR);
virtual HRESULT STDMETHODCALLTYPE GetData(int, int, BSTR*);
virtual HRESULT STDMETHODCALLTYPE FlushData(void);
virtual HRESULT STDMETHODCALLTYPE Refresh(void);
virtual HRESULT STDMETHODCALLTYPE GetVisibleRows(int*);
virtual HRESULT STDMETHODCALLTYPE GethGrid(HWND*);
virtual HRESULT STDMETHODCALLTYPE GethCell(int, int, HWND*);
virtual HRESULT STDMETHODCALLTYPE GethComboBox(int, HWND*);
virtual HRESULT STDMETHODCALLTYPE SetCellAttributes(int, int, int, int);
virtual HRESULT STDMETHODCALLTYPE DeleteRow(int);

//IConnectionPointContainer Interface Methods/Functions         
virtual HRESULT STDMETHODCALLTYPE EnumConnectionPoints(IEnumConnectionPoints** ppEnum);            //not implemented
virtual HRESULT STDMETHODCALLTYPE FindConnectionPoint(REFIID riid, IConnectionPoint** ppCP);

// IConnectionPoint
virtual HRESULT STDMETHODCALLTYPE GetConnectionInterface(IID* pIID);                               //not implemented
virtual HRESULT STDMETHODCALLTYPE GetConnectionPointContainer(IConnectionPointContainer** ppCPC);  //not implemented
virtual HRESULT STDMETHODCALLTYPE Advise(IUnknown* pUnknown, DWORD* pdwCookie);
virtual HRESULT STDMETHODCALLTYPE Unadvise(DWORD dwCookie);
virtual HRESULT STDMETHODCALLTYPE EnumConnections(IEnumConnections** ppEnum);                      //not implemented     

protected:                               
long m_lRef;                             
};                                         


class GridClassFactory : public IClassFactory                             
{                                                                     
public:                                                             
GridClassFactory();                                                   
virtual ~GridClassFactory();                                           

public:                                                             
//IUnknown                                                           
virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID, void**);           
virtual ULONG   STDMETHODCALLTYPE AddRef();                                   
virtual ULONG   STDMETHODCALLTYPE Release();                                 

//IclassFactory                                                     
virtual HRESULT STDMETHODCALLTYPE CreateInstance(LPUNKNOWN, REFIID, void**); 
virtual HRESULT STDMETHODCALLTYPE LockServer(BOOL);                           

protected:                                                           
long  m_lRef;                                                       
};                                                                   
//End Grid.h



//Grid.cpp
#define  MYDEBUG
#include <windows.h>
#include <objbase.h>   
#include <ocidl.h>
#include <olectl.h>
#include <cstdio>
#include "Grid.h"
#if defined MYDEBUG
extern FILE* fp;
#endif


FHGrid::FHGrid()   //C++ Constructor for class FHGrid
{                                                   
#if defined MYDEBUG
printf("  Entering FHGrid Constructor!\n");
#endif
m_lRef=0;
#if defined MYDEBUG
printf("    this->m_lRef = %d\n", this->m_lRef);
#endif
InterlockedIncrement(&g_lObjs);
#if defined MYDEBUG
printf("    g_lObjs       = %d\n", g_lObjs);
printf("    sizeof(*this) = %d\n",sizeof(*this));
#endif
#if defined MYDEBUG
printf("  Leaving FHGrid Constructor!\n");
#endif
}


FHGrid::~FHGrid()  //C++ Destructor for class FHGrid
{
#if defined MYDEBUG
printf("  Entering FHGrid Destructor!\n");
printf("    g_lObjs = %d\n", g_lObjs);
#endif
InterlockedDecrement(&g_lObjs);
#if defined MYDEBUG
printf("    g_lObjs = %d\n", g_lObjs);
printf("  Leaving FHGrid Destructor!\n");
#endif
}


HRESULT STDMETHODCALLTYPE FHGrid::QueryInterface(REFIID riid, void** ppv)
{
#if defined MYDEBUG
printf("    Entering FHGrid::QueryInterface()\n");     
printf("      this = %d\n", this);
#endif
*ppv=0;
if(riid==IID_IUnknown)
    *ppv=(IGrid*)this;
else if(riid==IID_IFHGrid)
    *ppv=(IGrid*)this;
else if(riid==IID_IConnectionPointContainer)
    *ppv=(IConnectionPointContainer*)this;
else if(riid==IID_IConnectionPoint)
    *ppv=(IConnectionPoint*)this;
if(*ppv)
{
    AddRef();
    #if defined MYDEBUG
    printf("      *ppv = %u\n",*ppv);
    printf("    Leaving FHGrid::QueryInterface()\n");
    #endif
    return S_OK;
}
#if defined MYDEBUG
printf("    Leaving FHGrid::QueryInterface()\n");
#endif

return(E_NOINTERFACE);
}


ULONG STDMETHODCALLTYPE FHGrid::AddRef()
{
#if defined MYDEBUG
printf("    Entering FHGrid::AddRef()\n");
printf("      m_lRef = %d\n", m_lRef);
#endif
InterlockedIncrement(&m_lRef);
#if defined MYDEBUG
printf("      m_lRef = %d\n", m_lRef);
printf("    Leaving FHGrid::AddRef()\n");
#endif

return m_lRef;
}


ULONG STDMETHODCALLTYPE FHGrid::Release()
{
#if defined MYDEBUG
printf("    Entering FHGrid::Release()\n");
printf("      m_lRef = %d\n", m_lRef);
#endif
if(InterlockedDecrement(&m_lRef)==0)
{
    #if defined MYDEBUG
    printf("      m_lRef = %d\n", m_lRef);
    printf("      Will Now Delete this ...\n");
    printf("    Leaving FHGrid::Release()\n");
    #endif
    delete this;
    return 0;
}
#if defined MYDEBUG
printf("      m_lRef = %d\n", m_lRef);
printf("    Leaving FHGrid::Release()\n");
#endif

return m_lRef;
}


HRESULT STDMETHODCALLTYPE FHGrid::CreateGrid
(
HWND hParent,
BSTR strSetup,
int x,
int y,
int cx,
int cy,
int iRows,
int iCols,
int iRowHt,
int iSelectionBackColor,
int iSelectionTextColor,
BSTR strFontName,
int iFontSize,
int iFontWeight
)
{
printf("Called FHGrid::CreateGrid()\n");
return S_OK;
}


HRESULT STDMETHODCALLTYPE FHGrid::SetRowCount(int iRowCount, int blnForce)
{
printf("Called FHGrid::SetRowCount()\n");
return S_OK;
}


HRESULT STDMETHODCALLTYPE FHGrid::GetRowCount(int* iRowCount)
{
printf("Called FHGrid::GetRowCount()\n");
return S_OK;
}


HRESULT STDMETHODCALLTYPE FHGrid::SetData(int iRow, int iCol, BSTR strData)
{
printf("Called FHGrid::SetData()\n");
return S_OK;
}


HRESULT STDMETHODCALLTYPE FHGrid::GetData(int iRow, int iCol, BSTR* strData)
{
printf("Called FHGrid::GetData()\n");
return S_OK;
}


HRESULT STDMETHODCALLTYPE FHGrid::FlushData()
{
printf("Called FHGrid::FlushData()\n");
return S_OK;
}


HRESULT STDMETHODCALLTYPE FHGrid::Refresh()
{
printf("Called FHGrid::Refresh()\n");
return S_OK;
}


HRESULT STDMETHODCALLTYPE FHGrid::GetVisibleRows(int* iVisibleRows)
{
printf("Called FHGrid::GetVisibleRows()\n");
return S_OK;
}


HRESULT STDMETHODCALLTYPE FHGrid::GethGrid(HWND* hWnd)
{
printf("Called FHGrid::GethGrid()\n");
return S_OK;
}


HRESULT STDMETHODCALLTYPE FHGrid::GethCell(int iRow, int iCol, HWND* hCell)
{
printf("Called FHGrid::GethCell()\n");
return S_OK;
}


HRESULT STDMETHODCALLTYPE FHGrid::GethComboBox(int iCol, HWND* hCombo)
{
printf("Called FHGrid::GethCell()\n");
return S_OK;
}


HRESULT STDMETHODCALLTYPE FHGrid::SetCellAttributes(int iRow, int iCol, int iBackColor, int iTextColor)
{
printf("Called FHGrid::SetCellAttributes()\n");
return S_OK;
}


HRESULT STDMETHODCALLTYPE FHGrid::DeleteRow(int iRow)
{
printf("Called FHGrid::DeleteRow()\n");
return S_OK;
}


HRESULT FHGrid::EnumConnectionPoints(IEnumConnectionPoints** ppEnum)
{
printf("Called FHGrid::EnumConnectionPoints()\n");
return E_NOTIMPL;
}


HRESULT FHGrid::FindConnectionPoint(REFIID riid, IConnectionPoint** ppCP)
{
printf("Called FHGrid::FindConnectionPoint()\n");
return E_NOINTERFACE;
}


HRESULT FHGrid::GetConnectionInterface(IID* pIID)
{
printf("Called FHGrid::GetConnectionInterface()\n");
return E_NOTIMPL;
}


HRESULT FHGrid::GetConnectionPointContainer(IConnectionPointContainer** ppCPC)
{
printf("Called FHGrid::GetConnectionPointContainer()\n");
return E_NOTIMPL;
}


HRESULT FHGrid::Advise(IUnknown* pUnknown, DWORD* pdwCookie)
{
printf("Called FHGrid::Advise()\n");
return E_FAIL;
}


HRESULT FHGrid::Unadvise(DWORD dwCookie)
{
printf("Called FHGrid::Unadvise()\n");
return NOERROR;
}


HRESULT FHGrid::EnumConnections(IEnumConnections** ppEnum)
{
printf("Called FHGrid::EnumConnections()\n");
return E_NOTIMPL;
}


// Grid Class Factory
GridClassFactory::GridClassFactory()
{
#if defined MYDEBUG
printf("    Entering GridClassFactory Constructor!\n");
#endif
m_lRef=0;
#if defined MYDEBUG
printf("      this->m_lRef = %d\n", this->m_lRef);
#endif
#if defined MYDEBUG
printf("    Leaving GridClassFactory Constructor!\n");
#endif
}


GridClassFactory::~GridClassFactory()  //GridClassFactory Destructor
{
#if defined MYDEBUG
printf("  Entering GridClassFactory Destructor!\n");
#endif
#if defined MYDEBUG
printf("    this->m_lRef = %d\n", this->m_lRef);
#endif
#if defined MYDEBUG
printf("  Leaving GridClassFactory Destructor!\n");
#endif
}


HRESULT STDMETHODCALLTYPE GridClassFactory::QueryInterface(REFIID riid, void** ppv)
{
printf("    Entering GridClassFactory::QueryInterface()\n");
*ppv=0;
if(riid==IID_IUnknown || riid==IID_IClassFactory)
    *ppv=this;
#if defined MYDEBUG
printf("      *ppv = %u\n", *ppv);
#endif
if(*ppv)
{
    AddRef();
    printf("    Leaving GridClassFactory::QueryInterface()\n");
    return S_OK;
}

return E_NOINTERFACE;
}


ULONG STDMETHODCALLTYPE GridClassFactory::AddRef()
{
#if defined MYDEBUG
printf("      Entering GridClassFactory::AddRef()!\n");
printf("        this->m_lRef = %d\n", this->m_lRef);
#endif
InterlockedIncrement(&m_lRef);
#if defined MYDEBUG
printf("        this->m_lRef = %d\n", this->m_lRef);
printf("      Leaving GridClassFactory::AddRef()!\n");
#endif

return this->m_lRef;
}


ULONG STDMETHODCALLTYPE GridClassFactory::Release()
{
#if defined MYDEBUG
printf("  Entering GridClassFactory::Release()!\n");
printf("    this->m_lRef = %d\n", this->m_lRef);
#endif
InterlockedDecrement(&m_lRef);
if(this->m_lRef==0)
{
    printf("    this->m_lRef = %d\n", this->m_lRef);
    delete this;
    #if defined MYDEBUG
    printf("    GridClassFactory Has Been Destroyed!\n");
    printf("  Leaving GridClassFactory::Release()!\n");
    #endif
    return 0;
}
#if defined MYDEBUG
printf("    this->m_lRef = %d\n", this->m_lRef);
printf("  Leaving GridClassFactory::Release()!\n");
#endif

return m_lRef;
}


HRESULT STDMETHODCALLTYPE GridClassFactory::CreateInstance(LPUNKNOWN pUnkOuter, REFIID riid, void** ppvObj)
{
FHGrid* pGrid=NULL;
HRESULT hr;

#if defined MYDEBUG
printf("  Entering GridClassFactory::CreateInstance()\n");
#endif
*ppvObj=0;
pGrid=new FHGrid;
#if defined MYDEBUG
printf("    pGrid          = %u\n",pGrid);
printf("    sizeof(*pGrid) = %d\n",sizeof(*pGrid));
#endif
if(pGrid==NULL)
    return E_OUTOFMEMORY;
hr=pGrid->QueryInterface(riid,ppvObj);
if(FAILED(hr))
    delete pGrid;
#if defined MYDEBUG
printf("  Leaving GridClassFactory::CreateInstance()\n");
#endif

return hr;
}


HRESULT STDMETHODCALLTYPE GridClassFactory::LockServer(BOOL fLock)
{
if(fLock)
    InterlockedIncrement(&g_lLocks);
else
    InterlockedDecrement(&g_lLocks);

return S_OK;
}
//End Grid.cpp


continued ...

Frederick J. Harris

#2
     Visual Studio 2008 Pro puts a bunch of shortcuts on the Start Menu and one of those will start a command line session where paths and environment variables are set up to invoke either the 32 bit or 64 bit tool chain.  So I made a directory for my 64 bit code at C:\Code\VStudio\VC++9\64_Bit\FHGrid and I navigate to there from  where VStudio's command prompt opens up.  I use midl to create the type library, then rc and cvtres to create the FHGridRes.obj file containing the resources.  Then I use cl.exe to link all the pieces together and create the dll.  Here is the complete 64 bit console output for building the dll from the above code ...


Setting environment for using Microsoft Visual Studio 2008 Beta2 x64 tools.

c:\Program Files (x86)\Microsoft Visual Studio 9.0\VC>CD\

c:\>CD C:\Code\VStudio\VC++9\64_Bit\FHGrid

C:\Code\VStudio\VC++9\64_Bit\FHGrid>midl FHGrid.idl
Microsoft (R) 32b/64b MIDL Compiler Version 7.00.0500
Copyright (c) Microsoft Corporation 1991-2006. All rights reserved.
64 bit Processing .\FHGrid.idl
FHGrid.idl
64 bit Processing C:\Program Files\\Microsoft SDKs\Windows\v6.0A\include\unknwn.idl
unknwn.idl
64 bit Processing C:\Program Files\\Microsoft SDKs\Windows\v6.0A\include\wtypes.idl
wtypes.idl
64 bit Processing C:\Program Files\\Microsoft SDKs\Windows\v6.0A\include\basetsd.h
basetsd.h
64 bit Processing C:\Program Files\\Microsoft SDKs\Windows\v6.0A\include\guiddef.h
guiddef.h
64 bit Processing C:\Program Files\\Microsoft SDKs\Windows\v6.0A\include\oaidl.idl
oaidl.idl
64 bit Processing C:\Program Files\\Microsoft SDKs\Windows\v6.0A\include\objidl.idl
objidl.idl
64 bit Processing C:\Program Files\\Microsoft SDKs\Windows\v6.0A\include\oaidl.acf
oaidl.acf

C:\Code\VStudio\VC++9\64_Bit\FHGrid>rc.exe /v /foFHGridRes.res FHGrid.rc
Microsoft (R) Windows (R) Resource Compiler Version 6.0.5724.0
Copyright (C) Microsoft Corporation.  All rights reserved.

Using codepage 1252 as default
Creating FHGridRes.res

FHGrid.rc.
Writing TYPELIB:1,      lang:0x409,     size 5056

C:\Code\VStudio\VC++9\64_Bit\FHGrid>cvtres.exe /MACHINE:X64 /v FHGridRes.res
Microsoft (R) Windows Resource To Object Converter Version 9.00.21022.08
Copyright (C) Microsoft Corporation.  All rights reserved.

adding resource. type:TYPELIB, name:1, language:0x0409, flags:0x30, size:5056

C:\Code\VStudio\VC++9\64_Bit\FHGrid>cl Server.cpp Grid.cpp Registry.cpp FHGridRes.obj UUID.lib Advapi32.lib Ole32.lib OleAut32.lib FHGrid.def /O1 /Os /FeFHGrid.dll /LD
Microsoft (R) C/C++ Optimizing Compiler Version 15.00.21022.08 for x64
Copyright (C) Microsoft Corporation.  All rights reserved.

Server.cpp
Grid.cpp
Registry.cpp
Generating Code...
Microsoft (R) Incremental Linker Version 9.00.21022.08
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:FHGrid.dll
/dll
/implib:FHGrid.lib
/def:FHGrid.def
Server.obj
Grid.obj
Registry.obj
FHGridRes.obj
UUID.lib
Advapi32.lib
Ole32.lib
OleAut32.lib
   Creating library FHGrid.lib and object FHGrid.exp

C:\Code\VStudio\VC++9\64_Bit\FHGrid>


     Next you'll want to Register the Server with RegSvr32.exe.  You may need to run your command prompt window using the 'Run As Administrator' option to get that to work.  Note I don't have a hard coded debug output file created in DllRegisterServer(), but rather I believe my code will work wherever you want to create this.  Here is part of that routine that gets called by RegSvr32...


STDAPI DllRegisterServer()           
{
ITypeLib* pTypeLib=NULL;
TCHAR szBuffer[512];
wchar_t szWide[512];
HRESULT hr=E_FAIL;

#if defined MYDEBUG
GetCurrentDirectory(512,szBuffer);
_tcscat(szBuffer,_T("\\Output.txt"));
fp=_tfopen(szBuffer,_T("w"));
if(!fp)
    return E_FAIL;
fprintf(fp,"Entering DLLRegisterServer\n");
...
...


Here is the Output.txt file I got through the registration when I was testing with ansi...


Entering DLLRegisterServer
  szBuffer = C:\Code\VStudio\VC++9\64_Bit\FHGrid\FHGrid.dll
  iReturn = 46
  szWide  = C:\Code\VStudio\VC++9\64_Bit\FHGrid\FHGrid.dll
  LoadTypeLibEx() Succeeded!
  Entering RegisterServer()
    szFriendlyName = Fred Harris Grid Control v1
    szVerIndProgID = FHGrid.Grid
    szProgID       = FHGrid.Grid.1
    szModule       = C:\Code\VStudio\VC++9\64_Bit\FHGrid\FHGrid.dll
    Entering CLSIDToTChar()
      iStrLen = 39
      wszCLSID = {30000000-0000-0000-0000-000000000000}
      szCLSID = {30000000-0000-0000-0000-000000000000}
      pRet    = 39
    Leaving CLSIDToTChar()
    szCLSID        = {30000000-0000-0000-0000-000000000000}
    szKey   = {30000000-0000-0000-0000-000000000000}
    Entering SetKeyAndValue()
      szKey    = CLSID\{30000000-0000-0000-0000-000000000000}
      szSubkey = (null)
      szValue  = Fred Harris Grid Control v1
    Leaving SetKeyAndValue()
    Entering SetKeyAndValue()
      szKey    = CLSID\{30000000-0000-0000-0000-000000000000}
      szSubkey = InprocServer32
      szValue  = C:\Code\VStudio\VC++9\64_Bit\FHGrid\FHGrid.dll
      szKeyBuf = CLSID\{30000000-0000-0000-0000-000000000000}\InprocServer32
    Leaving SetKeyAndValue()
    Entering SetKeyAndValue()
      szKey    = CLSID\{30000000-0000-0000-0000-000000000000}
      szSubkey = ProgID
      szValue  = FHGrid.Grid.1
      szKeyBuf = CLSID\{30000000-0000-0000-0000-000000000000}\ProgID
    Leaving SetKeyAndValue()
    Entering SetKeyAndValue()
      szKey    = CLSID\{30000000-0000-0000-0000-000000000000}
      szSubkey = VersionIndependentProgID
      szValue  = FHGrid.Grid
      szKeyBuf = CLSID\{30000000-0000-0000-0000-000000000000}\VersionIndependentProgID
    Leaving SetKeyAndValue()
    Entering SetKeyAndValue()
      szKey    = FHGrid.Grid
      szSubkey = (null)
      szValue  = Fred Harris Grid Control v1
    Leaving SetKeyAndValue()
    Entering SetKeyAndValue()
      szKey    = FHGrid.Grid
      szSubkey = CLSID
      szValue  = {30000000-0000-0000-0000-000000000000}
      szKeyBuf = FHGrid.Grid\CLSID
    Leaving SetKeyAndValue()
    Entering SetKeyAndValue()
      szKey    = FHGrid.Grid
      szSubkey = CurVer
      szValue  = FHGrid.Grid.1
      szKeyBuf = FHGrid.Grid\CurVer
    Leaving SetKeyAndValue()
    Entering SetKeyAndValue()
      szKey    = FHGrid.Grid.1
      szSubkey = (null)
      szValue  = Fred Harris Grid Control v1
    Leaving SetKeyAndValue()
    Entering SetKeyAndValue()
      szKey    = FHGrid.Grid.1
      szSubkey = CLSID
      szValue  = {30000000-0000-0000-0000-000000000000}
      szKeyBuf = FHGrid.Grid.1\CLSID
    Leaving SetKeyAndValue()
  Leaving RegisterServer()
  RegisterServer() Succeeded!
Leaving DllRegisterServer()


     I intend to discuss the above code in detail, but to do that one pretty much needs a client/host to work with.  So here is Client.h and Client.cpp.  This is a Windows GUI app that creates a little Window/Dialog/Form ... 


//Client.h
#ifndef CLIENT_H_INCLUDED
#define CLIENT_H_INCLUDED
#define dim(x) (sizeof(x) / sizeof(x[0]))

#define IDC_STATIC                  -1
#define IDC_UNLOAD_GRID             1500 
#define IDC_DUMP_COM_MEMORY         1505

struct                              WindowsEventArguments
{
HWND                               hWnd;
WPARAM                             wParam;
LPARAM                             lParam;
HINSTANCE                          hIns;
};

typedef WindowsEventArguments*      lpWndEventArgs;

long fnWndProc_OnCreate             (lpWndEventArgs Wea);
long fnWndProc_OnCommand            (lpWndEventArgs Wea);
long fnWndProc_OnPaint              (lpWndEventArgs Wea);
long fnWndProc_OnDestroy            (lpWndEventArgs Wea);

struct EVENTHANDLER
{
unsigned int                       iMsg;
long                               (*fnPtr)(lpWndEventArgs);
};

const EVENTHANDLER EventHandler[]=
{
{WM_CREATE,                        fnWndProc_OnCreate},
{WM_COMMAND,                       fnWndProc_OnCommand},
{WM_PAINT,                         fnWndProc_OnPaint},
{WM_DESTROY,                       fnWndProc_OnDestroy}
};

#endif // CLIENT_H_INCLUDED




//Client.cpp
// cl Client.cpp Kernel32.lib User32.lib Gdi32.lib UUID.lib Ole32.lib OleAut32.lib /O1 /Os /MT /GA
// CD C:\Code\VStudio\VC++9\64_Bit\FHGrid
#define     UNICODE
#define     _UNICODE                                   //At this point the grid doesn't exist yet, however, the COM infrastructure code in
#include    <windows.h>                                //which it will eventually be created does.  This client app exercises that COM
#include    <objbase.h>                                //infrastructure code by instantiating an FHGrid object and iterating through its
#include    <tchar.h>                                  //various structures and virtual function tables (VTables) to elucidate its memory
#include    <fcntl.h>                                  //footprint.  The app is a GUI Windows App and creates a small window with two buttons
#include    <io.h>                                     //on it.  The button on the left outputs the memory footprint of the various interfaces
#include    <stdio.h>                                  //and COM structures to the console window which the program creates.  The button on
#include    <ocidl.h>                                  //the right releases the COM object.
#include    "Client.h"                                 
const CLSID CLSID_FHGrid           =                   {0x30000000, 0x0000, 0x0000, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}             };
const IID   IID_IFHGrid            =                   {0x30000000, 0x0000, 0x0000, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01}             };
const IID   IID_IFHGrid_Events     =                   {0x30000000, 0x0000, 0x0000, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02}             };
const IID   LIBID_FHGrid           =                   {0x30000000, 0x0000, 0x0000, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03}             };
const IID   IID_Junk               =                   {0x12345678, 0x1234, 0x5678, {0x12,0x34,0x56,0x78,0x98,0x76,0x54,0x32}             };


interface IGrid : IUnknown
{
virtual HRESULT STDMETHODCALLTYPE CreateGrid          (HWND, BSTR, int, int, int, int, int, int, int, int, int, BSTR, int, int           ) = 0;
virtual HRESULT STDMETHODCALLTYPE SetRowCount         (int, int                                                                          ) = 0;
virtual HRESULT STDMETHODCALLTYPE GetRowCount         (int*                                                                              ) = 0;
virtual HRESULT STDMETHODCALLTYPE SetData             (int, int, BSTR                                                                    ) = 0;
virtual HRESULT STDMETHODCALLTYPE GetData             (int, int, BSTR*                                                                   ) = 0;
virtual HRESULT STDMETHODCALLTYPE FlushData           (void                                                                              ) = 0;
virtual HRESULT STDMETHODCALLTYPE Refresh             (void                                                                              ) = 0;
virtual HRESULT STDMETHODCALLTYPE GetVisibleRows      (int                                                                               ) = 0;
virtual HRESULT STDMETHODCALLTYPE GethGrid            (HWND*                                                                             ) = 0;
virtual HRESULT STDMETHODCALLTYPE GethCell            (int, int, HWND*                                                                   ) = 0;
virtual HRESULT STDMETHODCALLTYPE GethComboBox        (int, HWND*                                                                        ) = 0;
virtual HRESULT STDMETHODCALLTYPE SetCellAttributes   (int, int, int, int                                                                ) = 0;
virtual HRESULT STDMETHODCALLTYPE DeleteRow           (int                                                                               ) = 0;
};


long fnWndProc_OnCreate(lpWndEventArgs Wea)            //This code can be compiled for either 32 bit or 64 bit.  If compiled for 32 bit it will
{                                                      //attempt to load a 32 bit grid.  If compiled with a 64 bit C++ compiler it will attempt
IClassFactory* pClassFactory=NULL;                    //to load the 64 bit version.  Of course, to succeed those versions have to be registered.
SMALL_RECT sm_rect;
IGrid* pGrid=NULL;
HANDLE hStdOut;
HWND hCtl=NULL;
HRESULT hr;
FILE* hf=0;
COORD pt;
int hCrt; 

Wea->hIns=((LPCREATESTRUCT)Wea->lParam)->hInstance;
AllocConsole();
hStdOut=GetStdHandle(STD_OUTPUT_HANDLE);      //This mess at left is some pretty obscure stuff related
hCrt=_open_osfhandle((long)hStdOut,_O_TEXT);  //to the fact that when a GUI process is started by
hf=_fdopen( hCrt, "w" );                      //Windows the standard output handles are zeroed out and
__iob_func()[1]=*hf;                          //printf needs them.  This code re-initializes it so printf
pt.X=120, pt.Y=600;                           //works.  I had to change it a little from what it used to be.
SetConsoleScreenBufferSize(hStdOut,pt);
sm_rect.Left=0, sm_rect.Top=0, sm_rect.Right=119, sm_rect.Bottom=39;
SetConsoleWindowInfo(hStdOut,TRUE,&sm_rect);
printf("Entering fnWndProc_OnCreate()\n");
printf("  sizeof(IGrid*) = %d\n",sizeof(IGrid*));
hr=CoInitialize(NULL);
if(SUCCEEDED(hr))
{
    hCtl=CreateWindow(_T("button"),_T("Dump COM Memory"),WS_CHILD|WS_VISIBLE,30,120,150,25,Wea->hWnd,(HMENU)IDC_DUMP_COM_MEMORY,Wea->hIns,NULL);
    hCtl=CreateWindow(_T("button"),_T("Destroy Grid"),WS_CHILD|WS_VISIBLE,225,120,150,25,Wea->hWnd,(HMENU)IDC_UNLOAD_GRID,Wea->hIns,NULL);
    printf("  CoInitialize() Succeeded!\n");
    hr=CoGetClassObject(CLSID_FHGrid,CLSCTX_INPROC_SERVER,NULL,IID_IClassFactory,(void**)&pClassFactory);
    if(SUCCEEDED(hr))
    {
       printf("  CoGetClassObject() Succeeded!\n");
       printf("  pClassFactory = %u\n",pClassFactory);
       hr=pClassFactory->CreateInstance(NULL,IID_IFHGrid,(void**)&pGrid);
       if(SUCCEEDED(hr))
       {
          printf("  IClassFactory->CreateInstance() Succeeded!\n");
          printf("  pGrid = %u\n",pGrid);
          SetWindowLongPtr(Wea->hWnd,0,(LONG_PTR)pGrid);
       }
       else
          printf("  IClassFactory->CreateInstance() Failed!\n");
       pClassFactory->Release();
    }
    else
       printf("  CoGetClassObject() Failed!\n");
}
else
    printf("  CoInitialize() Failed!\n");
printf("Leaving fnWndProc_OnCreate()\n\n");

return 0;
}


void DumpCOMMemory(HWND hHost)   
{                                 
IConnectionPointContainer* pCnPtCnt=NULL;
size_t* pVTbl=0;
size_t* pIUnk=0;
size_t* VTbl=0;
HRESULT hr;

printf("  Entering DumpCOMMemory()\n");
IGrid* pGrid=(IGrid*)GetWindowLongPtr(hHost,0);
printf("    pGrid     = %u\n",pGrid);
if(pGrid)
{
   pVTbl=(size_t*)pGrid;
   printf("    pVTbl     = %u\n",pVTbl);
   printf("    &pVTbl[0] = %u\n", &pVTbl[0]);
   printf("    &pVTbl[1] = %u\n", &pVTbl[1]);
   printf("    &pVTbl[2] = %u\n", &pVTbl[2]);
   VTbl=(size_t*)pVTbl[0];
   printf("    VTbl      = %u\n",VTbl);
   printf("    VTbl[0]   = %u\n\n", VTbl[0]);

   //IGrid Function Pointers
   HRESULT (STDMETHODCALLTYPE* pQueryInterface             )(size_t* pThis, REFIID riid, void** ppv                                          );
   ULONG   (STDMETHODCALLTYPE* pAddRef                     )(size_t*                                                                         );
   ULONG   (STDMETHODCALLTYPE* pRelease                    )(size_t*                                                                         );
   HRESULT (STDMETHODCALLTYPE* pCreateGrid                 )(size_t*, HWND, BSTR, int, int, int, int, int, int, int, int, int, BSTR, int, int);
   HRESULT (STDMETHODCALLTYPE* pSetRowCount                )(size_t*, int, int                                                               );
   HRESULT (STDMETHODCALLTYPE* pGetRowCount                )(size_t*, int*                                                                   );
   HRESULT (STDMETHODCALLTYPE* pSetData                    )(size_t*, int, int, BSTR                                                         );
   HRESULT (STDMETHODCALLTYPE* pGetData                    )(size_t*, int, int, BSTR*                                                        );
   HRESULT (STDMETHODCALLTYPE* pFlushData                  )(size_t*                                                                         );
   HRESULT (STDMETHODCALLTYPE* pRefresh                    )(size_t*                                                                         );
   HRESULT (STDMETHODCALLTYPE* pGetVisibleRows             )(size_t*, int*                                                                   );
   HRESULT (STDMETHODCALLTYPE* pGethGrid                   )(size_t*, HWND*                                                                  );
   HRESULT (STDMETHODCALLTYPE* pGethCell                   )(size_t*, int, int, HWND*                                                        );
   HRESULT (STDMETHODCALLTYPE* pGethComboBox               )(size_t*, int, HWND*                                                             );
   HRESULT (STDMETHODCALLTYPE* pSetCellAttributes          )(size_t*, int, int, int, int                                                     );
   HRESULT (STDMETHODCALLTYPE* pDeleteRow                  )(size_t*, int                                                                    );

   //IConnectionPointContainer Function Pointers
   HRESULT (STDMETHODCALLTYPE* pEnumConnectionPoints       )(size_t* This, IEnumConnectionPoints** ppEnum                                    );
   HRESULT (STDMETHODCALLTYPE* pFindConnectionPoint        )(size_t* This, REFIID riid, IConnectionPoint** ppCP                              );

   //IConnectionPoint Function Pointers
   HRESULT (STDMETHODCALLTYPE* pGetConnectionInterface     )(size_t*, IID* pIID                                                              );
   HRESULT (STDMETHODCALLTYPE* pGetConnectionPointContainer)(size_t*, IConnectionPointContainer** ppCPC                                      );
   HRESULT (STDMETHODCALLTYPE* pAdvise                     )(size_t*, IUnknown* pUnkSink, DWORD* pdwCookie                                   );
   HRESULT (STDMETHODCALLTYPE* pUnadvise                   )(size_t*, DWORD dwCookie                                                         );
   HRESULT (STDMETHODCALLTYPE* pEnumConnections            )(size_t*, IEnumConnections** ppEnum                                              );

   //IGrid::QueryInterface()
   pQueryInterface=(HRESULT (STDMETHODCALLTYPE*)(size_t*, REFIID, void**))VTbl[0];
   pQueryInterface(&pVTbl[0],IID_Junk,(void**)&pIUnk);

   //IGrid::AddRef()
   pAddRef=(ULONG (STDMETHODCALLTYPE*)(size_t*))VTbl[1];
   pAddRef(&pVTbl[0]);
   
   //IGrid::Release()
   pRelease=(ULONG (STDMETHODCALLTYPE*)(size_t*))VTbl[2];
   pRelease(&pVTbl[0]);

   printf("\n");
   printf("    &pVTbl[i]   &VTbl[j]        pFn=VTbl[j]     pFn() Function Pointer Call\n");
   printf("    ===========================================================================\n");

   //IGrid::QueryInterface()
   pQueryInterface=(HRESULT (STDMETHODCALLTYPE*)(size_t*, REFIID, void**))VTbl[0];
   printf("    %u\t%u\t%u\n",&pVTbl[0],&VTbl[0],pQueryInterface);
   
   //IGrid::AddRef()
   printf("    %u\t%u\t%u\n",&pVTbl[0],&VTbl[1],pAddRef);

   //IGrid::Release()
   printf("    %u\t%u\t%u\n",&pVTbl[0],&VTbl[2],pRelease);

   //IGrid::CreateGrid()
   pCreateGrid=(HRESULT (STDMETHODCALLTYPE*)(size_t*,HWND,BSTR,int,int,int,int,int,int,int,int,int,BSTR,int,int))VTbl[3];
   printf("    %u\t%u\t%u\t",&pVTbl[0],&VTbl[3],pCreateGrid);
   HWND hWnd=NULL;
   BSTR strNothing=NULL;
   pCreateGrid(&pVTbl[0], hWnd, strNothing, 0, 0, 0, 0, 0, 0, 0, 0, 0, strNothing, 0, 0);

   //IGrid::SetRowCount()
   pSetRowCount=(HRESULT (STDMETHODCALLTYPE*)(size_t*, int, int))VTbl[4];
   printf("    %u\t%u\t%u\t",&pVTbl[0],&VTbl[4],pSetRowCount);
   pSetRowCount(&pVTbl[0],0,0);

   //IGrid::GetRowCount()
   pGetRowCount=(HRESULT (STDMETHODCALLTYPE*)(size_t*, int*))VTbl[5];
   printf("    %u\t%u\t%u\t",&pVTbl[0],&VTbl[5],pSetRowCount);
   pGetRowCount(&pVTbl[0],0);

   //IGrid::SetData()
   pSetData=(HRESULT (STDMETHODCALLTYPE*)(size_t*, int, int, BSTR))VTbl[6];
   printf("    %u\t%u\t%u\t",&pVTbl[0],&VTbl[6],pSetData);
   pSetData(&pVTbl[0],0,0,strNothing);

   //IGrid::GetData()
   pGetData=(HRESULT (STDMETHODCALLTYPE*)(size_t*, int, int, BSTR*))VTbl[7];
   printf("    %u\t%u\t%u\t",&pVTbl[0],&VTbl[7],pGetData);
   pGetData(&pVTbl[0],0,0,&strNothing);

   //IGrid::FlushData()
   pFlushData=(HRESULT (STDMETHODCALLTYPE*)(size_t*))VTbl[8];
   printf("    %u\t%u\t%u\t",&pVTbl[0],&VTbl[8],pFlushData);
   pFlushData(&pVTbl[0]);

   //IGrid::Refresh()
   pRefresh=(HRESULT (STDMETHODCALLTYPE*)(size_t*))VTbl[9];
   printf("    %u\t%u\t%u\t",&pVTbl[0],&VTbl[9],pRefresh);
   pRefresh(&pVTbl[0]);

   //IGrid::GetVisibleRows()
   pGetVisibleRows=(HRESULT (STDMETHODCALLTYPE*)(size_t*, int*))VTbl[10];
   printf("    %u\t%u\t%u\t",&pVTbl[0],&VTbl[10],pGetVisibleRows);
   pGetVisibleRows(&pVTbl[0],NULL);

   //IGrid::GethGrid()
   pGethGrid=(HRESULT (STDMETHODCALLTYPE*)(size_t*, HWND*))VTbl[11];
   printf("    %u\t%u\t%u\t",&pVTbl[0],&VTbl[11],pGethGrid);
   pGethGrid(&pVTbl[0],&hWnd);

   //IGrid::GethCell()
   pGethCell=(HRESULT (STDMETHODCALLTYPE*)(size_t*, int, int, HWND*))VTbl[12];   
   printf("    %u\t%u\t%u\t",&pVTbl[0],&VTbl[12],pGethCell);
   pGethCell(&pVTbl[0],0,0,&hWnd);

   //IGrid::GethComboBox()
   pGethComboBox=(HRESULT (STDMETHODCALLTYPE*)(size_t*, int, HWND*))VTbl[13];
   printf("    %u\t%u\t%u\t",&pVTbl[0],&VTbl[13],pGethComboBox);
   pGethComboBox(&pVTbl[0],0,&hWnd);

   //IGrid::SetCellAttributes()
   pSetCellAttributes=(HRESULT (STDMETHODCALLTYPE*)(size_t*, int, int, int, int))VTbl[14];
   printf("    %u\t%u\t%u\t",&pVTbl[0],&VTbl[14],pSetCellAttributes);
   pSetCellAttributes(&pVTbl[0],0,0,0,0);

   //IGrid::DeleteRow()
   pDeleteRow=(HRESULT (STDMETHODCALLTYPE*)(size_t*, int))VTbl[15];
   printf("    %u\t%u\t%u\t",&pVTbl[0],&VTbl[15],pDeleteRow);
   pDeleteRow(&pVTbl[0],0);
   printf("\n");
   //End IGrid VTable   
   
   VTbl=(size_t*)pVTbl[1];        //Get Base Address Of IConnectionPointContainer VTable
   //IConnectionPointContainer::EnumConnectionPoints()
   pEnumConnectionPoints=(HRESULT (STDMETHODCALLTYPE*)(size_t*, IEnumConnectionPoints**))VTbl[3];
   IEnumConnectionPoints* pEnumConnPts=NULL;
   printf("    %u\t%u\t%u\t",&pVTbl[1],&VTbl[3],pEnumConnectionPoints);
   pEnumConnectionPoints(&pVTbl[1],&pEnumConnPts);

   //IConnectionPointContainer::FindConnectionPoint()
   pFindConnectionPoint=(HRESULT (STDMETHODCALLTYPE*)(size_t*, REFIID, IConnectionPoint**))VTbl[4];
   printf("    %u\t%u\t%u\t",&pVTbl[1],&VTbl[4],pFindConnectionPoint);
   IConnectionPoint* pConnectionPoint=NULL;
   pFindConnectionPoint(&pVTbl[1],IID_Junk,&pConnectionPoint);
   printf("\n");
   //End IConnectionPointContainer VTable
   
   VTbl=(size_t*)pVTbl[2];       //Get Base Address Of IConnectionPoint VTable
   //IConnectionPoint::GetConnectionInterface()
   pGetConnectionInterface=(HRESULT (STDMETHODCALLTYPE*)(size_t*, IID*))VTbl[3];
   printf("    %u\t%u\t%u\t",&pVTbl[2],&VTbl[3],pGetConnectionInterface);
   IID pJunk;
   pGetConnectionInterface(&pVTbl[2],&pJunk);

   //IConnectionPoint::GetConnectionPointContainer
   pGetConnectionPointContainer=(HRESULT (STDMETHODCALLTYPE*)(size_t*, IConnectionPointContainer**))VTbl[4];
   printf("    %u\t%u\t%u\t",&pVTbl[2],&VTbl[4],pGetConnectionPointContainer);
   pGetConnectionPointContainer(&pVTbl[2],&pCnPtCnt);

   //IConnectionPoint::Advise()
   pAdvise=(HRESULT (STDMETHODCALLTYPE*)(size_t*, IUnknown*, DWORD*))VTbl[5];
   printf("    %u\t%u\t%u\t",&pVTbl[2],&VTbl[5],pAdvise);
   DWORD dwCookie;
   pAdvise(&pVTbl[2],(IUnknown*)pCnPtCnt,&dwCookie);

   //IConnectionPoint::Unadvise()
   pUnadvise=(HRESULT (STDMETHODCALLTYPE*)(size_t*, DWORD))VTbl[6];
   printf("    %u\t%u\t%u\t",&pVTbl[2],&VTbl[6],pUnadvise);
   pUnadvise(&pVTbl[2],dwCookie);

   //IConnectionPoint::EnumConnections
   pEnumConnections=(HRESULT (STDMETHODCALLTYPE*)(size_t*, IEnumConnections**))VTbl[7];
   IEnumConnections* pIEnumConnections=NULL;
   printf("    %u\t%u\t%u\t",&pVTbl[2],&VTbl[7],pEnumConnections);
   pEnumConnections(&pVTbl[2],&pIEnumConnections);

   printf("\n");
   hr=pGrid->QueryInterface(IID_IConnectionPointContainer, (void**)&pCnPtCnt);
   if(SUCCEEDED(hr))
   {
      printf("    pGrid->QueryInterface(IID_IConnectionPointContainer) Succeeded!\n");
      printf("    pCnPtCnt = %u\n",pCnPtCnt);
      pCnPtCnt->Release();
   }
   else
      printf("   pGrid->QueryInterface(IID_IConnectionPointContainer) Failed!\n");
}
printf("  Leaving DumpCOMMemory()\n");
}


void DestroyGrid(HWND hHost)
{
printf("  Entering DestroyGrid()\n");
IGrid* pGrid=(IGrid*)GetWindowLongPtr(hHost,0);
printf("  pGrid = %u\n",pGrid);
if(pGrid)
{
    pGrid->Release();
    SetWindowLongPtr(hHost,0,NULL);
}
printf("  Leaving DestroyGrid()\n");
}


long fnWndProc_OnCommand(lpWndEventArgs Wea)
{
switch(LOWORD(Wea->wParam))
{
    case IDC_UNLOAD_GRID:
      {
         printf("\nEntering fnWndProc_OnCommand()\n");
         DestroyGrid(Wea->hWnd);
         printf("Leaving fnWndProc_OnCommand()\n");
         break;
      }
    case IDC_DUMP_COM_MEMORY:
      {
         printf("\nEntering fnWndProc_OnCommand()\n");
         DumpCOMMemory(Wea->hWnd);
         printf("Leaving fnWndProc_OnCommand()\n");
         break;
      }
}

return 0;
}


long fnWndProc_OnPaint(lpWndEventArgs Wea)
{
PAINTSTRUCT ps;
int iBkMode;
HDC hDC;

hDC=BeginPaint(Wea->hWnd,&ps);
iBkMode=SetBkMode(hDC,TRANSPARENT);
TextOut(hDC,40,45,_T("The ActiveX Grid Control Will Eventually Go Here!"),49);
SetBkMode(hDC,iBkMode);
EndPaint(Wea->hWnd,&ps);

return 0;
}


long fnWndProc_OnDestroy(lpWndEventArgs Wea)
{
DestroyGrid(Wea->hWnd);
PostQuitMessage(0);
return 0;
}


LRESULT CALLBACK WndProc(HWND hwnd, unsigned int msg, WPARAM wParam, LPARAM lParam)
{
WindowsEventArguments Wea;

for(unsigned int i=0; i<dim(EventHandler); i++)
{
     if(EventHandler[i].iMsg==msg)
     {
        Wea.hWnd=hwnd, Wea.lParam=lParam, Wea.wParam=wParam;
        return (*EventHandler[i].fnPtr)(&Wea);
     }
}

return (DefWindowProc(hwnd, msg, wParam, lParam));
}


int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
TCHAR szClass[]=_T("64 Bit Grid");
WNDCLASSEX wc;
HWND hWnd;
MSG Msg;

wc.lpszClassName=szClass,                 wc.lpfnWndProc=WndProc,                   wc.cbSize=sizeof(WNDCLASSEX);
wc.style=0,                               wc.cbClsExtra=0,                          wc.cbWndExtra=40;
wc.hInstance=hInstance,                   wc.hIcon=LoadIcon(NULL,IDI_APPLICATION),  wc.hCursor=LoadCursor(NULL,IDC_ARROW);
wc.hbrBackground=(HBRUSH)COLOR_BTNSHADOW, wc.lpszMenuName=NULL,                     wc.hIconSm=LoadIcon(NULL, IDI_APPLICATION);
RegisterClassEx(&wc);
hWnd=CreateWindow(szClass,_T("64 Bit Grid ActiveX Control"),WS_OVERLAPPEDWINDOW,350,550,425,200,NULL,NULL,hInstance,NULL);
ShowWindow(hWnd,nCmdShow);
while(GetMessage(&Msg, NULL, 0, 0))
{
    TranslateMessage(&Msg);
    DispatchMessage(&Msg);
}  CoFreeUnusedLibraries(), CoUninitialize(), MessageBox(NULL,_T("... Before I Throw It Out!"),_T("Come And Get It ..."),MB_OK);

return (int)Msg.wParam;
}


continued ...

Frederick J. Harris

#3
     It was fun to get this working both 32 and 64 bit.  The same exact code can be used for either 32 or 64 bit, if compiled with the respective 32 or 64 bit compiler.  The neat part of this code is a procedure named ...

void DumpCOMMemory(HWND);

     In that code I iterate through every interface and interface member using function pointers.  Except for QueryInterface(), AddRef(), and Release(), all the interface functions are just 'stubbed out' and only contain printf output statements showing they were called.  Note that to expedite this I modified the interface member function signatures of the function pointers to make iterating through them more in the nature of iterating through an array.  Actually, the memory footprint created by COM objects is very much 'array like'.  The variable type I used for collecting memory addresses of VTables and VTable pointers was size_t.  Using this variable type works for both 32 and 64 bit.  It is defined as an unsigned int in x86 and as an unsigned int* in x64, thereby being 4 bytes and 8 bytes respectively.

     Client.cpp creates Client.exe and is a GUI program which, however, creates an AllocConsole() console window to show debug output of what's going on.  There are two buttons on the form/dialog.  The button on the left executes the DumpCOMMemory() procedure.  Here is the console output I get when running 64 bit, if I start the program, click on the 'DumpCOMMemory' button, then exit  the program.  I'm thinking I'll put comments  prefaced with the C++ '//' symbol to the right of the various debug output lines to describe details I think are noteworthy or important...


Entering fnWndProc_OnCreate()                      // OnCreate() is kind of a Constructor for the
  sizeof(IGrid*) = 8                               // Client's main and only window.  In there I create
  CoInitialize() Succeeded!                        // the console then call CoGetClassFactory().  That's
                                                   // where the DllGetClassObject() function gets called.
  Entering DllGetClassObject()                     // That would be system code - not any of mine here.
    Entering GridClassFactory Constructor!         // Note that the sizeof(IGrid*) = 8.  Actually, the
      this->m_lRef = 0                             // size of any pointer is 8 because its a 64 bit app.
    Leaving GridClassFactory Constructor!          // Anyway, DllGetClassObject() instantiates a
    pCF = 2314112                                  // GridClassFactory() object (look in Grid.cpp), and
    Entering GridClassFactory::QueryInterface()    // the GridClassFactory object has a method named
      *ppv = 2314112                               // CreateInstance(), and that's where an FHGrid object
      Entering GridClassFactory::AddRef()!         // gets created.
        this->m_lRef = 0
        this->m_lRef = 1                           // Which brings us to GridClassFactory::CreateInstance()
      Leaving GridClassFactory::AddRef()!          // (see below). Take note of the line ...
    Leaving GridClassFactory::QueryInterface()
  Leaving DllGetClassObject()                      //                sizeof(*this) = 32

  Entering GridClassFactory::AddRef()!             // What that's saying is that the size in memory of   
    this->m_lRef = 1                               // what's been created/instantiated at pGrid - which
    this->m_lRef = 2                               // afterall is a pointer to a grid, is 32 bytes.  Do
  Leaving GridClassFactory::AddRef()!              // you know why that is?  I guess you know I'm going
                                                   // to tell you!  If you look at Grid.h you'll see the
  Entering GridClassFactory::Release()!            // C++ Class declaration for FHGrid.  You'll note that 
    this->m_lRef = 2                               // that Class publically inherits from three other
    this->m_lRef = 1                               // C++ Abstract Base classes, i.e., ...
  Leaving GridClassFactory::Release()!
                                                   //       IGrid
  Entering GridClassFactory::QueryInterface()      //       IConnectionPointContainer
    *ppv = 2314112                                 //       IConnectionPoint
    Entering GridClassFactory::AddRef()!
      this->m_lRef = 1                             // An Abstract Base Class is a Class that hasn't of yet
      this->m_lRef = 2                             // been implemented.  If you look at the IGrid interface
    Leaving GridClassFactory::AddRef()!            // in Interfaces.h you'll see methods in that object
  Leaving GridClassFactory::QueryInterface()       // prefaced with the 'virtual' keyword and ending with an
                                                   // '=0;'  IGrid as well as IConnectionPointContainer and
  Entering GridClassFactory::Release()!            // IConnectionPoint are actually structs, but that
    this->m_lRef = 2                               // distinction is greatly blurred in C++.  The point is
    this->m_lRef = 1                               // though that these three structs/interfaces define 
  Leaving GridClassFactory::Release()!             // memory blocks that the C++ compiler will create when
                                                   // it creates an FHGrid object.  The IGrid interface for 
  CoGetClassObject() Succeeded!                    // x64 will be 128 bytes because it has 16 member
  pClassFactory = 2314112                          // functions (count them) including the three of
                                                   // IUnknown, from which it inherits.  And now get this -
  Entering GridClassFactory::CreateInstance()      // a pointer to those 128 bytes occupies 8 bytes of the   
    Entering FHGrid Constructor!                   // 32 bytes that constitutes the memory allocation for   
      this->m_lRef  = 0                            // an FHGrid object.  And as the code just left shows,
      g_lObjs       = 1                            // pGrid = 2,314,144.  At that address is stored the
      sizeof(*this) = 32                           // pointer to the IGrid VTable - which itself is a 128 
    Leaving FHGrid Constructor!                    // byte (64 for x86) memory allocation/block.  So that 
    pGrid           = 2314144                      // accounts for 8 of the 32 bytes.  What do you think
    sizeof(*pGrid)  = 32                           // is stored at the remaining 24 bytes?  Well, After the
    Entering FHGrid::QueryInterface()              // IGrid VTable pointer comes the
      this = 2314144                               // IConnectionPointContainer VTable pointer.  That would
      Entering FHGrid::AddRef()                    // be stored in the next 8 bytes starting at 2,314,152.
        m_lRef = 0                                 // And following that in the next 8 bytes is the
        m_lRef = 1                                 // IConnectionPoint VTable Pointer at 2,314,160.  The
      Leaving FHGrid::AddRef()                     // remaining 8 bytes of the 32 is the single member
      *ppv = 2314144                               // variable of the FHGrid Class - m_lRef (see FHGrid.h). 
    Leaving FHGrid::QueryInterface()
  Leaving GridClassFactory::CreateInstance()       // So, every time an instance of FHGrid is created the
                                                   // compiler will allocate 32 bytes for instance data.
  IClassFactory->CreateInstance() Succeeded!       // It may occur to you that 32 bytes doesn't seem like
  pGrid = 2314144                                  // enough.  Shouldn't the code of the interface functions
                                                   // be included in the size of the class?  Actually not.
  Entering GridClassFactory::Release()!            // Interfaces are 'stateless'.  They have no member
    this->m_lRef = 1                               // variables where any instance data is stored, and when
    this->m_lRef = 0                               // a program is loaded which contains a class only one
    Entering GridClassFactory Destructor!          // copy of the member functions need to be loaded in
      this->m_lRef = 0                             // memory, and those functions don't belong to any
    Leaving GridClassFactory Destructor!           // particular instance.  They do operate on instance
    GridClassFactory Has Been Destroyed!           // data but the data they operate on is stored in the
  Leaving GridClassFactory::Release()!             // class instance data - not in the interfaces.  So
Leaving fnWndProc_OnCreate()                       // that's why we only need 32 bytes in this case.  To
                                                   // illustrate all this lets look at the output just left
                                                   // where somebody clicked on the 'DumpCOMMemory' button
Entering fnWndProc_OnCommand()                     // on the Client.exe window.  There we see that 2,314,144
  Entering DumpCOMMemory()                         // number again.  Its a virtual function table pointer -
    pGrid     = 2314144                            // pVTbl.  Its the number PowerBASIC would give you if
    pVTbl     = 2314144                            // you used its Objptr() function on the number returned
                                                   // by something like so ...
    &pVTbl[0] = 2314144
    &pVTbl[1] = 2314152                            // pObject = NewCom "FHGrid.Grid"
    &pVTbl[2] = 2314160
                                                   // You can think of these structures as I've described
    VTbl      = 2147547400                         // above as integral arrays, and that's just how the   
    VTbl[0]   = 2147488996                         // code in DumpCOMMemory() iterates through them.  For
                                                   // example, at 2,314,144 is stored the number
    Entering FHGrid::QueryInterface()              // 2,147,547,400.  That number is the base address of
      this = 2314144                               // the IGrid VTable.  You'll see that number in row 1
    Leaving FHGrid::QueryInterface()               // column 2 just below.  Note those numbers increment
                                                   // in steps of 8 bytes, and there are 16 'slots' for the
    Entering FHGrid::AddRef()                      // addresses of the 16 member functions of the IGrid
      m_lRef = 1                                   // interface.  In column 3 are the numbers stored in
      m_lRef = 2                                   // the VTable slots.  Those column three numbers are the
    Leaving FHGrid::AddRef()                       // actual memory addresses for the interface functions.
                                                   // In DumpCOMMemory() you'll see where I've created
    Entering FHGrid::Release()                     // function pointer prototypes to iterate through all
      m_lRef = 2                                   // these functions and call them through these addresses.
      m_lRef = 1                                   
    Leaving FHGrid::Release()

    &pVTbl[i]   &VTbl[j]        pFn=VTbl[j]     pFn() Function Pointer Call
    ===========================================================================
    2314144     2147547400      2147488996
    2314144     2147547408      2147489260
    2314144     2147547416      2147489340
    2314144     2147547424      2147489472      Called FHGrid::CreateGrid()
    2314144     2147547432      2147489496      Called FHGrid::SetRowCount()
    2314144     2147547440      2147489496      Called FHGrid::GetRowCount()
    2314144     2147547448      2147489544      Called FHGrid::SetData()
    2314144     2147547456      2147489568      Called FHGrid::GetData()
    2314144     2147547464      2147489592      Called FHGrid::FlushData()
    2314144     2147547472      2147489616      Called FHGrid::Refresh()
    2314144     2147547480      2147489640      Called FHGrid::GetVisibleRows()
    2314144     2147547488      2147489664      Called FHGrid::GethGrid()
    2314144     2147547496      2147489688      Called FHGrid::GethCell()
    2314144     2147547504      2147489688      Called FHGrid::GethCell()
    2314144     2147547512      2147489712      Called FHGrid::SetCellAttributes()
    2314144     2147547520      2147489736      Called FHGrid::DeleteRow()

    2314152     2147547368      2147489760      Called FHGrid::EnumConnectionPoints()
    2314152     2147547376      2147489788      Called FHGrid::FindConnectionPoint()

    2314160     2147547296      2147489816      Called FHGrid::GetConnectionInterface()
    2314160     2147547304      2147489844      Called FHGrid::GetConnectionPointContainer()
    2314160     2147547312      2147489872      Called FHGrid::Advise()
    2314160     2147547320      2147489900      Called FHGrid::Unadvise()
    2314160     2147547328      2147489924      Called FHGrid::EnumConnections()

    Entering FHGrid::QueryInterface()
      this = 2314144                            // Let me illustrate how FHGrid::CreateGrid was called. 
      Entering FHGrid::AddRef()                 // Note that since this is a member of the IGrid
        m_lRef = 1                              // interface (VTable), the address 2,314,144 holds the
        m_lRef = 2                              // base address of its VTable, which is 2,147,547,400.
      Leaving FHGrid::AddRef()                  // Note that QueryInterface(), AddRef(), and Release()
      *ppv = 2314152                            // are the 1st three functions in the IGrid VTable, i.e.,
    Leaving FHGrid::QueryInterface()
    pGrid->QueryInterface(IID_IConnectionPointContainer) Succeeded!
    pCnPtCnt = 2314152
    Entering FHGrid::Release()                  // VTbl[0], VTbl[1], and VTbl[2], which are stored at
      m_lRef = 2                                // &VTbl[0], &VTbl[1], and &VTbl[2].  CreateGrid() is the
      m_lRef = 1                                // 1st member after Release, which, in zero based terms, is
    Leaving FHGrid::Release()                   // at VTbl[3], which is stored at &VTbl[3].  Those numbers
  Leaving DumpCOMMemory()                       // would be 2,147,489,472 and 2,147,547,424, respectively.
Leaving fnWndProc_OnCommand()                   // To prove this we can make a function pointer call at the
                                                // 2,147,489,472 number, i.e., VTb[3], and we'll get the
                                                // output shown above, i.e., 'Called FHGrid::CreateGrid()'.
Entering fnWndProc_OnCommand()                  // Of course, that's easier said than done, as calling
  Entering DestroyGrid()                        // object member functions is nasty in PowerBASIC and about
    pGrid = 2314144                             // five times worse in C syntax, but it can nevertheless be
    Entering FHGrid::Release()                  // done.
      m_lRef = 1
      m_lRef = 0                                // Discussion continued just below ...
      Will Now Delete this ...
    Leaving FHGrid::Release()
    Entering FHGrid Destructor!
      g_lObjs = 1
      g_lObjs = 0
    Leaving FHGrid Destructor!
  Leaving DestroyGrid()
Leaving fnWndProc_OnCommand()

Entering DestroyGrid()
  pGrid = 0
Leaving DestroyGrid()

Entering DllCanUnloadNow()
  g_lObjs  = 0
  g_lLocks = 0
  I'm Outta Here!
Leaving DllCanUnloadNow()


By the way, here's the output from the exact same Client.exe program and COM object but running 32 bit ...


Entering fnWndProc_OnCreate()
  sizeof(IGrid*) = 4
  CoInitialize() Succeeded!

  Entering DllGetClassObject()
    Entering GridClassFactory Constructor!
      this->m_lRef = 0
    Leaving GridClassFactory Constructor!
    pCF = 7342712
    Entering GridClassFactory::QueryInterface()
      *ppv = 7342712
      Entering GridClassFactory::AddRef()!
        this->m_lRef = 0
        this->m_lRef = 1
      Leaving GridClassFactory::AddRef()!
    Leaving GridClassFactory::QueryInterface()
  Leaving DllGetClassObject()

  Entering GridClassFactory::AddRef()!
    this->m_lRef = 1
    this->m_lRef = 2
  Leaving GridClassFactory::AddRef()!

  Entering GridClassFactory::Release()!
    this->m_lRef = 2
    this->m_lRef = 1
  Leaving GridClassFactory::Release()!

  Entering GridClassFactory::QueryInterface()
    *ppv = 7342712
    Entering GridClassFactory::AddRef()!
      this->m_lRef = 1
      this->m_lRef = 2
    Leaving GridClassFactory::AddRef()!
  Leaving GridClassFactory::QueryInterface()

  Entering GridClassFactory::Release()!
    this->m_lRef = 2
    this->m_lRef = 1
  Leaving GridClassFactory::Release()!

  CoGetClassObject() Succeeded!
  pClassFactory = 7342712

  Entering GridClassFactory::CreateInstance()
    Entering FHGrid Constructor!
      this->m_lRef = 0
      g_lObjs = 1
    Leaving FHGrid Constructor!
    pGrid = 7342728
    Entering FHGrid::QueryInterface()
      this = 7342728
      Entering FHGrid::AddRef()
        m_lRef = 0
        m_lRef = 1
      Leaving FHGrid::AddRef()
      *ppv = 7342728
    Leaving FHGrid::QueryInterface()
  Leaving GridClassFactory::CreateInstance()

  IClassFactory->CreateInstance() Succeeded!
  pGrid = 7342728

  Entering GridClassFactory::Release()!
    this->m_lRef = 1
    this->m_lRef = 0
    Entering GridClassFactory Destructor!
      this->m_lRef = 0
    Leaving GridClassFactory Destructor!
    GridClassFactory Has Been Destroyed!
  Leaving GridClassFactory::Release()!
Leaving fnWndProc_OnCreate()


Entering fnWndProc_OnCommand()
  Entering DumpCOMMemory()
    pGrid     = 7342728
    pVTbl     = 7342728
    &pVTbl[0] = 7342728
    &pVTbl[1] = 7342732
    &pVTbl[2] = 7342736
    VTbl      = 268498668
    VTbl[0]   = 268440522

    Entering FHGrid::QueryInterface()
      this = 7342728
    Leaving FHGrid::QueryInterface()

    Entering FHGrid::AddRef()
      m_lRef = 1
      m_lRef = 2
    Leaving FHGrid::AddRef()

    Entering FHGrid::Release()
      m_lRef = 2
      m_lRef = 1
    Leaving FHGrid::Release()

    &pVTbl[i]   &VTbl[j]        pFn=VTbl[j]     pFn() Function Pointer Call
    ===========================================================================
    7342728     268498668       268440522
    7342728     268498672       268440709
    7342728     268498676       268440779
    7342728     268498680       268440896       Called FHGrid::CreateGrid()
    7342728     268498684       268440912       Called FHGrid::SetRowCount()
    7342728     268498688       268440912       Called FHGrid::GetRowCount()
    7342728     268498692       268440944       Called FHGrid::SetData()
    7342728     268498696       268440960       Called FHGrid::GetData()
    7342728     268498700       268440976       Called FHGrid::FlushData()
    7342728     268498704       268440992       Called FHGrid::Refresh()
    7342728     268498708       268441008       Called FHGrid::GetVisibleRows()
    7342728     268498712       268441024       Called FHGrid::GethGrid()
    7342728     268498716       268441040       Called FHGrid::GethCell()
    7342728     268498720       268441056       Called FHGrid::GethCell()
    7342728     268498724       268441072       Called FHGrid::SetCellAttributes()
    7342728     268498728       268441088       Called FHGrid::DeleteRow()

    7342732     268498656       268441104       Called FHGrid::EnumConnectionPoints()
    7342732     268498660       268441123       Called FHGrid::FindConnectionPoint()

    7342736     268498620       268441142       Called FHGrid::GetConnectionInterface()
    7342736     268498624       268441161       Called FHGrid::GetConnectionPointContainer()
    7342736     268498628       268441180       Called FHGrid::Advise()
    7342736     268498632       268441199       Called FHGrid::Unadvise()
    7342736     268498636       268441215       Called FHGrid::EnumConnections()

    Entering FHGrid::QueryInterface()
      this = 7342728
      Entering FHGrid::AddRef()
        m_lRef = 1
        m_lRef = 2
      Leaving FHGrid::AddRef()
      *ppv = 7342732
    Leaving FHGrid::QueryInterface()

    pGrid->QueryInterface(IID_IConnectionPointContainer) Succeeded!
    pConnectionPointContainer = 7342732

    Entering FHGrid::Release()
      m_lRef = 2
      m_lRef = 1
    Leaving FHGrid::Release()
  Leaving DumpCOMMemory()
Leaving fnWndProc_OnCommand()

Entering DestroyGrid()
  pGrid = 7342728
  Entering FHGrid::Release()
    m_lRef = 1
    m_lRef = 0
    Will Now Delete this ...
  Leaving FHGrid::Release()
  Entering FHGrid Destructor!
    g_lObjs = 1
    g_lObjs = 0
  Leaving FHGrid Destructor!
Leaving DestroyGrid()

Entering DllCanUnloadNow()
  g_lObjs  = 0
  g_lLocks = 0
  I'm Outta Here!
Leaving DllCanUnloadNow()


     Here's how it works, that is, calling IGrid::CreateGrid() through a function pointer.  First, if you've attempted to build this code and have run midl.exe on my FHGrid.idl file that utility would have created a number of output files, one of which would be FHGrid.h.  I didn't use that file in my project but I could have.  It contains, among other things, C++ and C interface definitions derived from the Idl code in the Idl file.  Here are the C++ and C declarations of interface function CreateGrid.  First C++ ...


virtual HRESULT STDMETHODCALLTYPE CreateGrid
(
  /* [in] */ HWND hParent,
  /* [in] */ BSTR strSetup,
  /* [in] */ int x,
  /* [in] */ int y,
  /* [in] */ int cx,
  /* [in] */ int cy,
  /* [in] */ int iRows,
  /* [in] */ int iCols,
  /* [in] */ int iRowHt,
  /* [in] */ int iSelectionBackColor,
  /* [in] */ int iSelectionTextColor,
  /* [in] */ BSTR strFontName,
  /* [in] */ int iFontSize,
  /* [in] */ int iFontWeight
) = 0;


In C its not going to be exactly the same because there is a certain amount of nastiness that C++ hides from us.  The price you pay for that is you might not understand what's going on behind the scenes.  It becomes a function pointer call in C ..


HRESULT (STDMETHODCALLTYPE* CreateGrid)
(
  /* [in] */ IGrid* This,
  /* [in] */ HWND hParent,
  /* [in] */ BSTR strSetup,
  /* [in] */ int x,
  /* [in] */ int y,
  /* [in] */ int cx,
  /* [in] */ int cy,
  /* [in] */ int iRows,
  /* [in] */ int iCols,
  /* [in] */ int iRowHt,
  /* [in] */ int iSelectionBackColor,
  /* [in] */ int iSelectionTextColor,
  /* [in] */ BSTR strFontName,
  /* [in] */ int iFontSize,
  /* [in] */ int iFontWeight
);


If you count them there are more parameters there; 15 to be exact.  The other major difference though is that the text 'CreateGrid' is enclosed within parentheses along with the STDMETHODCALLTYPE, and there is that mysterious '*' in there too.  That little '*' symbol plus the parentheses is a dead giveaway for a C/C++ function pointer call.  In this latter C declaration the 1st parameter is IGrid* This.  Remember above where I stated that interfaces are 'stateless', i.e., there are no member variables in interfaces?  Well, that's true but interface member functions have to work on instance data somehow, for example Get()/Set() mutators that change class instance data.  But how do they know where that instance data is at?  Well, you're looking at it.  Every time an interface member function is called the first parameter of the call will be a pointer to where the class instance data is at.  In C its plainly visible but in C++ or high level PowerBASIC its the hidden this/me pointer.

I wanted to call all these various entities with an array like syntax and iterate through them so this is what I did.  I created the variables pVTbl and VTbl of type size_t.  This variable type is declared in various low level Windows or C Run Time headers, and is 4 bytes in x86 and 8 bytes in x64.  At the top of DumpCOMMemory() I retrieved my stored IGrid* returned from IClassFactory::CreateInstance() in fnWndProc_OnCreate().  So I cast it like so ...


size_t* pVTbl=0;
size_t* pIUnk=0;
size_t* VTbl=0;

IGrid* pGrid=(IGrid*)GetWindowLongPtr(hHost,0);
pVTbl=(size_t*)pGrid;
printf("    pGrid     = %u\n", pGrid);
printf("    pVTbl     = %u\n", pVTbl);
printf("    &pVTbl[0] = %u\n", &pVTbl[0]);
printf("    &pVTbl[1] = %u\n", &pVTbl[1]);
printf("    &pVTbl[2] = %u\n", &pVTbl[2]);


And that produced this output ...


pGrid     = 2314144                           
pVTbl     = 2314144                           
&pVTbl[0] = 2314144
&pVTbl[1] = 2314152                           
&pVTbl[2] = 2314160


The only thing I want to change above to use the C function pointer declaration is that first IGrid* parameter.  I'd rather use size_t instead, which is OK so long as the correct address is stored in my size_t variable.  Also, I'll call my function pointer pCreateGrid instead ...
   
pCreateGrid=(HRESULT (STDMETHODCALLTYPE*)(size_t*,HWND,BSTR,int,int,int,int,int,int,int,int,int,BSTR,int,int))VTbl[3];

That looks complicated and it is but can be easily understood by taking it apart in pieces.  In its simplest form it would be nice if one could do simply this ...

pCreateGrid = VTbl[3];

That says "assign whatever is at VTbl[3] to pCreateGrid".  The number 2147489472 is the address of the IGrid::CreateGrid() member function, but C/C++ won't allow it to be assigned that way because pCreateGrid was declared as a function pointer that returns an HRESULT, has STDMETHODCALLTYPE calling convention, and has 15 parameters of various types.  So to make the compiler happy one must cast VTbl[3] to a function pointer type that returns an HRESULT, uses STDMETHODCALLTYPE calling convention, and has those 15 various parameters.  Casts in C/C++ usually look pretty simple, for example, casting the return from the GetWindowLongPtr() above from a LONG_PTR to an IGrid* i.e. ...

IGrid* pGrid=(IGrid*)GetWindowLongPtr(hHost,0);

... where this simple term does the trick ...

(IGrid*)

But in this function pointer case the expression between the parentheses is rather nasty.  In fact, you need the entire function pointer declaration to the right of the equal sign except the name of the function pointer, that is, pCreateGrid ...


(HRESULT  (STDMETHODCALLTYPE*)  (size_t*,HWND,BSTR,int,int,int,int,int,int,int,int,int,BSTR,int,int))
    ^             ^                                               ^                       
return     stack protocol                              15 Various Parameters


So if you look up in DumpCOMMemory() you'll see the entire sequence of these lines ...


//IGrid::CreateGrid()
pCreateGrid=(HRESULT (STDMETHODCALLTYPE*)(size_t*,HWND,BSTR,int,int,int,int,int,int,int,int,int,BSTR,int,int))VTbl[3];
printf("    %u\t%u\t%u\t",&pVTbl[0],&VTbl[3],pCreateGrid);
HWND hWnd=NULL;
BSTR strNothing=NULL;
pCreateGrid(&pVTbl[0], hWnd, strNothing, 0, 0, 0, 0, 0, 0, 0, 0, 0, strNothing, 0, 0);

... which created this line of output ...


&pVTbl[i]   &VTbl[j]        pFn=VTbl[j]     pFn() Function Pointer Call
===========================================================================
.......     ..........      ..........
2314144     2147547424      2147489472      Called FHGrid::CreateGrid()
.......     ..........      ..........


Enough of this.  Its time to start building (rebuilding actually) the grid!!!


Mike Stefanik

I understand that you're likely doing this for the educational experience, but is there a practical matter as to why you're not using ATL? It really does eliminate the vast majority of the COM funk from your code and let you focus on the actual implementation rather than the scaffolding.

Frederick J. Harris

#5
Well, maybe I do need more practice, so the educational value isn't to be discounted.  It took me awhile to put this together, even though I've done it all before.  So I think I benefited from the practice! 

COM is involved I would say, and I was somewhat daunted by the issue of doing everything in x64.  I've simply not done much x64 and I really had no idea how switching to x64 Operating systems would affect everything having to do with COM.  Much to my surprise I've found everything – at least what I've done so far – works just like 32 bit.  So having reviewed the fundamentals at this low a level as I did with x64 was a good idea I think for me.  So far I'm amazed at how well Microsoft has made everything with x64 work out. 

I'm somewhat familiar with ATL and I've made a few learning type projects with it.  Its also impressive in terms of how Microsoft set it up to reduce code bloat.  I've no better reason for not using it other than my simply not liking it.  I prefer to figure out things myself and understand how they work rather than relying on auto-generated code.  I think at base its because I like to be somewhat self reliant, and having started with coding before the internet was much of a part of the picture, I've never proceeded with the idea in mind that if I'd get bad stuck, all I'd have to do is shoot out a question on some coding forum and piles of folks would jump in and attempt to solve my problems for me – hence the need to fully understand the basics.  So I don't like to get too far ahead of myself. 

Then too I'm no friend of templates.  I'm sure you've run into C coders like me who've made off with what they wanted from C++ (classes, reference parameters) and left the rest (templates, endless arguments about object design and class inheritance hierarchies).   

I realize it was Microsoft's idea that everybody would use ATL to build COM components, simply because so much of the underlying COM infrastructure code was both boilerplate and complex.  So C++ had ATL and Visual Basic had what it had – ActiveX Control visual designers and so forth.  But none of that helped us much with PowerBASIC.  If you wanted to build ActiveX Controls with PowerBASIC you pretty much had to start from scratch and fully understand everything to the point of building Vtables in memory, so on and so forth.  Actually, that was my main interest in ATL – to reverse engineer the auto-generated code so to speak,  so I could see what it did.  The reason for this was because there was little to no documentation out there on building ActiveX Controls in the raw, the way it would have to be done in PowerBASIC, or C for that matter without ATL.  And you know, now that I think of it, those internet articles by Jeff Glatt – I'm sure you remember them "COM In Plain C" ...

http://www.codeproject.com/Articles/13601/COM-in-plain-C

... were really, really popular.  A lot of C++ coders I believe were somewhat fed up with the various 'text substitution engines', ATL, MFC, etc., coming out of Microsoft, and just wanted to know how stuff worked.

But I'm not above saving a little time.  Because so much of the COM infrastructure code is so boilerplate, only a little modification and addition to what I have posted above – a simple window and some logic to call back into an event sink in the server, and I'll have a pretty good control template of my own for future use.    And you know, that isn't a whole lot different from how a lot of us SDK coders operate with our many years collections of various template code.

James C. Fuller

Mike,
My reason for following Fred's work is I use Visual Studio Express 2013 and there is no ATL/MFC so bare to the metal is the only way to go.

James

Frederick J. Harris

If you are following the code James I'd appreciate if you would let me know of any issues you run into with the newer compilers.  I had thought of doing what you are doing, that is, installing the latest SDK, since I'm doing command line compiling anyway so Visual Studio's fancy wizards aren't helping me anyway!  But I paid $550 for it a few years back (pretty much for my Windows CE data collector work - which didn't pan out.  I hated Windows Mobile, like just about everyone else, and reverted to Windows CE.  And I had free tools for that work (Microsoft's eMbedded Visual C++ 4.0)).   

The other thing is, I'd actually prefer to use GNU, but that rotten problem I had years ago with not being able to figure out how to export DllGetClassObject() so that Microsoft's COM Service Control Manager ( SCUM ) could read it - well, that still has me stumped.  Maybe that would be something you might want to look into someday because I know you like to use the GNU tools.   And I hate like the dickens to admit defeat.  But that defeated me no ifs ands or buts.  I lost weeks on that.  And I know it can be done.  I just can't figure out how to do it. 

James C. Fuller

Fred,
  A week or so ago I went back to old COM code that I thought I had working based on your original com project.
I beat on it awhile but other things took precedence. I hope to get back to it but I got a bit burned out on it the first time around.

Your code here is a bit beyond what I want to do. I have no need for an ActiveX grid so I lack your motivation.
I'm just hoping I can gain a bit of insight into 64bit COM from your work.

James