• Welcome to Jose's Read Only Forum 2023.
 

Finished C++ Conversion Of PowerBASIC Grid Code. I'm Happy!

Started by Frederick J. Harris, January 03, 2014, 05:46:50 PM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

Frederick J. Harris

     Isn't it a wonderful feeling to have tackled a complex and important coding project and to have finally finished it with a good outcome?  That's how I feel now with having translated my COM based PowerBASIC Grid Control over to C++.  So I know how you felt Patrice when you finally got your dlls/products working in C++!  While I used Microsoft's VC 9 from Visual Studio 2008 to compile the grid for both x86 and x64, I wrote a client for testing purposes which comes in six different flavors (different languages and compilers).  Before I get to the grid I'd like to talk about that testing client a little, as that's what I've been working on for the past couple days since having completed the grid work last week.  So this will be like a movie that starts at the end, which has been a popular theatrical technique for some time now. 

     First, of course, I used the same compiler, i.e., Microsoft's, for two of the clients, that is, an x86 and a x64 version.  Then I used my favorite C++ compiler, which is the one I got with my Code::Blocks 10.05 installation - GCC 4.4.1-2 circa 2009 or so.  That only makes 32 bit binaries though.  The next two would be the latest MinGW GCC compiler, release date October 2013, which does both x86 and x64 exes....

http://tdm-gcc.tdragon.net/download

     Finally, I cobbled together a PowerBASIC client using version 10, i.e., PBWin 10.  Of course, that would be just for the 32 bit version of the grid.  I've got to say it was pretty cool running all six of those clients - some 32 bit and some 64 bit - at the same time on my laptop!  Especially when you consider that they all have the exact same GUIDS, ProgIDs, the works!  And except for the PowerBASIC code they all stem from the exact same source code, line for line.  The client program screen footprint is fairly small so they all fit on my screen at the same time.  There is just a grid with 8 rows of dummy data and seven buttons that exercise various functionality of the grid when clicked, such as retrieving data, filling the grid, coloring rows, selecting rows, showing the Combo Box in Column 5, etc.  All the clients were compiled with settings to minimize code size and strip debugging symbols from the executable.  Here's the results.  Read 'em and weep any of you PowerBASIC or Bob Zale haters out there!


                                 ActiveX Grid Clients

Compiler                       Architecture   Date       Size    Easy String Handling
=======================================================================================
PowerBASIC Windows 10.03            x86      3/1/2011     25K    Yes  Native PowerBASIC

tdm-mingw-1.908.0-4.4.1-2 GCC       x86     10/4/2009     27K     No  C Style Strings

tdm-mingw-1.908.0-4.4.1-2 GCC       x86     10/4/2009     35K    Yes  My String Class

Microsoft VC++ 9 VStudio 2008       x64     11/8/2007     80K    Yes  My String Class
                                                                                                                                                                               
tdm64-gcc-4.8.1-3         GCC       x86     10/6/2013    159K    Yes  My String Class

tdm64-gcc-4.8.1-3         GCC       x64     10/6/2013    224K    Yes  My String Class


     So PowerBASIC wins at 25K.  Note above I couldn't even beat my PowerBASIC client using C Style strings.  That would be the 2nd example above that came in 27K using the older GCC compiler.  That surprised me.  Usually if I resort to C style strings and leave out any uses of 'class' or 'new' in a C++ program, I can beat out PowerBASIC's size by a hair. For some reason it didn't work here.  PowerBASIC comes out smaller by a couple K.

     Note the massive bloated executables from the most recent GCC offering - 224K is the smallest I could bring down the client on the x64 version.  And this is for exactly the same program and functionality as the 25K PowerBASIC 10 version.  What the deal is there are the new C++ 11 coding standards, which apparently forces incorporation of the winpthreads library into every binary created by the package.  In fact, the installation of that package started with this unusual and noteworthy warning which had been lacking in their previous installations ...

Quote
Hi! Sorry for the interruption, but you may want to take at least a few seconds
to look into some recent license changes for the software you're about to
install.

Parts of the "winpthreads" library will be compiled into every binary file (EXE
or DLL) you create. It's a necessary evil that is currently required in order to
provide support for threads and concurrency in programs compiled by GCC.

The license for winpthreads requires you to reproduce its text in every copy or
substantial portion of the winpthreads library that you distribute. This means
that even if you just want to distribute a single small executable, created with
TDM-GCC (or any winpthreads-based GCC release), you must include a copy of that
license.

Check the license out in the file "COPYING.winpthreads.txt", which will be
installed along with TDM-GCC. Consult with a lawyer if you have any concerns
about how you can use this software.

Does this new license hurt your usage of GCC? Let the developers know!
 
File a feature request asking them to change the winpthreads license or get rid
of GCC's winpthreads requirement, at:

https://sourceforge.net/p/mingw-w64/feature-requests/new/

Send an email to the MinGW-w64 mailing list, at:

mingw-w64-public@lists.sourceforge.net

     So there you have it.  Bob Zale is gone and the "Brave New World" we are entering is a world of bloat ware!  Bloat ware rules!  For all the good it will do I'm going to complain loudly at the above Source Forge email address.  I've heard the saying that 'Progress Rides In A Hearse', so when some of us old folks who bemoan such things are all gone there won't even be any memory of small and efficient programs. 

     Anyway, now to the grid.  My biggest concern (other than whether or not I'd successfully manage the translation and the thing would work) was how many K in size it would end up being.  I was proud of the fact that my PowerBASIC grid was about 50K and the UPX executable packer ...

http://upx.sourceforge.net/

... could compress it to a miniscule 22K!  I knew I'd have to use MSVC for the compiler because I wanted 64 bit, and in my opinion VC9 seemed to create pretty big binaries.  It turned out pretty good though.  I'm happy!  At first I was coming in about 100K in the release build.  I'm using my String Class rather than the one in the C++ Standard Library, so that helped quite a bit - probably to the tune of 30 or 40 K.  Then just here at the end it occurred to me that my grid code makes very little use of my String Class code (although I do need it badly for its ParseCount / Parse functionality), so the idea occurred to me that I could include a special build of the String Class which would only contain the parsing code my grid really needed and nothing else! I was actually able to remove most of the code, and that brought the release build down to 86K.  So I saved about 14 K there.  Finally, the good folks at UPX just released a new version of their exe packer that does x64 binaries (my old version didn't work on x64s - I tried).  So its coming in at 43 K packed.  I don't think that's too bad for a reasonably featured x64 ActiveX Grid Control with built in self-registration.   

     All the PowerBASIC code for my translated grid came in around 3400 lines of code and except for the *.idl file its all in one file which is posted and downloadable from here ...

http://www.jose.it-berater.org/smfforum/index.php?topic=4642.0

     I have the C++ version code organized pretty much according to C++ coding standards, such as they are, and here's what the file/project setup which I'm about to post looks like, with lines of code to the right ...


FHGrid.dll -- ActiveX Grid Control
==================================
FHGrid.idl          58
FHGrid.def           7
Strings.h           81
Strings.cpp        806
Server.cpp         241
Registry.h           5
Registry.cpp       170
Interfaces.h        36
Grid.h             117
Grid.cpp          1121
WinCode.h           59
WinCode.cpp       1311
===================================
                  4012


     By the way, the way I worked it with my special build of the string class, is I created this #define in it ...

#define   JUST_NEED_PARSE

... and if that's not commented out, then everything I don't need is left out using #ifndef conditional compilation logic.  You'll see that if you look at the string class code.  You'll see that in the Strings.cpp file.

I'll start posting the code now, and discuss it afterwards ...

Frederick J. Harris

We'll start small.  Here is FHGrid.def ...


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


And even slaller, FHGrid.rc


1 TYPELIB "FHGrid.tlb"


Now, FHGrid.idl


// 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] COLORREF iSelectionBackColor,
  [in] COLORREF 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;
}
};


Maybe I can fit a few headers ...


//Grid.h
#ifndef     GRID_H_INCLUDED
#define     GRID_H_INCLUDED
#include    "Interfaces.h"
#define     MAX_CONNECTIONS  4
#define     dwIdx(r,c) ((r-1)*pGridData->iCols+(c-1))
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, COLORREF, COLORREF, 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, COLORREF, COLORREF);
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

public:
HWND          hWndCtrl;
IGridEvents** pISink;
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;
};


struct GridData
{
int         iCtrlId;
HWND        hParent;
HWND        hGrid;
HWND        hBase;
HWND        hPane;
HWND        hCtrlInCell;
int         cx;
int         cy;
HWND        hHeader;
int         iCols;
int         iRows;
int         iVisibleRows;
int         iRowHeight;
int         iPaneHeight;
int         iEditedCellRow;
int         iEditedRow;
int         iEditedCol;
FHGrid*     pComObj;
DWORD*      pColWidths;
DWORD*      pCellCtrlTypes;
HWND*       pCellHandles;
TCHAR**     pGridMemory;
COLORREF*   pTextColor;
HBRUSH*     pBackColor;
COLORREF*   pCreatedColors;
HBRUSH*     pCreatedBrushes;
HWND*       pVButtons;
HWND*       pCtrlHdls;
COLORREF    iSelectionBackColor;
COLORREF    iSelectionTextColor;
int         blnRowSelected;
int         iSelectedRow;
int         iFontSize;
int         iFontWeight;
HFONT       hFont;
TCHAR       szFontName[28];
};

void Initialize();
#endif
//End Grid.h

Frederick J. Harris

Interfaces.h


//IFunctions.h
#ifndef INTERFACES_H_INCLUDED
#define INTERFACES_H_INCLUDED
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_IFHGridEvents      =                    {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, COLORREF, COLORREF, 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, COLORREF, COLORREF                                                     ) = 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;
};
#endif


WinCode.h

//WinCode.h
#ifndef WIN_CODE_H_INCLUDED
#define WIN_CODE_H_INCLUDED

#define GRID_CELL_CTRL_NONE         0
#define GRID_CELL_CTRL_EDIT         1
#define GRID_CELL_CTRL_COMBO        2
#define GRID_CELL_CTRL_CHECK        3
#define IDC_STATIC                  -1
#define MAX_COLORS                  15
#define ID_PANE                     1500
#define ID_HEADER                   1505
#define ID_CELL                     1600
#define IDC_EDIT                    1605
#define IDC_COMBO                   1705

