• Welcome to Jose's Read Only Forum 2023.
 

ProgEx 42: Using ActiveX Controls In Win32 SDK Apps - MSFlexGrid.ocx Example

Started by Frederick J. Harris, November 28, 2011, 05:20:32 PM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

Frederick J. Harris

     This tutorial will show how to use licensed ActiveX Controls in a Win32 SDK style application.  I am especially indebted to Jose Roca for the incredible work he did with this topic about ten years ago which allowed us to use these controls in PowerBASIC.  I'll use the MSFlexGrid Ocx (MSFlxGrd.ocx) as an example.  To the best of my recollection that grid control was first distributed with either Visual Basic 5 or Visual Basic 6 circa 1997 – 1998 or so.  It may have been distributed with other Microsoft products but I'm not sure.  I believe the MSFlexGrid makes a good example because it was especially useful and widely distributed.  Also, being as it's a 'Licensed ActiveX Control' it presents some special difficulties that must be overcome for its use in Win32 projects.   The technique I'm showing here should be generally applicable to any ActiveX Control.  Unlicensed controls will be particularly easier.   

     But first I need to put the issue in perspective for the reader and state what the nature of *.ocx controls are and why it is difficult to use them in native C++ SDK style apps.  They were largely created by C++ COM (Component Object Model) developers for consumption by developers using RAD (Rapid Application Development) tools such as Visual Basic, Visual Basic For Applications, VBScript, Java Script, etc.  They contain functionality within themselves to be used in a drag and drop visual design environment.  One can, for example, drag a representation of the MSFlexGrid onto a 'Form' from the Visual Basic Toolbox, position it on the form, adjust its size, set 'properties', so on and so forth, and all without writing one line of code.  In many cases all the code writing one must do involves writing code in 'Event Procedures' which the design environment makes readily available.

     ActiveX Controls in many ways represent the most sophisticated of the Object Linking And Embedding technologies Microsoft developed in the early to mid 1990s.  The original intention of this technology was to allow Excel Spreadsheets to be embedded within Word documents – no small feat to be sure.  Using this rough analogy with regard to the MSFlexGrid OCX, the grid is the Excel Spreadsheet, i.e., the 'document', as it were, and the Visual Basic design environment is the 'document container'.  The document container manipulates the document, and a complex interplay of many COM interfaces is involved in the communication between the document, i.e., grid, and the container, i.e., Visual Basic.  This is what Andrew W. Troelsen says about ActiveX Controls in chapter 14 of his book "Developer's Workshop To COM And ATL 3.0"...

Quote
     An ActiveX Control is a high powered coclass, quite different from the previous COM objects built thus far, in that they typically offer a visual aspect.... As far as COM-based technologies go, the ActiveX Control is at the top of the mountain.  An ActiveX control is a graphical building block, used in the context of another application.

Here are just some of the interfaces that an ActiveX Control Container might want to implement...

IDispatch
IOleClientSite
IOleContainer
IOleControlSite
IOleInPlaceFrame
IOleInPlaceSite
IOleInPlaceSiteEx
IOleInplaceUIWindow
IOleWindow
IParseDisplayName
IServiceProvider
ISimpleFrameSite
IOleObject
IOleInPlaceObject
IOleInPlaceActiveObject     

And here are some an ActiveX Control might need to implement...

IPropertyNotifySink
ISpecifyPropertyPages
IPropertyPage2
IOleObject
IOleInPlaceObject
IOleInPlaceActiveObject
IOleControl
IOleWindow
IDataObject
IViewObject2
IConnectionPointContainer
IConnectionPoint
IProvideClassInfo2
IrunnableObject
IPersist
IQuickActivate

     So I guess you are thinking its too complex to undertake in raw C++?  No, its doable, but it isn't trivial.  For one thing there are a few Active Template Library external non-template related functions that take care of most of the grunt work of a minimalist ActiveX Control Container.  So what I'm saying here is that those few functions dispense for us of the whole visual designer thing that we as C++ Win32 Sdk style coders don't use.  So what I'm saying is that we can deal with all of the above.  However, there are still more and other problems confronting us.

     As we know, in C++ when we want to avail ourselves of some code functionality in a library somewhere, we need a header file for the functions or classes in that library.  Even if we don't have much in the way of documentation on the library, its oftentimes possible to figure one's way through it just with the function prototypes.  But there isn't any C++ header file for the MSFlxGrd.ocx.  COM doesn't work that way. 

     The designers of COM intended for it to be language agnostic.  At its core COM is a binary standard designed for interoperability of components.  And the components can be written in any language capable of creating the correct binary layout.  In terms of a language agnostic version of the C/C++ header file, Type Libraries were created.  In fact, before one even begins to create a COM component such as the MSFlexGrid control, one creates an 'interface definition' file, and this file looks very much like a C header file.  It is written in something known as MIDL, i.e., Microsoft Interface Definition Language.  One runs this file through the MIDL compiler and a type library is created.  It is a binary object; not like a text header file.  It will have a .tlb extension.  When the code to create/implement the ActiveX Control is compiled with a suitable compiler such as a C, C++ or PowerBASIC compiler, the type library, i.e., .tlb file, can be compiled into the dll/ocx as a resource.  One can view the IDL (Interface Definition Language) of a component such as the MSFlexGrid Control using the OLEView.exe tool provided by Microsoft.  You'll find it on any computer with any Microsoft development tools.  With it you can navigate to the ocx/dll of interest such as C:\Windows\system32\MSFlxGrd.ocx, and view the IDL.  Its just a short jump from IDL to a C++ header, and that's how I obtained a MSFlexGrid.h header for use in the code I'm providing here.  I translated the IDL line by line to a C++ header.  It is rather tedious work, and took me several hours.  But it is doable. 

     Other languages such as PowerBASIC provide automated tools that provide a suitable language specific header with  just a few mouse clicks.  I find it hard to believe nothing like this has been provided in the  C/C++ world, but so far I haven't found it.  I believe I'll create one myself soon to help automate this process, but for this example the one I came up with was created just as I described above, i.e., by hand from OLEView Type Library information. 

     In terms of Visual Basic 4 - 6, that language was able to utilize these ActiveX Controls through use of the control's Type Library, and in fact it maintained its 'Object Browser' accessed I believe from the F1 or F2 keys to bring this tool up.  And of course, that's how 'Intellisense' comes up with object methods as you type code into your editor.  So as you may be beginning to see, COM objects and ActiveX controls are a different kind of beast from what we usually work with in C++.

     So with that understanding we can begin to write the code.  If you want to be able to use this code to use a MSFlexGrid Ocx control in one of your applications you'll need to have your own licensed MSFlexGrid control presumably purchased by you in your purchase of any Microsoft development product that included the control, such as Visual Basic 6 or Visual Studio 6.  With your purchase of the control you will have a unique license for your control in the form of a 16 byte GUID, i.e., a globally unique identifier.  Perhaps I'd best discuss that topic a bit now, because it is the issue that caused me the greatest difficulty in developing this code I'm going to provide. 

     The license key becomes one of the parameters of one of the Atl functions we must call to create an instance of this licensed control, specifically, AtlAxCreateControlLic()...


AtlAxCreateControlLic  Creates a licensed ActiveX control, initializes it, and hosts it in the specified window.

ATLAPI AtlAxCreateControlLic
(
LPCOLESTR lpszName,
HWND hWnd,
IStream* pStream,
IUnknown** ppUnkContainer,
BSTR bstrLic = NULL
);

Parameters

lpszName        A pointer to a string to be passed to the control. Must be formatted in one of the following ways:

                * A ProgID such as "MSCAL.Calendar.7"
                * A CLSID such as "{8E27C92B-1264-101C-8A2F-040224009C02}"
                * A URL such as "http://www.microsoft.com"
                * A reference to an Active document such as "file://\\Documents\MyDoc.doc"

hWnd            Handle to the window that the control will be attached to.

pStream         A pointer to a stream that is used to initialize the properties of the control. Can be NULL.

ppUnkContainer  The address of a pointer that will receive the IUnknown of the container. Can be NULL.

bstrLic         The BSTR containing the license for the control.

