I am struggling with an intermittent C++ application crash.
I am not a C++ programmer but am tasked with solving this problem, so very much hope you can help me.
Often the app runs fine, then on occasion crashes with an exception.
When entering debug from running the exe, line of code as seen highlighted seems to be at fault - please see first screen shot.
I have expanded some of the Locals in the second screen shot.
This line of code calls a function 'ClearVariant' the code being as follows for this function:
/*
* ClearVariant
*
* Zeros a variant structure without regard to current contents
*/
void CXLAutomation::ClearVariant(VARIANTARG *pvarg)
{
pvarg->vt = VT_EMPTY;
pvarg->wReserved1 = 0;
pvarg->wReserved2 = 0;
pvarg->wReserved3 = 0;
pvarg->lVal = 0;
}
The entire cpp file is at the end of the post.
The OpenExcelFile is function that leads to this problem - as you can from the call stack in the screen shots.
// XLAutomation.cpp: implementation of the CXLAutomation class.
//This is C++ modification of the AutoXL C-sample from
//Microsoft Excel97 Developer Kit, Microsoft Press 1997
//////////////////////////////////////////////////////////////////////
#include "stdafx.h"
//#include "XLAutomationTester.h"
#include "XLAutomation.h"
#include <ole2ver.h>
#include <string.h>
#include <winuser.h>
#include <stdio.h>
#include <string>
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
/*
* Arrays of argument information, which are used to build up the arg list
* for an IDispatch call. These arrays are statically allocated to reduce
* complexity, but this code could be easily modified to perform dynamic
* memory allocation.
*
* When arguments are added they are placed into these arrays. The
* Vargs array contains the argument values, and the lpszArgNames array
* contains the name of the arguments, or a NULL if the argument is unnamed.
* Flags for the argument such as NOFREEVARIANT are kept in the wFlags array.
*
* When Invoke is called, the names in the lpszArgNames array are converted
* into the DISPIDs expected by the IDispatch::Invoke function. The
* IDispatch::GetIDsOfNames function is used to perform the conversion, and
* the resulting IDs are placed in the DispIds array. There is an additional
* slot in the DispIds and lpszArgNames arrays to allow for the name and DISPID
* of the method or property being invoked.
*
* Because these arrays are static, it is important to call the ClearArgs()
* function before setting up arguments. ClearArgs() releases any memory
* in use by the argument array and resets the argument counters for a fresh
* Invoke.
*/
//int m_iArgCount;
//int m_iNamedArgCount;
//VARIANTARG m_aVargs[MAX_DISP_ARGS];
//DISPID m_aDispIds[MAX_DISP_ARGS + 1]; // one extra for the member name
//LPOLESTR m_alpszArgNames[MAX_DISP_ARGS + 1]; // used to hold the argnames for GetIDs
//WORD m_awFlags[MAX_DISP_ARGS];
//////////////////////////////////////////////////////////////////////
CXLAutomation::CXLAutomation()
{
m_pdispExcelApp = NULL;
m_pdispWorkbook = NULL;
m_pdispWorksheet = NULL;
m_pdispActiveChart = NULL;
InitOLE();
StartExcel();
//SetExcelVisible(TRUE);
//CreateWorkSheet();
//CreateXYChart();
}
CXLAutomation::CXLAutomation(BOOL bVisible)
{
m_pdispExcelApp = NULL;
m_pdispWorkbook = NULL;
m_pdispWorksheet = NULL;
m_pdispActiveChart = NULL;
InitOLE();
StartExcel();
SetExcelVisible(bVisible);
CreateWorkSheet();
//CreateXYChart();
}
CXLAutomation::~CXLAutomation()
{
//ReleaseExcel();
ReleaseDispatch();
OleUninitialize();
}
BOOL CXLAutomation::InitOLE()
{
DWORD dwOleVer;
dwOleVer = CoBuildVersion();
// check the OLE library version
if (rmm != HIWORD(dwOleVer))
{
MessageBox(NULL, _T("Incorrect version of OLE libraries."), "Failed", MB_OK | MB_ICONSTOP);
return FALSE;
}
// could also check for minor version, but this application is
// not sensitive to the minor version of OLE
// initialize OLE, fail application if we can't get OLE to init.
if (FAILED(OleInitialize(NULL)))
{
MessageBox(NULL, _T("Cannot initialize OLE."), "Failed", MB_OK | MB_ICONSTOP);
return FALSE;
}
return TRUE;
}
BOOL CXLAutomation::StartExcel()
{
CLSID clsExcelApp;
// if Excel is already running, return with current instance
if (m_pdispExcelApp != NULL)
return TRUE;
/* Obtain the CLSID that identifies EXCEL.APPLICATION
* This value is universally unique to Excel versions 5 and up, and
* is used by OLE to identify which server to start. We are obtaining
* the CLSID from the ProgID.
*/
if (FAILED(CLSIDFromProgID(L"Excel.Application", &clsExcelApp)))
{
MessageBox(NULL, _T("Cannot obtain CLSID from ProgID"), "Failed", MB_OK | MB_ICONSTOP);
return FALSE;
}
// start a new copy of Excel, grab the IDispatch interface
if (FAILED(CoCreateInstance(clsExcelApp, NULL, CLSCTX_LOCAL_SERVER, IID_IDispatch, (void**)&m_pdispExcelApp)))
{
MessageBox(NULL, _T("Cannot start an instance of Excel for Automation."), "Failed", MB_OK | MB_ICONSTOP);
return FALSE;
}
return TRUE;
}
/*******************************************************************
*
* INVOKE
*
*******************************************************************/
/*
* INVOKE
*
* Invokes a method or property. Takes the IDispatch object on which to invoke,
* and the name of the method or property as a String. Arguments, if any,
* must have been previously setup using the AddArgumentXxx() functions.
*
* Returns TRUE if the call succeeded. Returns FALSE if an error occurred.
* A messagebox will be displayed explaining the error unless the DISP_NOSHOWEXCEPTIONS
* flag is specified. Errors can be a result of unrecognized method or property
* names, bad argument names, invalid types, or runtime-exceptions defined
* by the recipient of the Invoke.
*
* The argument list is reset via ClearAllArgs() if the DISP_FREEARGS flag is
* specified. If not specified, it is up to the caller to call ClearAllArgs().
*
* The return value is placed in pvargReturn, which is allocated by the caller.
* If no return value is required, pass NULL. It is up to the caller to free
* the return value (ReleaseVariant()).
*
* This function calls IDispatch::GetIDsOfNames for every invoke. This is not
* very efficient if the same method or property is invoked multiple times, since
* the DISPIDs for a particular method or property will remain the same during
* the lifetime of an IDispatch object. Modifications could be made to this code
* to cache DISPIDs. If the target application is always the same, a similar
* modification is to statically browse and store the DISPIDs at compile-time, since
* a given application will return the same DISPIDs in different sessions.
* Eliminating the extra cross-process GetIDsOfNames call can result in a
* signficant time savings.
*/
BOOL CXLAutomation::ExlInvoke(IDispatch *pdisp, LPOLESTR szMember, VARIANTARG * pvargReturn,
WORD wInvokeAction, WORD wFlags)
{
HRESULT hr;
DISPPARAMS dispparams;
unsigned int uiArgErr;
EXCEPINFO excep;
// Get the IDs for the member and its arguments. GetIDsOfNames expects the
// member name as the first name, followed by argument names (if any).
m_alpszArgNames[0] = szMember;
hr = pdisp->GetIDsOfNames( IID_NULL, m_alpszArgNames,
1 + m_iNamedArgCount, LOCALE_SYSTEM_DEFAULT, m_aDispIds);
if (FAILED(hr))
{
if (!(wFlags & DISP_NOSHOWEXCEPTIONS))
ShowException(szMember, hr, NULL, 0);
return FALSE;
}
if (pvargReturn != NULL)
ClearVariant(pvargReturn);
// if doing a property put(ref), we need to adjust the first argument to have a
// named arg of DISPID_PROPERTYPUT.
if (wInvokeAction & (DISPATCH_PROPERTYPUT | DISPATCH_PROPERTYPUTREF))
{
m_iNamedArgCount = 1;
m_aDispIds[1] = DISPID_PROPERTYPUT;
pvargReturn = NULL;
}
dispparams.rgdispidNamedArgs = m_aDispIds + 1;
dispparams.rgvarg = m_aVargs;
dispparams.cArgs = m_iArgCount;
dispparams.cNamedArgs = m_iNamedArgCount;
excep.pfnDeferredFillIn = NULL;
hr = pdisp->Invoke(m_aDispIds[0], IID_NULL, LOCALE_SYSTEM_DEFAULT,
wInvokeAction, &dispparams, pvargReturn, &excep, &uiArgErr);
if (wFlags & DISP_FREEARGS)
ClearAllArgs();
if (FAILED(hr))
{
// display the exception information if appropriate:
if (!(wFlags & DISP_NOSHOWEXCEPTIONS))
ShowException(szMember, hr, &excep, uiArgErr);
// free exception structure information
SysFreeString(excep.bstrSource);
SysFreeString(excep.bstrDescription);
SysFreeString(excep.bstrHelpFile);
return FALSE;
}
return TRUE;
}
/*
* ClearVariant
*
* Zeros a variant structure without regard to current contents
*/
void CXLAutomation::ClearVariant(VARIANTARG *pvarg)
{
pvarg->vt = VT_EMPTY;
pvarg->wReserved1 = 0;
pvarg->wReserved2 = 0;
pvarg->wReserved3 = 0;
pvarg->lVal = 0;
}
/*
* ClearAllArgs
*
* Clears the existing contents of the arg array in preparation for
* a new invocation. Frees argument memory if so marked.
*/
void CXLAutomation::ClearAllArgs()
{
int i;
for (i = 0; i < m_iArgCount; i++)
{
if (m_awFlags[i] & DISPARG_NOFREEVARIANT)
// free the variant's contents based on type
ClearVariant(&m_aVargs[i]);
else
//ClearVariant(&m_aVargs[i]);
ReleaseVariant(&m_aVargs[i]);
}
m_iArgCount = 0;
m_iNamedArgCount = 0;
}
void CXLAutomation::ReleaseVariant(VARIANTARG *pvarg)
{
VARTYPE vt;
VARIANTARG *pvargArray;
long lLBound, lUBound, l;
vt = pvarg->vt & 0xfff; // mask off flags
// check if an array. If so, free its contents, then the array itself.
if (V_ISARRAY(pvarg))
{
// variant arrays are all this routine currently knows about. Since a
// variant can contain anything (even other arrays), call ourselves
// recursively.
if (vt == VT_VARIANT)
{
SafeArrayGetLBound(pvarg->parray, 1, &lLBound);
SafeArrayGetUBound(pvarg->parray, 1, &lUBound);
if (lUBound > lLBound)
{
lUBound -= lLBound;
SafeArrayAccessData(pvarg->parray, (void**)&pvargArray);
for (l = 0; l < lUBound; l++)
{
ReleaseVariant(pvargArray);
pvargArray++;
}
SafeArrayUnaccessData(pvarg->parray);
}
}
else
{
return ;//1; // non-variant type
// MessageBox(NULL, _T("ReleaseVariant: Array contains non-variant type"), "Failed", MB_OK | MB_ICONSTOP);
}
// Free the array itself.
SafeArrayDestroy(pvarg->parray);
}
else
{
switch (vt)
{
case VT_DISPATCH:
//(*(pvarg->pdispVal->lpVtbl->Release))(pvarg->pdispVal);
pvarg->pdispVal->Release();
break;
case VT_BSTR:
SysFreeString(pvarg->bstrVal);
break;
case VT_I2:
case VT_BOOL:
case VT_R8:
case VT_ERROR: // to avoid erroring on an error return from Excel
// no work for these types
break;
default:
return;// 2; //unknonw type
// MessageBox(NULL, _T("ReleaseVariant: Unknown type"), "Failed", MB_OK | MB_ICONSTOP);
break;
}
}
ClearVariant(pvarg);
return ;//0;
}
BOOL CXLAutomation::SetExcelVisible(BOOL bVisible)
{
if (m_pdispExcelApp == NULL)
return FALSE;
ClearAllArgs();
AddArgumentBool(NULL, 0, bVisible);
return ExlInvoke(m_pdispExcelApp, L"Visible", NULL, DISPATCH_PROPERTYPUT, DISP_FREEARGS);
}
BOOL CXLAutomation::SetExcelFileValidation(BOOL bFileValidation)
{
if (m_pdispExcelApp == NULL)
return FALSE;
ClearAllArgs();
AddArgumentBool(NULL, 0, bFileValidation);
return ExlInvoke(m_pdispExcelApp, L"FileValidation", NULL, DISPATCH_PROPERTYPUT, DISP_FREEARGS);
}
/*******************************************************************
*
* ARGUMENT CONSTRUCTOR FUNCTIONS
*
* Each function adds a single argument of a specific type to the list
* of arguments for the current invoke. If appropriate, memory may be
* allocated to represent the argument. This memory will be
* automatically freed the next time ClearAllArgs() is called unless
* the NOFREEVARIANT flag is specified for a particular argument. If
* NOFREEVARIANT is specified it is the responsibility of the caller
* to free the memory allocated for or contained within the argument.
*
* Arguments may be named. The name string must be a C-style string
* and it is owned by the caller. If dynamically allocated, the caller
* must free the name string.
*
*******************************************************************/
/*
* Common code used by all variant types for setting up an argument.
*/
void CXLAutomation::AddArgumentCommon(LPOLESTR lpszArgName, WORD wFlags, VARTYPE vt)
{
ClearVariant(&m_aVargs[m_iArgCount]);
m_aVargs[m_iArgCount].vt = vt;
m_awFlags[m_iArgCount] = wFlags;
if (lpszArgName != NULL)
{
m_alpszArgNames[m_iNamedArgCount + 1] = lpszArgName;
m_iNamedArgCount++;
}
}
BOOL CXLAutomation::AddArgumentDispatch(LPOLESTR lpszArgName, WORD wFlags, IDispatch * pdisp)
{
AddArgumentCommon(lpszArgName, wFlags, VT_DISPATCH);
m_aVargs[m_iArgCount++].pdispVal = pdisp;
return TRUE;
}
BOOL CXLAutomation::AddArgumentInt2(LPOLESTR lpszArgName, WORD wFlags, int i)
{
AddArgumentCommon(lpszArgName, wFlags, VT_I2);
m_aVargs[m_iArgCount++].iVal = i;
return TRUE;
}
BOOL CXLAutomation::AddArgumentBool(LPOLESTR lpszArgName, WORD wFlags, BOOL b)
{
AddArgumentCommon(lpszArgName, wFlags, VT_BOOL);
// Note the variant representation of True as -1
m_aVargs[m_iArgCount++].boolVal = b ? -1 : 0;
return TRUE;
}
BOOL CXLAutomation::AddArgumentDouble(LPOLESTR lpszArgName, WORD wFlags, double d)
{
AddArgumentCommon(lpszArgName, wFlags, VT_R8);
m_aVargs[m_iArgCount++].dblVal = d;
return TRUE;
}
BOOL CXLAutomation::ReleaseExcel()
{
if (m_pdispExcelApp == NULL)
return TRUE;
// Tell Excel to quit, since for automation simply releasing the IDispatch
// object isn't enough to get the server to shut down.
// Note that this code will hang if Excel tries to display any message boxes.
// This can occur if a document is in need of saving. The CreateChart() code
// always clears the dirty bit on the documents it creates, avoiding this problem.
ClearAllArgs();
ExlInvoke(m_pdispExcelApp, L"Quit", NULL, DISPATCH_METHOD, 0);
// Even though Excel has been told to Quit, we still need to release the
// OLE object to account for all memory.
ReleaseDispatch();
return TRUE;
}
//Create an empty workshet
BOOL CXLAutomation::CreateWorkSheet()
{
if(NULL == m_pdispExcelApp)
return FALSE;
BOOL fResult;
VARIANTARG varg1, varg2;
IDispatch *pdispRange = NULL;
IDispatch *pdispActiveSheet = NULL;
IDispatch *pdispActiveCell = NULL;
IDispatch *pdispCrt = NULL;
// Set wb = [application].Workbooks.Add(template := xlWorksheet)
ClearAllArgs();
if (!ExlInvoke(m_pdispExcelApp, L"Workbooks", &varg1, DISPATCH_PROPERTYGET, 0))
return FALSE;
ClearAllArgs();
AddArgumentInt2(L"Template", 0, xlWorksheet);
fResult = ExlInvoke(varg1.pdispVal, L"Add", &varg2, DISPATCH_METHOD, 0);
ReleaseVariant(&varg1);
if (!fResult)
return FALSE;
m_pdispWorkbook = varg2.pdispVal;
// Set ws = wb.Worksheets(1)
ClearAllArgs();
AddArgumentInt2(NULL, 0, 1);
if (!ExlInvoke(m_pdispWorkbook, L"Worksheets", &varg2, DISPATCH_PROPERTYGET, 0))
goto CreateWsBail;
m_pdispWorksheet = varg2.pdispVal;
fResult = TRUE;
CreateWsExit:
if (pdispRange != NULL)
pdispRange->Release();
if (pdispCrt != NULL)
pdispCrt->Release();
return fResult;
CreateWsBail:
fResult = FALSE;
goto CreateWsExit;
}
/*
* OLE and IDispatch use a BSTR as the representation of strings.
* This constructor automatically copies the passed-in C-style string
* into a BSTR. It is important to not set the NOFREEVARIANT flag
* for this function, otherwise the allocated BSTR copy will probably
* get lost and cause a memory leak.
*/
BOOL CXLAutomation::AddArgumentOLEString(LPOLESTR lpszArgName, WORD wFlags, LPOLESTR lpsz)
{
BSTR b;
b = SysAllocString(lpsz);
if (!b)
return FALSE;
AddArgumentCommon(lpszArgName, wFlags, VT_BSTR);
m_aVargs[m_iArgCount++].bstrVal = b;
return TRUE;
}
BOOL CXLAutomation::AddArgumentCString(LPOLESTR lpszArgName, WORD wFlags, CString szStr)
{
BSTR b;
b = szStr.AllocSysString();
if (!b)
return FALSE;
AddArgumentCommon(lpszArgName, wFlags, VT_BSTR);
m_aVargs[m_iArgCount++].bstrVal = b;
return TRUE;
}
/*
* Constructs an 1-dimensional array containing variant strings. The strings
* are copied from an incoming array of C-Strings.
*/
BOOL CXLAutomation::AddArgumentCStringArray(LPOLESTR lpszArgName, WORD wFlags, LPOLESTR *paszStrings, int iCount)
{
SAFEARRAY *psa;
SAFEARRAYBOUND saBound;
VARIANTARG *pvargBase;
VARIANTARG *pvarg;
int i, j;
saBound.lLbound = 0;
saBound.cElements = iCount;
psa = SafeArrayCreate(VT_VARIANT, 1, &saBound);
if (psa == NULL)
return FALSE;
SafeArrayAccessData(psa, (void**) &pvargBase);
pvarg = pvargBase;
for (i = 0; i < iCount; i++)
{
// copy each string in the list of strings
ClearVariant(pvarg);
pvarg->vt = VT_BSTR;
if ((pvarg->bstrVal = SysAllocString(*paszStrings++)) == NULL)
{
// memory failure: back out and free strings alloc'ed up to
// now, and then the array itself.
pvarg = pvargBase;
for (j = 0; j < i; j++)
{
SysFreeString(pvarg->bstrVal);
pvarg++;
}
SafeArrayDestroy(psa);
return FALSE;
}
pvarg++;
}
SafeArrayUnaccessData(psa);
// With all memory allocated, setup this argument
AddArgumentCommon(lpszArgName, wFlags, VT_VARIANT | VT_ARRAY);
m_aVargs[m_iArgCount++].parray = psa;
return TRUE;
}
//Clean up: release dipatches
void CXLAutomation::ReleaseDispatch()
{
if(NULL != m_pdispExcelApp)
{
m_pdispExcelApp->Release();
m_pdispExcelApp = NULL;
}
if(NULL != m_pdispWorksheet)
{
m_pdispWorksheet->Release();
m_pdispWorksheet = NULL;
}
if(NULL != m_pdispWorkbook)
{
m_pdispWorkbook->Release();
m_pdispWorkbook = NULL;
}
if(NULL != m_pdispActiveChart)
{
m_pdispActiveChart->Release();
m_pdispActiveChart = NULL;
}
}
void CXLAutomation::ShowException(LPOLESTR szMember, HRESULT hr, EXCEPINFO *pexcep, unsigned int uiArgErr)
{
TCHAR szBuf[512];
switch (GetScode(hr))
{
case DISP_E_UNKNOWNNAME:
wsprintf(szBuf, "%s: Unknown name or named argument.", szMember);
break;
case DISP_E_BADPARAMCOUNT:
wsprintf(szBuf, "%s: Incorrect number of arguments.", szMember);
break;
case DISP_E_EXCEPTION:
wsprintf(szBuf, "%s: Error %d: ", szMember, pexcep->wCode);
if (pexcep->bstrDescription != NULL)
lstrcat(szBuf, (char*)pexcep->bstrDescription);
else
lstrcat(szBuf, "<<No Description>>");
break;
case DISP_E_MEMBERNOTFOUND:
wsprintf(szBuf, "%s: method or property not found.", szMember);
break;
case DISP_E_OVERFLOW:
wsprintf(szBuf, "%s: Overflow while coercing argument values.", szMember);
break;
case DISP_E_NONAMEDARGS:
wsprintf(szBuf, "%s: Object implementation does not support named arguments.",
szMember);
break;
case DISP_E_UNKNOWNLCID:
wsprintf(szBuf, "%s: The locale ID is unknown.", szMember);
break;
case DISP_E_PARAMNOTOPTIONAL:
wsprintf(szBuf, "%s: Missing a required parameter.", szMember);
break;
case DISP_E_PARAMNOTFOUND:
wsprintf(szBuf, "%s: Argument not found, argument %d.", szMember, uiArgErr);
break;
case DISP_E_TYPEMISMATCH:
wsprintf(szBuf, "%s: Type mismatch, argument %d.", szMember, uiArgErr);
break;
default:
wsprintf(szBuf, "%s: Unknown error occured.", szMember);
break;
}
MessageBox(NULL, szBuf, "OLE Error", MB_OK | MB_ICONSTOP);
}
//Open Microsoft Excel file and switch to the firs available worksheet.
BOOL CXLAutomation::OpenExcelFile(CString szFileName)
{
//Leave if the file cannot be open
if(NULL == m_pdispExcelApp)
return FALSE;
if(szFileName.IsEmpty())
return FALSE;
VARIANTARG varg1, vargWorkbook, vargWorksheet;
SetExcelFileValidation(FALSE);
ClearAllArgs();
if (!ExlInvoke(m_pdispExcelApp, L"Workbooks", &varg1, DISPATCH_PROPERTYGET, 0))
return FALSE;
ClearAllArgs();
AddArgumentCString(L"Filename", 0, szFileName);
if (!ExlInvoke(varg1.pdispVal, L"Open", &vargWorkbook, DISPATCH_PROPERTYGET, DISP_FREEARGS))
return FALSE;
//Now let's get the first worksheet of this workbook
ClearAllArgs();
AddArgumentInt2(NULL, 0, 1);
if (!ExlInvoke(vargWorkbook.pdispVal, L"Worksheets", &vargWorksheet, DISPATCH_PROPERTYGET, DISP_FREEARGS))
return FALSE;
//Close the empty worksheet
ClearAllArgs();
//if (!ExlInvoke(m_pdispWorkbook, L"Close", NULL, DISPATCH_PROPERTYGET, DISP_FREEARGS))
// return FALSE;
SetExcelVisible(TRUE);
//Remember the newly open worksheet
m_pdispWorkbook = vargWorkbook.pdispVal;
m_pdispWorksheet = vargWorksheet.pdispVal;
ReleaseDispatch();
return TRUE;
}
At first glance it seems your pvarg may not be fully consistent / not providing what's expected. The clear function does no checks, so it will just write, regardless. Or try to.
But that may be a gross oversimplification without a full and thorough look at all involved code.
Adding this here as your project is closed on Freelancer and who knows, it may help you or another think in the right direction at a future date anyway.
I fixed the problem by not using that MS class library at all - it is way too complicated and prone to problems.
I found a simple code sample here and adapted it for my needs.
I have added code to open the Excel file as I need to, here is the source which I hope will help anyone with a similar problem.
Whoever down voted the question - please reconsider and vote it back up.
// Start server and get IDispatch...
IDispatch *pXlApp;
hr = CoCreateInstance(clsid, NULL, CLSCTX_LOCAL_SERVER, IID_IDispatch, (void **)&pXlApp);
if(FAILED(hr)) {
::MessageBox(NULL, "Excel not registered properly", "Error", 0x10010);
return -2;
}
// Make it visible (i.e. app.visible = 1)
{
VARIANT x;
x.vt = VT_I4;
x.lVal = 1;
AutoWrap(DISPATCH_PROPERTYPUT, NULL, pXlApp, L"Visible", 1, x);
}
// Get Workbooks collection
IDispatch *pXlBooks;
{
VARIANT result;
VariantInit(&result);
AutoWrap(DISPATCH_PROPERTYGET, &result, pXlApp, L"Workbooks", 0);
pXlBooks = result.pdispVal;
}
// Open Excel file
{
VARIANT result;
VariantInit(&result);
VARIANT fname;
fname.vt = VT_BSTR;
std::string str = GetAppPath() + "\\test.xlsm";
fname.bstrVal=::SysAllocString(CA2W (str.c_str ()));
AutoWrap(DISPATCH_METHOD, &result, pXlBooks, L"Open", 1, fname);
}
// Release references...
pXlBooks->Release();
pXlApp->Release();
I wrote a solution that involved OpenProcess, EnumProcessModules, GetModuleInformation and GetModuleBaseName, but apparently EnumProcessModules and GetModuleBaseName do not exist in Windows CE! What alternative is there?
I found a way to do this with CreateToolhelp32Snapshot, Module32First, Module32Next, Process32First and Process32Next. First you have to get a list of modules, then search through the list of modules to find the desired address.
#include <Tlhelp32.h>
struct MyModuleInfo
{
BYTE* Base;
HMODULE Handle;
DWORD Size;
enum { MaxNameLen = 36 };
TCHAR Name[MaxNameLen];
};
bool GetModuleList(vector<MyModuleInfo>& moduleList)
{
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS | TH32CS_SNAPMODULE | TH32CS_GETALLMODS, 0);
if (hSnapshot == INVALID_HANDLE_VALUE)
return false;
MODULEENTRY32 moduleInfo;
moduleInfo.dwSize = sizeof(moduleInfo);
if (Module32First(hSnapshot, &moduleInfo)) do {
MyModuleInfo myInfo;
myInfo.Handle = moduleInfo.hModule;
myInfo.Base = moduleInfo.modBaseAddr;
myInfo.Size = moduleInfo.modBaseSize;
memcpy(myInfo.Name, moduleInfo.szModule, min(sizeof(myInfo.Name), sizeof(moduleInfo.szModule)));
myInfo.Name[myInfo.MaxNameLen-1] = '\0';
moduleList.push_back(myInfo);
} while (Module32Next(hSnapshot, &moduleInfo));
// The module list obtained above only contains DLLs! To get the EXE files
// also, we must call Process32First and Process32Next in a loop.
PROCESSENTRY32 processInfo;
processInfo.dwSize = sizeof(processInfo);
if (Process32First(hSnapshot, &processInfo)) do {
MyModuleInfo myInfo;
myInfo.Handle = NULL; // No handle given
myInfo.Base = (BYTE*)processInfo.th32MemoryBase;
myInfo.Size = 0x800000; // No size provided! Allow max 8 MB
memcpy(myInfo.Name, processInfo.szExeFile, min(sizeof(myInfo.Name), sizeof(processInfo.szExeFile)));
myInfo.Name[myInfo.MaxNameLen-1] = '\0';
moduleList.push_back(myInfo);
} while(Process32Next(hSnapshot, &processInfo));
// Debug output
for (int i = 0; i < (int)moduleList.size(); i++) {
MyModuleInfo& m = moduleList[i];
TRACE(_T("%-30s: 0x%08x - 0x%08x\n"), m.Name, (DWORD)m.Base, (DWORD)m.Base + m.Size);
}
CloseToolhelp32Snapshot(hSnapshot);
return true;
}
const MyModuleInfo* GetModuleForAddress(vector<MyModuleInfo>& moduleList, void* address)
{
for (int m = 0; m < (int)moduleList.size(); m++) {
const MyModuleInfo& mInfo = moduleList[m];
if (address >= mInfo.Base && address < mInfo.Base + mInfo.Size)
return &mInfo;
}
return NULL;
}