#define dim(x) (sizeof(x) / sizeof(x[0]))
LRESULT CALLBACK fnGridProc(HWND hwnd, unsigned int msg, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK fnPaneProc(HWND hwnd, unsigned int msg, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK fnBaseProc(HWND hwnd, unsigned int msg, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK fnCellProc(HWND hwnd, unsigned int msg, WPARAM wParam, LPARAM lParam);


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

typedef WindowsEventArguments*      lpWndEventArgs;

long fnGridProc_OnCreate            (lpWndEventArgs Wea);
long fnGridProc_OnSize              (lpWndEventArgs Wea);
long fnGridProc_OnHScroll           (lpWndEventArgs Wea);
long fnGridProc_OnVScroll           (lpWndEventArgs Wea);
long fnGridProc_OnKeyDown           (lpWndEventArgs Wea);
long fnGridProc_OnCommand           (lpWndEventArgs Wea);
long fnGridProc_OnDestroy           (lpWndEventArgs Wea);

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

const EVENTHANDLER EventHandler[]=
{
{WM_CREATE,                        fnGridProc_OnCreate},
{WM_SIZE,                          fnGridProc_OnSize},
{WM_HSCROLL,                       fnGridProc_OnHScroll},
{WM_VSCROLL,                       fnGridProc_OnVScroll},
{WM_KEYDOWN,                       fnGridProc_OnKeyDown},
{WM_COMMAND,                       fnGridProc_OnCommand},
{WM_DESTROY,                       fnGridProc_OnDestroy}
};

#endif


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);


Frederick J. Harris

Grid.cpp


//Grid.cpp
#define   UNICODE
#define   _UNICODE
//#define MYDEBUG
#include  <windows.h>
#include  <commctrl.h>
#include  <objbase.h>
#include  <ocidl.h>
#include  <olectl.h>
#include  <tchar.h>
#include  <cstdio>
#include  "Grid.h"
#include  "WinCode.h"

extern WNDPROC   fnEditWndProc;
extern HINSTANCE g_hModule;
extern long      g_CtrlId;

#if defined MYDEBUG
extern FILE*     fp;
#endif


FHGrid::FHGrid()   //C++ Constructor for class FHGrid
{
#if defined MYDEBUG
printf("  Entering FHGrid Constructor!\n");
#endif
this->pISink=NULL,  this->hWndCtrl=NULL,  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
}


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("      this->pISink = %u\n\n",this->pISink);
    printf("      i\t\t&this->pISink[i]\t\tthis->pISink[i]\n");
    printf("      ============================================================\n");
    for(size_t i=0; i<MAX_CONNECTIONS; i++)
        printf("      %u\t\t%u\t\t\t\t%u\n",i,&this->pISink[i],this->pISink[i]);
    printf("    \nLeaving FHGrid::Release()\n");
    #endif
    DestroyWindow(this->hWndCtrl);
    CoTaskMemFree(this->pISink);
    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, COLORREF iSelBckClr, COLORREF iSelTxtClr, BSTR strFntNme, int iFntSize, int iFntWt)
{
GridData* pGridData=NULL;
DWORD dwStyle;
HWND hGrid=0;
GridData gd;

#ifndef UNICODE
return E_FAIL;
#endif
#if defined MYDEBUG
printf("Entering FHGrid::CreateGrid()\n");
_ftprintf(fp,_T("Entering FHGrid::CreateGrid()\n"));
_ftprintf(fp,_T("  this       = %u\n"),this);
_ftprintf(fp,_T("  hParent    = %u\n"),hParent);
_ftprintf(fp,_T("  strSetup   = %s\n"),strSetup);
_ftprintf(fp,_T("  x          = %d\n"),x);
_ftprintf(fp,_T("  y          = %d\n"),y);
_ftprintf(fp,_T("  cx         = %d\n"),cx);
_ftprintf(fp,_T("  cy         = %d\n"),cy);
_ftprintf(fp,_T("  iRows      = %d\n"),iRows);
_ftprintf(fp,_T("  iCols      = %d\n"),iCols);
_ftprintf(fp,_T("  iRowHt     = %d\n"),iRowHt);
_ftprintf(fp,_T("  iSelBckClr = %d\n"),iSelBckClr);
_ftprintf(fp,_T("  iSelTxtClr = %d\n"),iSelTxtClr);
_ftprintf(fp,_T("  strFntNme  = %s\n"),strFntNme);
_ftprintf(fp,_T("  iFntSize   = %d\n"),iFntSize);
_ftprintf(fp,_T("  iFntWt     = %d\n"),iFntWt);
#endif
dwStyle                       = WS_CHILD | WS_VISIBLE | WS_HSCROLL | WS_VSCROLL;
gd.hParent                    = hParent;
gd.cx                         = cx;
gd.cy                         = cy;
gd.iRows                      = iRows;
gd.iCols                      = iCols;
gd.iRowHeight                 = iRowHt;
gd.iFontSize                  = iFntSize;
gd.iFontWeight                = iFntWt;
gd.iSelectionBackColor        = iSelBckClr;
gd.iSelectionTextColor        = iSelTxtClr;
gd.iCtrlId=g_CtrlId;
wcscpy(gd.szFontName,strFntNme);
#if defined MYDEBUG
_ftprintf(fp,_T("  gd.szFontName       = %s\n\n"),gd.szFontName);
_ftprintf(fp,_T("  g_CtrlId            = %d\n"),g_CtrlId);
#endif
hGrid=CreateWindowEx(WS_EX_OVERLAPPEDWINDOW,L"Grid",strSetup,dwStyle,x,y,cx,cy,hParent,(HMENU)g_CtrlId,g_hModule,&gd);
if(hGrid==0)
    return E_FAIL;
g_CtrlId++;
this->hWndCtrl=hGrid;
pGridData=(GridData*)GetWindowLongPtr(hGrid,0);
if(!pGridData)
    return E_FAIL;
pGridData->pComObj=this;

if(iSelBckClr==0)
    pGridData->iSelectionBackColor=RGB(0x41,0x69,0xE1);  //  RGB_ROYALBLUE;    // %RGB_ROYALBLUE = 0xE16941
if(iSelTxtClr==0)
    pGridData->iSelectionTextColor=RGB(0x80,0x00,0x00);  //  RGB_MAROON;       // %RGB_MAROON    = 0x000080
this->SetCellAttributes(0,0,pGridData->iSelectionBackColor,pGridData->iSelectionTextColor);
SetFocus(hGrid);

#if defined MYDEBUG
_ftprintf(fp,_T("  hGrid                          = %u\n"),hGrid);
_ftprintf(fp,_T("  pGridData                      = %u\n"),pGridData);
_ftprintf(fp,_T("  pGridData->pComObj             = %u\n"),pGridData->pComObj);
_ftprintf(fp,_T("  this->hWndCtrl                 = %u\n"),this->hWndCtrl);
_ftprintf(fp,_T("  &gd                            = %u\n"),&gd);
_ftprintf(fp,_T("  pGridData->blnRowSelected      = %d\n"),pGridData->blnRowSelected);
_ftprintf(fp,_T("  pGridData->iSelectionBackColor = %0x\n"),pGridData->iSelectionBackColor);
_ftprintf(fp,_T("  pGridData->iSelectionTextColor = %0x\n"),pGridData->iSelectionTextColor);
_ftprintf(fp,_T("Leaving FHGrid::CreateGrid()\n\n"));
printf("  Leaving FHGrid::CreateGrid()\n");
#endif

return S_OK;
}


HRESULT STDMETHODCALLTYPE FHGrid::SetRowCount(int iRowCount, int blnForce)
{
GridData* pGridData=NULL;
int iSize=0,blnFree=0;
HANDLE hHeap=0;
SCROLLINFO si;
int i;

#ifdef MYDEBUG
_ftprintf(fp,_T("Entering FHGrid::SetRowCount()\n"));
_ftprintf(fp,_T("  i         blnFree\n"));
_ftprintf(fp,_T("  =================\n"));
#endif
pGridData=(GridData*)GetWindowLongPtr(this->hWndCtrl,0);
iSize=pGridData->iRows*pGridData->iCols;
hHeap=GetProcessHeap();
for(i=0; i<iSize; i++)
{
     blnFree=HeapFree(hHeap,0,pGridData->pGridMemory[i]);
     #ifdef MYDEBUG
     _ftprintf(fp,_T("  %d\t%d\n"),i,blnFree);
     #endif
}
blnFree=HeapFree(hHeap,0,pGridData->pGridMemory);
#ifdef MYDEBUG
_ftprintf(fp,_T("\n  HeapFree(hHeap,0,pGridData->pGridMemory) = %d\n"),blnFree);
#endif
blnFree=HeapFree(hHeap,0,pGridData->pTextColor);
#ifdef MYDEBUG
_ftprintf(fp,_T("  HeapFree(hHeap,0,pGridData->pTextColor) = %d\n"),blnFree);
#endif
blnFree=HeapFree(hHeap,0,pGridData->pBackColor);
#ifdef MYDEBUG
_ftprintf(fp,_T("  HeapFree(hHeap,0,pGridData->pBackColor) = %d\n"),blnFree);
#endif

//Create A New Memory Block
if(iRowCount < pGridData->iVisibleRows)
{
    #ifdef MYDEBUG
    _ftprintf(fp,_T("  Got In Where iRowCount < iVisibleRows"));
    #endif
    iRowCount=pGridData->iVisibleRows+1;
}
iSize = iRowCount * pGridData->iCols;
pGridData->pGridMemory=(TCHAR**)HeapAlloc(hHeap,HEAP_ZERO_MEMORY,iSize*sizeof(void*));
if(!pGridData->pGridMemory)
    return E_FAIL;
pGridData->iRows=iRowCount;
ZeroMemory(&si, sizeof(SCROLLINFO));
si.cbSize=sizeof(SCROLLINFO);
si.fMask=SIF_RANGE | SIF_PAGE | SIF_POS;
si.nMin=1;
si.nMax=pGridData->iRows;
si.nPage=pGridData->iVisibleRows;
si.nPos=1;
SetScrollInfo(this->hWndCtrl,SB_VERT,&si,TRUE);
pGridData->pTextColor=(COLORREF*)HeapAlloc(hHeap,HEAP_ZERO_MEMORY,iSize*sizeof(void*));
#ifdef MYDEBUG
_ftprintf(fp,_T("  pGridData->pTextColor = %u\n"),pGridData->pTextColor);
#endif
if(!pGridData->pTextColor)
    return E_FAIL;
pGridData->pBackColor=(HBRUSH*)HeapAlloc(hHeap,HEAP_ZERO_MEMORY,iSize*sizeof(void*));
#ifdef MYDEBUG
_ftprintf(fp,_T("  pGridData->pBackColor = %u\n"),pGridData->pBackColor);
#endif
if(!pGridData->pBackColor)
    return E_FAIL;
#ifdef MYDEBUG
_ftprintf(fp,_T("Leaving FHGrid::SetRowCount()\n\n"));
#endif

return S_OK;
}


HRESULT STDMETHODCALLTYPE FHGrid::GetRowCount(int* iRowCount)
{
#ifdef MYDEBUG
_ftprintf(fp,_T("Entering FHGrid::GetRowCount()\n"));
#endif
GridData* pGridData=(GridData*)GetWindowLong(this->hWndCtrl,0);
if(pGridData)
{
    *iRowCount=pGridData->iRows;
    #ifdef MYDEBUG
    _ftprintf(fp,_T("  *iRowCount = %d\n"),*iRowCount);
    _ftprintf(fp,_T("Leaving FHGrid::GetRowCount()\n"));
    #endif
    return S_OK;
}
#ifdef MYDEBUG
_ftprintf(fp,_T("  *iRowCount = %d\n"),*iRowCount);
_ftprintf(fp,_T("Leaving FHGrid::GetRowCount()\n"));
#endif

return E_FAIL;
}


HRESULT STDMETHODCALLTYPE FHGrid::SetData(int iRow, int iCol, BSTR strData)
{
GridData* pGridData=NULL;
HANDLE hHeap=NULL;
int iIndex;

#ifdef MYDEBUG
printf("Called FHGrid::SetData()\n");
_ftprintf(fp,_T("Entering FHGrid::SetData()\n"));
_ftprintf(fp,_T("  this         = %u\n"),this);
_ftprintf(fp,_T("  iRow         = %d\n"),iRow);
_ftprintf(fp,_T("  iCol         = %d\n"),iCol);
_ftprintf(fp,_T("  strData      = %s\n"),strData);
#endif
pGridData=(GridData*)GetWindowLongPtr(this->hWndCtrl,0);
if(pGridData)
{
    if(iRow && iRow<=pGridData->iRows)
    {
       if(iCol && iCol<=pGridData->iCols)
       {
          iIndex=dwIdx(iRow,iCol);
          hHeap=GetProcessHeap();
          #ifdef MYDEBUG
          _ftprintf(fp,_T("  iIndex       = %d\n"),iIndex);
          _ftprintf(fp,_T("  pGridData->pGridMemory[iIndex] = %u\n"),pGridData->pGridMemory[iIndex]);
          _ftprintf(fp,_T("  pGridData->pGridMemory[iIndex] = %s\n"),pGridData->pGridMemory[iIndex]);
          #endif
          if(pGridData->pGridMemory[iIndex]==NULL)
          {
             #ifdef MYDEBUG
             _ftprintf(fp,_T("  Got In Where Memory Is Null\n"));
             #endif
             pGridData->pGridMemory[iIndex]=(TCHAR*)HeapAlloc(hHeap,HEAP_ZERO_MEMORY,(SysStringLen(strData)*sizeof(TCHAR))+sizeof(TCHAR));
             if(pGridData->pGridMemory[iIndex])
                _tcscpy(pGridData->pGridMemory[iIndex],strData);
             else
                return S_FALSE;
          }
          else
          {
             if(wcscmp(pGridData->pGridMemory[iIndex],strData)==0)  // Both Strings Are The Same, So No Need To Replace.  Therefore, Just Return.
             {
                #ifdef MYDEBUG
                _ftprintf(fp,_T("  Got In Where Parameter Is Same As Memory, So Just Return.\n"));
                #endif
                return S_OK;
             }
             else
             {
                #ifdef MYDEBUG
                _ftprintf(fp,_T("  Got In Where We'll Allocate Memory And Copy Parameter To it.\n"));
                #endif
                HeapFree(hHeap,0,pGridData->pGridMemory[iIndex]);
                pGridData->pGridMemory[iIndex]=(TCHAR*)HeapAlloc(hHeap,HEAP_ZERO_MEMORY,(SysStringLen(strData)*sizeof(TCHAR))+sizeof(TCHAR));
                if(pGridData->pGridMemory[iIndex])
                   _tcscpy(pGridData->pGridMemory[iIndex],strData);
                else
                   return S_FALSE;
             }
          }
       }
       else
          return S_FALSE;
    }
    else
       return S_FALSE;
}
else
    return S_FALSE;
#ifdef MYDEBUG
_ftprintf(fp,_T("Leaving FHGrid::SetData()\n\n"));
#endif

return S_OK;
}


HRESULT STDMETHODCALLTYPE FHGrid::GetData(int iRow, int iCol, BSTR* strData)
{
#ifdef MYDEBUG
printf("Called FHGrid::GetData()\n");
#endif
GridData* pGridData=(GridData*)GetWindowLongPtr(this->hWndCtrl,0);
if(pGridData)
{
    if(iRow<=pGridData->iRows && iRow>0 && iCol<=pGridData->iCols && iCol>0)
    {
       int iIndex=dwIdx(iRow,iCol);
       SysReAllocString(strData,pGridData->pGridMemory[iIndex]);
       return S_OK;
    }
}

return E_FAIL;
}


HRESULT STDMETHODCALLTYPE FHGrid::FlushData()
{
GridData* pGridData=NULL;
HWND hGrid;

#ifdef MYDEBUG
printf("Called FHGrid::FlushData()\n");
_ftprintf(fp,_T("  Entering FHGrid::FlushData()\n"));
#endif
hGrid=this->hWndCtrl;
pGridData=(GridData*)GetWindowLongPtr(hGrid,0);
#ifdef MYDEBUG
_ftprintf(fp,_T("    hGrid     = %u\n"),hGrid);
_ftprintf(fp,_T("    pGridData = %u\n"),pGridData);
#endif
if(pGridData->hCtrlInCell)
{
    #ifdef MYDEBUG
    _ftprintf(fp,_T("    Got In Where pGridData->hCtrlInCell = TRUE!\n"));
    #endif
    int iLen=GetWindowTextLength(pGridData->hCtrlInCell);
    HANDLE hHeap=GetProcessHeap();
    TCHAR* pZStr=(TCHAR*)HeapAlloc(hHeap,HEAP_ZERO_MEMORY,(iLen+1)*sizeof(TCHAR));
    if(pZStr)
    {
       GetWindowText(pGridData->hCtrlInCell,pZStr,iLen+1);
       BSTR strData=SysAllocString(pZStr);
       this->SetData(pGridData->iEditedRow,pGridData->iEditedCol,strData);
       SetWindowLongPtr(pGridData->hCtrlInCell,GWLP_WNDPROC,(LONG_PTR)fnEditWndProc);
       SetParent(pGridData->hCtrlInCell,hGrid);
       SetWindowPos(pGridData->hCtrlInCell,HWND_BOTTOM,0,0,0,0,SWP_HIDEWINDOW);
       pGridData->hCtrlInCell=0;
       this->Refresh();
    }
    else
    {
       return E_FAIL;
    }
}
#ifdef MYDEBUG
_ftprintf(fp,_T("  Leaving FHGrid::FlushData()\n"));;
#endif


return S_OK;
}


HRESULT STDMETHODCALLTYPE FHGrid::Refresh()
{
int iRows,iCols,iCountCells,iReturn,iIdx;
GridData* pGridData=NULL;
TCHAR* pText=NULL;
SCROLLINFO si;

#ifdef MYDEBUG
printf("Called FHGrid::Refresh()\n");
_ftprintf(fp,_T("Entering FHGrid::Refresh()\n"));
#endif
pGridData=(GridData*)GetWindowLongPtr(this->hWndCtrl,0);
#ifdef MYDEBUG
_ftprintf(fp,_T("  pGridData    = %u\n"),pGridData);
#endif
if(pGridData)
{
    iRows       = pGridData->iVisibleRows;
    iCols       = pGridData->iCols;
    iCountCells =iRows*iCols;
    si.cbSize   = sizeof(SCROLLINFO);
    si.fMask    = SIF_POS;
    iReturn     = GetScrollInfo(this->hWndCtrl,SB_VERT,&si);
    #ifdef MYDEBUG
    _ftprintf(fp,_T("  iReturn                 = %d\n"),iReturn);
    _ftprintf(fp,_T("  pGridData->iVisibleRows = %d\n"),pGridData->iVisibleRows);
    _ftprintf(fp,_T("  pGridData->iCols        = %d\n"),pGridData->iCols);
    _ftprintf(fp,_T("  iCountCells             = %d\n"),iCountCells);
    _ftprintf(fp,_T("  si.nPos                 = %d\n\n"),si.nPos);
    _ftprintf(fp,_T("  i    pGridData->pCellHandles[i]  pGridData->pGridMemory[i]    pText   pGridData->pBackColor[iIdx]  pGridData->pTextColor[iIdx]\n"));
    _ftprintf(fp,_T("  ==============================================================================================================================\n"));
    #endif
    if(iReturn)
    {
       for(int i=0; i<iCountCells; i++)
       {
           iIdx=iCols*(si.nPos-1)+i;
           SetWindowLongPtr(pGridData->pCellHandles[i],0,(LONG_PTR)pGridData->pGridMemory[iIdx]);
           SetWindowLongPtr(pGridData->pCellHandles[i],2*sizeof(void*),(LONG_PTR)pGridData->pTextColor[iIdx]);
           SetWindowLongPtr(pGridData->pCellHandles[i],3*sizeof(void*),(LONG_PTR)pGridData->pBackColor[iIdx]);
           InvalidateRect(pGridData->pCellHandles[i],NULL, TRUE);
           pText=(TCHAR*)pGridData->pGridMemory[i];
           #ifdef MYDEBUG
           _ftprintf
           (
             fp,
             _T("  %d\t\t%u\t\t\t%u\t\t\t%s\t\t\t0x%x\t\t\t0x%x\n"),
             i,pGridData->pCellHandles[i],pGridData->pGridMemory[i],pText,pGridData->pBackColor[iIdx],pGridData->pTextColor[iIdx]
           );
           #endif
       }
    }
}
#ifdef MYDEBUG
_ftprintf(fp,_T("Leaving FHGrid::Refresh()\n"));
#endif

return S_OK;
}


HRESULT STDMETHODCALLTYPE FHGrid::GetVisibleRows(int* iVisibleRows)
{
GridData* pGridData=(GridData*)GetWindowLongPtr(this->hWndCtrl,0);
if(pGridData)
{
    *iVisibleRows=pGridData->iVisibleRows;
    return S_OK;
}

return E_FAIL;
}



HRESULT STDMETHODCALLTYPE FHGrid::GethGrid(HWND* hWnd)
{
GridData* pGridData=(GridData*)GetWindowLongPtr(this->hWndCtrl,0);
if(pGridData)
{
    *hWnd=pGridData->hGrid;
    return S_OK;
}

return E_FAIL;
}


HRESULT STDMETHODCALLTYPE FHGrid::GethCell(int iRow, int iCol, HWND* hCell)
{
GridData* pGridData=(GridData*)GetWindowLongPtr(this->hWndCtrl,0);
if(pGridData)
{
    if(iRow<=pGridData->iRows && iRow)
    {
       if(iCol<=pGridData->iCols && iCol)
       {
          int iIndex=dwIdx(iRow,iCol);
          *hCell=pGridData->pCellHandles[iIndex];
          return S_OK;
       }
    }
}

return E_FAIL;
}


HRESULT STDMETHODCALLTYPE FHGrid::GethComboBox(int iCol, HWND* hCombo)
{
#ifdef MYDEBUG
_ftprintf(fp,_T("Entering FHGrid::GethComboBox()\n"));
#endif
GridData* pGridData=(GridData*)GetWindowLongPtr(this->hWndCtrl,0);
if(pGridData->pCellCtrlTypes[iCol-1]==GRID_CELL_CTRL_COMBO)
    *hCombo=(HWND)pGridData->pCtrlHdls[iCol-1];
else
    return E_FAIL;
#ifdef MYDEBUG
_ftprintf(fp,_T("  *hCombo = %u\n"),*hCombo);
_ftprintf(fp,_T("Leaving FHGrid::GethComboBox()\n\n"));
#endif

return S_OK;
}


HRESULT STDMETHODCALLTYPE FHGrid::SetCellAttributes(int iRow, int iCol, COLORREF iBackColor, COLORREF iTextColor)
{
int iIdx=0,i=0,blnFound=0;
GridData* pGridData=NULL;

#if defined MYDEBUG
printf("Called FHGrid::SetCellAttributes()\n");
_ftprintf(fp,_T("  Entering FHGrid::SetCellAttributes()\n"));
_ftprintf(fp,_T("    iRow       = %u\n"),iRow);
_ftprintf(fp,_T("    iCol       = %u\n"),iCol);
_ftprintf(fp,_T("    iBackColor = 0x%x\n"),iBackColor);
_ftprintf(fp,_T("    iTextColor = 0x%x\n"),iTextColor);
#endif
pGridData=(GridData*)GetWindowLongPtr(this->hWndCtrl,0);
if(iRow && iCol)
{
    #if defined MYDEBUG
    _ftprintf(fp,_T("    Got In Where iRow And iCol Are Both Something!\n"));
    #endif
    iIdx=dwIdx(iRow,iCol);
    pGridData->pTextColor[iIdx] = iTextColor;
    #if defined MYDEBUG
    _ftprintf(fp,_T("    iIdx       = %u\n"),iIdx);
    #endif
}
else
{
    #if defined MYDEBUG
    _ftprintf(fp,_T("    Got In Where Either iRow Or iCol Ain't Shit!\n"));
    #endif
}
#if defined MYDEBUG
_ftprintf(fp,_T("    pGridData->iCols               = %d\n"),pGridData->iCols);
_ftprintf(fp,_T("    iIdx                           = %u\n"),iIdx);
_ftprintf(fp,_T("    pGridData->pTextColor          = %d\n"),pGridData->pTextColor);
_ftprintf(fp,_T("    pGridData->pTextColor[iIdx]    = 0x%x\n"),pGridData->pTextColor[iIdx]);
_ftprintf(fp,_T("    pGridData->pCellHandles[iIdx]  = %u\n"),pGridData->pCellHandles[iIdx]);
_ftprintf(fp,_T("    pGridData->pCreatedColors[0]   = %u\n"),pGridData->pCreatedColors[0]);
_ftprintf(fp,_T("    pGridData->pCreatedBrushes[0]  = %u\n\n"),pGridData->pCreatedBrushes[0]);
_ftprintf(fp,_T("    i          pGridData->pCreatedColors[i]            iBackColor\n"));
_ftprintf(fp,_T("    =============================================================\n"));
#endif
//pGridMemory                         As Dword Ptr  'Will be storing ZStr Ptrs here.  We need a
//pTextColor                          As Dword Ptr  'Will be storing RGB values here, i.e., %Red, %Blue, etc
//pBackColor                          As Dword Ptr  'Will be storing HBRUSHs here.  May be zero for default brush.
//pCreatedColors                      As Dword Ptr  'Colors so far asked for by user per grid instance, e.g., %Red, %Yellow, %Blue, etc.
//pCreatedBrushes                     As Dword Ptr  'Will be storing created HBRUSHs here.  Accumulate them. Numbers such as &HA0556789
for(i=1; i<=pGridData->pCreatedColors[0]; i++)
{
     #if defined MYDEBUG
     _ftprintf(fp,_T("    %d\t\t0x%x\t\t0x%x\n"),i,pGridData->pCreatedColors[i],iBackColor);
     #endif
     if(pGridData->pCreatedColors[i]==iBackColor)
     {
        blnFound=TRUE;
        break;
     }
}
#if defined MYDEBUG
_ftprintf(fp,_T("    blnFound = %d\n"),blnFound);
_ftprintf(fp,_T("    i        = %d\n"),i);
#endif
if(blnFound)  // Color Was Already Created
{
    if(iRow && iCol)
    {
       pGridData->pBackColor[iIdx]=pGridData->pCreatedBrushes[i];
    }
    #if defined MYDEBUG
    _ftprintf(fp,_T("\n"));
    _ftprintf(fp,_T("    Got In Where blnFound = %True!\n"));
    _ftprintf(fp,_T("    pGridData->pCreatedBrushes[i] = 0x%x\n"),pGridData->pCreatedBrushes[i]);
    #endif
}
else          // Need To Create Brush And Store It
{
    #if defined MYDEBUG
    _ftprintf(fp,_T("\n"));
    _ftprintf(fp,_T("    Got In Where blnFound = False!\n"));
    _ftprintf(fp,_T("    pGridData->pCreatedBrushes[0] = %d   << Before\n"),pGridData->pCreatedBrushes[0]);
    #endif
    if((int)pGridData->pCreatedBrushes[0]<MAX_COLORS)  // Test to see if pGridData->pCreatedBrushes[0]
    {                                                  // is less than 15, i.e., %MAX_COLORS
        int iCreatedBrushes=(int)pGridData->pCreatedBrushes[0];
        iCreatedBrushes++;
        pGridData->pCreatedBrushes[0]=(HBRUSH)iCreatedBrushes;
        int iCreatedColors=(int)pGridData->pCreatedColors[0];
        iCreatedColors++;
        pGridData->pCreatedColors[0]=(COLORREF)iCreatedColors;
        #if defined MYDEBUG
        _ftprintf(fp,_T("    pGridData->pCreatedColors[0] = %d\n"),pGridData->pCreatedColors[0]);
        _ftprintf(fp,_T("    Will Be Able To Create Another Brush!\n"));
        #endif
     }
     else
     {
        #if defined MYDEBUG
        _ftprintf(fp,_T("    Can't Create Another Brush!\n"));
        _ftprintf(fp,_T("  Leaving IGrid_SetCellAttributes()\n"));
        #endif
        return E_FAIL;                                // We've already created 15 brushes (Or Whatever MAX_COLORS Is).
     }
     #if defined MYDEBUG
     _ftprintf(fp,_T("    pGridData->pCreatedBrushes[0] = %d   << After\n"),pGridData->pCreatedBrushes[0]);
     #endif
     pGridData->pCreatedBrushes[(int)pGridData->pCreatedBrushes[0]] = CreateSolidBrush(iBackColor);
     pGridData->pCreatedColors[pGridData->pCreatedColors[0]]   = iBackColor;
     #if defined MYDEBUG
     _ftprintf(fp,_T("    pGridData->pCreatedBrushes[(int)pGridData->pCreatedBrushes[0]] = 0x%x\n"),pGridData->pCreatedBrushes[(int)pGridData->pCreatedBrushes[0]]);
     #endif
     if(iRow && iCol)
     {
        pGridData->pBackColor[iIdx] = pGridData->pCreatedBrushes[(int)pGridData->pCreatedBrushes[0]];
        #if defined MYDEBUG
        _ftprintf(fp,_T("    Have Just Assigned A Newly Created Brush To pBackColor[] At correct Location\n"));
        _ftprintf(fp,_T("    pGridData->pBackColor[iIdx] = 0x%x\n"),pGridData->pBackColor[iIdx]);
        #endif
     }
}
#if defined MYDEBUG
_ftprintf(fp,_T("  Leaving FHGrid::SetCellAttributes()\n\n"));
#endif

return S_OK;
}


HRESULT STDMETHODCALLTYPE FHGrid::DeleteRow(int iRow)
{
#if defined MYDEBUG
_ftprintf(fp,_T("Entering FHGrid::DeleteRow()\n"));
#endif
GridData* pGridData=(GridData*)GetWindowLongPtr(this->hWndCtrl,0);
int iSize=(pGridData->iRows-1)*pGridData->iCols-1;
int iStart=dwIdx(iRow,1);
int iCols=pGridData->iCols;
for(int i=iStart; i<=iSize; i++)
{
     pGridData->pGridMemory[i] = pGridData->pGridMemory[i+iCols];
     pGridData->pTextColor[i]  = pGridData->pTextColor[i+iCols];
     pGridData->pBackColor[i]  = pGridData->pBackColor[i+iCols];
}
iStart=dwIdx(pGridData->iRows,1);
for(int i=iStart; i<iStart+iCols; i++)
{
     pGridData->pGridMemory[i] = 0;
     pGridData->pTextColor[i]  = 0;
     pGridData->pBackColor[i]  = 0;
}
for(int i=1; i<=iCols; i++)
     pGridData->pComObj->SetCellAttributes(iRow,i,pGridData->iSelectionBackColor,pGridData->iSelectionTextColor);
#if defined MYDEBUG
_ftprintf(fp,_T("Leaving FHGrid::DeleteRow()\n"));
#endif

return S_OK;
}


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


HRESULT FHGrid::FindConnectionPoint(REFIID riid, IConnectionPoint** ppCP)
{
#ifdef MYDEBUG
printf("Entering FHGrid::FindConnectionPoint()\n");
printf("  this  = %u\n",this);
printf("  *ppCP = %u\n",*ppCP);
#endif
HRESULT hr=this->QueryInterface(IID_IConnectionPoint,(void**)ppCP);
if(SUCCEEDED(hr))
{
    #ifdef MYDEBUG
    printf("  QueryInterface(IID_IConnectionPoint) Succeeded!\n");
    printf("  *ppCP = %u\n",*ppCP);
    printf("Leaving FHGrid::FindConnectionPoint()\n");
    #endif
    return hr;
}
else
{
    #ifdef MYDEBUG
    printf("  QueryInterface(IID_IConnectionPoint) Failed!\n");
    printf("Leaving FHGrid::FindConnectionPoint()\n");
    #endif
    return E_NOINTERFACE;
}
}


HRESULT FHGrid::GetConnectionInterface(IID* pIID)
{
return E_NOTIMPL;
}


HRESULT FHGrid::GetConnectionPointContainer(IConnectionPointContainer** ppCPC)
{
return E_NOTIMPL;
}


HRESULT FHGrid::Advise(IUnknown* pUnkSink, DWORD* pdwCookie)
{
int blnFoundOpenSlot=FALSE,i;

#ifdef MYDEBUG
printf("  Entering FHGrid::Advise()\n");
printf("    this     = %u\n",this);
printf("    pUnkSink = %u\n\n",pUnkSink);
printf("    i   &this->pISink[i]     this->pISink[i]\n");
printf("    ===========================================\n");
#endif
for(i=0; i<MAX_CONNECTIONS; i++)
{
     #ifdef MYDEBUG
     printf("    %d\t%u\t\t\t\t%d\n",i,&this->pISink[i],this->pISink[i]);
     #endif
     if(this->pISink[i]==0)
     {
        blnFoundOpenSlot=TRUE;
        break;
     }
}
if(blnFoundOpenSlot)
{
    HRESULT hr=pUnkSink->QueryInterface(IID_IFHGridEvents,(void**)&this->pISink[i]);
    if(SUCCEEDED(hr))
    {
       #ifdef MYDEBUG
       printf("    i               = %d\n",i);
       printf("    pUnkSink->QueryInterface() For Client Sink Succeeded!\n");
       printf("    this->pISink[i] = %u\n",this->pISink[i]);
       printf("  Leaving FHGrid::Advise()\n");
       #endif
       *pdwCookie=(DWORD)i;
       return S_OK;
    }
    else
    {
       #ifdef MYDEBUG
       printf("    pUnkSink->QueryInterface() For Client Sink Failed!\n");
       #endif
    }
}
else
{
    #ifdef MYDEBUG
    printf("    Couldn't Find An Open Slot To Store Connection!\n");
    #endif
}
#ifdef MYDEBUG
printf("  Leaving FHGrid::Advise()\n");
#endif

return E_FAIL;
}


HRESULT FHGrid::Unadvise(DWORD dwCookie)
{
#ifdef MYDEBUG
printf("Entering FHGrid::Unadvise()\n");
printf("  dwCookie = %u\n",dwCookie);
#endif
IUnknown* pIUnknown=this->pISink[dwCookie];
pIUnknown->Release();
this->pISink[dwCookie]=0;
#ifdef MYDEBUG
printf("  pIUnknown = %u\n",pIUnknown);
printf("Leaving FHGrid::Unadvise()\n");
#endif

return NOERROR;
}


HRESULT FHGrid::EnumConnections(IEnumConnections** ppEnum)
{
return E_NOTIMPL;
}


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
}

