IShellDispatch NameSpace-call fails, don't know why - c++

First time working with COM from C++, been using C# and Delphi before.
So here's the code
HRESULT Result;
Result = CoInitialize(nullptr);
if (!SUCCEEDED(Result))
return Result;
IShellDispatch* API;
Result = CoCreateInstance(CLSID_Shell, nullptr, CLSCTX_INPROC_SERVER, IID_IShellDispatch, (void **)&API);
if ((!SUCCEEDED(Result)) || (nullptr == API))
goto Cleanup;
VARIANT Source1, Source2, Destination, Options;
VariantInit(&Destination);
Destination.vt = VT_BSTR;
Destination.bstrVal = SysAllocString(OutFile.c_str());
Folder* PDestination;
Result = API->NameSpace(Destination, &PDestination);
if (!SUCCEEDED(Result))
goto Cleanup;
VariantInit(&Source1);
VariantInit(&Source2);
Source1.vt = VT_BSTR;
Source2.vt = VT_BSTR;
Source1.bstrVal = SysAllocString(InFile1.c_str());
Source2.bstrVal = SysAllocString(InFile2.c_str());
VariantInit(&Options);
Options.vt = VT_I4;
Options.lVal = FOF_NO_UI;
PDestination->CopyHere(Source1, Options);
Sleep(1000);
PDestination->CopyHere(Source2, Options);
Sleep(1000);
PDestination->Release();
API->Release();
Cleanup:
CoUninitialize();
return Result;
OutFile, InFile1 and InFile2 are of type wstring as the program is (mostly) C++. I've managed to create an empty ZIP file, that's not the issue. The issue is that I get an exception claiming that PDestination is nullptr.
If it helps, the variants themselves seems to work. Inspecting them in the debugger yields the paths I provided. I've noticed though how the NameSpace-call returns an S_FALSE. Does anyone know what I can do about that?
Using a library doesn't exactly work, it's corporate code and there's some truly draconian rules when it comes to linking external code so using the Windows API is the easiest way.

Related

Why does Avira consider "CoCreateInstance()" function as a malware?

I have created c++ console application using visual studio 2017 which is extract .zip file. But, when I run application.exe file, Avira antivirus detects my exe as a malware.
I found CoCreateInstance(CLSID_Shell, NULL, CLSCTX_INPROC_SERVER, IID_IShellDispatch, (void **)&pISD) function where actual Avira create problem.
Why does Avira consider CoCreateInstance(CLSID_Shell, NULL, CLSCTX_INPROC_SERVER, IID_IShellDispatch, (void **)&pISD) function as a malware?
My Function:
bool Utils::Unzip2Folder(BSTR lpZipFile, BSTR lpFolder)
{
IShellDispatch *pISD;
Folder *pZippedFile = 0L;
Folder *pDestination = 0L;
long FilesCount = 0;
IDispatch* pItem = 0L;
FolderItems *pFilesInside = 0L;
VARIANT Options, OutFolder, InZipFile, Item;
CoInitialize(NULL);
__try {
if (CoCreateInstance(CLSID_Shell, NULL, CLSCTX_INPROC_SERVER, IID_IShellDispatch, (void **)&pISD) != S_OK)
return 1;
InZipFile.vt = VT_BSTR;
InZipFile.bstrVal = lpZipFile;
pISD->NameSpace(InZipFile, &pZippedFile);
if (!pZippedFile)
{
pISD->Release();
return 1;
}
OutFolder.vt = VT_BSTR;
OutFolder.bstrVal = lpFolder;
pISD->NameSpace(OutFolder, &pDestination);
if (!pDestination)
{
pZippedFile->Release();
pISD->Release();
return 1;
}
pZippedFile->Items(&pFilesInside);
if (!pFilesInside)
{
pDestination->Release();
pZippedFile->Release();
pISD->Release();
return 1;
}
pFilesInside->get_Count(&FilesCount);
if (FilesCount < 1)
{
pFilesInside->Release();
pDestination->Release();
pZippedFile->Release();
pISD->Release();
return 0;
}
pFilesInside->QueryInterface(IID_IDispatch, (void**)&pItem);
Item.vt = VT_DISPATCH;
Item.pdispVal = pItem;
Options.vt = VT_I4;
Options.lVal = 1024 | 512 | 16 | 4;//http://msdn.microsoft.com/en-us/library/bb787866(VS.85).aspx
bool retval = pDestination->CopyHere(Item, Options) == S_OK;
pItem->Release(); pItem = 0L;
pFilesInside->Release(); pFilesInside = 0L;
pDestination->Release(); pDestination = 0L;
pZippedFile->Release(); pZippedFile = 0L;
pISD->Release(); pISD = 0L;
return retval;
}
__finally
{
CoUninitialize();
}
}
There is no real good answer. Why AV systems detect some files as false positives.
Most of this false positives are based on some heuristic.
Small programs seam to be more problematic than larger ones.
Programs with less dependencies to other DLLs seams to be more problematic than complex EXE with dependencies.
Some mixes of API functions seem to be more problematic tahn other. Usage of more complex API functions and UI seem to be less problematic.
Signed executable are less risky for such heuristic traos in AV systems.
A lot of programs (and signatures of companies) are internally white listed. Your program is unknown.
This function is surely not the only API function you use. Check Depends and you find more!
Just add your exe path to the exclusion list of Avira... in the development phase of small tools I had to do this more than once.
Final tipp: Ask Avira and send them you file. They may change there heuristic pattern match so this is no longer a false positive.
I had this effects with Avira serval times.

