我正在努力解决间歇性的C ++应用程序崩溃问题。 我不是C ++程序员,但我的任务是解决这个问题,所以非常希望你能帮助我。
通常应用程序运行良好,然后偶尔崩溃,但有例外。
从运行exe进入调试时,突出显示的代码行似乎有问题 - 请看第一个屏幕截图。
我在第二个屏幕截图中扩展了一些本地人。
这行代码调用函数'ClearVariant',该函数的代码如下:
/*
* 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;
}
整个cpp文件位于帖子的末尾。 OpenExcelFile是导致此问题的功能 - 正如您可以从屏幕截图中的调用堆栈中那样。
// 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;
}
答案 0 :(得分:2)
乍一看,似乎你的pvarg可能不完全一致/没有提供预期的结果。 clear函数不进行检查,因此无论如何都会写入。或尝试。
但如果没有对所有相关代码进行全面彻底的审视,那么这可能是一种严重的过度简化。
在此处添加此项,因为您的项目在Freelancer上已关闭,谁知道,无论如何,它可能会帮助您或其他人在未来的日期思考正确的方向。
答案 1 :(得分:1)
我通过根本不使用那个MS类库来修复问题 - 它太复杂了,容易出问题。
我找到了一个简单的代码示例here,并根据我的需要进行了调整。 我已经添加了代码来打开我需要的Excel文件,这里是我希望能帮助有类似问题的人的源代码。
无论是谁投票支持这个问题 - 请重新考虑并重新投票。
// 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();