// 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;
else
{
    pGrid->pISink=(IGridEvents**)CoTaskMemAlloc(MAX_CONNECTIONS*sizeof(void*));
    #if defined MYDEBUG
    printf("    pGrid->pISink = %u\n",pGrid->pISink);
    printf("    pGrid->pISink[0] = %u\n",pGrid->pISink[0]);
    printf("    pGrid->pISink[1] = %u\n",pGrid->pISink[1]);
    printf("    pGrid->pISink[2] = %u\n",pGrid->pISink[2]);
    printf("    pGrid->pISink[3] = %u\n",pGrid->pISink[3]);
    #endif
    if(pGrid->pISink==NULL)
       return E_OUTOFMEMORY;
    else
    {
       pGrid->pISink[0]=(IGridEvents*)0;
       pGrid->pISink[1]=(IGridEvents*)NULL;
       pGrid->pISink[2]=(IGridEvents*)NULL;
       pGrid->pISink[3]=(IGridEvents*)NULL;
       #if defined MYDEBUG
       printf("    pGrid->pISink = %u\n",pGrid->pISink);
       printf("    pGrid->pISink[0] = %u\n",pGrid->pISink[0]);
       printf("    pGrid->pISink[1] = %u\n",pGrid->pISink[1]);
       printf("    pGrid->pISink[2] = %u\n",pGrid->pISink[2]);
       printf("    pGrid->pISink[3] = %u\n",pGrid->pISink[3]);
       #endif
    }
}
#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;
}


void Initialize()
{
INITCOMMONCONTROLSEX uCC;
TCHAR szClassName[16];
short int iRet=0;
WNDCLASSEX wc;

#if defined MYDEBUG
printf("    Entering Initialize()\n");
#endif

uCC.dwSize = sizeof(uCC);             // Initialize Common Controls (need header control).
uCC.dwICC  = ICC_LISTVIEW_CLASSES;
InitCommonControlsEx(&uCC);

_tcscpy(szClassName,_T("Grid"));      // Register Grid Class
wc.lpszClassName  = szClassName;
wc.lpfnWndProc    = fnGridProc;
wc.cbSize         = sizeof(wc);
wc.style          = 0;
wc.cbClsExtra     = 0;
wc.cbWndExtra     = sizeof(void*);
wc.hInstance      = g_hModule;
wc.hIcon          = LoadIcon(NULL,IDI_APPLICATION);
wc.hCursor        = LoadCursor(NULL,IDC_ARROW);
wc.hbrBackground  = (HBRUSH)GetStockObject(LTGRAY_BRUSH);
wc.lpszMenuName   = NULL;
wc.hIconSm        = NULL;
iRet              = RegisterClassEx(&wc);
#if defined MYDEBUG
printf("      RegisterClassEx(Grid) = %d\n",iRet);
#endif

_tcscpy(szClassName,_T("Pane"));      // Register Pane Class
wc.lpszClassName  = szClassName;
wc.lpfnWndProc    = fnPaneProc;
wc.cbSize         = sizeof(wc);
wc.style          = 0;
wc.cbClsExtra     = 0;
wc.cbWndExtra     = sizeof(void*);
wc.hInstance      = g_hModule;
wc.hIcon          = LoadIcon(NULL,IDI_APPLICATION);
wc.hCursor        = LoadCursor(NULL,IDC_ARROW);
wc.hbrBackground  = (HBRUSH)GetStockObject(WHITE_BRUSH);
wc.lpszMenuName   = NULL;
wc.hIconSm        = NULL;
iRet              = RegisterClassEx(&wc);
#if defined MYDEBUG
printf("      RegisterClassEx(Pane) = %d\n",iRet);
#endif

_tcscpy(szClassName,_T("Base"));      // Register Pane Class
wc.lpszClassName  = szClassName;
wc.lpfnWndProc    = fnBaseProc;
wc.cbSize         = sizeof(wc);
wc.style          = 0;
wc.cbClsExtra     = 0;
wc.cbWndExtra     = 0;
wc.hInstance      = g_hModule;
wc.hIcon          = LoadIcon(NULL,IDI_APPLICATION);
wc.hCursor        = LoadCursor(NULL,IDC_ARROW);
wc.hbrBackground  = (HBRUSH)GetStockObject(GRAY_BRUSH);
wc.lpszMenuName   = NULL;
wc.hIconSm        = NULL;
iRet              = RegisterClassEx(&wc);
#if defined MYDEBUG
printf("      RegisterClassEx(Pane) = %d\n",iRet);
#endif

_tcscpy(szClassName,_T("Cell"));      // Register Cell Class
wc.lpszClassName  = szClassName;
wc.lpfnWndProc    = fnCellProc;
wc.cbSize         = sizeof(wc);
wc.style          = 0;
wc.cbClsExtra     = 0;
wc.cbWndExtra     = 4*sizeof(void*);
wc.hInstance      = g_hModule;
wc.hIcon          = LoadIcon(NULL,IDI_APPLICATION);
wc.hCursor        = LoadCursor(NULL,IDC_ARROW);
wc.hbrBackground  = (HBRUSH)GetStockObject(WHITE_BRUSH);
wc.lpszMenuName   = NULL;
wc.hIconSm        = NULL;
iRet              = RegisterClassEx(&wc);
#if defined MYDEBUG
printf("      RegisterClassEx(Cell) = %d\n",iRet);
printf("    Leaving Initialize()\n");
#endif
}
//End Grid.cpp


Frederick J. Harris

#4
WinCode.cpp will have to be broken up.  I'll try 1st half ...


#define  UNICODE
#define  _UNICODE
#include <windows.h>
#include <commctrl.h>
#include <objbase.h>
#include <ocidl.h>
#include <olectl.h>
#include <cstdio>
#include <tchar.h>
#include "Strings.h"
#include "Grid.h"
#include "WinCode.h"
//#define MYDEBUG
#if defined MYDEBUG
extern   FILE* fp;
#endif
WNDPROC  fnEditWndProc;  // edit control subclass