Getting another process command line in Windows

I am trying to get another process' command-line parameters (on WinXP 32bit).
I do the following:
hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ | PROCESS_TERMINATE, FALSE, ProcList.proc_id_as_numbers[i]);
BytesNeeded = sizeof(PROCESS_BASIC_INFORMATION);
ZwQueryInformationProcess(hProcess, ProcessBasicInformation, UserPool, sizeof(PROCESS_BASIC_INFORMATION), &BytesNeeded);
pbi = (PPROCESS_BASIC_INFORMATION)UserPool;
BytesNeeded = sizeof(PEB);
res = ZwReadVirtualMemory(hProcess, pbi->PebBaseAddress, UserPool, sizeof(PEB), &BytesNeeded);
/* zero value returned */
peb = (PPEB)UserPool;
BytesNeeded = sizeof(RTL_USER_PROCESS_PARAMETERS);
res = ZwReadVirtualMemory(hProcess, peb->ProcessParameters, UserPool, sizeof(RTL_USER_PROCESS_PARAMETERS), &BytesNeeded);
ProcParam = (PRTL_USER_PROCESS_PARAMETERS)UserPool;
After the first call, pbi.UniqueProcessID is correct.
But, after calling ZwReadVirtualMemory(), I get the command-line for my process, not the requested one.
I also used ReadProcessMemory() & NtQueryInformationProcess(), but get the same result.
Can anybody help?
On this forum thread, it is said that this code works. Unfortunately, I do not have access to post on that forum to ask them.
It looks like ZwReadVirtualMemory is called only once. That is not enough. It has to be called for each level of pointer indirection. In other words when you retrieve a pointer it points to other process' address space. You cannot read it directly. You have to call ZwReadVirtualMemory again. For the case of those data structures ZwReadVirtualMemory has to be called 3 times: once to read PEB (that is what the code above does), once to read RTL_USER_PROCESS_PARAMETERS and once to read UNICODE_STRING's buffer.
The following code fragment worked for me (error handling omitted for clarity and I used documented ReadProcessMemory API instead of ZwReadVirtualMemory):
LONG status = NtQueryInformationProcess(hProcess,
0,
pinfo,
sizeof(PVOID)*6,
NULL);
PPEB ppeb = (PPEB)((PVOID*)pinfo)[1];
PPEB ppebCopy = (PPEB)malloc(sizeof(PEB));
BOOL result = ReadProcessMemory(hProcess,
ppeb,
ppebCopy,
sizeof(PEB),
NULL);
PRTL_USER_PROCESS_PARAMETERS pRtlProcParam = ppebCopy->ProcessParameters;
PRTL_USER_PROCESS_PARAMETERS pRtlProcParamCopy =
(PRTL_USER_PROCESS_PARAMETERS)malloc(sizeof(RTL_USER_PROCESS_PARAMETERS));
result = ReadProcessMemory(hProcess,
pRtlProcParam,
pRtlProcParamCopy,
sizeof(RTL_USER_PROCESS_PARAMETERS),
NULL);
PWSTR wBuffer = pRtlProcParamCopy->CommandLine.Buffer;
USHORT len = pRtlProcParamCopy->CommandLine.Length;
PWSTR wBufferCopy = (PWSTR)malloc(len);
result = ReadProcessMemory(hProcess,
wBuffer,
wBufferCopy, // command line goes here
len,
NULL);
Why we see see the command line of our own process? That is because processes are laid out in a similar way. Command line and PEB-related structures are likely to have the same addresses. So if you missed ReadProcessMemory you end up exactly with local process' command line.
I was trying to do this same thing using mingw & Qt. I ran into a problem with "undefined reference to CLSID_WbemLocator". After some research, it seems that the version of libwbemuuid.a which was included with my version of mingw only defined IID_IWbemLocator but not CLSID_WbemLocator.
I found that manually defining CLSID_WbemLocator works (although its probably not the "correct" way of doing things).
The final working code:
#include <QDebug>
#include <QString>
#include <QDir>
#include <QProcess>
#define _WIN32_DCOM
#include <windows.h>
#include "TlHelp32.h"
#include <stdio.h>
#include <tchar.h>
#include <wbemidl.h>
#include <comutil.h>
const GUID CLSID_WbemLocator = { 0x4590F811,0x1D3A,0x11D0,{ 0x89,0x1F,0x00,0xAA,0x00,0x4B,0x2E,0x24 } }; //for some reason CLSID_WbemLocator isn't declared in libwbemuuid.a (although it probably should be).
int getProcessInfo(DWORD pid, QString *commandLine, QString *executable)
{
HRESULT hr = 0;
IWbemLocator *WbemLocator = NULL;
IWbemServices *WbemServices = NULL;
IEnumWbemClassObject *EnumWbem = NULL;
//initializate the Windows security
hr = CoInitializeEx(0, COINIT_MULTITHREADED);
hr = CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_DEFAULT, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE, NULL);
hr = CoCreateInstance(CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID *) &WbemLocator);
//connect to the WMI
hr = WbemLocator->ConnectServer(L"ROOT\\CIMV2", NULL, NULL, NULL, 0, NULL, NULL, &WbemServices);
//Run the WQL Query
hr = WbemServices->ExecQuery(L"WQL", L"SELECT ProcessId,CommandLine,ExecutablePath FROM Win32_Process", WBEM_FLAG_FORWARD_ONLY, NULL, &EnumWbem);
qDebug() << "Got here." << (void*)hr;
// Iterate over the enumerator
if (EnumWbem != NULL) {
IWbemClassObject *result = NULL;
ULONG returnedCount = 0;
while((hr = EnumWbem->Next(WBEM_INFINITE, 1, &result, &returnedCount)) == S_OK) {
VARIANT ProcessId;
VARIANT CommandLine;
VARIANT ExecutablePath;
// access the properties
hr = result->Get(L"ProcessId", 0, &ProcessId, 0, 0);
hr = result->Get(L"CommandLine", 0, &CommandLine, 0, 0);
hr = result->Get(L"ExecutablePath", 0, &ExecutablePath, 0, 0);
if (ProcessId.uintVal == pid)
{
*commandLine = QString::fromUtf16((ushort*)(long)CommandLine.bstrVal);// + sizeof(int)); //bstrs have their length as an integer.
*executable = QString::fromUtf16((ushort*)(long)ExecutablePath.bstrVal);// + sizeof(int)); //bstrs have their length as an integer.
qDebug() << *commandLine << *executable;
}
result->Release();
}
}
// Release the resources
EnumWbem->Release();
WbemServices->Release();
WbemLocator->Release();
CoUninitialize();
//getchar();
return(0);
}
and in my Qt project file (.pro) I link to the following libraries:
LIBS += -lole32 -lwbemuuid
Duplicate of How to query a running process for it's parameters list? (windows, C++) , so I'll just copy my answer from there over here:
You can't reliably get that information. There are various tricks to try and retrieve it, but there's no guarantee that the target process hasn't already mangled that section of memory. Raymond Chen discussed this awhile back on The Old New Thing.
You need to be more disciplined with checking return codes. It may be that any of your ZwReadVirtualMemory calls yield an error code which points you into the right direction.
In particular, the ProcList.proc_id_as_numbers[i] part suggests that you're executing this code in a loop. Chances are that the procPeb.ProcessParameters structure is still filled with the values of an earlier loop iteration - and since the ZwReadVirtualMemory call fails on your target process, you get to see the command line of whatever process was previously queried.
You don't have to read the VM of the target process to do this. Just make sure you have the correct Process ID for the target process.
Once you have the process handle via OpenProcess, you can then use NtQueryInformationProcess to get detailed process info. Use the ProcessBasicInformation option to get the PEB of the process - this contains another structure pointer RTL_USER_PROCESS_PARAMETERS, through which you can get the command line.