Return Value    One of the standard HRESULT values.


     In developing and writing this code I wanted it to work in Visual C++ 6 and up in terms of Microsoft products, and also with MinGW.  Unfortunately, the above necessary Atl function is not included with any of the MinGW dlls, libs, or includes, and in fact isn't even in the stock includes that shipped with Visual C++ 6.0 in 1998.  The function can be found in various dlls such as Atl.dll, Atl71.dll, Atl80.dll, and Atl90.dll.  I have only ever seen one lib and that would be atl.lib.  Some versions of this lib export the AtlAxCreateControlLic symbol and some don't.  What I have found is that with any Microsoft compiler greater than Visual C++ 6 this function will be present and exported from Atl.lib and Atl.dll.  With Visual C++ 6 you'll get a compiler error if you try to use this function, and there isn't any easy way to create a licensed control without it.  Because of all this what I decided to do and demo for you was use explicit linking against Atl71.dll, Atl80.dll or Atl90.dll.  This will work for any version of Microsoft's compilers or for MinGW such as Code::Blocks.  So to use this code you need two things then, 1) A legal copy of MSFlexGrid.ocx with license GUID and 2) A Copy of Atl71.dll, Atl80.dll, or Atl90.dll.  You are probably wondering at this point where you are going to find the License Key GUID of your MSFlexGrid ActiveX Control.  Well, you won't find it.  You need code to access it, which I'll now provide.  But first a little background.

     When you purchased and installed any Microsoft product that gave you the rights to develop with the MSFlexGrid and other ActiveX controls, you possessed what Microsoft terms a 'Machine License' for those controls.  What this allows is that the machine with the 'Machine License' is able to create instances of the control at will without needing to provide license information through the IClassFactory2 interface, which interface is somewhat akin to what the 'new' operator does in standard C++, i.e., it creates objects.  However, such an executable when transferred to another machine without a 'Machine License' won't be able to create the control, even if the MSFlexGrid Ocx has been satisfactorily registered on it with RegSvr32.exe.  This is a critical issue because most of us developers wish to provide our creations to others to use either freely or for payment, so a way must be found to instantiate the control on machines without a 'Machine License', i.e., machines that aren't development machines with Visual Studio installed. 

     The way Microsoft has it worked out is that there are methods of the IClassFactory2 interface which when called allow a developer to obtain the machine license key for the control, and this license key can be embedded within the code of applications one distributes.  Of course, one doesn't distribute one's source code with the license in it, but rather the executable which when executed will have the license key within it that will allow it to run and create the control.  Here is a C++ program I wrote with Code::Blocks that you can run to retrieve your license key from within the MSFlxGrd.ocx ActiveX Control.  You'll need to run this code to get your license before we can go any further (Of course, you won't need to run this code if you have Jose Roca's TypeLib Browser, as that tool furnishes the license key for you).  As I explained above, you need to run this code on a machine with a Machine License for the MSFlexGrid control, and the MSFlexGrid control must be properly registered on your system.  It should be if you have Visual Studio 6 or any of its separate products installed.  You might want to check your various system directories for MSFlxGrd.ocx first, because, the path to the file in the code below needs to be correct...


#define  UNICODE
#define  _UNICODE
#include <windows.h>
#include <ocidl.h>
#include <stdio.h>

const CLSID CLSID_MSFlexGrid   = {0x6262D3A0,0x531B,0x11CF,{0x91,0xF6,0xC2,0x86,0x3C,0x38,0x5E,0x30}};
const IID   IID_IClassFactory2 = {0xB196B28F,0xBAB4,0x101A,{0xB6,0x9C,0x00,0xAA,0x00,0x34,0x1D,0x07}};
HRESULT (__stdcall* ptrDllGetClassObject) (const CLSID&, const IID&, void**); // function pointer declaration

int main()
{
IClassFactory2* pClassFactory=NULL;
BSTR strLicenseKey=NULL;
HMODULE hDll=NULL;
LICINFO LicInfo;
HRESULT hr;

hDll=LoadLibrary(L"C:\\Windows\\system32\\MSFlxGrd.ocx");   // <<< You may need to adjust this!!!!!!!
if(hDll)
{
    ptrDllGetClassObject=(HRESULT (__stdcall*)(REFCLSID, REFIID, void**))GetProcAddress(hDll, "DllGetClassObject");
    hr=ptrDllGetClassObject(CLSID_MSFlexGrid, IID_IClassFactory2, (void**)&pClassFactory);
    if(SUCCEEDED(hr))
    {
       hr=pClassFactory->GetLicInfo(&LicInfo);                                // This code tests to see if the
       wprintf(L"LicInfo.fRuntimeKeyAvail = %d\n",LicInfo.fRuntimeKeyAvail);  // object is amenable to giving
       wprintf(L"LicInfo.fLicVerified     = %d\n",LicInfo.fLicVerified);      // us a license key.
       if(LicInfo.fRuntimeKeyAvail && LicInfo.fLicVerified)                   
       {                                                                     
          hr=pClassFactory->RequestLicKey(NULL,&strLicenseKey);
          if(SUCCEEDED(hr))
          {
             wprintf(L"strLicenseKey            = %s\n",strLicenseKey);
             SysFreeString(strLicenseKey);
          }
          else
             wprintf(L"pClassFactory->RequestLicKey(NULL,&strLicenseKey) Failed!\n");
       }
       pClassFactory->Release();
    }
    else
       wprintf(L"ptrDllGetClassObject() Failed!\n");
    FreeLibrary(hDll);
}
else
    wprintf(L"LoadLibrary() Failed On MSFlxGrd.ocx\n");
getchar();

return 0;
}


     The output from that on one of my XP machines was as follows.  Note you'll need YOUR license key – not mine!!! 

/*
LicInfo.fRuntimeKeyAvail = 1
LicInfo.fLicVerified     = 1
strLicenseKey            = 72E67120-5959-11cf-91F6-C2863C385E30
*/   

     Further note that the above code won't succeed on any machine but a machine with a 'Machine License'.  If your machine has a registered MSFlexGrid Control on it put there by some application that installed it, but the machine doesn't have the developer license from one of Microsoft's products such as Visual Studio, the code won't succeed.

     So you are going to have to first run that code to get your license key and use that specific key in my code below to get my demo program to run.  You'll see where in my fnWndProc_OnCreate() message handler in Main.cpp where I call AtlAxCreateControlLic() through function pointer PFNATLAXCREATECONTROLLIC.  The applicable code looks like this....