long fnGridProc_OnCreate(lpWndEventArgs Wea)
{
int iEndRectHt,iHalf,iRemainder,iDifference,iNewGridHt;
int iCols=0,iFlds=0,iHdlCount=0,iCtr=0,iSize=0;
CREATESTRUCT* pCreateStruct=NULL;
GridData* pGridData1=NULL;
GridData* pGridData2=NULL;
String* strParseData;
HANDLE hHeap=NULL;
TCHAR szText[64];
String strSetup;
HDITEM hdrItem;
DWORD dwStyle;
HWND hCell;
HDC hDC;
RECT rc;

#if defined MYDEBUG
_ftprintf(fp,_T("  Entering fnGridProc_OnCreate()\n"));
#endif
pCreateStruct=(CREATESTRUCT*)Wea->lParam;
Wea->hIns=pCreateStruct->hInstance;
pGridData1=(GridData*)pCreateStruct->lpCreateParams;
strSetup=(TCHAR*)pCreateStruct->lpszName;
#if defined MYDEBUG
_ftprintf(fp,_T("    Wea->hWnd                  = %u\n"),Wea->hWnd);
_ftprintf(fp,_T("    pCreateStruct              = %u\n"),pCreateStruct);
_ftprintf(fp,_T("    pGridData1                 = %u\n"),pGridData1);
_ftprintf(fp,_T("    strSetup.lpStr()           = %s\n"),strSetup.lpStr());
_ftprintf(fp,_T("    pCreateStruct->hwndParent  = %u\n"),pCreateStruct->hwndParent);
_ftprintf(fp,_T("    pCreateStruct->x           = %d\n"),pCreateStruct->x);
_ftprintf(fp,_T("    pCreateStruct->y           = %d\n"),pCreateStruct->y);
_ftprintf(fp,_T("    pCreateStruct->cx          = %d\n"),pCreateStruct->cx);
_ftprintf(fp,_T("    pCreateStruct->cy          = %d\n"),pCreateStruct->cy);
_ftprintf(fp,_T("    pCreateStruct->hMenu       = %u\n"),pCreateStruct->hMenu);
_ftprintf(fp,_T("    pGridData1->cx             = %d\n"),pGridData1->cx);
_ftprintf(fp,_T("    pGridData1->iRows          = %d\n"),pGridData1->iRows);
_ftprintf(fp,_T("    pGridData1->iCols          = %d\n"),pGridData1->iCols);
_ftprintf(fp,_T("    pGridData1->iRowHeight     = %d\n"),pGridData1->iRowHeight);
_ftprintf(fp,_T("    pGridData1->szFontName     = %s\n"),pGridData1->szFontName);
_ftprintf(fp,_T("    pGridData1->iFontSize      = %d\n"),pGridData1->iFontSize);
_ftprintf(fp,_T("    pGridData1->iFontWeight    = %d\n"),pGridData1->iFontWeight);
#endif
pGridData1->hGrid=Wea->hWnd;
GetClientRect(Wea->hWnd,&rc);
iCols=strSetup.ParseCount(_T(','));
#if defined MYDEBUG
_ftprintf(fp,_T("    rc.left                    = %d\n"),rc.left);
_ftprintf(fp,_T("    rc.top                     = %d\n"),rc.top);
_ftprintf(fp,_T("    rc.right                   = %d\n"),rc.right);
_ftprintf(fp,_T("    rc.bottom                  = %d\n"),rc.bottom);
_ftprintf(fp,_T("    iCols                      = %d\n"),iCols);
#endif
if(iCols!=pGridData1->iCols)
    return -1;
hHeap=GetProcessHeap();
if(!hHeap)
    return -1;
pGridData2=(GridData*)HeapAlloc(hHeap,HEAP_ZERO_MEMORY,sizeof(GridData));
if(!pGridData2)
    return -1;
SetWindowLongPtr(Wea->hWnd,0,(LONG_PTR)pGridData2);
pGridData1->blnRowSelected=FALSE;
#if defined MYDEBUG
_ftprintf(fp,_T("    pGridData2                 = %u\n"),pGridData2);
#endif
memcpy(pGridData2,pGridData1,sizeof(GridData));
iNewGridHt=pCreateStruct->cy;
iHalf=pGridData2->iRowHeight/2;
iRemainder=rc.bottom % pGridData2->iRowHeight;
#ifdef MYDEBUG
_ftprintf(fp,_T("    iNewGridHt                 = %d\n"),iNewGridHt);
_ftprintf(fp,_T("    pGridData2->iRowHeight     = %d\n"),pGridData2->iRowHeight);
_ftprintf(fp,_T("    iHalf                      = %d\n"),iHalf);
_ftprintf(fp,_T("    iRemainder                 = %d\n"),iRemainder);
#endif
if(iRemainder>=iHalf)
{
    iDifference=pGridData2->iRowHeight-iRemainder;
    iEndRectHt=rc.bottom+(pGridData2->iRowHeight-iRemainder);
}
else
{
    iDifference=-iRemainder;
    iEndRectHt=rc.bottom-iRemainder;
}
iNewGridHt=iNewGridHt+iDifference;
#ifdef MYDEBUG
_ftprintf(fp,_T("    iEndRectHt                 = %d\n"),iEndRectHt);
_ftprintf(fp,_T("    iDifference                = %d\n"),iDifference);
_ftprintf(fp,_T("    iNewGridHt                 = %d\n"),iNewGridHt);
#endif
pGridData2->iVisibleRows=(iEndRectHt-pGridData2->iRowHeight)/pGridData2->iRowHeight;
if(pGridData2->iRows<pGridData2->iVisibleRows)
    pGridData2->iRows=pGridData2->iVisibleRows+1;
pGridData2->iPaneHeight=(pGridData2->iVisibleRows+1)*pGridData2->iRowHeight;
#if defined MYDEBUG
_ftprintf(fp,_T("    pGridData2->hParent        = %u\n"),pGridData2->hParent);
_ftprintf(fp,_T("    pGridData2->hGrid          = %u\n"),pGridData2->hGrid);
_ftprintf(fp,_T("    pGridData2->iCtrlId        = %d\n"),pGridData2->iCtrlId);
_ftprintf(fp,_T("    pGridData2->cx             = %u\n"),pGridData2->cx);
_ftprintf(fp,_T("    pGridData2->cy             = %u\n"),pGridData2->cy);
_ftprintf(fp,_T("    pGridData2->iRows          = %d\n"),pGridData2->iRows);
_ftprintf(fp,_T("    pGridData2->iCols          = %d\n"),pGridData2->iCols);
_ftprintf(fp,_T("    pGridData2->iRowHeight     = %d\n"),pGridData2->iRowHeight);
_ftprintf(fp,_T("    pGridData2->szFontName     = %s\n"),pGridData2->szFontName);
_ftprintf(fp,_T("    pGridData2->iFontSize      = %d\n"),pGridData2->iFontSize);
_ftprintf(fp,_T("    pGridData2->iFontWeight    = %d\n"),pGridData2->iFontWeight);
_ftprintf(fp,_T("    pGridData2->iVisibleRows   = %d\n"),pGridData2->iVisibleRows);
_ftprintf(fp,_T("    pGridData2->iPaneHeight    = %d\n"),pGridData2->iPaneHeight);
_ftprintf(fp,_T("    pGridData2->blnRowSelected = %d\n\n"),pGridData2->blnRowSelected);
_ftprintf(fp,_T("    i         strParseData(i) \n"));
_ftprintf(fp,_T("    ============================\n"));
#endif
strParseData=new String[iCols];
strSetup.Parse(strParseData,_T(','));
for(unsigned int i=0; i<iCols; i++)
{
     strParseData[i].Trim();
     #if defined MYDEBUG
     _ftprintf(fp,_T("    %d\t%s\n"),i,strParseData[i].lpStr());
     #endif
}
pGridData2->pColWidths=(DWORD*)HeapAlloc(hHeap,HEAP_ZERO_MEMORY,sizeof(DWORD)*(iCols+1));
if(!pGridData2->pColWidths)
    goto CleanUp;
#if defined MYDEBUG
_ftprintf(fp,_T("\n    pGridData2->pColWidths    = %u\n"),pGridData2->pColWidths);
#endif
pGridData2->hBase=CreateWindowEx(0,_T("Base"),_T(""),WS_CHILD|WS_VISIBLE,0,0,0,0,Wea->hWnd,(HMENU)1499,Wea->hIns,0);
pGridData2->hPane=CreateWindowEx(0,_T("Pane"),_T(""),WS_CHILD|WS_VISIBLE,0,0,0,0,pGridData2->hBase,(HMENU)ID_PANE,Wea->hIns,0);
dwStyle=WS_CHILD | WS_BORDER | WS_VISIBLE | HDS_HOTTRACK | HDS_HORZ;
pGridData2->hHeader=CreateWindowEx(0,WC_HEADER,_T(""),dwStyle,0,0,0,0,pGridData2->hPane,(HMENU)ID_HEADER,Wea->hIns,0);
#if defined MYDEBUG
_ftprintf(fp,_T("    pGridData2->hBase         = %u\n"),pGridData2->hBase);
_ftprintf(fp,_T("    pGridData2->hPane         = %u\n"),pGridData2->hPane);
_ftprintf(fp,_T("    pGridData2->hHeader       = %u\n"),pGridData2->hHeader);
_ftprintf(fp,_T("    HDF_LEFT                  = %d\n"),HDF_LEFT);
_ftprintf(fp,_T("    HDF_CENTER                = %d\n"),HDF_CENTER);
_ftprintf(fp,_T("    HDF_RIGHT                 = %d\n\n"),HDF_RIGHT);
#endif
SetWindowLongPtr(pGridData2->hPane,0,(LONG_PTR)pGridData2);
pGridData2->pCellCtrlTypes=(DWORD*)HeapAlloc(hHeap,HEAP_ZERO_MEMORY,iCols*sizeof(DWORD));    // no extra bytes allocated here, so its zero based
if(!pGridData2->pCellCtrlTypes)
    goto CleanUp;
pGridData2->pCtrlHdls=(HWND*)HeapAlloc(hHeap,HEAP_ZERO_MEMORY,iCols*sizeof(HWND*));          // no extra bytes allocated here either, so its zero based
if(!pGridData2->pCtrlHdls)
    goto CleanUp;
hdrItem.mask=HDI_FORMAT | HDI_WIDTH | HDI_TEXT;
#if defined MYDEBUG
_ftprintf(fp,_T("    i\tstrFieldData[0]\tpGridData2->pColWidths[i]\tstrFieldData[1]\tstrFieldData[2]\tstrFieldData[3]\tpPos[i]\t\tpGridData2->pCellCtrlTypes[i]\n"));
_ftprintf(fp,_T("    ============================================================================================================================================\n"));
#endif
int* pPos=(int*)HeapAlloc(hHeap,HEAP_ZERO_MEMORY,iCols*sizeof(int));    // and no extra bytes allocated here
if(!pPos)
    goto CleanUp;
pGridData2->pColWidths[iCols]=0;
for(unsigned int i=0; i<iCols; i++)
{
     iFlds=strParseData[i].ParseCount(_T(':'));
     String* strFieldData=new String[iFlds];
     strParseData[i].Parse(strFieldData,_T(':'));
     pGridData2->pColWidths[i]=(DWORD)strFieldData[0].iVal();
     pGridData2->pColWidths[iCols]=pGridData2->pColWidths[iCols]+pGridData2->pColWidths[i];
     hdrItem.cxy=pGridData2->pColWidths[i];
     _tcscpy(szText,strFieldData[1].lpStr());   // here's the line that's crashing it!!!!!!!!!!!!!!!!!!!!
     hdrItem.pszText=szText;
     hdrItem.cchTextMax=_tcslen(szText);
     hdrItem.fmt=0;
     if(strFieldData[2]==_T("<"))
        hdrItem.fmt=HDF_LEFT;
     else
     {
        if(strFieldData[2]==_T("^"))
           hdrItem.fmt=HDF_CENTER;
        else
           hdrItem.fmt=HDF_RIGHT;
     }
     hdrItem.fmt=hdrItem.fmt|HDF_STRING;
     Header_InsertItem(pGridData2->hHeader,i,&hdrItem);
     if(i)
        pPos[i]=pPos[i-1]+pGridData2->pColWidths[i-1];
     if(strFieldData[3]==_T("none"))
        pGridData2->pCellCtrlTypes[i]=0;
     else
     {
        if(strFieldData[3]==_T("edit"))
           pGridData2->pCellCtrlTypes[i]=1;
        else
        {
           if(strFieldData[3]==_T("combo"))
              pGridData2->pCellCtrlTypes[i]=2;
           if(strFieldData[3]==_T("check"))
              pGridData2->pCellCtrlTypes[i]=0;
        }
     }
     #if defined MYDEBUG
     _ftprintf
     (
      fp,
      _T("    %d\t%s\t\t%u\t\t\t\t%s\t%s\t\t%s\t\t%d\t\t%u\n"),
      i,strFieldData[0].lpStr(),pGridData2->pColWidths[i],strFieldData[1].lpStr(),strFieldData[2].lpStr(),strFieldData[3].lpStr(),pPos[i],pGridData2->pCellCtrlTypes[i]
     );
     #endif
     delete [] strFieldData;
}
#if defined MYDEBUG
_ftprintf(fp,_T("\n"));
_ftprintf(fp,_T("    iCols = %u\n\n"),iCols);
_ftprintf(fp,_T("    i\tpGridData2->pColWidths[i]\n"));
_ftprintf(fp,_T("    ================================\n"));
for(unsigned int i=0; i<=iCols; i++)
     _ftprintf(fp,_T("    %u\t\t%u\n"),i,pGridData2->pColWidths[i]);
_ftprintf(fp,_T("\n"));
_ftprintf(fp,_T("    i\tpGridData2->pCtrlHdls[i]\n"));
_ftprintf(fp,_T("    ==================================\n"));
#endif
int blnEditCreated = 0;
int iCboCtr        = 0;
for(unsigned int i=0; i<iCols; i++)
{
     // Edit Control In Column
     if(pGridData2->pCellCtrlTypes[i]==GRID_CELL_CTRL_EDIT)
     {
        if(blnEditCreated==FALSE)
        {
           dwStyle = WS_CHILD | ES_AUTOHSCROLL;
           pGridData2->pCtrlHdls[i]=CreateWindowEx(0,_T("edit"),_T(""),dwStyle,0,0,0,0,Wea->hWnd,(HMENU)IDC_EDIT,Wea->hIns,0);
           pGridData2->hCtrlInCell=pGridData2->pCtrlHdls[i];
           blnEditCreated=TRUE;
        }
        else
        {
           pGridData2->pCtrlHdls[i]=pGridData2->hCtrlInCell;
        }
     }

     // Combo Box In Column
     if(pGridData2->pCellCtrlTypes[i]==GRID_CELL_CTRL_COMBO)
     {
        dwStyle=WS_CHILD | CBS_DROPDOWNLIST | WS_VSCROLL; //Or %CBS_NOINTEGRALHEIGHT
        pGridData2->pCtrlHdls[i]=CreateWindowEx(0,_T("combobox"),_T(""),dwStyle,0,0,0,0,Wea->hWnd,(HMENU)(IDC_COMBO+iCboCtr),Wea->hIns,0);
        iCboCtr++;
     }

     #if defined MYDEBUG
     _ftprintf(fp,_T("    %u\t\t%u\n"),i,pGridData2->pCtrlHdls[i]);
     #endif
}
pGridData2->hCtrlInCell=0;    //    DANGER!!!!!! Addition!  I don't know why this needes to be initially set?

#if defined MYDEBUG
_ftprintf(fp,_T("\n"));
_ftprintf(fp,_T("    pGridData2->pColWidths[iCols] = %d\n"),pGridData2->pColWidths[iCols]);
_ftprintf(fp,_T("\n"));
#endif

MoveWindow(Wea->hWnd,pCreateStruct->x,pCreateStruct->y,pCreateStruct->cx,iNewGridHt,FALSE);
MoveWindow(pGridData2->hBase,12,0,rc.right-12,pGridData2->iPaneHeight,FALSE);
MoveWindow(pGridData2->hPane,0,0,pGridData2->pColWidths[iCols],pGridData2->iPaneHeight,FALSE);
MoveWindow(pGridData2->hHeader,0,0,pGridData2->pColWidths[iCols],pGridData2->iRowHeight,TRUE);
delete [] strParseData;
pGridData2->pVButtons=(HWND*)HeapAlloc(hHeap,HEAP_ZERO_MEMORY,(pGridData2->iVisibleRows+1)*sizeof(void*));
#if defined MYDEBUG
_ftprintf(fp,_T("    pGridData2->pVButtons = %u\n\n"),pGridData2->pVButtons);
_ftprintf(fp,_T("    i\tpGridData2->pVButtons[i]\n"));
_ftprintf(fp,_T("    ===================================\n"));
#endif
if(!pGridData2->pVButtons)
    goto CleanUp;
else
{
    for(unsigned int i=0; i<=pGridData2->iVisibleRows; i++)
    {
        pGridData2->pVButtons[i]=CreateWindowEx(0,_T("button"),_T(""),WS_CHILD|WS_VISIBLE|BS_FLAT,0,pGridData2->iRowHeight*i,12,pGridData2->iRowHeight,Wea->hWnd,(HMENU)(20000+i),Wea->hIns,0);
        #if defined MYDEBUG
        _ftprintf(fp,_T("    %u\t%u\n"),i,pGridData2->pVButtons[i]);
        #endif

    }
}

// Now gonna try to create font ...
#if defined MYDEBUG
_ftprintf(fp,_T("\n    Now Gonna Try To Create Font...\n"));
_ftprintf(fp,_T("    pGridData2->szFontName = %s\n"),pGridData2->szFontName);
#endif
if(pGridData2->szFontName)
{
    hDC=GetDC(Wea->hWnd);
    pGridData2->hFont=CreateFont(-1*(pGridData2->iFontSize*GetDeviceCaps(hDC,LOGPIXELSY))/72,0,0,0,pGridData2->iFontWeight,0,0,0,ANSI_CHARSET,0,0,DEFAULT_QUALITY,0,pGridData2->szFontName);
    #if defined MYDEBUG
    _ftprintf(fp,_T("    pGridData2->hFont = %u\n\n"),pGridData2->hFont);
    #endif
    ReleaseDC(Wea->hWnd,hDC);
}
else
    goto CleanUp;

iHdlCount=pGridData2->iCols*pGridData2->iVisibleRows;
pGridData2->pCellHandles=(HWND*)HeapAlloc(hHeap,HEAP_ZERO_MEMORY,iHdlCount*sizeof(void*));
#if defined MYDEBUG
_ftprintf(fp,_T("    iCols                    = %d\n"),iCols);
_ftprintf(fp,_T("    pGridData2->iCols        = %u\n"),pGridData2->iCols);
_ftprintf(fp,_T("    iHdlCount                = %d\n"),iHdlCount);
_ftprintf(fp,_T("    pGridData2->pCellHandles = %u\n"),pGridData2->pCellHandles);
#endif
if(!pGridData2->pCellHandles)
    goto CleanUp;
dwStyle=WS_CHILD | WS_VISIBLE | WS_BORDER;
#if defined MYDEBUG
_ftprintf(fp,_T("\n"));
_ftprintf(fp,_T("    i\t\tj\tiPos(j)\tyLoc\thCell\n"));
_ftprintf(fp,_T("    =============================================================\n"));
#endif
for(int i=0; i<pGridData2->iVisibleRows; i++)
{
     for(int j=0; j<pGridData2->iCols; j++)
     {
         hCell=CreateWindowEx(0,_T("Cell"),_T(""),dwStyle,pPos[j],pGridData2->iRowHeight+(i*pGridData2->iRowHeight),pGridData2->pColWidths[j],pGridData2->iRowHeight,pGridData2->hPane,(HMENU)(ID_CELL+iCtr),Wea->hIns,0);
         pGridData2->pCellHandles[iCtr]=hCell;
         SetWindowLongPtr(hCell,sizeof(void*),(LONG_PTR)pGridData2->hFont);   // 4 for 32 bit; 8 for 64 bit
         #if defined MYDEBUG
         _ftprintf(fp,_T("    %d\t\t%d\t%u\t%d\t%u\n"),i,j,pPos[j],pGridData2->iRowHeight+(i*pGridData2->iRowHeight),hCell);
         #endif
         iCtr++;
     }
}

// grid memory
iSize=pGridData2->iCols*pGridData2->iRows;
pGridData2->pGridMemory=(TCHAR**)HeapAlloc(hHeap,HEAP_ZERO_MEMORY,iSize*sizeof(void*));
if(!pGridData2->pGridMemory)
    goto CleanUp;
pGridData2->pTextColor=(COLORREF*)HeapAlloc(hHeap,HEAP_ZERO_MEMORY,iSize*sizeof(void*));
if(!pGridData2->pTextColor)
    goto CleanUp;
pGridData2->pBackColor=(HBRUSH*)HeapAlloc(hHeap,HEAP_ZERO_MEMORY,iSize*sizeof(void*));
if(!pGridData2->pBackColor)
    goto CleanUp;
pGridData2->pCreatedColors=(COLORREF*)HeapAlloc(hHeap,HEAP_ZERO_MEMORY,(MAX_COLORS+1)*sizeof(void*));
if(!pGridData2->pCreatedColors)
    goto CleanUp;
pGridData2->pCreatedBrushes=(HBRUSH*)HeapAlloc(hHeap,HEAP_ZERO_MEMORY,(MAX_COLORS+1)*sizeof(void*));
if(!pGridData2->pCreatedBrushes)
    goto CleanUp;
#ifdef MYDEBUG
_ftprintf(fp,_T("    iSize                   = %d\n"),iSize);
_ftprintf(fp,_T("    pGridData2->pGridMemory = %u\n"),pGridData2->pGridMemory);
#endif

//Done
if(pPos)
    HeapFree(hHeap,0,pPos);
#if defined MYDEBUG
_ftprintf(fp,_T("    Everything Allocated OK!\n"));
_ftprintf(fp,_T("  Leaving fnGridProc_OnCreate()\n\n"));
#endif
return 0;

CleanUp:
if(pGridData2->pColWidths)
{
    HeapFree(hHeap,0,pGridData2->pColWidths);
    pGridData2->pColWidths=NULL;
}
if(pGridData2->pCellCtrlTypes)
{
    HeapFree(hHeap,0,pGridData2->pCellCtrlTypes);
    pGridData2->pCellCtrlTypes=NULL;
}
if(pGridData2->pCtrlHdls)
{
    HeapFree(hHeap,0,pGridData2->pCtrlHdls);
    pGridData2->pCtrlHdls=NULL;
}
if(pPos)
    HeapFree(hHeap,0,pPos);
if(pGridData2->pVButtons)
{
    HeapFree(hHeap,0,pGridData2->pVButtons);
    pGridData2->pVButtons=NULL;
}
if(pGridData2->hFont)
    DeleteObject(pGridData2->hFont);
if(pGridData2->pCellHandles)
    HeapFree(hHeap,0,pGridData2->pCellHandles);
if(pGridData2->pGridMemory)
    HeapFree(hHeap,0,pGridData2->pGridMemory);
if(pGridData2->pTextColor)
    HeapFree(hHeap,0,pGridData2->pTextColor);
if(pGridData2->pBackColor)
    HeapFree(hHeap,0,pGridData2->pBackColor);
if(pGridData2->pCreatedColors)
    HeapFree(hHeap,0,pGridData2->pCreatedColors);
if(pGridData2->pCreatedBrushes)
    HeapFree(hHeap,0,pGridData2->pCreatedBrushes);
#if defined MYDEBUG
_ftprintf(fp,_T("  Leaving fnGridProc_OnCreate()\n\n"));
#endif

return 0;
}