error LNK2001: unresolved external symbol _IID_IDirectDraw2

I work with piece of legacy code which uses direct draw and I'm in rather embarrassing situation.
Not long ago I've updated my system and had to adapt to the new situation (loading ddraw.dll) and everything worked fine.
Today I explored another legacy solution which also uses classes (files) I've changed, but I'm stuck with above mentioned linking error. I've checked and compared project properties and they seam fine.
This is code for directX initialization, "troublesome" code is bold.
typedef int (__stdcall *DirectDrawCreateFunc)(GUID FAR* a ,LPDIRECTDRAW FAR* b, IUnknown FAR* c);
/* init_directx:
* Low-level DirectDraw initialization routine.
*/
int CDCUtils::init_directx(HWND allegro_wnd)
{
LPDIRECTDRAW directdraw1;
HRESULT hr;
LPVOID temp;
HINSTANCE ddraw = LoadLibrary("%WINDIR%\system32\ddraw.dll");
if(ddraw== NULL)
{
return -1;
}
_ddrawLib =ddraw;
DirectDrawCreateFunc ddFunc = (DirectDrawCreateFunc)GetProcAddress(ddraw,"DirectDrawCreate");
if(ddFunc)
{
/* first we have to set up the DirectDraw1 interface... */
hr = ddFunc(NULL, &directdraw1, NULL);
if (FAILED(hr))
return -1;
}
///* first we have to set up the DirectDraw1 interface... */
//hr = DirectDrawCreate(NULL, &directdraw1, NULL);
//if (FAILED(hr))
// return -1;
//...then query the DirectDraw2 interface
//This is the only place where IID_IDirectDraw2 is mentioned in entire solution
hr=directdraw1->QueryInterface(IID_IDirectDraw2, &temp);
if (FAILED(hr))
return -1;
_directdraw = (LPDIRECTDRAW2)temp;
directdraw1->Release();
/* set the default cooperation level */
hr = IDirectDraw2_SetCooperativeLevel(_directdraw, allegro_wnd, DDSCL_NORMAL);
if (FAILED(hr))
return -1;
/* get capabilities */
_ddcaps.dwSize = sizeof(_ddcaps);
hr = IDirectDraw2_GetCaps(_directdraw, &_ddcaps, NULL);
if (FAILED(hr)) {
TRACE("Can't get driver caps\n");
return -1;
}
_dxHwnd=allegro_wnd;
return 0;
}
Any ideas?
Why it works in one solution and not in this one?
Oh linker I loathe thee.
Did you add dxguid.lib to your project's linker inputs?
Make sure you add Dxguid.lib in your project.

