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