long fnGridProc_OnSize(lpWndEventArgs Wea)
{
GridData* pGridData=NULL;
SCROLLINFO si;
int iCols;

#if defined MYDEBUG
_ftprintf(fp,_T("  Entering fnGridProc_OnSize()\n"));
_ftprintf(fp,_T("    Wea->hWnd = %u\n"),Wea->hWnd);
#endif
pGridData=(GridData*)GetWindowLongPtr(Wea->hWnd,0);
iCols=pGridData->iCols;
#ifdef MYDEBUG
_ftprintf(fp,_T("    pGridData    = %u\n"),pGridData);
_ftprintf(fp,_T("    iCols        = %d\n"),iCols);
#endif

//Set Up Horizontal Scrollbar
ZeroMemory(&si, sizeof(SCROLLINFO));
si.cbSize = sizeof(SCROLLINFO);
si.fMask  = SIF_RANGE | SIF_PAGE | SIF_POS | SIF_DISABLENOSCROLL;
si.nMin   = 0;
si.nMax   = pGridData->pColWidths[iCols];
si.nPage  = pGridData->cx-33; // 33 is the width of vert
si.nPos   = 0;                // btns + width scroll bar + window edge
SetScrollInfo(Wea->hWnd,SB_HORZ,&si,TRUE);
#ifdef MYDEBUG
_ftprintf(fp,_T("    Horizontal Scrollbar....\n"));
_ftprintf(fp,_T("    si.nMin    = %d\n"),si.nMin);
_ftprintf(fp,_T("    si.nMax    = %d\n"),si.nMax);
_ftprintf(fp,_T("    si.nPos    = %d\n"),si.nPos);
#endif

//Set Up Verticle Scrollbar
ZeroMemory(&si, sizeof(SCROLLINFO));
si.cbSize = sizeof(SCROLLINFO);
si.fMask  = SIF_RANGE | SIF_PAGE | SIF_POS;
si.nMin   = 1;
si.nMax   = pGridData->iRows;
si.nPage  = pGridData->iVisibleRows;
si.nPos   = 1;
SetScrollInfo(Wea->hWnd,SB_VERT,&si,TRUE);
#ifdef MYDEBUG
_ftprintf(fp,_T("    Verticle Scrollbar....\n"));
_ftprintf(fp,_T("    si.nMin    = %d\n"), si.nMin);
_ftprintf(fp,_T("    si.nMax    = %d\n"), si.nMax);
_ftprintf(fp,_T("    si.nPos    = %d\n"), si.nPos);
_ftprintf(fp,_T("    Leaving %WM_SIZE Case\n"));
_ftprintf(fp,_T("  Leaving fnGridProc_OnSize()\n\n"));
#endif

return 0;
}


long fnGridProc_OnHScroll(lpWndEventArgs Wea)
{
GridData* pGridData=NULL;
int iCols,iScrollPos;
SCROLLINFO si;

#if defined MYDEBUG
_ftprintf(fp,_T("Entering fnGridProc_OnHScroll()\n"));
_ftprintf(fp,_T("  Wea->hWnd = %u\n"),Wea->hWnd);
#endif
ZeroMemory(&si, sizeof(SCROLLINFO));
pGridData = (GridData*)GetWindowLongPtr(Wea->hWnd,0);
iCols     = pGridData->iCols;
si.cbSize = sizeof(SCROLLINFO);
si.fMask  = SIF_ALL;
GetScrollInfo(pGridData->hGrid,SB_HORZ,&si);
iScrollPos=si.nPos;
#ifdef MYDEBUG
_ftprintf(fp,_T("    Before Adjustments\n"));
_ftprintf(fp,_T("    si.nMin    = %d\n"), si.nMin);
_ftprintf(fp,_T("    si.nMax    = %d\n"), si.nMax);
_ftprintf(fp,_T("    si.nPos    = %d\n"), si.nPos);
#endif
switch(LOWORD(Wea->wParam))
{
   case SB_LINELEFT:
     {
        if(si.nPos > si.nMin)
           si.nPos=si.nPos-50;
        break;
     }
   case SB_PAGELEFT:
     {
        si.nPos = si.nPos - si.nPage;
        break;
     }
   case SB_LINERIGHT:
     {
        if(si.nPos<si.nMax)
           si.nPos=si.nPos+50;
        break;
     }
   case SB_PAGERIGHT:
     {
        si.nPos = si.nPos + si.nPage;
        break;
     }
   case SB_THUMBTRACK:
     {
        si.nPos=si.nTrackPos;
        break;
     }
}
si.fMask = SIF_POS;
SetScrollInfo(pGridData->hGrid,SB_HORZ,&si,TRUE);
GetScrollInfo(pGridData->hGrid,SB_HORZ,&si);
if(iScrollPos!=si.nPos)
{
    if(si.nPos==0)
       SetWindowPos(pGridData->hPane,HWND_TOP,0,0,pGridData->pColWidths[iCols],pGridData->iPaneHeight,SWP_SHOWWINDOW);
    else
       SetWindowPos(pGridData->hPane,HWND_TOP,-si.nPos,0,pGridData->pColWidths[iCols],pGridData->iPaneHeight,SWP_SHOWWINDOW);
}
#ifdef MYDEBUG
_ftprintf(fp,_T("    After All Adjustments\n"));
_ftprintf(fp,_T("    si.nMin    = %d\n"),si.nMin);
_ftprintf(fp,_T("    si.nMax    = %d\n"),si.nMax);
_ftprintf(fp,_T("    si.nPos    = %d\n"),si.nPos);
_ftprintf(fp,_T("    Leaving %WM_HSCROLL Case\n"));
_ftprintf(fp,_T("Leaving fnGridProc_OnHScroll()\n\n"));
#endif

return 0;
}


long fnGridProc_OnVScroll(lpWndEventArgs Wea)
{
GridData* pGridData=NULL;
int iCols,iScrollPos;
SCROLLINFO si;
HWND hCell;

#if defined MYDEBUG
_ftprintf(fp,_T("Entering fnGridProc_OnVScroll()\n"));
_ftprintf(fp,_T("  Wea->hWnd        = %u\n"),Wea->hWnd);
#endif
ZeroMemory(&si, sizeof(SCROLLINFO));
pGridData = (GridData*)GetWindowLongPtr(Wea->hWnd,0);
pGridData->pComObj->FlushData();
si.cbSize = sizeof(SCROLLINFO);
si.fMask  = SIF_ALL;
GetScrollInfo(pGridData->hGrid,SB_VERT,&si);
iScrollPos=si.nPos;
#ifdef MYDEBUG
_ftprintf(fp,_T("  Before Adjustments\n"));
_ftprintf(fp,_T("  si.nMin          = %d\n"), si.nMin);
_ftprintf(fp,_T("  si.nMax          = %d\n"), si.nMax);
_ftprintf(fp,_T("  si.nPos          = %d\n"), si.nPos);
_ftprintf(fp,_T("  pGridData->hGrid = %u\n"), pGridData->hGrid);
_ftprintf(fp,_T("  pGridData        = %u\n"), pGridData);
#endif
switch(LOWORD(Wea->wParam))             // this originally didn't work
{
    case SB_LINEUP:
      {
         if(si.nPos > si.nMin)
            si.nPos=si.nPos-1;
         break;
      }
    case SB_PAGEUP:
      {
         si.nPos = si.nPos - si.nPage;
         break;
      }
    case SB_LINEDOWN:
      {
         if(si.nPos<si.nMax)
            si.nPos=si.nPos+1;
         break;
      }
    case SB_PAGEDOWN:
      {
         si.nPos = si.nPos + si.nPage;
         break;
      }
    case SB_THUMBTRACK:
      {
         si.nPos=si.nTrackPos;
         break;
      }
}
si.fMask=SIF_POS;
SetScrollInfo(pGridData->hGrid,SB_VERT,&si,TRUE);
GetScrollInfo(pGridData->hGrid,SB_VERT,&si);
if(iScrollPos!=si.nPos)
{
     int iNum,iLast;
     iNum=pGridData->iCols*(si.nPos-1);
     iLast=(pGridData->iCols * pGridData->iVisibleRows) - 1;
     for(int i=0; i<=iLast; i++)
     {
       hCell=pGridData->pCellHandles[i];
       SetWindowLongPtr(hCell,0,(LONG_PTR)pGridData->pGridMemory[iNum]);
       SetWindowLongPtr(hCell,2*sizeof(void*),(LONG_PTR)pGridData->pTextColor[iNum]);
       SetWindowLongPtr(hCell,3*sizeof(void*),(LONG_PTR)pGridData->pBackColor[iNum]);
       iNum++;
     }
}
InvalidateRect(pGridData->hGrid,NULL,TRUE);
#ifdef MYDEBUG
_ftprintf(fp,_T("  After All Adjustments\n"));
_ftprintf(fp,_T("  si.nMin    = %d\n"),si.nMin);
_ftprintf(fp,_T("  si.nMax    = %d\n"),si.nMax);
_ftprintf(fp,_T("  si.nPos    = %d\n"),si.nPos);
_ftprintf(fp,_T("  Leaving %WM_VSCROLL Case\n"));
_ftprintf(fp,_T("Leaving fnGridProc_OnVScroll()\n\n"));
#endif

return 0;
}

Frederick J. Harris

2nd half WinCode.cpp


long fnGridProc_OnKeyDown(lpWndEventArgs Wea)
{
#if defined MYDEBUG
_ftprintf(fp,_T("Entering fnGridProc_OnKeyDown()\n"));
_ftprintf(fp,_T("  Wea->hWnd = %u\n"),Wea->hWnd);
#endif
if(Wea->wParam==VK_DELETE)
{
    GridData* pGridData=(GridData*)GetWindowLongPtr(Wea->hWnd,0);
    if(pGridData->blnRowSelected)
    {
       #if defined MYDEBUG
       _ftprintf(fp,_T("  pGridData = %u\n"),pGridData);
       _ftprintf(fp,_T("  A Row Is Selected, Namely, Row #%d\n"),pGridData->iSelectedRow);
       #endif
       for(int i=0; i<MAX_CONNECTIONS; i++)
       {
           if(pGridData->pComObj->pISink[i])
              pGridData->pComObj->pISink[i]->Grid_OnDelete(pGridData->iSelectedRow);
       }
       pGridData->pComObj->Refresh();
    }
}
#if defined MYDEBUG
_ftprintf(fp,_T("Leaving fnGridProc_OnKeyDown()\n\n"));
#endif

return 0;
}


long fnGridProc_OnCommand(lpWndEventArgs Wea)
{
#if defined MYDEBUG
_ftprintf(fp,_T("Entering fnGridProc_OnCommand()\n"));
_ftprintf(fp,_T("  Wea->hWnd = %u\n"),Wea->hWnd);
_ftprintf(fp,_T("  LOWORD(Wea->wParam) = %d\n"),LOWORD(Wea->wParam));
#endif
if(LOWORD(Wea->wParam)>20000)
{
    GridData* pGridData=(GridData*)GetWindowLongPtr(Wea->hWnd,0);
    pGridData->pComObj->FlushData();
    SCROLLINFO si;
    ZeroMemory(&si, sizeof(SCROLLINFO));
    si.cbSize = sizeof(SCROLLINFO);
    si.fMask=SIF_POS;
    GetScrollInfo(pGridData->hGrid,SB_VERT,&si);
    int iCellRow=LOWORD(Wea->wParam)-20000;
    int iGridRow=si.nPos+iCellRow-1;
    #if defined MYDEBUG
    _ftprintf(fp,_T("  iCellRow = %d\n"),iCellRow);
    _ftprintf(fp,_T("  iGridRow = %d\n"),iGridRow);
    _ftprintf(fp,_T("  pGridData->blnRowSelected = %d\n"),pGridData->blnRowSelected);
    #endif
    if(pGridData->blnRowSelected)
    {
       if(iGridRow==pGridData->iSelectedRow)
       {
           #ifdef MYDEBUG
           _ftprintf(fp,_T("  We Got In Where iGridRow = @pGridData.iSelectedRow!\n"));
           #endif
           for(int i=1; i<=pGridData->iCols; i++)
               pGridData->pComObj->SetCellAttributes(pGridData->iSelectedRow,i,0x00FFFFFF,0);
           pGridData->iSelectedRow=0, pGridData->blnRowSelected=FALSE;
       }
       else
       {
           #ifdef MYDEBUG
           _ftprintf(fp,_T("  We Got In Where iGridRow != pGridData->iSelectedRow!\n"));
           #endif
           for(int i=1; i<=pGridData->iCols; i++)
               pGridData->pComObj->SetCellAttributes(pGridData->iSelectedRow,i,0x00FFFFFF,0);
           pGridData->iSelectedRow=iGridRow;
           for(int i=1; i<=pGridData->iCols; i++)
               pGridData->pComObj->SetCellAttributes(iGridRow,i,pGridData->iSelectionBackColor,pGridData->iSelectionTextColor);
       }
    }
    else
    {
       #ifdef MYDEBUG
       _ftprintf(fp,_T("  We Got In Where pGridData->blnSelected = FALSE!\n"));
       _ftprintf(fp,_T("  pGridData->iSelectionBackColor         = 0x%x\n"),pGridData->iSelectionBackColor);
       _ftprintf(fp,_T("  pGridData->iSelectionTextColor         = 0x%x\n"),pGridData->iSelectionTextColor);
       #endif
       for(int i=1; i<=pGridData->iCols; i++)
           pGridData->pComObj->SetCellAttributes(iGridRow,i,pGridData->iSelectionBackColor,pGridData->iSelectionTextColor);
       pGridData->blnRowSelected=TRUE;
       pGridData->iSelectedRow=iGridRow;
       #ifdef MYDEBUG
       _ftprintf(fp,_T("  pGridData.iSelectedRow    = %d\n"),pGridData->iSelectedRow);
       _ftprintf(fp,_T("  pGridData->blnRowSelected = %d\n"),pGridData->blnRowSelected);
       #endif
    }
    pGridData->pComObj->Refresh();
    for(int i=0; i<MAX_CONNECTIONS; i++)
    {
        if(pGridData->pComObj->pISink[i])
           pGridData->pComObj->pISink[i]->Grid_OnRowSelection(iGridRow,pGridData->blnRowSelected);
    }
    SetFocus(Wea->hWnd);
}
#if defined MYDEBUG
_ftprintf(fp,_T("Leaving fnGridProc_OnCommand()\n\n"));
#endif

return 0;
}