Get file type from windows registry in c++

I am trying to display file type of given filename (based on extension) using AssocQueryKey() API function.
The problem is thar return wrong HKEY value sometimes. For example the following function works correctly on win 7 ultimate x64, but fails for some extensions like ".mp3" on my win xp x86 machine (other extensions works though).
Even when "succeeded" and returns S_OK, GetLastError() is 1008 ALWAYS after AssocQueryKey() call:
// Return STL string representation of file type from windows registry
stlstring GetFileTypeFromRegistry(const stlstring& m_filename)
{
CRegKey reg;
HKEY key = {0};
stlstring s;
//Get file extension
LPCTSTR fExt = PathFindExtension(m_filename.c_str());
if(AssocQueryKey(NULL, ASSOCKEY_CLASS, fExt, TEXT(""), &key) != S_OK)
DisplayError(_T("AssocQueryKey != S_OK"), GetLastError());
else
DisplayError(_T("AssocQueryKey == S_OK"), GetLastError());
if(reg.Open ( key, NULL, KEY_QUERY_VALUE) != ERROR_SUCCESS){
reg.Close();
DisplayError((LPTSTR)fExt);
return s;
}
//DWORD out = 0;
/*WCHAR *h = new WCHAR[1024];
ZeroMemory(h, sizeof(h));
AssocQueryStringByKey(0, ASSOCSTR_EXECUTABLE, HKEY_CLASSES_ROOT, NULL, h, &out);
//MessageBox(0,_T("gbtbb"),h,MB_OK);
delete[] h;*/
ULONG m_sz = 256;
//if( reg.QueryStringValue(NULL, NULL, &m_sz) == ERROR_SUCCESS){
TCHAR *m_regstring = new TCHAR[m_sz + 1];
if(reg.QueryStringValue(NULL, m_regstring, &m_sz) == ERROR_SUCCESS){
//DisplayError(_T(""));
s += m_regstring;
/*delete[] m_regstring; m_regstring = NULL;
reg.Close();
return s;*/
} else {
DisplayError(_T("CRegKey::QueryStringValue()"), GetLastError());
}
s += m_regstring;
delete[] m_regstring; m_regstring = NULL;
reg.Close();
return s;
/*}
reg.Close();
return s;*/
}
Any ideas on this ?? This function is from a DLL which is loaded by windows explorer, implementing IQueryInfo::GetInfoTip() if that matters.
You shouldn't use GetLastError for functions that return the error code directly. The MSDN page for AssocQueryKey says "Returns S_OK if successful, or a COM error value otherwise.", which means you already get the error code in the return value.
If you just want to get the file type information, there's a much simpler solution: SHGetFileInfo. It's really simple to use, like this:
SHFILEINFO shfi;
SHGetFileInfo(filename, 0, &shfi, sizeof(shfi), SHGFI_TYPENAME | SHGFI_USEFILEATTRIBUTES);
// shfi.szTypeName now contains the file type string of the given filename