...
...
pAtlCreateControlLicensed=(PFNATLAXCREATECONTROLLIC)GetProcAddress(hIns,"AtlAxCreateControlLic");
if(pAtlCreateControlLicensed)
{
   strProgId=SysAllocString(_T("MSFlexGridLib.MSFlexGrid"));              // strProgId  is a BSTR
   strLicense = SysAllocString(L"72E67120-5959-11cf-91F6-C2863C385E30");  // strLicense is a BSTR
   hr=pAtlCreateControlLicensed(strProgId,hContainer,NULL,&ppUnkContainer,strLicense);
   if(SUCCEEDED(hr))
{
      ....
      ....


     To repeat, you need to take your license code from your run of the above program and substitute it in Main.cpp into the strLicense variable as shown above.  Be careful to get it right!  'Close enough' is only good in horseshoes and hand-grenades!

     Having gotten through all that and armed with your MSFlexGrid license from the above code we are ready to proceed.  The structure of the project is as follows...


Main.cpp        // Contains WinMain() start up code, Window Procedure, And Main Window Message Handlers
GridEvents.cpp  // Implementation of IDispatch based CEventSink and DMSFlexGridEvents (outgoing interface)

Main.h          // Some function pointer declarations I need and stuff to make my message crackers work
GridEvents.h    // Declaration of my CEventSink Class which implements abstract base class copied from Oleview output
MSFlexGrid.h    // C++ translation of idl code from Oleview.  This is an interface/vtable maintained by MSFlexGrid server


Here is the code...


Frederick J. Harris


// Main.cpp
#define  UNICODE                                              //Main.cpp - MSFlexGrid (MSFlxGrd.ocx) Demo.  Link ole32.lib, oleauto32.lib, uuid.lib
#define  _UNICODE
#include <windows.h>                                          // This code demonstrates how to use the MSFlexGrid ActiveX Control in a Win32 SDK
#include <ocidl.h>                                            // style application without any (or at best little) help from MFC or ATL. We will
#include <tchar.h>                                            // use a few ATL functions to provide an ActiveX Control Container for us, but
#include <cstdio>                                             // other than that, it will be plain Win32 with no fancy templates or macros.
#include "GridEvents.h"
#include "Main.h"
#include "MSFlexGrid.h"
const IID LIBID_MSFlexGrid                                    = {0x5E9E78A0,0x531B,0x11CF,{0x91,0xF6,0xC2,0x86,0x3C,0x38,0x5E,0x30}};
const IID IID_MSFlexGrid                                      = {0x5F4DF280,0x531B,0x11CF,{0x91,0xF6,0xC2,0x86,0x3C,0x38,0x5E,0x30}};
const IID IID_MSFlexGridEvents                                = {0x609602E0,0x531B,0x11CF,{0x91,0xF6,0xC2,0x86,0x3C,0x38,0x5E,0x30}};
const IID IID_IConnectionPointContainer                       = {0xB196B284,0xBAB4,0x101A,{0xB6,0x9C,0x00,0xAA,0x00,0x34,0x1D,0x07}};
HINSTANCE hAtlIns=NULL;                                       //I hate globals; but we'll use one here!  See below.


struct StartOLEProcess                                        // The reason this strange thing - 'StartOLEProcess' is here is that in testing this
{                                                             // thing with Windows 2000/XP I was getting crashes at program termination, i.e., 'x'ing
StartOLEProcess()                                            // out to end program, caused presumably by either CoUninitialize() or FreeLibrary() not
{                                                            // synchronizing well with this process's termination code.  So note here that an
  TCHAR szBuffer[256];                                        // instance of StartOLEProcess named 'InitializeOLE' will be created before WinMain()
  CoInitialize(NULL);                                         // even starts, and its destructor will be called after WinMain() exits.  I put the
  GetCurrentDirectory(256,szBuffer);                          // CoInitialize(), LoadLibrary(), CoUninitialize(), and FreeLibrary() calls in this
  _tcscat(szBuffer,_T("\\Atl90.dll"));                        // object's Constructor and Destructor calls, and that seemed to solve the problem.  One
  hAtlIns=LoadLibrary(szBuffer);                              // does what one must, I suppose!
}

~StartOLEProcess()
{
  CoUninitialize();
  FreeLibrary(hAtlIns);
}
}InitializeOLE;


long fnWndProc_OnCreate(lpWndEventArgs Wea)                   //             WNDCLASSEX::cbWndExtra Bytes
{                                                             //
IConnectionPointContainer* pConnectionPointContainer=NULL;   // Offset      What In The World Is Stored There???
PFNATLAXCREATECONTROLLIC pAtlCreateControlLicensed=NULL;     // ================================================================================
PFNATLAXGETCONTROL pAtlAxGetControl=NULL;                    // 0  -  3     ppUnkContainer    - pointer to IUnknown of ActiveX Control Container
IConnectionPoint* pConnectionPoint=NULL;                     // 4  -  7     pUnkGrid          - pointer to IUnknown of ActiveX Control
PFNATLAXWININIT pAtlAxWinInit=NULL;                          // 8  -  11    pIGrid            - Pointer to MSFlexGrid Interface
BSTR strProgId,strLicense,strText;                           // 12 -  15    pConnectionPoint  - Pointer to MSFlexGrid Events Connection Point
IUnknown* ppUnkContainer=NULL;                               // 16 -  19    dwCookie          - Handle to established Connection Point
CEventSink* pEventSink=NULL;
IMSFlexGrid* pIGrid=NULL;
IUnknown* pUnkGrid=NULL;                                     // You can think of fnWndProc_OnCreate() as the Constructor Call for the whole appli-
DWORD dwCookie=0;                                            // cation.  If any COM services are used, CoInitialize() is needed.  We immediately get
HWND hContainer;                                             // our HINSTANCE from the lParam of the WM_CREATE message.  Then we create a 'Container'
HRESULT hr;                                                  // window.  This helps with positioning the ActiveX Control where you want it as is
                                                              // necessary with most child window controls.  COM Objects posess an entity known as
Wea->hIns=((LPCREATESTRUCT)Wea->lParam)->hInstance;          // their ProgId, i.e., 'Program ID'.  The MSFlexGrid ActiveX Control's ProgId is
hContainer=CreateWindowEx                                    // MSFlexGridLib.MSFlexGrid.  These names can be found in the Windows Registry under
(                                                            // HKEY_Classes_Root.  The native string type for COM is the BSTR type.  This type of
  WS_EX_OVERLAPPEDWINDOW,                                     // string is quite unlike null terminated C strings.  It is byte length prefixed, twice
  _T("static"),                                               // null terminated, and can contain nulls.  It is allocated with the OLE String Engine
  _T(""),                                                     // System String Functions.  This whole topic (if you are unfamiliar with it) could take
  WS_CHILD|WS_VISIBLE,                                        // you quite some time to learn.  Since this application is a C++ SDK style Win32 project
  10,                                                         // and is not associated in any way with MFC Visual Designers, Visual Basic Visual
  10,                                                         // Designers, etc., a great deal of the internal functionality of the MSFlexGrid ActiveX
  282,                                                        // Control is not going to be used.  For you see, ActiveX Controls, particularly of the
  250,                                                        // *.ocx variety, were designed to run 'In-Place Active' within a Visual Designer, where
  Wea->hWnd,                                                  // the programmer using the control in a project would see a visual representation of
  (HMENU)ID_CONTAINER,                                        // the control at 'design time' as it is being 'embedded' within an OLE Controller
  Wea->hIns,                                                  // application such as Visual Basic.  In that mode there would be 'resizing handles'
  0                                                           // visible which the visual developer could use to adjust the runtime size, as well as
);                                                           // other design time characteristics.  Since advanced C++ developers such as ourselves
if(hAtlIns)                                                  // (that is, Real Men) can dispense with such nonesense, in this app we will just use a
{                                                            // few simple Active Template Library functions (AtlAxWinInit(), AtlAxCreateControl(),
    pAtlAxWinInit=(PFNATLAXWININIT)GetProcAddress             // AtlAxGetControl()) to create a minimal ActiveX Control Container for us.  The
    (                                                         // functionality for this is contained in various versions of Atl.dll such as Atl.dll,
     hAtlIns,                                                 // Atl71.dll, Atl80.dll, Atl90.dll, etc.  Note that what you are seeing just left are
     "AtlAxWinInit"                                           // function pointer calls to access those ATL functions I just mentioned because of a
    );                                                        // real ugly problem with creating licensed versions of ActiveX Controls.  For you see,
    if(pAtlAxWinInit)                                         // to create a licensed version of a control one needs to call AtlAxCreateControlLic(),
    {                                                         // and this function was added to the various versions of the Atl.dll and Atl.lib after
       hr=pAtlAxWinInit();                                    // the original versions came out with Visual C++ 6.0.  And I wanted this code to work
       if(SUCCEEDED(hr))                                      // with VC6.  And in any case, I couldn't link with Atl.lib even if that function was
       {                                                                          // listed there, because I also wanted this code to work with MinGW,
          pAtlCreateControlLicensed=(PFNATLAXCREATECONTROLLIC)GetProcAddress      // and Atl.dll and Atl.lib are not part of the MinGW compiler
          (                                                                       // collection set of includes and libs - Hense the need for explicit
           hAtlIns,"AtlAxCreateControlLic"                                        // linking to some version of Atl.dll, which you will have to
          );                                                                      // obtain for yourself in some way if you want to use this code.  I'd
          if(pAtlCreateControlLicensed)                                           // recommend Atl71.dll, Atl80.dll, or Atl90.dll.  At the top of Main.h
          {                                                                       // you'll see the function declaration typedefs for function pointer
             strProgId=SysAllocString(_T("MSFlexGridLib.MSFlexGrid"));            // calls on the Atl functions needed.  Just left the
             strLicense=SysAllocString(L"72E67120-5959-11cf-91F6-C2863C385E30");  // AtlAxCreateControlLic() call returns a pointer to a pointer to the
             hr=pAtlCreateControlLicensed                                         // IUnknown interface of the ActiveX Control Container within Atl.dll.
             (                                                                    // The next function - AtlAxGetControl(), returns a pointer to the
              strProgId,hContainer,NULL,&ppUnkContainer,strLicense                // IUnknown interface of the ActiveX Control itself hosted by the
             );                                                                   // 'Container' and parented by our CreateWindowEx() static control
             if(SUCCEEDED(hr))                                                    // just created above.  Having successfully accomplished all this, our
             {                                                        // next step is to do a QueryInterface on the IUnknown of the grid for the
                SetWindowLong(Wea->hWnd,0,(long)ppUnkContainer);      // IMSFlexGrid interface on the grid.  The IMSFlexGrid interface can be found in
                pAtlAxGetControl=(PFNATLAXGETCONTROL)GetProcAddress   // MSFlexGrid.h.  This file I obtained by using OLEView.exe and opening up the
                (                                                     // MSFlexGrid Type Library.  Then I by hand converted the MIDL (Microsoft Interface
                 hAtlIns,"AtlAxGetControl"                            // Definition Language) to a proper C++ header file.  It took quite a few hours!
                );                                                    // What I need to do is make a 'Type Library Browser' tool like PowerBASIC supplies
                if(pAtlAxGetControl)                                  // with its compilers, or the Jose Roca PowerBAIC type lib browser, so one can auto-
                {                                                     // generate these headers for ActiveX/COM objects.  I'll get to that shortly.  But
                   hr=pAtlAxGetControl(hContainer,&pUnkGrid);         // back to the code!  Once we get an IMSFlexGrid pointer we can call methods on the
                   if(SUCCEEDED(hr))                                  // object to set properties such as number of rows, number of columns, etc.  However,
                   {                                                  // the critical step that must happen next is that our client app (this one) must
                      SetWindowLong(Wea->hWnd,4,(long)pUnkGrid);      // provide an 'event sink' to 'sink' events fired from the MSFlexGrid control.
                      hr=pUnkGrid->QueryInterface                     // ActiveX Controls in particular and COM objects in general don't communicate with
                      (                                               // their controller through a Window Procedure as with custom controls.  Rather,
                       IID_MSFlexGrid,(void**)&pIGrid                 // they call into an event sink after being notified by the controller of the
                      );                                              // address of a suitable COM interface to which they'll fire events.  The specifics
                      if(SUCCEEDED(hr))                               // of this are as follows:  One creates an abstract base class and inherits from
                         SetWindowLong(Wea->hWnd,8,(long)pIGrid);     // this in a class of one's choosing within the client controller class.  The
                      else                                            // method signatures in this abstract base class are obtained from the type library.
                         return 0;                                    // What came out of the type lib for this app was 'DMSFlexGridEvents'.  See
                      pIGrid->SetCols(4);                             // GridEvents.h.  In this app I made a seperate header for this 'outgoing' interface.
                      pIGrid->SetRows(20);                            // I'd recommend you open up OLEView.exe and go to File >> Open Typelib and view
                      pIGrid->SetColWidth(0,200);                     // MSFlxGrd.ocx.  Near the bottom you'll see this...
                      pIGrid->SetColWidth(1,1200);
                      pIGrid->SetColWidth(2,1200);                    // coclass MSFlexGrid
                      pIGrid->SetColWidth(3,1200);                    // {
                      strText=SysAllocString(L"Column 1");            //  [default]         interface IMSFlexGrid;
                      pIGrid->SetCol(1), pIGrid->SetRow(0);           //  [default, source] dispinterface DMSFlexGridEvents;
                      pIGrid->SetText(strText);                       // }
                      SysReAllocString(&strText,L"Column 2");         //
                      pIGrid->SetCol(2), pIGrid->SetText(strText);    // What that means is that the COM Class MSFlexGrid contains two interfaces, 1) a
                      SysReAllocString(&strText,L"Column 3");         // default 'incomming' interface of the typical kind, and 2) a 'dispatch' interface
                      pIGrid->SetCol(3), pIGrid->SetText(strText);    // that is a 'source' of events fired out from the server, i.e., an 'outgoing' interface.
                      SysFreeString(strText);                         // To set up the connection and inform the server of the event sink one does a Query-
                      pIGrid->Refresh();                              // Interface call on the IMSFlexGrid interface for the IConnectionPointContainer interface.
                      hr=pUnkGrid->QueryInterface                     // Obtaining that one then calls IConnectionPointContainer::FindConnectionPoint() Once
                      (                                               // obtaining an IConnectionPoint* as the return from that call (the last parameter is
                       IID_IConnectionPointContainer,                 // where its returned - actual function returns are always HRESULTs), one can then call
                       (void**)&pConnectionPointContainer             // IConnectionPoint::Advise() and pass through to the server (the grid) the address of
                      );                                              // the client sink interface.  Its a bit more roundabout than just indicated, but that's
                      if(FAILED(hr))                                  // the gist of it.  Yes, its complicated.  Note in our code below left we used the C++
                         return 0;                                    // new operator to instantiate the client sink class - CEventSink.  In that class we...
                      hr=pConnectionPointContainer->FindConnectionPoint       // must implement the three members of IUnknown and the four members of IDispatch.
                      (                                                       // We optionally can implement within that class whatever of the DMSFlexGrid
                       IID_MSFlexGridEvents,&pConnectionPoint                 // event methods that we wish to take action on.  In this demo app I decided to
                      );                                                      // only implement code for the keypress event so that you could see that the
                      if(SUCCEEDED(hr))                                       // events can be made to work.  If the event interface wasn't a pure IDispatch
                      {                                                       // interface but was rather an IUnknown or dual interface it would have been
                         pConnectionPointContainer->Release();                // necessary to at least stub out all the other event methods.  But with a pure
                         pConnectionPointContainer=NULL;                      // dispinterface there is no vtable access beyond the three IUnknown and four
                         SetWindowLong(Wea->hWnd,12,(long)pConnectionPoint);  // IDispatch methods.  One simply receives dispid (dispatch id) values in
                      }                                                       // IDispatch::Invoke from the server, i.e., the grid, and one does with them
                      else                                                    // whatever one wants.  A few words about interfaces and VTables.  The abstract
                         return 0;                                            // base class eventually resolves to an array of function pointers that are
                      pEventSink=new CEventSink;                              // pointers to the objects methods.  In the specific case of our event sink,
                      pEventSink->StoreGridPointer(pIGrid);                   // after 'new' is called, pEventSink will point to the base allocation of
                      hr=pConnectionPoint->Advise                             // CEventSink class.  At that address is a pointer to the DMSFlexGridEvents
                      (                                                       // interface/vtable.  In the 11th zero based pointer offset in that VTable is
                       (IUnknown*)pEventSink, &dwCookie                       // the pointer to CEventSink::KeyPress().  Again, because the events are a part
                      );                                              // of a pure dispinterface, I would not have been obligated to create the CEventClass
                      if(SUCCEEDED(hr))                               // like that.  I could have just included the three IUnknown functions and the four
                         SetWindowLong(Wea->hWnd,16,(long)dwCookie);  // IDispatch functions in it.  Then the actual event procedures could have been simple
                    }                                                 // external functions not part of any class, but nonetheless called from IDispatch::Invoke().
                }                                                     // To understand all this you probably need to get Dale Rogerson's 'Inside COM' or Guy
             }                                                        // and Henry Eddon's 'Inside DCOM'.  Also, on my board at http://www.itberater.org can
             SysFreeString(strProgId);                                // be found a good bit about interfaces, Vtbls, pointers and such.
             SysFreeString(strLicense);
          }
       }
    }
}

return 0;
}


long fnWndProc_OnDestroy(lpWndEventArgs Wea)
{
IConnectionPoint* pConnectionPoint=NULL;
IUnknown* ppUnkContainer=NULL;
IMSFlexGrid* pIGrid=NULL;
IUnknown* pUnkGrid=NULL;
DWORD dwCookie=0;

pConnectionPoint=(IConnectionPoint*)GetWindowLong(Wea->hWnd,12);
dwCookie=(DWORD)GetWindowLong(Wea->hWnd,16);
if(pConnectionPoint)
{
    pConnectionPoint->Unadvise(dwCookie);
    pConnectionPoint->Release();
}
pIGrid=(IMSFlexGrid*)GetWindowLong(Wea->hWnd,8);
if(pIGrid)
    pIGrid->Release();
pUnkGrid=(IUnknown*)GetWindowLong(Wea->hWnd,4);
if(pUnkGrid)
    pUnkGrid->Release();
ppUnkContainer=(IUnknown*)GetWindowLong(Wea->hWnd,0);
if(ppUnkContainer)
    ppUnkContainer->Release();
PostQuitMessage(0);

return 0;
}


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

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

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


int WINAPI WinMain(HINSTANCE hIns, HINSTANCE hPrevIns, LPSTR lpszArgument, int iShow)
{
TCHAR szClassName[]=_T("MSFlexGrid Demo");
WNDCLASSEX wc;
MSG messages;
HWND hWnd;

wc.lpszClassName=szClassName;                wc.lpfnWndProc=fnWndProc;
wc.cbSize=sizeof (WNDCLASSEX);               wc.style=CS_DBLCLKS;
wc.hIcon=LoadIcon(NULL,IDI_APPLICATION);     wc.hInstance=hIns;
wc.hIconSm=LoadIcon(NULL, IDI_APPLICATION);  wc.hCursor=LoadCursor(NULL,IDC_ARROW);
wc.hbrBackground=(HBRUSH)COLOR_BTNSHADOW;    wc.cbWndExtra=20;
wc.lpszMenuName=NULL;                        wc.cbClsExtra=0;
RegisterClassEx(&wc);
hWnd=CreateWindowEx(0,szClassName,szClassName,WS_OVERLAPPEDWINDOW,75,75,320,305,HWND_DESKTOP,0,hIns,0);
ShowWindow(hWnd,iShow);
while(GetMessage(&messages,NULL,0,0))
{
    TranslateMessage(&messages);
    DispatchMessage(&messages);
}

return messages.wParam;
}



//Main.h
#define dim(x) (sizeof(x) / sizeof(x[0]))
typedef HRESULT (__stdcall* PFNATLAXWININIT)(void);
typedef HRESULT (__stdcall* PFNATLAXCREATECONTROLLIC)(LPCOLESTR lpszName, HWND hWnd, IStream* pStream, IUnknown** ppUnkContainer, BSTR bstrLic);
typedef HRESULT (__stdcall* PFNATLAXGETCONTROL)(HWND hContainer, IUnknown** ppIUnknown);


typedef struct WindowsEventArguments
{
HWND                         hWnd;
WPARAM                       wParam;
LPARAM                       lParam;
HINSTANCE                    hIns;
}WndEventArgs, *lpWndEventArgs;

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

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

const EVENTHANDLER EventHandler[]=
{
{WM_CREATE,                  fnWndProc_OnCreate},
{WM_DESTROY,                 fnWndProc_OnDestroy}
};

#define ID_CONTAINER          1500



//GridEvents.h
#ifndef GridEvents_h
#define GridEvents_h
#include "MSFlexGrid.h"


interface DMSFlexGridEvents : IDispatch
{
virtual void __stdcall Click()=0;
virtual void __stdcall KeyDown(short* KeyCode, short Shift)=0;
virtual void __stdcall DblClick()=0;
virtual void __stdcall KeyPress(short* KeyAscii)=0;
virtual void __stdcall KeyUp(short* KeyCode, short Shift)=0;
virtual void __stdcall MouseDown(short Button, short Shift, int x, int y)=0;
virtual void __stdcall MouseMove(short Button, short Shift, int x, int y)=0;
virtual void __stdcall MouseUp(short Button, short Shift, int x, int y)=0;
virtual void __stdcall SelChange()=0;
virtual void __stdcall RowColChange()=0;
virtual void __stdcall EnterCell()=0;
virtual void __stdcall LeaveCell()=0;
virtual void __stdcall Scroll()=0;
virtual void __stdcall Compare(long Row1, long Row2, short* Cmp)=0;
virtual void __stdcall OLEStartDrag(IDispatch** Data, long* AllowedEffects)=0;
virtual void __stdcall OLEGiveFeedback(long* Effect, VARIANT_BOOL* DefaultCursors)=0;
virtual void __stdcall OLESetData(IDispatch** Data, short* DataFormat)=0;
virtual void __stdcall OLECompleteDrag(long* Effect)=0;
virtual void __stdcall OLEDragOver(IDispatch** Data, long* Effect, short* Button, short* Shift, float* x, float* y, short* State)=0;
virtual void __stdcall OLEDragDrop(IDispatch** Data, long* Effect, short* Button, short* Shift, float* x, float* y)=0;
};


class CEventSink : public DMSFlexGridEvents     //CEventSink
{
public:
CEventSink();
virtual ~CEventSink();
HRESULT __stdcall QueryInterface(REFIID iid, void** ppv);
ULONG   __stdcall AddRef();
ULONG   __stdcall Release();
HRESULT __stdcall GetTypeInfoCount(UINT* pCountTypeInfo);
HRESULT __stdcall GetTypeInfo(UINT iTypeInfo, LCID lcid, ITypeInfo** ppITypeInfo);
HRESULT __stdcall GetIDsOfNames(REFIID riid, LPOLESTR* rgszNames, UINT cNames, LCID lcid, DISPID* rgDispId);
HRESULT __stdcall Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS* pDispParams, VARIANT* pVarResult, EXCEPINFO* pExcepInfo, UINT* puArgErr);
void    __stdcall StoreGridPointer(IMSFlexGrid* pIGrid);
void    __stdcall Click(){}
void    __stdcall KeyDown(short* KeyCode, short Shift){}
void    __stdcall DblClick(){}
void    __stdcall KeyPress(short* KeyAscii);                                // 0xfffffda5    // << this one implemented!!!
void    __stdcall KeyUp(short* KeyCode, short Shift){}
void    __stdcall MouseDown(short Button, short Shift, int x, int y){}
void    __stdcall MouseMove(short Button, short Shift, int x, int y){}
void    __stdcall MouseUp(short Button, short Shift, int x, int y){}
void    __stdcall SelChange(){}
void    __stdcall RowColChange(){}
void    __stdcall EnterCell(){}
void    __stdcall LeaveCell(){}
void    __stdcall Scroll(){}
void    __stdcall Compare(long Row1, long Row2, short* Cmp){}
void    __stdcall OLEStartDrag(IDispatch** Data, long* AllowedEffects){}
void    __stdcall OLEGiveFeedback(long* Effect, VARIANT_BOOL* DefaultCursors){}
void    __stdcall OLESetData(IDispatch** Data, short* DataFormat){}
void    __stdcall OLECompleteDrag(long* Effect){}
void    __stdcall OLEDragOver(IDispatch** Data, long* Effect, short* Button, short* Shift, float* x, float* y, short* State){}
void    __stdcall OLEDragDrop(IDispatch** Data, long* Effect, short* Button, short* Shift, float* x, float* y){}

private:
long         m_cRef;
ITypeInfo*   m_pTypeInfo;
IMSFlexGrid* pGrid;
};
#endif



//GridEvents.cpp
#define  UNICODE
#define  _UNICODE
#include <windows.h>
#include <objbase.h>
#include <ocidl.h>
#include <tchar.h>
#include <string>
#include "Main.h"
#include "GridEvents.h"
#include "MSFlexGrid.h"
extern "C" const IID LIBID_MSFlexGrid     = {0x5E9E78A0,0x531B,0x11CF,{0x91,0xF6,0xC2,0x86,0x3C,0x38,0x5E,0x30}};
extern "C" const IID IID_MSFlexGridEvents = {0x609602E0,0x531B,0x11CF,{0x91,0xF6,0xC2,0x86,0x3C,0x38,0x5E,0x30}};


CEventSink::CEventSink() : m_cRef(0)       //CEventSink Constructor
{
ITypeLib* pTypeLib=NULL;
HRESULT hr;

this->m_pTypeInfo=NULL;
hr=LoadRegTypeLib(LIBID_MSFlexGrid,1,0,LANG_NEUTRAL,&pTypeLib);
if(SUCCEEDED(hr))
{
    hr = pTypeLib->GetTypeInfoOfGuid(IID_MSFlexGridEvents, &m_pTypeInfo);
    pTypeLib->Release();
}
}


CEventSink::~CEventSink()
{
if(this->m_pTypeInfo)
    this->m_pTypeInfo->Release();
}


HRESULT CEventSink::QueryInterface(REFIID riid, void** ppv)
{
if(riid == IID_IUnknown)
    *ppv = (IUnknown*)this;
else if(riid == IID_IDispatch)
    *ppv = (IDispatch*)this;
else if(riid == IID_MSFlexGridEvents)
    *ppv = (DMSFlexGridEvents*)this;
else
{
    *ppv = NULL;
    return E_NOINTERFACE;
}
AddRef();

return S_OK;
}


ULONG CEventSink::AddRef()
{
return ++m_cRef;
}


ULONG CEventSink::Release()
{
if(--m_cRef != 0)
    return m_cRef;
else
    delete this;

return 0;
}


HRESULT CEventSink::GetTypeInfoCount(UINT* pCountTypeInfo)
{
*pCountTypeInfo = 1;
return S_OK;
}


HRESULT CEventSink::GetTypeInfo(UINT iTypeInfo, LCID lcid, ITypeInfo** ppITypeInfo)
{
*ppITypeInfo = NULL;
if(iTypeInfo != 0)
    return DISP_E_BADINDEX;
m_pTypeInfo->AddRef();
*ppITypeInfo = m_pTypeInfo;
return S_OK;
}


HRESULT CEventSink::GetIDsOfNames(REFIID riid, LPOLESTR* rgszNames, UINT cNames, LCID lcid, DISPID* rgDispId)
{
if(riid != IID_NULL)
    return DISP_E_UNKNOWNINTERFACE;
return DispGetIDsOfNames(m_pTypeInfo, rgszNames, cNames, rgDispId);
}


HRESULT CEventSink::Invoke(DISPID dispId, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS* pDParms, VARIANT* pVarResult, EXCEPINFO* pErInfo, UINT* pErr)
{
if(riid != IID_NULL)
    return DISP_E_UNKNOWNINTERFACE;
else
{
    switch(dispId)
    {
      case 0xfffffda5:  // KeyPress
        {
           this->KeyPress(pDParms->rgvarg[0].piVal);
           break;
        }
    }
}

return S_OK;
}


void CEventSink::KeyPress(short* KeyAscii)
{
BSTR strCellText=SysAllocString(L"");
this->pGrid->GetText(&strCellText);
std::wstring strMyText=strCellText;
strMyText+=*KeyAscii;
SysReAllocString(&strCellText,strMyText.c_str());
this->pGrid->SetText(strCellText);
}

void CEventSink::StoreGridPointer(IMSFlexGrid* pIGrid)
{
this->pGrid=pIGrid;
}


Frederick J. Harris



//MSFlexGrid.h
// Generated .IDL file (by the OLE/COM Object Viewer)  typelib filename: MSFLXGRD.OCX
// Forward declare all types defined in this typelib
#ifndef   MSFlexGrid_h
#define   MSFlexGrid_h
interface IVBDataObject;
interface IVBDataObjectFiles;
interface IRowCursor;
interface IMSFlexGrid;
interface DMSFlexGridEvents;


interface IVBDataObject : IDispatch
{
virtual HRESULT __stdcall Clear();
virtual HRESULT __stdcall GetData(short sFormat, VARIANT* pvData);
virtual HRESULT __stdcall GetFormat(short sFormat, VARIANT_BOOL* pbFormatSupported);
virtual HRESULT __stdcall SetData(VARIANT vValue, VARIANT vFormat);
virtual HRESULT __stdcall Files(IVBDataObjectFiles** pFiles);
};

interface IVBDataObjectFiles : IDispatch
{
virtual HRESULT __stdcall Item(long lIndex, BSTR* bstrItem);
virtual HRESULT __stdcall Count(long* plCount);
virtual HRESULT __stdcall Add(BSTR bstrFilename, VARIANT vIndex);
virtual HRESULT __stdcall Clear();
virtual HRESULT __stdcall Remove(VARIANT vIndex);
virtual HRESULT __stdcall _NewEnum(IUnknown** ppUnk);
};

typedef enum OLEDragConstants
{
flexOLEDragManual = 0,
flexOLEDragAutomatic = 1
}OLEDragConstants;

typedef enum OLEDropConstants
{
flexOLEDropNone = 0,
flexOLEDropManual = 1
}OLEDropConstants;

typedef enum DragOverConstants
{
flexEnter = 0,
flexLeave = 1,
flexOver = 2
}DragOverConstants;

typedef enum ClipBoardConstants
{
flexCFText = 1,
flexCFBitmap = 2,
flexCFMetafile = 3,
flexCFDIB = 8,
flexCFPalette = 9,
flexCFEMetafile = 14,
flexCFFiles = 15,
flexCFRTF = -16639
}ClipBoardConstants;

typedef enum OLEDropEffectConstants
{
flexOLEDropEffectNone    = 0,
flexOLEDropEffectCopy    = 1,
flexOLEDropEffectMove    = 2,
flexOLEDropEffectScroll  = 0x80000000   //-2147483648
}OLEDropEffectConstants;

interface IRowCursor : IDispatch {};

typedef enum ErrorConstants
{
flexerrIllegaFunctionCall = 5,
flexerrObjIllegalUse = 425,
flexerrClipInvalidFormat = 461,
flexerrDataObjectLocked = 672,
flexerrExpectedAnArgument = 673,
flexerrRecursiveOLEDrag = 674,
flexerrUserFormatNotBinArray = 675,
flexerrDataNotSetForFormat = 676,
flexerrUnknownError = 600,
flexerrSubscript = 381,
flexerrBadValue = 380,
flexerrGetNotSupported = 394,
flexerrSetNotPermitted = 387,
flexerrOutOfMemory = 7,
flexerrVB30000 = 30000,
flexerrVB30001 = 30001,
flexerrVB30002 = 30002,
flexerrVB30004 = 30004,
flexerrVB30005 = 30005,
flexerrVB30006 = 30006,
flexerrVB30008 = 30008,
flexerrVB30009 = 30009,
flexerrVB30010 = 30010,
flexerrVB30011 = 30011,
flexerrVB30013 = 30013,
flexerrVB30014 = 30014,
flexerrVB30015 = 30015,
flexerrVB30016 = 30016,
flexerrVB30017 = 30017
}ErrorConstants;

typedef enum AppearanceSettings
{
flexFlat = 0,
flex3D = 1
}AppearanceSettings;

typedef enum BorderStyleSettings
{
flexBorderNone = 0,
flexBorderfloat = 1
}BorderStyleSettings;

typedef enum FocusRectSettings
{
flexFocusNone = 0,
flexFocusLight = 1,
flexFocusHeavy = 2
}FocusRectSettings;

typedef enum HighLightSettings
{
flexHighlightNever = 0,
flexHighlightAlways = 1,
flexHighlightWithFocus = 2
}HighLightSettings;

typedef enum ScrollBarsSettings
{
flexScrollBarNone = 0,
flexScrollBarHorizontal = 1,
flexScrollBarVertical = 2,
flexScrollBarBoth = 3
}ScrollBarsSettings;

typedef enum TextStyleSettings
{
flexTextFlat = 0,
flexTextRaised = 1,
flexTextInset = 2,
flexTextRaisedLight = 3,
flexTextInsetLight = 4
}TextStyleSettings;

typedef enum FillStyleSettings
{
flexFillfloat = 0,
flexFillRepeat = 1
}FillStyleSettings;

typedef enum GridLineSettings
{
flexGridNone = 0,
flexGridFlat = 1,
flexGridInset = 2,
flexGridRaised = 3
}GridLineSettings;

typedef enum SelectionModeSettings
{
flexSelectionFree = 0,
flexSelectionByRow = 1,
flexSelectionByColumn = 2
}SelectionModeSettings;

typedef enum MergeCellsSettings
{
flexMergeNever = 0,
flexMergeFree = 1,
flexMergeRestrictRows = 2,
flexMergeRestrictColumns = 3,
flexMergeRestrictAll = 4
}MergeCellsSettings;

typedef enum PictureTypeSettings
{
flexPictureColor = 0,
flexPictureMonochrome = 1
}PictureTypeSettings;

typedef enum AllowUserResizeSettings
{
flexResizeNone = 0,
flexResizeColumns = 1,
flexResizeRows = 2,
flexResizeBoth = 3
}AllowUserResizeSettings;

typedef enum MousePointerSettings
{
flexDefault = 0,
flexArrow = 1,
flexCross = 2,
flexIBeam = 3,
flexIcon = 4,
flexSize = 5,
flexSizeNESW = 6,
flexSizeNS = 7,
flexSizeNWSE = 8,
flexSizeEW = 9,
flexUpArrow = 10,
flexHourglass = 11,
flexNoDrop = 12,
flexArrowHourGlass = 13,
flexArrowQuestion = 14,
flexSizeAll = 15,
flexCustom = 99
}MousePointerSettings;

typedef enum SortSettings
{
flexSortNone = 0,
flexSortGenericAscending = 1,
flexSortGenericDescending = 2,
flexSortNumericAscending = 3,
flexSortNumericDescending = 4,
flexSortStringNoCaseAscending = 5,
flexSortStringNoCaseDescending = 6,
flexSortStringAscending = 7,
flexSortStringDescending = 8
}SortSettings;

typedef enum AlignmentSettings
{
flexAlignLeftTop = 0,
flexAlignLeftCenter = 1,
flexAlignLeftBottom = 2,
flexAlignCenterTop = 3,
flexAlignCenterCenter = 4,
flexAlignCenterBottom = 5,
flexAlignRightTop = 6,
flexAlignRightCenter = 7,
flexAlignRightBottom = 8,
flexAlignGeneral = 9
}AlignmentSettings;


interface IMSFlexGrid : IDispatch
{
virtual HRESULT __stdcall GetRows(long* Rows)=0;
virtual HRESULT __stdcall SetRows(long Rows)=0;
virtual HRESULT __stdcall GetCols(long* Cols)=0;
virtual HRESULT __stdcall SetCols(long Cols)=0;
virtual HRESULT __stdcall GetFixedRows(long* FixedRows)=0;
virtual HRESULT __stdcall SetFixedRows(long FixedRows)=0;
virtual HRESULT __stdcall GetFixedCols(long* FixedCols)=0;
virtual HRESULT __stdcall SetFixedCols(long FixedCols)=0;
virtual HRESULT __stdcall GetVersion(short* Version)=0;
virtual HRESULT __stdcall GetFormatString(BSTR* FormatString)=0;
virtual HRESULT __stdcall SetFormatString(BSTR FormatString)=0;
virtual HRESULT __stdcall GetTopRow(long* TopRow)=0;
virtual HRESULT __stdcall SetTopRow(long TopRow)=0;
virtual HRESULT __stdcall GetLeftCol(long* LeftCol)=0;
virtual HRESULT __stdcall SetLeftCol(long LeftCol)=0;
virtual HRESULT __stdcall GetRow(long* Row)=0;
virtual HRESULT __stdcall SetRow(long Row)=0;
virtual HRESULT __stdcall GetCol(long* Col)=0;
virtual HRESULT __stdcall SetCol(long Col)=0;
virtual HRESULT __stdcall GetRowSel(long* RowSel)=0;
virtual HRESULT __stdcall SetRowSel(long RowSel)=0;
virtual HRESULT __stdcall GetColSel(long* ColSel)=0;
virtual HRESULT __stdcall SetColSel(long ColSel)=0;
virtual HRESULT __stdcall GetText(BSTR* Text)=0;
virtual HRESULT __stdcall SetText(BSTR Text)=0;
virtual HRESULT __stdcall GetBackColor(unsigned* BackColor)=0;
virtual HRESULT __stdcall SetBackColor(unsigned BackColor)=0;
virtual HRESULT __stdcall GetForeColor(unsigned* ForeColor)=0;
virtual HRESULT __stdcall SetForeColor(unsigned ForeColor)=0;
virtual HRESULT __stdcall GetBackColorFixed(unsigned* BackColorFixed)=0;
virtual HRESULT __stdcall SetBackColorFixed(unsigned BackColorFixed)=0;
virtual HRESULT __stdcall GetForeColorFixed(unsigned* ForeColorFixed)=0;
virtual HRESULT __stdcall SetForeColorFixed(unsigned ForeColorFixed)=0;
virtual HRESULT __stdcall GetBackColorSel(unsigned* BackColorSel)=0;
virtual HRESULT __stdcall SetBackColorSel(unsigned BackColorSel)=0;
virtual HRESULT __stdcall GetForeColorSel(unsigned* ForeColorSel)=0;
virtual HRESULT __stdcall SetForeColorSel(unsigned ForeColorSel)=0;
virtual HRESULT __stdcall GetBackColorBkg(unsigned* BackColorBkg)=0;
virtual HRESULT __stdcall SetBackColorBkg(unsigned BackColorBkg)=0;
virtual HRESULT __stdcall GetWordWrap(VARIANT_BOOL* WordWrap)=0;
virtual HRESULT __stdcall SetWordWrap(VARIANT_BOOL WordWrap)=0;
virtual HRESULT __stdcall GetFont(IFontDisp** Font)=0;
virtual HRESULT __stdcall SetFont(IFontDisp* Font)=0;
virtual HRESULT __stdcall GetFontWidth(float* FontWidth)=0;
virtual HRESULT __stdcall SetFontWidth(float FontWidth)=0;
virtual HRESULT __stdcall GetCellFontName(BSTR* CellFontName)=0;
virtual HRESULT __stdcall SetCellFontName(BSTR CellFontName)=0;
virtual HRESULT __stdcall GetCellFontSize(float* CellFontSize)=0;
virtual HRESULT __stdcall SetCellFontSize(float CellFontSize)=0;
virtual HRESULT __stdcall GetCellFontBold(VARIANT_BOOL* CellFontBold)=0;
virtual HRESULT __stdcall SetCellFontBold(VARIANT_BOOL CellFontBold)=0;
virtual HRESULT __stdcall GetCellFontItalic(VARIANT_BOOL* CellFontItalic)=0;
virtual HRESULT __stdcall SetCellFontItalic(VARIANT_BOOL CellFontItalic)=0;
virtual HRESULT __stdcall GetCellFontUnderline(VARIANT_BOOL* CellFontUnderline)=0;
virtual HRESULT __stdcall SetCellFontUnderline(VARIANT_BOOL CellFontUnderline)=0;
virtual HRESULT __stdcall GetCellFontStrikeThrough(VARIANT_BOOL* CellFontStrikeThrough)=0;
virtual HRESULT __stdcall SetCellFontStrikeThrough(VARIANT_BOOL CellFontStrikeThrough)=0;
virtual HRESULT __stdcall GetCellFontWidth(float* CellFontWidth)=0;
virtual HRESULT __stdcall SetCellFontWidth(float CellFontWidth)=0;
virtual HRESULT __stdcall GetTextStyle(TextStyleSettings* TextStyle)=0;
virtual HRESULT __stdcall SetTextStyle(TextStyleSettings TextStyle)=0;
virtual HRESULT __stdcall GetTextStyleFixed(TextStyleSettings* TextStyleFixed)=0;
virtual HRESULT __stdcall SetTextStyleFixed(TextStyleSettings TextStyleFixed)=0;
virtual HRESULT __stdcall GetScrollTrack(VARIANT_BOOL* ScrollTrack)=0;
virtual HRESULT __stdcall SetScrollTrack(VARIANT_BOOL ScrollTrack)=0;
virtual HRESULT __stdcall GetFocusRect(FocusRectSettings* FocusRect)=0;
virtual HRESULT __stdcall SetFocusRect(FocusRectSettings FocusRect)=0;
virtual HRESULT __stdcall GetHighLight(HighLightSettings* HighLight)=0;
virtual HRESULT __stdcall SetHighLight(HighLightSettings HighLight)=0;
virtual HRESULT __stdcall GetRedraw(VARIANT_BOOL* Redraw)=0;
virtual HRESULT __stdcall SetRedraw(VARIANT_BOOL Redraw)=0;
virtual HRESULT __stdcall GetScrollBars(ScrollBarsSettings* ScrollBars)=0;
virtual HRESULT __stdcall SetScrollBars(ScrollBarsSettings ScrollBars)=0;
virtual HRESULT __stdcall GetMouseRow(long* MouseRow)=0;
virtual HRESULT __stdcall GetMouseCol(long* MouseCol)=0;
virtual HRESULT __stdcall GetCellLeft(long* CellLeft)=0;
virtual HRESULT __stdcall GetCellTop(long* CellTop)=0;
virtual HRESULT __stdcall GetCellWidth(long* CellWidth)=0;
virtual HRESULT __stdcall GetCellHeight(long* CellHeight)=0;
virtual HRESULT __stdcall GetRowHeightMin(long* RowHeightMin)=0;
virtual HRESULT __stdcall SetRowHeightMin(long RowHeightMin)=0;
virtual HRESULT __stdcall GetFillStyle(FillStyleSettings* FillStyle)=0;
virtual HRESULT __stdcall SetFillStyle(FillStyleSettings FillStyle)=0;
virtual HRESULT __stdcall GetGridLines(GridLineSettings* GridLines)=0;
virtual HRESULT __stdcall SetGridLines(GridLineSettings GridLines)=0;
virtual HRESULT __stdcall GetGridLinesFixed(GridLineSettings* GridLinesFixed)=0;
virtual HRESULT __stdcall SetGridLinesFixed(GridLineSettings GridLinesFixed)=0;
virtual HRESULT __stdcall GetGridColor(unsigned* GridColor)=0;
virtual HRESULT __stdcall SetGridColor(unsigned GridColor)=0;
virtual HRESULT __stdcall GetGridColorFixed(unsigned* GridColorFixed)=0;
virtual HRESULT __stdcall SetGridColorFixed(unsigned GridColorFixed)=0;
virtual HRESULT __stdcall GetCellBackColor(unsigned* CellBackColor)=0;
virtual HRESULT __stdcall SetCellBackColor(unsigned CellBackColor)=0;
virtual HRESULT __stdcall GetCellForeColor(unsigned* CellForeColor)=0;
virtual HRESULT __stdcall SetCellForeColor(unsigned CellForeColor)=0;
virtual HRESULT __stdcall GetCellAlignment(short* CellAlignment)=0;
virtual HRESULT __stdcall SetCellAlignment(short CellAlignment)=0;
virtual HRESULT __stdcall GetCellTextStyle(TextStyleSettings* CellTextStyle)=0;
virtual HRESULT __stdcall SetCellTextStyle(TextStyleSettings CellTextStyle)=0;
virtual HRESULT __stdcall GetCellPictureAlignment(short* CellPictureAlignment)=0;
virtual HRESULT __stdcall SetCellPictureAlignment(short CellPictureAlignment)=0;
virtual HRESULT __stdcall GetClip(BSTR* Clip)=0;
virtual HRESULT __stdcall SetClip(BSTR Clip)=0;
virtual HRESULT __stdcall SetSort(short rhs)=0;
virtual HRESULT __stdcall GetSelectionMode(SelectionModeSettings* SelectionMode)=0;
virtual HRESULT __stdcall SetSelectionMode(SelectionModeSettings SelectionMode)=0;
virtual HRESULT __stdcall GetMergeCells(MergeCellsSettings* MergeCells)=0;
virtual HRESULT __stdcall SetMergeCells(MergeCellsSettings MergeCells)=0;
virtual HRESULT __stdcall GetAllowBigSelection(VARIANT_BOOL* AllowBigSelection)=0;
virtual HRESULT __stdcall SetAllowBigSelection(VARIANT_BOOL AllowBigSelection)=0;
virtual HRESULT __stdcall GetAllowUserResizing(AllowUserResizeSettings* AllowUserResizing)=0;
virtual HRESULT __stdcall SetAllowUserResizing(AllowUserResizeSettings AllowUserResizing)=0;
virtual HRESULT __stdcall GetBorderStyle(BorderStyleSettings* BorderStyle)=0;
virtual HRESULT __stdcall SetBorderStyle(BorderStyleSettings BorderStyle)=0;
virtual HRESULT __stdcall hWnd(long* hWnd)=0;
virtual HRESULT __stdcall GetEnabled(VARIANT_BOOL* Enabled)=0;
virtual HRESULT __stdcall SetEnabled(VARIANT_BOOL Enabled)=0;
virtual HRESULT __stdcall GetAppearance(AppearanceSettings* Appearance)=0;
virtual HRESULT __stdcall SetAppearance(AppearanceSettings Appearance)=0;
virtual HRESULT __stdcall GetMousePointer(MousePointerSettings* MousePointer)=0;
virtual HRESULT __stdcall SetMousePointer(MousePointerSettings MousePointer)=0;
virtual HRESULT __stdcall GetMouseIcon(IPictureDisp** MouseIcon)=0;
virtual HRESULT __stdcall SetMouseIcon(IPictureDisp* MouseIcon)=0;
virtual HRESULT __stdcall GetPictureType(PictureTypeSettings* PictureType)=0;
virtual HRESULT __stdcall SetPictureType(PictureTypeSettings PictureType)=0;
virtual HRESULT __stdcall GetPicture(IPictureDisp** Picture)=0;
virtual HRESULT __stdcall GetCellPicture(IPictureDisp** CellPicture)=0;
virtual HRESULT __stdcall SetCellPicture(IPictureDisp* CellPicture)=0;
virtual void    __stdcall AboutBox()=0;
virtual HRESULT __stdcall GetTextArray(long index, BSTR* TextArray)=0;
virtual HRESULT __stdcall SetTextArray(long index, BSTR TextArray)=0;
virtual HRESULT __stdcall GetColAlignment(long index, short* ColAlignment)=0;
virtual HRESULT __stdcall SetColAlignment(long index, short ColAlignment)=0;
virtual HRESULT __stdcall GetColWidth(long index, long* ColWidth)=0;
virtual HRESULT __stdcall SetColWidth(long index, long ColWidth)=0;
virtual HRESULT __stdcall GetRowHeight(long index, long* RowHeight)=0;
virtual HRESULT __stdcall SetRowHeight(long index, long RowHeight)=0;
virtual HRESULT __stdcall GetMergeRow(long index, VARIANT_BOOL* MergeRow)=0;
virtual HRESULT __stdcall SetMergeRow(long index, VARIANT_BOOL MergeRow)=0;
virtual HRESULT __stdcall GetMergeCol(long index, VARIANT_BOOL* MergeCol)=0;
virtual HRESULT __stdcall SetMergeCol(long index, VARIANT_BOOL MergeCol)=0;
virtual HRESULT __stdcall RowPosition(long index, long rhs)=0;
virtual HRESULT __stdcall ColPosition(long index, long rhs)=0;
virtual HRESULT __stdcall GetRowData(long index, long* RowData)=0;
virtual HRESULT __stdcall SetRowData(long index, long RowData)=0;
virtual HRESULT __stdcall GetColData(long index, long* ColData)=0;
virtual HRESULT __stdcall SetColData(long index, long ColData)=0;
virtual HRESULT __stdcall GetTextMatrix(long Row, long Col, BSTR* TextMatrix)=0;
virtual HRESULT __stdcall SetTextMatrix(long Row, long Col, BSTR TextMatrix)=0;
virtual HRESULT __stdcall AddItem(BSTR Item, VARIANT index)=0;  // index is optional
virtual HRESULT __stdcall RemoveItem(long index)=0;
virtual void    __stdcall Clear()=0;
virtual void    __stdcall Refresh()=0;
virtual HRESULT __stdcall GetDataSource(IRowCursor** DataSource)=0;
virtual HRESULT __stdcall SetDataSource(IRowCursor* DataSource)=0;
virtual HRESULT __stdcall RowIsVisible(long index, VARIANT_BOOL* RowIsVisible)=0;
virtual HRESULT __stdcall ColIsVisible(long index, VARIANT_BOOL* ColIsVisible)=0;
virtual HRESULT __stdcall RowPos(long index, long* RowPos)=0;
virtual HRESULT __stdcall ColPos(long index, long* ColPos)=0;
virtual HRESULT __stdcall GetGridLineWidth(short* GridLineWidth)=0;
virtual HRESULT __stdcall SetGridLineWidth(short GridLineWidth)=0;
virtual HRESULT __stdcall GetFixedAlignment(long index, short* FixedAlignment)=0;
virtual HRESULT __stdcall SetFixedAlignment(long index, short FixedAlignment)=0;
virtual HRESULT __stdcall GetFontName(BSTR* FontName)=0;
virtual HRESULT __stdcall SetFontName(BSTR FontName)=0;
virtual HRESULT __stdcall GetFontSize(float* FontSize)=0;
virtual HRESULT __stdcall SetFontSize(float FontSize)=0;
virtual HRESULT __stdcall GetFontBold(VARIANT_BOOL* FontBold)=0;
virtual HRESULT __stdcall SetFontBold(VARIANT_BOOL FontBold)=0;
virtual HRESULT __stdcall GetFontItalic(VARIANT_BOOL* FontItalic)=0;
virtual HRESULT __stdcall SetFontItalic(VARIANT_BOOL FontItalic)=0;
virtual HRESULT __stdcall GetFontStrikethru(VARIANT_BOOL* FontStrikethru)=0;
virtual HRESULT __stdcall SetFontStrikethru(VARIANT_BOOL FontStrikethru)=0;
virtual HRESULT __stdcall GetFontUnderline(VARIANT_BOOL* FontUnderline)=0;
virtual HRESULT __stdcall SetFontUnderline(VARIANT_BOOL FontUnderline)=0;
virtual HRESULT __stdcall GetRightToLeft(VARIANT_BOOL* RightToLeft)=0;
virtual HRESULT __stdcall SetRightToLeft(VARIANT_BOOL RightToLeft)=0;
virtual HRESULT __stdcall GetOLEDropMode(OLEDropConstants* psOLEDropMode)=0;
virtual HRESULT __stdcall SetOLEDropMode(OLEDropConstants psOLEDropMode)=0;
virtual HRESULT __stdcall OLEDrag()=0;
};
#endif