long fnGridProc_OnDestroy(lpWndEventArgs Wea)
{
GridData* pGridData=NULL;
HANDLE hHeap=NULL;
TCHAR* pMem=NULL;
BOOL blnFree=0;
int iCtr=0,i,j;

#if defined MYDEBUG
_ftprintf(fp,_T("Entering fnGridProc_OnDestroy()\n"));
#endif
pGridData=(GridData*)GetWindowLongPtr(Wea->hWnd,0);
#if defined MYDEBUG
_ftprintf(fp,_T("  Wea->hWnd                           = %u\n"),Wea->hWnd);
_ftprintf(fp,_T("  pGridData                           = %u\n\n"),pGridData);
#endif
if(pGridData)
{
    #if defined MYDEBUG
    _ftprintf(fp,_T("  i\tpGridData->pColWidths[i]\n"));
    _ftprintf(fp,_T("  =============================\n"));
    for(i=0; i<5; i++)
        _ftprintf(fp,_T("  %u\t%u\n"),i,pGridData->pColWidths[i]);
    _ftprintf(fp,_T("\n  pGridData->pColWidths[5]            = %u\n"),pGridData->pColWidths[5]);
    #endif

    hHeap=GetProcessHeap();
    if(hHeap)
    {
       #if defined MYDEBUG
       _ftprintf(fp,_T("  pGridData->pColWidths               = %u\n"),pGridData->pColWidths);
       #endif
       if(pGridData->pColWidths)
          blnFree=HeapFree(hHeap,0,pGridData->pColWidths);
       #if defined MYDEBUG
       _ftprintf(fp,_T("  blnFree(pGridData->pColWidths)      = %u\n"),blnFree);
       #endif
       if(pGridData->pCellCtrlTypes)
          blnFree=HeapFree(hHeap,0,pGridData->pCellCtrlTypes);
       #if defined MYDEBUG
       _ftprintf(fp,_T("  blnFree(pGridData->pCellCtrlTypes)  = %u\n"),blnFree);
       #endif
       if(pGridData->pCtrlHdls)
          blnFree=HeapFree(hHeap,0,pGridData->pCtrlHdls);
       #if defined MYDEBUG
       _ftprintf(fp,_T("  blnFree(pGridData->pCtrlHdls)       = %u\n"),blnFree);
       #endif
       if(pGridData->pVButtons)
       {
          blnFree=HeapFree(hHeap,0,pGridData->pVButtons);
          pGridData->pVButtons=NULL;
          #if defined MYDEBUG
          _ftprintf(fp,_T("  blnFree(pGridData->pVButtons)       = %u\n"),blnFree);
          #endif
       }

       if(pGridData->hFont)
       {
          blnFree=DeleteObject(pGridData->hFont);
          #if defined MYDEBUG
          _ftprintf(fp,_T("  blnFree(pGridData->hFont)           = %u\n"),blnFree);
          #endif
       }

       if(pGridData->pCellHandles)
       {
          blnFree=HeapFree(hHeap,0,pGridData->pCellHandles);
          #if defined MYDEBUG
          _ftprintf(fp,_T("  blnFree(pGridData->pCellHandles)    = %u\n"),blnFree);
          #endif
          blnFree=0;
       }

       #if defined MYDEBUG
       _ftprintf(fp,_T("\n"));
       _ftprintf(fp,_T("  i             j               iCtr            pMem            pMem            blnFree\n"));
       _ftprintf(fp,_T("  =====================================================================================\n"));
       #endif
       for(i=1; i<=pGridData->iRows; i++)
       {
         for(j=1; j<=pGridData->iCols; j++)
         {
           pMem=pGridData->pGridMemory[iCtr];
           if(pMem)
           {
              #if defined MYDEBUG
              _ftprintf(fp,_T("  %d\t\t%d\t\t%d\t\t%u\t\t%s\t\t"),i,j,iCtr,pMem,pMem);
              #endif
              blnFree=HeapFree(hHeap,0,pMem);
              pMem=NULL;
              #ifdef MYDEBUG
              _ftprintf(fp,_T("%d\n"),blnFree);
              #endif
              blnFree=0;
           }
           else
           {
              #if defined MYDEBUG
              _ftprintf(fp,_T("  %d\t\t%d\t\t%d\t\t%u\t\t%s\t\t%d\n"),i,j,iCtr,pMem,pMem,blnFree);
              #endif
           }
           iCtr++;
         }
       }

       if(pGridData->pGridMemory)
       {
          blnFree=HeapFree(hHeap,0,pGridData->pGridMemory);
          #if defined MYDEBUG
          _ftprintf(fp,_T("\n  blnFree(pGridData->pGridMemory)     = %u\n"),blnFree);
          #endif
       }
       if(pGridData->pTextColor)
       {
          blnFree=HeapFree(hHeap,0,pGridData->pTextColor);
          #if defined MYDEBUG
          _ftprintf(fp,_T("  blnFree(pGridData->pTextColor)      = %u\n"),blnFree);
          #endif
       }
       if(pGridData->pBackColor)
       {
          blnFree=HeapFree(hHeap,0,pGridData->pBackColor);
          #if defined MYDEBUG
          _ftprintf(fp,_T("  blnFree(pGridData->pBackColor)      = %u\n"),blnFree);
          #endif
       }

       #if defined MYDEBUG
       _ftprintf(fp,_T("  pGridData->pCreatedBrushes[0]       = %d\n"),pGridData->pCreatedBrushes[0]);
       _ftprintf(fp,_T("  pGridData->pCreatedColors[0]        = %d\n\n"),pGridData->pCreatedColors[0]);
       _ftprintf(fp,_T("  i\tDeleteObject(i)\n"));
       _ftprintf(fp,_T("  =====================\n"));
       #endif
       for(i=1; i<=(int)pGridData->pCreatedBrushes[0]; i++)
       {
           if(pGridData->pCreatedBrushes[i])
           {
              blnFree=DeleteObject(pGridData->pCreatedBrushes[i]);
              #if defined MYDEBUG
              _ftprintf(fp,_T("  %d\t%d\n"),i,blnFree);
              #endif
           }
       }
       if(pGridData->pCreatedColors)
       {
          blnFree=HeapFree(hHeap,0,pGridData->pCreatedColors);
          #if defined MYDEBUG
          _ftprintf(fp,_T("\n  blnFree(pGridData->pCreatedColors)  = %u\n"),blnFree);
          #endif
       }
       if(pGridData->pCreatedBrushes)
       {
          blnFree=HeapFree(hHeap,0,pGridData->pCreatedBrushes);
          #if defined MYDEBUG
          _ftprintf(fp,_T("  blnFree(pGridData->pCreatedBrushes) = %u\n"),blnFree);
          #endif
       }
       blnFree=HeapFree(hHeap,0,pGridData);
       #if defined MYDEBUG
       _ftprintf(fp,_T("  blnFree                             = %u\n"),blnFree);
       #endif
    }
}
#if defined MYDEBUG
_ftprintf(fp,_T("Leaving fnGridProc_OnDestroy()\n\n"));
#endif

return 0;
}


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

for(unsigned int i=0; i<7; 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));
}