Excel OpenText method

I keep getting the ambiguous error code of 0x800A03EC.
I've been searching quite a bit to see if I could find a specific reason for the error but unfortunately that code seems to cover a multitude of possible errors. I will copy and paste the code that seems to be giving me problems and hopefully someone will be able to provide me with some feedback on how I might solve the problem. I am using a method called AutoWrap that I came across in this kb21686 article.I'll add that method here:
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);
MessageBox(NULL,_T("IDispatch error"),_T("LError"),MB_OK | MB_ICONEXCLAMATION);
_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_s(buf, "IDispatch::GetIDsOfNames(\"%s\") failed w/err 0x%08lx", szName, hr);
MessageBox(NULL, CString(buf), _T("AutoWrap()"), MB_OK | MB_ICONEXCLAMATION);
_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!
hr = pDisp->Invoke(dispID, IID_NULL, LOCALE_SYSTEM_DEFAULT, autoType, &dp, pvResult, NULL, NULL);
if(FAILED(hr)) {
sprintf_s(buf, "IDispatch::Invoke(\"%s\"=%08lx) failed w/err 0x%08lx", szName, dispID, hr);
MessageBox(NULL, CString(buf), _T("AutoWrap()"), MB_OK | MB_ICONEXCLAMATION);
_exit(0);
return hr;
}
// End variable-argument section...
va_end(marker);
delete [] pArgs;
return hr;
}
Everything works fine up until I make this call:
AutoWrap(DISPATCH_PROPERTYGET, &result, pXlBooks, L"OpenText",18,param1,vtMissing,vtMissing,paramOpt,paramOpt,
vtMissing,vtMissing,vtMissing,paramTrue,vtMissing,vtMissing,vtMissing,vtMissing,vtMissing,vtMissing,vtMissing
,vtMissing,vtMissing);
The parameters passed to the function are initialized as:
VARIANT param1,paramOpt,paramFalse,paramTrue;
param1.vt = VT_BSTR;
paramOpt.vt = VT_I2;
paramOpt.iVal = 1;
paramFalse.vt = VT_BOOL;
paramFalse.boolVal = 0;
paramTrue.vt = VT_BOOL;
paramTrue.boolVal = 1;
//param1.bstrVal = ::SysAllocString(L"C:\\Documents and Settings\\donaldc\\My Documents\\DepositSlip.xls");
param1.bstrVal = ::SysAllocString(L"C:\\logs\\TestOut.txt");
If I uncomment the commented out param1 and make a call to Open and pass it that version of param1 everything works wonderfully. Unfortunately when Invoke is called on the OpenText method I get the 0x800A03EC error code. 90% of what I find when searching is performing automation using interop in C# and the other 10% is doing the same thing in VB and while the C# examples are helpful they don't help to explain the parameters being passed when using C++ very well. I feel like it's all a problem with parameters but I'm having difficulty in figuring out exactly what the problem with them is.
Thanks in advance for any help you can offer and pelase let me know if I need to post more code.
From the KB article you linked to:
One caveat is that if you pass
multiple parameters, they need to be
passed in reverse-order.
From MSDN, the parameters to OpenText are:
expression.OpenText(Filename, Origin, StartRow, DataType,
TextQualifier, ConsecutiveDelimiter, Tab, Semicolon, Comma,
Space, Other, OtherChar, FieldInfo, TextVisualLayout, DecimalSeparator,
ThousandsSeparator, TrailingMinusNumbers, Local)
So if param1 holds your filename then you are currently trying to pass that as the Local parameter and you aren't passing anything to the Filename parameter which is required
Even though this question was put ages ago, I want to answer it.
I struggled much through the day with this, but got it done eventually.
Code 0x800a03ec is ambiguous, it means essentially, (NO OFFENSE MEANT!), you dumbhead! you did something pretty stupid, try finding it out for yourself. Close to the old "syntax error" without further elucidation.
So, there is no single meaning for code 0x800a03ec and, googling around, you can see it occurs when using zero based addressing in ranges, and many other gotcha situations.
The gotcha in this case is that you have to pass the parameters to a DISPATCH_METHOD call IN REVERSE. Obviously, with a single parameter this will not lead to trouble, and many dispatch calls can do with a single parameter.
So, when I want to open a workbook with just the filename, everything is OK.
AutoWrap(DISPATCH_METHOD, &Result, pExcelWorkbooks, "Open", 1, fn);
But e.g. the following code does not work:
_variant_t fn("MyExcelBook.xlsx"), updatelinks(0), readonly(true);
VARIANT Result;
hr = Autowrap(DISPATCH_METHOD, &Result, pExcelWorkbooks, "Open", 3, fn, updatelinks, readonly);
It produces 0x800a03ec and won't load the workbook.
To amend this without rewriting all your calls, the AutoWrap function should be extended as follows:
// Allocate memory for arguments..
VARIANT * pArgs = new VARIANT[cArgs + 1];
// Extract arguments..
if (autoType & DISPATCH_METHOD)
{ // reverse (variable) DISPATCH parameters after cArgs
for (int i = 1; i <= cArgs; i++)
pArgs[cArgs-i] = va_arg(marker, VARIANT);
}
else
{
for (int i = 0; i < cArgs; i++)
pArgs[i] = va_arg(marker, VARIANT);
}
(only relevant part shown, see earlier post for the whole method).
So now I call the C++ version of workbooks.open()
_variant_t fn("MyExcelBook.xlsx"), updatelinks(0), readonly(true), optional(DISP_E_PARAMNOTFOUND, VT_ERROR);
VARIANT Result; readonly,
hr = Autowrap(DISPATCH_METHOD, &Result, pExcelWorkbooks, "Open", 7, fn, updatelinks,
optional, optional, optional, readonly);
// copy the dispatch pointer to the workbook pointer
if (Result.vt == VT_DISPATCH)
{
pExcelWorkbook = Result.pdispVal;
// save the workbook pointer to close it later if needed
if (pExcelWorkbook)
IDworkBooks.push_back(pExcelWorkbook);
}
As you can see, you do not have to fill in the tail options you are not going to use anyway, as in some VB code and C#.
Happy coding,
Jan
PS, I see the reverse thing was noted before (see above).. I couldn't figure out where I had seen it until now...