Trying to automate Excel from C++
The background to this is that I am trying to do some winsock
programming in Excel. It is pretty simple. I am trapping the winsock
message by hooking into events for the Excel window and when it is a
winsock event I am calling a macro in Excel via COM.
There is a macro "callback(byval socketHandle as long, byval
socketevent as long)" but it doesn't get called. HR on the invoke is
S_OK:
// AptFIXcelSocket.cpp : Defines the entry point for the DLL
application.
//
#include "stdafx.h"
#include "AptFIXcelSocket.h"
#include <stdio.h>
#include <winsock.h>
#include <Objbase.h>
HHOOK hHook;
// This is an example of an exported variable
APTFIXCELSOCKET_API UINT WM_WINSOCK_MESSAGE=0;
WNDPROC callbackProc = NULL;
BSTR callbackProcName;
long currentWParam = 0;
long currentLParam = 0;
HRESULT AutoWrap(int autoType, VARIANT *pvResult, IDispatch *pDisp,
LPOLESTR ptName, int cArgs...) {
// Begin variable-argument list...
va_list marker;
va_start(marker, cArgs);
if(!pDisp) {
MessageBox(NULL, "NULL IDispatch passed to AutoWrap()", "Error",
0x10010);
_exit(0);
}
// Variables used...
DISPPARAMS dp = { NULL, NULL, 0, 0 };
DISPID dispidNamed = DISPID_PROPERTYPUT;
DISPID dispID;
HRESULT hr;
char buf[200];
char szName[200];
// Convert down to ANSI
WideCharToMultiByte(CP_ACP, 0, ptName, -1, szName, 256, NULL, NULL);
// Get DISPID for name passed...
hr = pDisp->GetIDsOfNames(IID_NULL, &ptName, 1, LOCALE_USER_DEFAULT,
&dispID);
if(FAILED(hr)) {
sprintf(buf, "IDispatch::GetIDsOfNames(\"%s\") failed w/err 0x%08lx
\n", szName, hr);
::OutputDebugString(buf);
// MessageBox(NULL, buf, "AutoWrap()", 0x10010);
// _exit(0);
return hr;
}
// Allocate memory for arguments...
VARIANT *pArgs = new VARIANT[cArgs+1];
// Extract arguments...
for(int i=0; i<cArgs; i++) {
pArgs[i] = va_arg(marker, VARIANT);
}
// Build DISPPARAMS
dp.cArgs = cArgs;
dp.rgvarg = pArgs;
// Handle special-case for property-puts!
if(autoType & DISPATCH_PROPERTYPUT) {
dp.cNamedArgs = 1;
dp.rgdispidNamedArgs = &dispidNamed;
}
// Make the call!
EXCEPINFO ei;
UINT arg = 0;
hr = pDisp->Invoke(dispID, IID_NULL, LOCALE_SYSTEM_DEFAULT,
autoType, &dp, pvResult, &ei, &arg);
if(FAILED(hr)) {
sprintf(buf, "IDispatch::Invoke(\"%s\"=%08lx) failed w/err 0x%08lx
\n", szName, dispID, hr);
::OutputDebugString(buf);
//MessageBox(NULL, buf, "AutoWrap()", 0x10010);
// _exit(0);
return hr;
}
// End variable-argument section...
va_end(marker);
delete [] pArgs;
return hr;
}
BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
{
WM_WINSOCK_MESSAGE = ::RegisterWindowMessage("Apt Winsock Message");
break;
}
case DLL_PROCESS_DETACH:
{
if( NULL != hHook )
{
AptCancelCallback();
}
break;
}
case DLL_THREAD_ATTACH:
{
// initialise
CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
break;
}
case DLL_THREAD_DETACH:
{
// initialise
::CoUninitialize();
break;
}
}
return TRUE;
}
APTFIXCELSOCKET_API long __stdcall AptGetMessageID()
{
return WM_WINSOCK_MESSAGE;
}
// This is an example of an exported function.
APTFIXCELSOCKET_API long __stdcall AptSetupCallback(HWND excelWnd,
HINSTANCE excelInst, char * callbackfunction)
{
if( NULL == hHook)
{
DWORD procID;
// Hook in for the message
hHook = SetWindowsHookEx
(
WH_GETMESSAGE,
GetMsgProc,
excelInst,
GetWindowThreadProcessId(excelWnd, (LPDWORD)(&procID))
);
if ( NULL != hHook )
{
// callbackProcName = ::SysAllocString(_T(callbackfunction));
}
}
return 0;
}
APTFIXCELSOCKET_API long __stdcall AptCancelCallback()
{
if( NULL != hHook )
{
UnhookWindowsHookEx(hHook);
hHook = 0;
}
return 0;
}
APTFIXCELSOCKET_API long __stdcall AptGetWParam()
{
long wparam = currentWParam;
char output[1028];
sprintf(output, "Excel retrieved WParam %d \n", currentWParam);
OutputDebugString ( output );
currentWParam = 0;
OutputDebugString ( "Cleared wparam \n" );
return wparam;
}
APTFIXCELSOCKET_API long __stdcall AptGetLParam()
{
long lparam = currentLParam;
char output[1028];
sprintf(output, "Excel retrieved LParam %d \n", currentLParam);
OutputDebugString ( output );
currentLParam = 0;
OutputDebugString ( "Cleared lparam \n" );
return lparam;
}
LRESULT CALLBACK GetMsgProc(int nCode, WPARAM wParam, LPARAM lParam)
{
if (nCode < 0)
{
return CallNextHookEx(hHook, nCode, wParam, lParam);
}
__try
{
// Only interested in winsock messages
LPMSG msg = (LPMSG)lParam;
if( WM_WINSOCK_MESSAGE == msg->message)
{
IDispatch *pXlApp;
IUnknown *pUnk;
CLSID clsid;
HRESULT hr = CLSIDFromProgID(L"Excel.Application", &clsid);
hr = GetActiveObject(clsid, NULL, (IUnknown**)&pUnk);
hr = pUnk->QueryInterface(IID_IDispatch, (void **)&pXlApp);
pUnk->Release();
VARIANT parm1;
VariantInit(&parm1);
parm1.vt = VT_BSTR;
parm1.bstrVal = ::SysAllocString(L"FIXcel.xla!callback");
VARIANT result;
VARIANT LParam;
VariantInit(&LParam);
LParam.vt = VT_I4;
LParam.lVal = msg->lParam;
VARIANT WParam;
VariantInit(&WParam);
WParam.vt = VT_I4;
WParam.lVal = msg->wParam;
hr = AutoWrap(DISPATCH_METHOD, &result, pXlApp, L"Run", 3,
parm1,WParam,LParam);
char output[1028];
OutputDebugString ( output );
msg = 0;
VariantClear(&parm1);
VariantClear(&LParam);
VariantClear(&WParam);
VariantClear(&result);
pXlApp->Release();
}
else if( WM_TIMER == msg->message )
{
// call back into Excel
// (*callbackProc)(msg->hwnd, msg->message, msg->wParam, msg-
lParam );
}
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
// Do nothing
}
// Call the
return CallNextHookEx(hHook, nCode, wParam, lParam);
}