LRESULT CALLBACK fnPaneProc(HWND hPane, unsigned int msg, WPARAM wParam, LPARAM lParam)
{
if(msg==WM_NOTIFY)
{
    int iCols,iRange,index,iHt;
    GridData* pGridData=NULL;
    NMHEADER* pNotify=NULL;
    SCROLLINFO si;

    #if defined MYDEBUG
    _ftprintf(fp,_T("Entering fnPaneProc() -- WM_NOTIFY Case\n"));
    #endif
    pNotify=(NMHEADER*)lParam;
    pGridData=(GridData*)GetWindowLongPtr(hPane,0);
    iCols=pGridData->iCols;
    #ifdef MYDEBUG
    _ftprintf(fp,_T("  pGridData              = %u\n"),pGridData);
    _ftprintf(fp,_T("  pGridData->iCols       = %d\n"),pGridData->iCols);
    _ftprintf(fp,_T("  pGridData->pComObj     = %u\n"),pGridData->pComObj);
    _ftprintf(fp,_T("  pGridData->hCtrlInCell = %u\n"),pGridData->hCtrlInCell);
    #endif
    switch(pNotify->hdr.code)
    {
      case HDN_TRACK:
        {
           #ifdef MYDEBUG
           _ftprintf(fp,_T("  Got HDN_TRACK Notification\n"));
           #endif
           if(pGridData->hCtrlInCell)
           {
              pGridData->pComObj->FlushData();
              pGridData->pComObj->Refresh();
           }
           if(pGridData->pColWidths)
           {
              pGridData->pColWidths[pNotify->iItem]=pNotify->pitem->cxy;
              #ifdef MYDEBUG
              _ftprintf(fp,_T("  pNotify->iItem         = %d\n"),pNotify->iItem);
              _ftprintf(fp,_T("  pNotify->pitem->cxy    = %d\n"),pNotify->pitem->cxy);
              #endif
           }
           pGridData->pColWidths[iCols]=0;
           for(int i=0; i<iCols; i++)
               pGridData->pColWidths[iCols]=pGridData->pColWidths[iCols]+pGridData->pColWidths[i];

           si.cbSize = sizeof(SCROLLINFO);
           si.fMask  = SIF_RANGE|SIF_PAGE|SIF_DISABLENOSCROLL;
           si.nMin   = 0;
           si.nMax   = pGridData->pColWidths[iCols];
           si.nPage  = pGridData->cx-33;
           iRange    = si.nMax-si.nMin;
           SetScrollInfo(pGridData->hGrid,SB_HORZ,&si,TRUE);
           if(iRange>si.nPage)
              SetWindowPos(pGridData->hPane,HWND_TOP,0,0,pGridData->pColWidths[iCols],pGridData->cy,SWP_NOMOVE|SWP_SHOWWINDOW);
           else
              SetWindowPos(pGridData->hPane,HWND_TOP,0,0,pGridData->pColWidths[iCols],pGridData->cy,SWP_SHOWWINDOW);
           SetWindowPos(pGridData->hHeader,HWND_BOTTOM,0,0,pGridData->pColWidths[iCols],pGridData->iRowHeight,SWP_NOMOVE|SWP_SHOWWINDOW);
           #ifdef MYDEBUG
           _ftprintf(fp,_T("    si.nMin                       = %u\n"),si.nMin);
           _ftprintf(fp,_T("    si.nMax                       = %u\n"),si.nMax);
           _ftprintf(fp,_T("    si.nPage                      = %u\n"),si.nPage);
           _ftprintf(fp,_T("    iRange                        = %d\n"),iRange);
           _ftprintf(fp,_T("    pGridData->pColWidths[iCols]  = %d\n"),pGridData->pColWidths[iCols]);
           #endif

           HANDLE hHeap=GetProcessHeap();
           int* pPos=(int*)HeapAlloc(hHeap,HEAP_ZERO_MEMORY,iCols*sizeof(int));
           if(!pPos)
              return 0;
           for(int i=1; i<iCols; i++)
               pPos[i]=pPos[i-1]+pGridData->pColWidths[i-1];
           if(pGridData->pCellHandles)
           {
              for(int i=0; i<pGridData->iVisibleRows; i++)
              {
                  for(int j=0; j<iCols; j++)
                  {
                      index=iCols*i+j;
                      iHt=pGridData->iRowHeight;
                      MoveWindow(pGridData->pCellHandles[index], pPos[j], iHt+(i*iHt), pGridData->pColWidths[j], iHt, FALSE);
                  }
              }
              InvalidateRect(pGridData->hGrid,NULL,TRUE);
           }
           HeapFree(hHeap,0,pPos);
           break;
        }
      case HDN_ENDTRACK:
        {
           #if defined MYDEBUG
           _ftprintf(fp,_T("  Got HDN_TRACK Notification\n"));
           #endif
           InvalidateRect(pGridData->hGrid,0,TRUE);
           break;
        }
    }
    #if defined MYDEBUG
    _ftprintf(fp,_T("Leaving fnPaneProc() -- WM_NOTIFY Case\n\n"));
    #endif
}

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


LRESULT CALLBACK fnBaseProc(HWND hwnd, unsigned int msg, WPARAM wParam, LPARAM lParam)
{
return (DefWindowProc(hwnd, msg, wParam, lParam));
}


LRESULT CALLBACK fnEditSubClass(HWND hEdit, UINT msg, WPARAM wParam, LPARAM lParam)
{
int blnCancel=0;
HWND hCell=GetParent(hEdit);
HWND hPane=GetParent(hCell);
HWND hBase=GetParent(hPane);
HWND hGrid=GetParent(hBase);
GridData* pGridData=(GridData*)GetWindowLongPtr(hGrid,0);
switch(msg)
{
   case WM_CHAR:
     {
        #ifdef MYDEBUG
        _ftprintf(fp,_T("Entering fnEditSubClass()  -- Case WM_CHAR\n"));
        _ftprintf(fp,_T("  hEdit     = %u\n"),hEdit);
        _ftprintf(fp,_T("  hCell     = %u\n"),hCell);
        _ftprintf(fp,_T("  hPane     = %u\n"),hPane);
        _ftprintf(fp,_T("  hBase     = %u\n"),hBase);
        _ftprintf(fp,_T("  hGrid     = %u\n"),hGrid);
        _ftprintf(fp,_T("  pGridData = %u\n"),pGridData);
        _ftprintf(fp,_T("  wParam    = %c\n"),wParam);
        #endif
        for(int i=0; i<MAX_CONNECTIONS; i++)
        {
            if(pGridData->pComObj->pISink[i])
            {
               pGridData->pComObj->pISink[i]->Grid_OnKeyPress((int)wParam, (int)lParam, pGridData->iEditedRow, pGridData->iEditedCol, &blnCancel);
               if(blnCancel)
                  return 0;
            }
        }
        if(wParam==VK_RETURN)
        {
           pGridData->pComObj->FlushData();
           pGridData->pComObj->Refresh();
           return 0;
        }
        else
           pGridData->hCtrlInCell=hEdit;
        #ifdef MYDEBUG
        _ftprintf(fp,_T("Leaving fnEditSubClass()\n"));
        #endif
        break;
     }
   case WM_KEYDOWN:
     {
        #ifdef MYDEBUG
        _ftprintf(fp,_T("Entering fnEditSubClass()  -- Case WM_KEYDOWN\n"));
        _ftprintf(fp,_T("  hEdit = %u\n"),hEdit);
        _ftprintf(fp,_T("  hCell = %u\n"),hCell);
        _ftprintf(fp,_T("  hPane = %u\n"),hPane);
        _ftprintf(fp,_T("  hBase = %u\n"),hBase);
        _ftprintf(fp,_T("  hGrid = %u\n"),hGrid);
        _ftprintf(fp,_T("  pGridData = %u\n"),pGridData);
        #endif
        for(int i=0; i<MAX_CONNECTIONS; i++)
        {
            if(pGridData->pComObj->pISink[i])
            {
               pGridData->pComObj->pISink[i]->Grid_OnKeyDown((int)wParam, (int)lParam, pGridData->iEditedCellRow, pGridData->iEditedRow, pGridData->iEditedCol, &blnCancel);
               if(blnCancel)
                  return 0;
            }
        }
        #ifdef MYDEBUG
        _ftprintf(fp,_T("Leaving fnEditSubClass()\n"));
        #endif
        break;
     }
   case WM_PASTE:
     {
        for(int i=0; i<MAX_CONNECTIONS; i++)
        {
            if(pGridData->pComObj->pISink[i])
            {
               pGridData->pComObj->pISink[i]->Grid_OnPaste(pGridData->iEditedCellRow, pGridData->iEditedRow, pGridData->iEditedCol);
               if(blnCancel)
                  return 0;
            }
        }
        break;
     }
   case WM_LBUTTONDBLCLK:
     {
        for(int i=0; i<MAX_CONNECTIONS; i++)
        {
            if(pGridData->pComObj->pISink[i])
            {
               pGridData->pComObj->pISink[i]->Grid_OnLButtonDblClk(pGridData->iEditedCellRow, pGridData->iEditedRow, pGridData->iEditedCol);
               if(blnCancel)
                  return 0;
            }
        }
        break;
     }

}

return CallWindowProc(fnEditWndProc,hEdit,msg,wParam,lParam);
}


BOOL CALLBACK EnumGridProc(HWND hWnd, LPARAM lParam)
{
#ifdef MYDEBUG
_ftprintf(fp,_T("  Called EnumGridProc() - %u\t%u\n"),hWnd,lParam);
#endif
if(GetClassLongPtr(hWnd,GCLP_WNDPROC)==lParam)
{
    #ifdef MYDEBUG
    _ftprintf(fp,_T("\n  Made A Match! - %u\t%u\n\n"),hWnd,lParam);
    #endif
    GridData* pGridData=(GridData*)GetWindowLongPtr(hWnd,0);
    pGridData->pComObj->FlushData();
}

return TRUE;
}


/*
' Offset     What's Stored There in WNDCLASSEX::cbWndExtra Bytes
' ==============================================================
' 0  -  3    pZStr, i.e., pointer to string data (ZStr) for cell      0  -  7
' 4  -  7    hFont, i.e., this was created in fnWndProc_OnCreate      8  -  15
' 8  - 11    dwColor, i.e., text color, and an RGB value             16  -  23
' 12 - 15    HBrush, and this is a HANDLE                            24  -  31
*/

LRESULT CALLBACK fnCellProc(HWND hCell, unsigned int msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
   case WM_CREATE:
     {
        SetWindowLongPtr(hCell,0,NULL);
        return 0;
     }
   case WM_LBUTTONDOWN:
     {
        int iCellBufferPos=0,blnFound=0,iGridMemOffset=0,iRow,iCol,i,j;
        HWND hGrid=NULL,hBase=NULL,hPane=NULL;
        GridData* pGridData=NULL;
        TCHAR* pZStr=NULL;
        SCROLLINFO si;
        HRESULT hr;
        #ifdef MYDEBUG
        _ftprintf(fp,_T("\nEntering fnCellProc()  --  case WM_LBUTTONDOWN\n"));
        #endif
        hPane=GetParent(hCell);
        hBase=GetParent(hPane);
        hGrid=GetParent(hBase);
        pGridData=(GridData*)GetWindowLongPtr(hPane,0);
        #ifdef MYDEBUG
        _ftprintf(fp,_T("  hCell              = %u\n"),hCell);
        _ftprintf(fp,_T("  hPane              = %u\n"),hPane);
        _ftprintf(fp,_T("  hBase              = %u\n"),hBase);
        _ftprintf(fp,_T("  hGrid              = %u\n"),hGrid);
        _ftprintf(fp,_T("  pGridData->hParent = %u\n"),pGridData->hParent);
        #endif
        EnumChildWindows(pGridData->hParent,EnumGridProc,(LPARAM)fnGridProc);
        ZeroMemory(&si, sizeof(SCROLLINFO));
        si.cbSize = sizeof(SCROLLINFO), si.fMask=SIF_POS;
        GetScrollInfo(hGrid,SB_VERT,&si);
        for(i=1; i<=pGridData->iVisibleRows; i++)
        {
            for(j=1; j<=pGridData->iCols; j++)
            {
                iCellBufferPos = dwIdx(i,j);
                if(pGridData->pCellHandles[iCellBufferPos]==hCell)
                {
                   iGridMemOffset = pGridData->iCols * (si.nPos -1) + iCellBufferPos;
                   pZStr=pGridData->pGridMemory[iGridMemOffset];
                   iRow=i, iCol=j;
                   pGridData->iEditedCellRow=iRow;
                   pGridData->iEditedRow=iRow+si.nPos-1;
                   pGridData->iEditedCol=iCol;
                   blnFound=TRUE;
                   goto Finished;
                }
          }
        }
        Finished:
        if(blnFound)
        {
           #ifdef MYDEBUG
           _ftprintf(fp,_T("  Found ZStr at %d\t%d\n"),i,j);
           _ftprintf(fp,_T("  si.nPos                = %u\n"),si.nPos);
           _ftprintf(fp,_T("  pZStr                  = %u\n"),pZStr);
           _ftprintf(fp,_T("  pZStr                  = %s\n"),pZStr);
           _ftprintf(fp,_T("  pGridData->hCtrlInCell = %u\n"),pGridData->hCtrlInCell);
           #endif
           pGridData->hCtrlInCell=pGridData->pCtrlHdls[iCol-1];
           SetParent(pGridData->hCtrlInCell,hCell);
           fnEditWndProc=(WNDPROC)SetWindowLongPtr(pGridData->hCtrlInCell,GWLP_WNDPROC,(LONG_PTR)fnEditSubClass);
           if(pGridData->hFont)
              SendMessage(pGridData->hCtrlInCell,WM_SETFONT,(WPARAM)pGridData->hFont,(LPARAM)TRUE);
           if(pGridData->pCellCtrlTypes[iCol-1]==GRID_CELL_CTRL_EDIT)
           {
              SetWindowPos(pGridData->hCtrlInCell,HWND_TOP,1,0,pGridData->pColWidths[iCol-1]-2,pGridData->iRowHeight,SWP_SHOWWINDOW);
              SetWindowText(pGridData->hCtrlInCell,pZStr);
              SetFocus(pGridData->hCtrlInCell);
              SendMessage(pGridData->hCtrlInCell,EM_SETSEL,(WPARAM)0,(LPARAM)-1);
           }
           if(pGridData->pCellCtrlTypes[iCol-1]==GRID_CELL_CTRL_COMBO)
           {
              SetWindowPos(pGridData->hCtrlInCell,HWND_TOP,1,0,pGridData->pColWidths[iCol-1]-2,180,SWP_SHOWWINDOW);
              SendMessage(pGridData->hCtrlInCell,CB_SETCURSEL,(WPARAM)-1,(LPARAM)0);
           }
           for(i=0; i<MAX_CONNECTIONS; i++)
           {
               if(pGridData->pComObj->pISink[i])
               {
                  pGridData->pComObj->pISink[i]->Grid_OnLButtonDown(iRow,pGridData->iEditedRow,iCol);
               }
           }
        }
        #ifdef MYDEBUG
        _ftprintf(fp,_T("Leaving fnCellProc()  --  case WM_LBUTTONDOWN\n\n"));
        #endif
        return 0;
     }
   case WM_PAINT:
     {
        PAINTSTRUCT ps;
        HDC hDC=BeginPaint(hCell,&ps);
        TCHAR* pBuffer=(TCHAR*)GetWindowLongPtr(hCell,0);                   // 0
        HFONT hFont=(HFONT)GetWindowLongPtr(hCell,sizeof(void*));           // 8
        COLORREF dwColor=(COLORREF)GetWindowLongPtr(hCell,2*sizeof(void*)); // 16
        HBRUSH hBrush=(HBRUSH)GetWindowLongPtr(hCell,3*sizeof(void*));      // 24
        if(pBuffer)
        {
           HFONT hTmp=NULL;
           COLORREF dwTmpClr=0;
           if(hFont)
              hTmp=(HFONT)SelectObject(hDC,hFont);
           if(dwColor)
              dwTmpClr=SetTextColor(hDC,dwColor);
           if(hBrush)
           {
              RECT rc;
              HBRUSH hTmpBr=(HBRUSH)SelectObject(hDC,hBrush);
              GetClientRect(hCell,&rc);
              FillRect(hDC,&rc,hBrush);
              SelectObject(hDC,hTmpBr);
           }
           int iBkMode=SetBkMode(hDC,TRANSPARENT);
           TextOut(hDC,1,0,pBuffer,_tcslen(pBuffer));
           if(hFont && hTmp)
              hFont=(HFONT)SelectObject(hDC,hTmp);
           if(dwColor)
              SetTextColor(hDC,dwTmpClr);
           SetBkMode(hDC,iBkMode);
        }
        EndPaint(hCell,&ps);
        return 0;
     }
}

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


Frederick J. Harris

Server.cpp


// Server.cpp
// CD C:\Code\VStudio\VC++9\FHGrid
// CD C:\Code\VStudio\VC++9\64_Bit\FHGrid
// cl Server.cpp Grid.cpp WinCode.cpp Registry.cpp Strings.cpp FHGridRes.obj Kernel32.lib comctl32.lib User32.lib Gdi32.lib UUID.lib Advapi32.lib Ole32.lib OleAut32.lib FHGrid.def /MT /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      "WinCode.h"
#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;
TCHAR szBuffer[512];
HRESULT hr;

#if defined MYDEBUG
if(fp==NULL)
{
    GetCurrentDirectory(512,szBuffer);
    _tcscat(szBuffer,_T("\\Output.txt"));
    fp=_tfopen(szBuffer,_T("w"));
}
printf("  Entering DllGetClassObject()\n");
_ftprintf(fp,_T("Entering DllGetClassObject()\n"));
_ftprintf(fp,_T("  szBuffer = %s\n"),szBuffer);
#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);
_ftprintf(fp,_T("  pCF = %d\n"),pCF);
#endif
hr=pCF->QueryInterface(riid,ppv);
if(FAILED(hr))
    delete pCF;
else
    Initialize();
#if defined MYDEBUG
printf("  Leaving DllGetClassObject()\n");
_ftprintf(fp,_T("Leaving DllGetClassObject()\n\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);
_ftprintf(fp,_T("Entering DllCanUnloadNow()\n"));
_ftprintf(fp,_T("  g_lObjs  = %d\n"),g_lObjs);
_ftprintf(fp,_T("  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");
    _ftprintf(fp,_T("  Gonna Hang Around A Bit Yet Wheather You Like It Or Not!\n"));
    _ftprintf(fp,_T("Leaving DllCanUnloadNow()\n"));
    #endif
    return S_FALSE;
}
else
{
    #if defined MYDEBUG
    printf("  I'm Outta Here!\n");
    printf("Leaving DllCanUnloadNow()\n");
    _ftprintf(fp,_T("  I'm Outta Here!\n"));
    _ftprintf(fp,_T("Leaving DllCanUnloadNow()\n"));
    fclose(fp);
    #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;
}


Frederick J. Harris

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 ;
}


Frederick J. Harris

Strings.cpp


//Strings.cpp
#define   UNICODE
#define   _UNICODE
#define   JUST_NEED_PARSE   // << keep this active for compiling grid code
#include "windows.h"        // otherwise, comment it out so the rest of the
#include  <stdlib.h>        // string class works
#include  <cstdio>
#include  <tchar.h>
#include  <math.h>
#include  <string.h>
#include  "Strings.h"


#ifndef JUST_NEED_PARSE
String operator+(TCHAR* lhs, String& rhs)         //global function
{
String sr=lhs;
sr=sr+rhs;

return sr;
}
#endif


String::String()
{
lpBuffer=new TCHAR[MINIMUM_ALLOCATION];
lpBuffer[0]=_T('\0');
this->iCapacity=MINIMUM_ALLOCATION-1;
this->iLen=0;
}


String::String(const TCHAR ch)  //Constructor: Initializes with TCHAR
{
this->iLen=1;
SIZE_T iNewSize=MINIMUM_ALLOCATION;
this->lpBuffer=new TCHAR[iNewSize];
this->iCapacity=iNewSize-1;
this->lpBuffer[0]=ch, this->lpBuffer[1]=_T('\0');
}


String::String(const TCHAR* pStr)  //Constructor: Initializes with TCHAR*
{
this->iLen=_tcslen(pStr);
SIZE_T iNewSize=(this->iLen/16+1)*16;
this->lpBuffer=new TCHAR[iNewSize];
this->iCapacity=iNewSize-1;
_tcscpy(lpBuffer,pStr);
}


String::String(const String& s)  //Constructor Initializes With Another String, i.e., Copy Constructor
{
SIZE_T iNewSize=(s.iLen/16+1)*16;
this->iLen=s.iLen;
this->lpBuffer=new TCHAR[iNewSize];
this->iCapacity=iNewSize-1;
_tcscpy(this->lpBuffer,s.lpBuffer);
}


#ifndef JUST_NEED_PARSE
String::String(const SIZE_T iSize, bool blnFillNulls)  //Constructor Creates String With Custom Sized
{                                                   //Buffer (rounded up to paragraph boundary)
SIZE_T iNewSize=(iSize/16+1)*16;
this->lpBuffer=new TCHAR[iNewSize];
this->iCapacity=iNewSize-1;
this->iLen=0;
this->lpBuffer[0]=_T('\0');
if(blnFillNulls)
{
    for(size_t i=0; i<this->iCapacity; i++)
        this->lpBuffer[i]=0;
}
}


String::String(SIZE_T iCount, const TCHAR ch)
{
SIZE_T iNewSize=(iCount/16+1)*16;
this->lpBuffer=new TCHAR[iNewSize];
this->iCapacity=iNewSize-1;
for(size_t i=0; i<iCount; i++)
     this->lpBuffer[i]=ch;
this->lpBuffer[iCount]=_T('\0');
this->iLen=iCount;
}


String::String(SSIZE_T iNum)
{
this->lpBuffer=new TCHAR[16];
this->iCapacity=15;
this->iLen=_stprintf(this->lpBuffer,_T("%d"),iNum);
}


String::String(SIZE_T iNum)
{
this->lpBuffer=new TCHAR[16];
this->iCapacity=15;
this->iLen=_stprintf(this->lpBuffer,_T("%u"),iNum);
}


String::String(double dblNum)
{
this->lpBuffer=new TCHAR[32];
this->iCapacity=31;
this->iLen=_stprintf(this->lpBuffer,_T("%10.14f"),dblNum);
}


String& String::operator=(double dblNum)
{
if(this->iCapacity<32)
{
    delete [] this->lpBuffer;
    lpBuffer=new TCHAR[32];
    this->iCapacity=31;
}
this->iLen=_stprintf(this->lpBuffer,_T("%10.14f"),dblNum);

return *this;
}


void String::SetTCHAR(unsigned iOffset, TCHAR ch)   //zero based!
{
if(iOffset<this->iCapacity)
{
    this->lpBuffer[iOffset]=ch;
    if(ch==_T('\0'))
    {
       if(iOffset<this->iLen || this->iLen==0)
          this->iLen=iOffset;
    }
}
}
#endif


String& String::operator=(const TCHAR ch)
{
this->lpBuffer[0]=ch, this->lpBuffer[1]=_T('\0');
this->iLen=1;
return *this;
}


String& String::operator=(const TCHAR* pStr)
{
size_t iNewLen=_tcslen(pStr);
if(iNewLen>this->iCapacity)
{
    delete [] this->lpBuffer;
    size_t iNewSize=(iNewLen*EXPANSION_FACTOR/16+1)*16;
    this->lpBuffer=new TCHAR[iNewSize];
    this->iCapacity=iNewSize-1;
}
_tcscpy(this->lpBuffer,pStr);
this->iLen=iNewLen;

return *this;
}


String& String::operator=(const String& strAnother)
{
if(this==&strAnother)
    return *this;
if(strAnother.iLen>this->iCapacity)
{
    delete [] this->lpBuffer;
    SIZE_T iNewSize=(strAnother.iLen*EXPANSION_FACTOR/16+1)*16;
    this->lpBuffer=new TCHAR[iNewSize];
    this->iCapacity=iNewSize-1;
}
_tcscpy(this->lpBuffer,strAnother.lpBuffer);
this->iLen=strAnother.iLen;

return *this;
}


#ifndef JUST_NEED_PARSE
String& String::operator=(int iNum)
{
if(this->iCapacity>=15)
    this->iLen=_stprintf(this->lpBuffer,_T("%d"),iNum);
else
{
    delete [] this->lpBuffer;
    this->lpBuffer=new TCHAR[16];
    this->iCapacity=15;
    this->iLen=_stprintf(this->lpBuffer,_T("%d"),iNum);
}

return *this;
}


String& String::operator=(unsigned int iNum)
{
  if(this->iCapacity>=15)
    this->iLen=_stprintf(this->lpBuffer,_T("%d"),iNum);
else
{
    delete [] this->lpBuffer;
    this->lpBuffer=new TCHAR[16];
    this->iCapacity=15;
    this->iLen=_stprintf(this->lpBuffer,_T("%d"),iNum);
}

return *this;
}


String String::operator+(const TCHAR ch)
{
SIZE_T iNewLen=this->iLen+1;

String s(iNewLen,false);
_tcscpy(s.lpBuffer,this->lpBuffer);
s.lpBuffer[iNewLen-1]=ch;
s.lpBuffer[iNewLen]=_T('\0');
s.iLen=iNewLen;

return s;
}


String String::operator+(const TCHAR* pStr)
{
SIZE_T iNewLen=(int)_tcslen(pStr)+this->iLen;
String s(iNewLen,false);
_tcscpy(s.lpBuffer,this->lpBuffer);
_tcscat(s.lpBuffer,pStr);
s.iLen=iNewLen;

return s;
}


String String::operator+(String& strRef)
{
SIZE_T iNewLen=strRef.iLen+this->iLen;
String s(iNewLen,false);
_tcscpy(s.lpBuffer,this->lpBuffer);
_tcscat(s.lpBuffer,strRef.lpBuffer);
s.iLen=iNewLen;

return s;
}


String& String::operator+=(const TCHAR ch)
{
SIZE_T iTot=this->iLen+1;
if(iTot>this->iCapacity)
{
    SIZE_T iNewSize=(iTot*EXPANSION_FACTOR/16+1)*16;
    TCHAR* pNew=new TCHAR[iNewSize];
    _tcscpy(pNew,this->lpBuffer);
    delete [] this->lpBuffer;
    this->lpBuffer=pNew;
    this->lpBuffer[iTot-1]=ch;
    this->lpBuffer[iTot]=_T('\0');
    this->iCapacity=iNewSize-1;
    this->iLen=iTot;
}
else
{
    this->lpBuffer[iTot-1]=ch;
    this->lpBuffer[iTot]=_T('\0');
    this->iLen=iTot;
}
return *this;
}


String& String::operator+=(const TCHAR* pStr)
{
size_t iStrlen=_tcslen(pStr);
SIZE_T iTot=iStrlen+this->iLen;
if(iTot>this->iCapacity)
{
    SIZE_T iNewSize=(iTot*EXPANSION_FACTOR/16+1)*16;
    TCHAR* pNew=new TCHAR[iNewSize];
    _tcscpy(pNew,this->lpBuffer);
    delete [] this->lpBuffer;
    this->lpBuffer=pNew;
    _tcscat(pNew,pStr);
    this->iCapacity=iNewSize-1;
    this->iLen=iTot;
}
else
{
    _tcscat(this->lpBuffer,pStr);
    this->iLen=iTot;
}
return *this;
}


String& String::operator+=(const String& strRef)
{
SIZE_T iTot=strRef.iLen+this->iLen;
if(iTot>this->iCapacity)
{
    SIZE_T iNewSize=(iTot*EXPANSION_FACTOR/16+1)*16;
    TCHAR* pNew=new TCHAR[iNewSize];
    _tcscpy(pNew,this->lpBuffer);
    delete [] this->lpBuffer;
    this->lpBuffer=pNew;
    _tcscat(pNew,strRef.lpBuffer);
    this->iCapacity=iNewSize-1;
    this->iLen=iTot;
}
else
{
    _tcscat(this->lpBuffer,strRef.lpBuffer);
    this->iLen=iTot;
}
return *this;
}


bool String::operator==(String& strRef)
{
if(_tcscmp(this->lpStr(),strRef.lpStr())==0)
    return true;
else
    return false;
}
#endif


bool String::operator==(const TCHAR* pStr)
{
if(_tcscmp(this->lpStr(),pStr)==0)
    return true;
else
    return false;
}


#ifndef JUST_NEED_PARSE
String String::Allocate(int iCount)
{
if((unsigned)iCount>this->iCapacity)
{
    delete [] lpBuffer;
    size_t iNewSize=(iCount*EXPANSION_FACTOR/16+1)*16;
    this->lpBuffer=new TCHAR[iNewSize];
    this->iCapacity=iNewSize-1;
}
this->lpBuffer[0]=_T('\0');
this->iLen=0;

return *this;
}


String& String::Make(const TCHAR ch, int iCount)    //Creates (Makes) a String with iCount TCHARs
{
if((unsigned)iCount>this->iCapacity)
{
    delete [] lpBuffer;
    size_t iNewSize=(iCount*EXPANSION_FACTOR/16+1)*16;
    this->lpBuffer=new TCHAR[iNewSize];
    this->iCapacity=iNewSize-1;
}
for(int i=0; i<iCount; i++)
     this->lpBuffer[i]=ch;
this->lpBuffer[iCount]=0;
this->iLen=iCount;
return *this;
}


String String::Left(int iNum)   //  strncpy = _tcsncpy
{
if((unsigned)iNum<this->iLen)
{
    int iNewSize=(iNum*EXPANSION_FACTOR/16+1)*16;
    String sr(iNewSize,false);
    _tcsncpy(sr.lpBuffer,this->lpBuffer,iNum);
    sr.lpBuffer[iNum]=0;
    sr.iLen=iNum;
    return sr;
}
else
{
    String sr=*this;
    return sr;
}
}


String String::Right(int iNum)  //Returns Right$(strMain,iNum)
{
if((unsigned)iNum<this->iLen)
{
    int iNewSize=(iNum*EXPANSION_FACTOR/16+1)*16;
    String sr(iNewSize,false);
    _tcsncpy(sr.lpBuffer,this->lpBuffer+this->iLen-iNum,iNum);
    sr.lpBuffer[iNum]=_T('\0');
    sr.iLen=iNum;
    return sr;
}
else
{
    String sr=*this;
    sr.iLen=this->iLen;
    return sr;
}
}


String String::Mid(SIZE_T iStart, SIZE_T iCount)
{
if(iStart<1)
{
    String sr;
    return sr;
}
if(iCount+iStart>this->iLen)
    iCount=this->iLen-iStart+1;
String sr(iCount,false);
_tcsncpy(sr.lpBuffer,this->lpBuffer+iStart-1,iCount);
sr.lpBuffer[iCount]=_T('\0');
sr.iLen=iCount;

return sr;
}


String String::Replace(TCHAR* pMatch, TCHAR* pNew)  //strncmp = _tcsncmp
{
SIZE_T i,iLenMatch,iLenNew,iCountMatches,iExtra,iExtraLengthNeeded,iAllocation,iCtr;
iLenMatch=(int)_tcslen(pMatch);
iCountMatches=0, iAllocation=0, iCtr=0;
iLenNew=(int)_tcslen(pNew);
if(iLenNew==0)
{
    String sr=this->Remove(pMatch,true); //return
    return sr;
}
else
{
    iExtra=iLenNew-iLenMatch;
    for(i=0; i<this->iLen; i++)
    {
        if(_tcsncmp(lpBuffer+i,pMatch,iLenMatch)==0)
           iCountMatches++;  //Count how many match strings
    }
    iExtraLengthNeeded=iCountMatches*iExtra;
    iAllocation=this->iLen+iExtraLengthNeeded;
    String sr(iAllocation,false);
    for(i=0; i<this->iLen; i++)
    {
        if(_tcsncmp(this->lpBuffer+i,pMatch,iLenMatch)==0)
        {
           _tcscpy(sr.lpBuffer+iCtr,pNew);
           iCtr+=iLenNew;
           i+=iLenMatch-1;
        }
        else
        {
           sr.lpBuffer[iCtr]=this->lpBuffer[i];
           iCtr++;
        }
        sr.lpBuffer[iCtr]=_T('\0');
    }
    sr.iLen=iCtr;
    return sr;
}
}


String String::Remove(TCHAR* pStr)
{
SIZE_T i,j,iStrLen,iParamLen;
TCHAR *pThis, *pThat, *p;
bool blnFoundBadTCHAR;

iStrLen=this->iLen;               //The length of this
String sr((int)iStrLen,false);    //Create new String big enough to contain original String (this)
iParamLen=(int)_tcslen(pStr);     //Get length of parameter (pStr) which contains TCHARs to be removed
pThis=this->lpBuffer;
p=sr.lpStr();
for(i=0; i<iStrLen; i++)
{
     pThat=pStr;
     blnFoundBadTCHAR=false;
     for(j=0; j<iParamLen; j++)
     {
         if(*pThis==*pThat)
         {
            blnFoundBadTCHAR=true;
            break;
         }
         pThat++;
     }
     if(!blnFoundBadTCHAR)
     {
        *p=*pThis;
         p++;        *p=_T('\0');
     }
     pThis++;
}
sr.iLen=(int)_tcslen(sr.lpStr());

return sr;
}


String String::Remove(const TCHAR* pMatch, bool blnCaseSensitive)
{
size_t i,iCountMatches=0,iCtr=0;

int iLenMatch=(int)_tcslen(pMatch);
for(i=0; i<this->iLen; i++)
{
     if(blnCaseSensitive)
     {
        if(_tcsncmp(lpBuffer+i,pMatch,iLenMatch)==0)  //_tcsncmp
           iCountMatches++;
     }
     else
     {
        if(_tcsnicmp(lpBuffer+i,pMatch,iLenMatch)==0) //__tcsnicmp
           iCountMatches++;
     }
}
SIZE_T iAllocation=this->iLen-(iCountMatches*iLenMatch);
String sr(iAllocation,false);
for(i=0; i<this->iLen; i++)
{
     if(blnCaseSensitive)
     {
        if(_tcsncmp(this->lpBuffer+i,pMatch,iLenMatch)==0)
           i+=iLenMatch-1;
        else
        {
           sr.lpBuffer[iCtr]=this->lpBuffer[i];
           iCtr++;
        }
        sr.lpBuffer[iCtr]=_T('\0');
     }
     else
     {
        if(_tcsnicmp(this->lpBuffer+i,pMatch,iLenMatch)==0)
           i+=iLenMatch-1;
        else
        {
           sr.lpBuffer[iCtr]=this->lpBuffer[i];
           iCtr++;
        }
        sr.lpBuffer[iCtr]=_T('\0');
     }
}
sr.iLen=iCtr;
return sr;
}
#endif


int String::ParseCount(const TCHAR c)  //returns one more than # of
{                                      //delimiters so it accurately
int iCtr=0;                           //reflects # of strings delimited
TCHAR* p;                             //by delimiter.

p=this->lpBuffer;
while(*p)
{
  if(*p==c)
     iCtr++;
  p++;
}

return ++iCtr;
}


void String::Parse(String* pStr, TCHAR delimiter)
{
unsigned int i=0;
TCHAR* pBuffer=0;
TCHAR* c;
TCHAR* p;

pBuffer=new TCHAR[this->iLen+1];
if(pBuffer)
{
    pBuffer[0]=0, p=pBuffer;
    c=this->lpBuffer;
    while(*c)
    {
       if(*c==delimiter)
       {
          pStr[i]=pBuffer,  p=pBuffer;
          i++,              pBuffer[0]=0;
       }
       else
       {
          *p=*c,  p++;
          *p=0;
       }
       c++;
    }
    pStr[i]=pBuffer;
    delete [] pBuffer;
}
}


#ifndef JUST_NEED_PARSE
SIZE_T iMatch(TCHAR* pThis, const TCHAR* pStr, bool blnCaseSensitive, bool blnStartBeginning, SIZE_T i, SIZE_T iParamLen)
{
if(blnCaseSensitive)
{
    if(_tcsncmp(pThis+i,pStr,iParamLen)==0)   //_tcsncmp
       return i+1;
    else
       return 0;
}
else
{
    if(_tcsnicmp(pThis+i,pStr,iParamLen)==0)  //__tcsnicmp
       return i+1;
    else
       return 0;
}
}


SIZE_T String::InStr(const TCHAR* pStr, bool blnCaseSensitive, bool blnStartBeginning)
{
SIZE_T i,iParamLen,iRange,iReturn;

if(*pStr==0)
    return 0;
iParamLen=_tcslen(pStr);
iRange=this->iLen-iParamLen;
if(blnStartBeginning)
{
    if(iRange>=0)
    {
       for(i=0; i<=iRange; i++)
       {
           iReturn=iMatch(this->lpBuffer,pStr,blnCaseSensitive,blnStartBeginning,i,iParamLen);
           if(iReturn)
              return iReturn;
       }
    }
}
else
{
    if(iRange>=0)
    {
       for(i=iRange; i>=0; i--)
       {
           iReturn=iMatch(this->lpBuffer,pStr,blnCaseSensitive,blnStartBeginning,i,iParamLen);
           if(iReturn)
              return iReturn;
       }
    }
}

return 0;
}


SIZE_T String::InStr(const String& s, bool blnCaseSensitive, bool blnStartBeginning)
{
SIZE_T i,iParamLen,iRange,iReturn;

if(s.iLen==0)
    return 0;
iParamLen=s.iLen;
iRange=this->iLen-iParamLen;
if(blnStartBeginning)
{
    if(iRange>=0)
    {
       for(i=0; i<=iRange; i++)
       {
           iReturn=iMatch(this->lpBuffer,s.lpBuffer,blnCaseSensitive,blnStartBeginning,i,iParamLen);
           if(iReturn)
              return iReturn;
       }
    }
}
else
{
    if(iRange>=0)
    {
       for(i=iRange; i>=0; i--)
       {
           iReturn=iMatch(this->lpBuffer,s.lpBuffer,blnCaseSensitive,blnStartBeginning,i,iParamLen);
           if(iReturn)
              return iReturn;
       }
    }
}

return 0;
}
#endif


void String::LTrim()
{
size_t iCt=0;

for(size_t i=0; i<this->iLen; i++)
{
     if(this->lpBuffer[i]==32 || this->lpBuffer[i]==9)
        iCt++;
     else
        break;
}
if(iCt)
{
    for(size_t i=iCt; i<=this->iLen; i++)
        this->lpBuffer[i-iCt]=this->lpBuffer[i];
}
this->iLen=this->iLen-iCt;
}


void String::RTrim()
{
SIZE_T iCt=0;

for(SIZE_T i=this->iLen-1; i>0; i--)
{
     if(this->lpBuffer[i]==9||this->lpBuffer[i]==10||this->lpBuffer[i]==13||this->lpBuffer[i]==32)
        iCt++;
     else
        break;
}
this->lpBuffer[this->iLen-iCt]=0;
this->iLen=this->iLen-iCt;
}


void String::Trim()
{
this->LTrim();
this->RTrim();
}


int String::iVal()
{
return _ttoi(this->lpBuffer);  //_ttoi
}


SIZE_T String::Len(void)
{
return this->iLen;
}


SIZE_T String::Capacity(void)
{
return this->iCapacity;
}


TCHAR* String::lpStr()
{
return lpBuffer;
}


#ifndef JUST_NEED_PARSE
void String::Print(bool blnCrLf)
{
_tprintf(_T("%s"),lpBuffer);
if(blnCrLf)
    _tprintf(_T("\n"));
}


void String::Print(TCHAR* pStr, bool blnCrLf)
{
_tprintf(_T("%s%s"),pStr,lpBuffer);
if(blnCrLf)
    _tprintf(_T("\n"));
}
#endif


String::~String()   //String Destructor
{
delete [] lpBuffer;
lpBuffer=0;
}



Frederick J. Harris

Strings.h


//Strings.h
#if !defined(STRINGS_H)
#define STRINGS_H
#define EXPANSION_FACTOR      2
#define MINIMUM_ALLOCATION   16

class String
{
public:
#ifndef JUST_NEED_PARSE
friend String operator+(TCHAR*, String&);
#endif
String();                                //Uninitialized Constructor
String(const TCHAR);                     //Constructor Initializes With A TCHAR.
String(const TCHAR*);                    //Constructor Initializes String With TCHAR*
String(const String&);                   //Constructor Initializes String With Another String (Copy Constructor)
#ifndef JUST_NEED_PARSE
String(const SIZE_T, bool);              //Constructor Creates String With User Specified Capacity and optionally nulls out
String(const SIZE_T, const TCHAR);       //Constructor initializes String with int # of TCHARs
#endif
String(SSIZE_T);                         //Constructor initializes String with int converted to String
String(SIZE_T);                          //Constructor initializes String with unsigned int converted to String
#ifndef JUST_NEED_PARSE
String(double);                          //Constructor initializes String with double converted to String
#endif
String& operator=(const TCHAR);          //Assign A TCHAR To A String
String& operator=(const TCHAR*);         //Assign A Null Terminated TCHARacter Array To A String
String& operator=(const String&);        //Assigns Another String To this One
#ifndef JUST_NEED_PARSE
String& operator=(int iNum);             //Assigns an int to a String
String& operator=(unsigned int iNum);    //Assigns an unsigned int to a String
String& operator=(double dblNum);        //Assign a double to a String
String operator+(const TCHAR);           //For adding TCHAR to String
String operator+(const TCHAR*);          //Adds a TCHAR* to this
String operator+(String&);               //Adds another String to this
String& operator+=(const TCHAR ch);      //Add TCHAR to this
String& operator+=(const String&);       //Adds a String to this and assigns it to left of equal sign
String& operator+=(const TCHAR*);        //Adds a TCHAR*to this and assigns it to left of equal sign
bool operator==(String&);                //Compares Strings For Case Sensitive Equality
#endif
bool operator==(const TCHAR*);           //Compares String Against TCHAR* For Case Sensitive Equality
#ifndef JUST_NEED_PARSE
String Allocate(int);                    //Allocates a String with a specified buffer size
String& Make(const TCHAR, int);          //Returns reference to this with iCount ch TCHARs in it
String Left(int);                        //Returns String of iNum Left Most TTCHARs of this
String Right(int);                       //Returns String of iNum Right Most TTCHARs of this
String Mid(SIZE_T, SIZE_T);              //Returns String consisting of number of TTCHARs from some offset
String Replace(TCHAR*, TCHAR*);          //Returns String with 1st TCHAR* parameter replaced with 2nd TCHAR* parameter
String Remove(TCHAR*);                   //Returns A String With All The TCHARs In A TCHAR* Removed (Individual TCHAR removal)
String Remove(const TCHAR*, bool);       //Returns a String with 1st parameter removed.  2nd is bool for case sensitivity.
SIZE_T InStr(const TCHAR*, bool, bool);  //Returns one based case sensitive/insensitive offset of a particular TCHAR pStr in a String, starting from left or right (left=true)
SIZE_T InStr(const String&, bool, bool); //Returns one based case sensitive/insensitive offset of where a particular String is in another String, starting from left or right (left=true)
#endif
int ParseCount(const TCHAR);             //Returns count of Strings delimited by a TTCHAR passed as a parameter
void Parse(String*, TCHAR);              //Returns array of Strings in first parameter as delimited by 2nd TTCHAR delimiter
#ifndef JUST_NEED_PARSE
void SetTCHAR(unsigned, TCHAR);               //Sets TCHAR at zero based offset in this
#endif
void LTrim();                            //Returns String with leading spaces/tabs removed
void RTrim();                            //Returns String with spaces/tabs removed from end
void Trim();                             //Returns String with both leading and trailing whitespace removed
int iVal();                              //Returns integral value of String
SIZE_T Len();                            //Returns Length Of String Controlled By this
SIZE_T Capacity();                       //Returns Maximum Permissable TCHARacter Count (One Less Than Allocation).
TCHAR* lpStr();                          //Returns TCHAR* To String
#ifndef JUST_NEED_PARSE
void Print(bool);                        //Outputs String To Console With Or Without CrLf.
void Print(TCHAR*, bool);                //Outputs String To Console With Or Without CrLf.
#endif
~String();                               //String Destructor

private:
TCHAR* lpBuffer;
SIZE_T iLen;
SIZE_T iCapacity;
};
#ifndef JUST_NEED_PARSE
String operator+(TCHAR* lhs, String& rhs);
#endif
#endif  //#if !defined(STRINGS_H)

Frederick J. Harris

I'll see if I can attach the zip containing the code and release/debug dlls in 64 bit ...

Frederick J. Harris

Looks like it went through.  That zip contains the release x64 grid - FHGrid.dll.  As said - 43K.  That won't help folks wanting to try PowerBASIC clients, but I'll post 32 bit versions soon!

Frederick J. Harris

Will try attaching 32 bit debug/release versions ...

Frederick J. Harris

If you try this please rename anything found such as FHGrid_Release.dll to FHGrid.dll and likewise for FHGrid_Debug.dll.  I just used those names to keep the files seperate!

Patrice Terrier

Ed,

Congrat :)

Did you compare the size using procedural coding over OOP ?
Patrice Terrier
GDImage (advanced graphic addon)
http://www.zapsolution.com