Problems understanding and using double pointers - c++

I am not sure how to use double pointers.
The function i need to use looks as following:
HRESULT GetBuffer(
[out] IMediaSample **ppBuffer,
[in] REFERENCE_TIME *pStartTime,
[in] REFERENCE_TIME *pEndTime,
[in] DWORD dwFlags
);
Documentation says:
ppBuffer [out]
Receives a pointer to the buffer's IMediaSample interface. The caller must release the interface.
This is what i tried using it:
HRESULT MCMyOutputPin::Deliver(IMediaSample* sample)
{
HRESULT hr = NO_ERROR;
myLogger->LogDebug("In Outputpin Deliver", L"D:\\TEMP\\yc.log");
if (sample->GetActualDataLength() > 0)
{
IMediaSample **outsample;
m_pAllocator->GetBuffer(outsample, NULL, NULL, NULL); //Access violation here
BYTE** sampleBuffer;
BYTE** newBuffer;
sample->GetPointer(sampleBuffer);
(*outsample)->GetPointer(newBuffer);
memcpy((void *)newBuffer, (void *)sampleBuffer, sizeof(**sampleBuffer));
m_pInputPin->Receive(*outsample);
sample->AddRef();
}
return hr;
//Forward to filter
}
Which gives me an:
Access violation reading location 0xFFFFFFFFFFFFFFFF.
Then i tried using the address operator:
hr = m_pAllocator->GetBuffer(&outsample, NULL, NULL, NULL); //outsample is set to NULL
BYTE* sampleBuffer = NULL;
BYTE* newBuffer = NULL;
sample->GetPointer(&sampleBuffer);
outsample->GetPointer(&newBuffer);
memcpy((void *)newBuffer, (void *)sampleBuffer, sizeof(*sampleBuffer));
m_pInputPin->Receive(outsample);
This sets outsample to NULL.
So what is the correct syntax to handle double pointers?

My first, high-level comment, is that you are not checking the return values of the functions that you call. It's a mistake to neglect error checking. Your first step is to add the necessary error checking.
HRESULT GetBuffer(
[out] IMediaSample **ppBuffer,
[in] REFERENCE_TIME *pStartTime,
[in] REFERENCE_TIME *pEndTime,
[in] DWORD dwFlags
);
The first parameter is used to return a IMediaSample* to the caller. You need to declare a variable of type IMediaSample*, and pass its address:
IMediaSample* sample;
....
hr = m_pAllocator->GetBuffer(&outsample, ...);
// check hr
....
So, outsample is of type IMediaSample*. When you take its address, with &outsample, you now have something of type IMediaSample**, which is what you need.
Remember that when working with interfaces, you always work with a pointer to the interface.
You've made the same mistake with the BYTE** parameters. Again, declare variables of type BYTE*, and pass the address of these variables to the functions that you call.
BYTE* sampleBuffer;
BYTE* newBuffer;
....
hr = sample->GetPointer(&sampleBuffer);
// check hr
hr = outsample->GetPointer(newBuffer);
// check hr
Using sizeof(**sampleBuffer) in your call to memcpy is wrong. In your code, where sampleBuffer is wrongly declared as BYTE**, sizeof(**sampleBuffer) is just sizeof(BYTE) which is always 1.
In fact you can conclude that any use of sizeof is incorrect here because sizeof is evaluated at compile time. You need to find the actual size of the dynamic buffer at runtime using whatever functionality these interfaces provide.
The call to sample->AddRef() looks a little suspect. I don't see any evidence that anything has taken a new reference to the interface.

IMediaSample **outsample;
m_pAllocator->GetBuffer(outsample, NULL, NULL, NULL);
You're passing the function the value of outsample, which is a garbage value since it doesn't point to anything. You want:
IMediaSample *outsample;
m_pAllocator->GetBuffer(&outsample, NULL, NULL, NULL);
This gives the function the address of outsample so that it can stuff the pointer it wants to return in it.

I try to simulate the function as test, which using int** as output . the problem here is the function is suppose *output is is valid, so in the first way ,you have to make sure that, you might want to do something as this
sample = new IMediaSample[1]
this is my sample code, hope it helps
#include <iostream>
using namespace std;
void test(int** output ,int* in)
{
*output = new int[1];
output[0][0] = in[0];
}
int main()
{
int a[] ={ 2 };
int* out1;
test(&out1 ,a);
cout << out1[0] << endl;
int** out2;
out2 = new int*[1];
test(out2 ,a);
cout << out2[0][0] << endl;
return 0;
}

Related

Getting the value of OpenSavePidlMRU in the registry

I'm trying to get the last opened directory by an open file dialog, and it seems that we can get it by first retrieving the key's name that contains the path using the first BYTE of the MRUListEx key, then the path can be obtained by reading the value of this key's name.
MRUListEx key can be find at: HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\ComDlg32\LastVisitedPidlMRU.
The problem is that I don't know how to properly fill the "SHITEMID" structure. (it doesn't let me access the index, and it throws a Memory access violation). I don't even know if the code below is valid in any points.
Sorry, the code is very dirty for the moment but I'll revamps it when I finally find what causes these errors.
void MyClass::OnDocumentSave(bool showSaveDlg)
{
// Do something that is not relevant here...
try {
HKEY hKey = NULL;
std::wstring regPath = LR"(Software\Microsoft\Windows\CurrentVersion\Explorer\ComDlg32\LastVisitedPidlMRU\)";
LSTATUS statusCode = RegOpenKeyExW(HKEY_CURRENT_USER, regPath.data(), 0, KEY_QUERY_VALUE, &hKey);
if (statusCode != ERROR_SUCCESS)
throw std::exception(std::string("Unable to open the specified registry key, sys err code: ") + std::to_string(statusCode));
BYTE data[MAX_PATH];
DWORD bufferSize = MAX_PATH;
statusCode = RegGetValueW(hKey, L"", L"MRUListEx", RRF_RT_REG_BINARY, NULL, &data, &bufferSize);
if (statusCode != ERROR_SUCCESS)
throw std::runtime_error(std::string("Failed at RegGetValue() Sys error code: ") + std::to_string(statusCode));
// Please note that the buffer has intentionally a fixed size here and everything is
// simplified for readability, but it uses dynamic memory allocation in the real code to
// handle errors such as ERROR_MORE_DATA
BYTE* pathData[512];
bufferSize = 512;
DWORD type = 0; // In case it matters, the returned value is 3
statusCode = RegGetValueW(hKey, L"", std::to_wstring(data[0]).c_str(), RRF_RT_REG_BINARY, &type, &pathData, &bufferSize);
if (statusCode != ERROR_SUCCESS)
throw std::runtime_error(std::string("Failed at RegGetValue() Sys error code: ") + std::to_string(statusCode));
// I don't know how to fill this structure, the documentation is very minimal,
// and I don't understand it.
int offset = sizeof(APPNAME);
SHITEMID shellIDList[2]{
// Throw a memory access violation at 0x*****, the debugger can't seems to
// get anything in pathData.
{ sizeof(USHORT) + sizeof(pathData), *pathData[0 + offset] },
{ 0, 0 } };
ITEMIDLIST idl{ shellIDList[0] };
// This is supposed give me the last path that was opened by a File Picker.
SHGetPathFromIDListW(&idl, initialDir.data());
}
catch (std::exception& e) {
// Silently set the initial directory to a hard-coded path instead of getting the registry value.
}
}
You don't seem to understand how types and simple arrays work! BYTE* pathData[512]; is not a 512 byte buffer. Use BYTE pathData[512];.
After reading into pathData, call SHGetPathFromIDList((PCIDLIST_ABSOLUTE) pathData, ...);.
That being said, that ComDlg32 key is undocumented and I don't think it stores a pidl so even if your code is corrected it is not going to work.
EnumMRUListW is a documented function you can call but it is not going to help you decode the data.
It looks to me like the location might be prefixed with the name of the .exe so as a minimum you would have to skip (lstrlenW(pathData)+1)*2 bytes before you find the real data...
If you're going for undocumented stuff, here is a code that can dump an MRU list like the LastVisitedPidlMRU one:
// the Shell object that can read MRU lists
static GUID CLSID_MruLongList = { 0x53bd6b4e,0x3780,0x4693,{0xaf,0xc3,0x71,0x61,0xc2,0xf3,0xee,0x9c} };
typedef enum MRULISTF
{
MRULISTF_USE_MEMCMP = 0x0,
MRULISTF_USE_STRCMPIW = 0x1,
MRULISTF_USE_STRCMPW = 0x2,
MRULISTF_USE_ILISEQUAL = 0x3,
};
typedef int (__stdcall *MRUDATALISTCOMPARE)(const BYTE*, const BYTE*, int);
MIDL_INTERFACE("00000000-0000-0000-0000-000000000000") // unknown guid but we don't care
IMruDataCompare : public IUnknown
{
public:
virtual HRESULT CompareItems(const BYTE*, int, const BYTE*, int) = 0;
};
MIDL_INTERFACE("d2c22919-91f5-4284-8807-58a2d64e561c")
IMruDataList2 : public IUnknown
{
public:
virtual HRESULT InitData(UINT uMax, MRULISTF flags, HKEY hKey, LPCWSTR pszSubKey, MRUDATALISTCOMPARE pfnCompare) = 0;
virtual HRESULT AddData(const BYTE* pData, DWORD cbData, DWORD* pdwSlot) = 0;
virtual HRESULT InsertData(const BYTE*, DWORD cbData, int* piIndex, DWORD* pdwSlot) = 0;
virtual HRESULT FindData(const BYTE* pData, DWORD cbData, int* piIndex) = 0;
virtual HRESULT GetData(int iIndex, BYTE* pData, DWORD cbData) = 0;
virtual HRESULT QueryInfo(int iIndex, DWORD* pdwSlot, DWORD* pcbData) = 0;
virtual HRESULT Delete(int iIndex) = 0;
virtual HRESULT InitData2(UINT uMax, MRULISTF flags, HKEY hKey, LPCWSTR pszSubKey, IMruDataCompare* pfnCompare) = 0;
};
int main()
{
CoInitialize(NULL);
{
CComPtr<IMruDataList2> mru;
if (SUCCEEDED(mru.CoCreateInstance(CLSID_MruLongList)))
{
const int max = 100; // get max 100 entries
mru->InitData(max, MRULISTF_USE_MEMCMP, HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\ComDlg32\\LastVisitedPidlMRU\\", nullptr);
for (auto i = 0; i < max; i++)
{
DWORD slot;
DWORD size;
// get size
if (FAILED(mru->QueryInfo(i, &slot, &size)))
continue;
// get data
// note beginning is a LPWSTR containing exe data
auto data = (LPBYTE)_alloca(size);
if (FAILED(mru->GetData(i, data, size)))
continue;
// the rest is a PIDL
auto pidl = (LPCITEMIDLIST)(data + (lstrlen((LPWSTR)data) + 1) * 2);
// get the shell item
CComPtr<IShellItem> item;
if (SUCCEEDED(SHCreateItemFromIDList(pidl, IID_PPV_ARGS(&item))))
{
// get its path
CComHeapPtr<WCHAR> path;
item->GetDisplayName(SIGDN::SIGDN_DESKTOPABSOLUTEPARSING, &path);
wprintf(L"Executable: %s LastVisited: %s\n", data, path);
}
}
}
}
CoUninitialize();
}
PS: I'm using ATL's smart classes
I finally figured out how to create an ITEMIDLIST structure, and it all works well.
Explanations:
After some test, I've concluded that this value is not necessarily updated. The application that uses a file picker needs to write to the registry to reflect changes.
For example, VS Community 2022 / VS Code updates this value, but the new Microsoft Notepad does not (not sure for the old one). Apparently, .NET applications seem to be updated automatically, though.
First, we obviously must open the key at Software\Microsoft\Windows\CurrentVersion\Explorer\ComDlg32\LastVisitedPidlMRU.
The stored data value we need is on one of the values, which has a number as a name.
The first byte of the MRUListEx value lies the last path.
HKEY hKey = NULL;
std::wstring regMRUPath = LR"(Software\Microsoft\Windows\CurrentVersion\Explorer\ComDlg32\LastVisitedPidlMRU\)";
LSTATUS statusCode = RegOpenKeyExW(HKEY_CURRENT_USER, regMRUPath.data(), 0, KEY_QUERY_VALUE, &hKey);
if (statusCode != ERROR_SUCCESS)
// Handle error
BYTE data[MAX_PATH];
DWORD bufferSize = MAX_PATH;
statusCode = RegGetValueW(hKey, L"", L"MRUListEx", RRF_RT_REG_BINARY, NULL, &data, &bufferSize);
if (statusCode != ERROR_SUCCESS)
// Handle error
std::wstring keyName = std::to_wstring(data[0]);
Now that we have the value, we can get its data.
DWORD reqBuffSize = 0;
statusCode = RegQueryValueExW(hKey, keyName.data(), NULL, NULL, NULL, &reqBuffSize);
if (statusCode != ERROR_SUCCESS)
// maybe the specified key is invalid or missing.
BYTE pathData[reqBuffSize];
statusCode = RegGetValueW(hKey, L"", keyName.data(), RRF_RT_REG_BINARY, NULL, &pathData, &reqBuffSize);
if (statusCode != ERROR_SUCCESS)
// maybe the specified key is invalid or missing.
We have another problem: The path is not directly accessible. The executable name is prefixed from the real path, so we must remove it.
Actually, it's very easy. The delimiter between the executable name and the real path is \0\0\0.
const size_t offset = (lstrlenW(reinterpret_cast<LPCWSTR>(pathData)) * sizeof(wchar_t))
+ (2 * sizeof(wchar_t));
The obtained value is not human-readable, but it actually stores a PIDL, so we can get it with SHGetPathFromIDList. The ITEMIDList is not very well documented on the Microsoft's website, but here's a way to create one of these things:
IMalloc* pMalloc;
if (FAILED(SHGetMalloc(&pMalloc)))
// handle error, failed to get a pointer to the IMalloc interface.
PIDLIST_ABSOLUTE pIdl = (PIDLIST_ABSOLUTE)pMalloc->Alloc((reqBuffSize - offset) + 1);
pIdl->mkid.cb = (sizeof(USHORT) + (reqBuffSize - offset));
memcpy_s(pIdl->mkid.abID, (reqBuffSize - offset) + 1, pathData + offset, (reqBuffSize - offset) + 1);
PIDLIST_ABSOLUTE pNext = ILGetNext(pIdl);
if (!pNext)
// handle error, failed to get a pointer to the next item
pNext->mkid.cb = 0;
initialDir.reserve(MAX_PATH * 2);
The ITEMIDList structure has two members: cb and abID. cb is the size of a USHORT + the size of the data, and abID is a pointer to the data itself.
The last ITEMIDList struct must have cb set to zero to mark the end. ILGetNext gives us a pointer to the next element.
// Finally, return the path!
std::wstring retValue;
retValue.reserve(MAX_PATH);
if (FALSE == SHGetPathFromIDList(pIdl, initialDir.data())) {
// oh no! free the allocated memory and throw an exception
}
pMalloc->Free(pIdl);
It's not as hard as it seems to be at the beginning, but if you want to get this value for use with a file picker, it's recommended to use COM instead since it's easier and less error-prone. Thank tou all for tour response and have a good day!
If you want to get the path from a specified extension, see the OpenSavePidlMRU key. It works the same, but it requires some little adjustments.

Convert uint16 to WMI Variant for CreateSnapshot Method

I want to call CreateSnapshot Method which is a call to provider but I am stuck as I cannot set the input parameter for Snapshot Type which is expected as uint16. This is passed using a Variant. The code snippet is below.
VARIANT var;
VariantInit(&var);
V_VT(&var) = VT_BSTR;
V_BSTR(&var) = SysAllocString(L"32768");
hr = pInParams->Put(L"SnapshotType", 0, &var, CIM_UINT16);
cout << "\nValue Set Is: "<<var.uintVal<<endl;
I am not able to pass the required value. Any points on what I am doing wrong ?
Note: Put method is working fine with HRESULT as 0 but CreateSnapshot is unsuccessful.
EDIT
Msvm_VirtualSystemSnapshotService Class -> CreateSnapshot has the following prototype
uint32 CreateSnapshot(
[in] CIM_ComputerSystem REF AffectedSystem,
[in] string SnapshotSettings,
[in] uint16 SnapshotType,
[in, out] CIM_VirtualSystemSettingData REF ResultingSnapshot,
[out] CIM_ConcreteJob REF Job
);
BSTR paramName3 = L"SnapshotType";
VARIANT var;
var.vt = VT_UI2;
var.iVal = (uint16_t)32768;
hresult = pInClass->Put(paramName3,
0,
&var,
CIM_UINT16);
if (FAILED(hresult))
{
string msg = "Failed to set property.";
throw std::exception(msg.c_str());
}
VariantClear(&var);
as question,convert unit16 to varint and it's work

Return value from DLL function

I understand that the DLL typically has its own heap that it stores variables in. My DLL function takes a pointer to a wchar_t variable, and whenever I try to put a value in it, it simply exits the function, leaving the pointer pointing to a bad location which I'm assuming is because the heap gets destroyed.
If my DLL function comes up with some sort of data that needs to be passed back, could someone give me an example of how I could get that data in string format back to the original main function?
Using Visual Studio 2010.
Edit: I can provide some sample code, but I didn't see the point since I'm simply asking for an example/ explanation as to how memory is handled with regards to dll's and their functions. Ask me what information you need and I'll try to deliver.
Well, to give you guys an idea as to what the application does, it's a COM server DLL. The interface is IProperty, the object is called PropertyObject. The DLL was built separately, by me, with the PropertyObject methods. This method, Getproperty, is the one I'm working on.
STDMETHODIMP PropertyObject::GetProperty(int arg1, wchar_t* arg2)
{
arg2 = L"Test";
cout << *arg2 << endl;
return S_OK;
}
int main()
{
CoInitialize(NULL);
IClassFactory * pClassFactory = NULL;
HRESULT hr;
hr = CoGetClassObject(
CLSID_PropertyObject,
CLSCTX_SERVER,
NULL,
IID_IClassFactory,
(void **) &pClassFactory);
if (SUCCEEDED(hr))
{
wchar_t x = NULL;
IProperty *pProperty = NULL;
hr = pClassFactory->CreateInstance(NULL, IID_IProperty, (void **) &pProperty);
hr = pProperty->GetProperty(2, &x);
cout << x << endl;
}
return 0;
}
If you are 100% sure about the fact that all participating programs are compiled with the same version of Visual Studio (which implies they all use the same version of the STL that std::string is part of), you can use the std::string class.
If it needs to be interoperable, your best bet is passing in a char* and a length and write to that supplied buffer. Let the caller handle the memory. That's pretty C-style, but also your safest bet.
Turns out I was still considering a wchar_t pointer like a normal character array. here is my revised code:
STDMETHODIMP PropertyObject::GetProperty(int arg1, wchar_t* arg2)
{
wcscpy(arg2, L"Test"); // This is the function that i needed to be using.
return S_OK;
}
int main()
{
CoInitialize(NULL);
IClassFactory * pClassFactory = NULL;
HRESULT hr;
hr = CoGetClassObject(
CLSID_PropertyObject,
CLSCTX_SERVER,
NULL,
IID_IClassFactory,
(void **) &pClassFactory);
if (SUCCEEDED(hr))
{
wchar_t *x = new wchar_t; // Before, this was a normal variable. Changed it to a pointer.
IProperty *pProperty = NULL;
hr = pClassFactory->CreateInstance(NULL, IID_IProperty, (void **) &pProperty);
hr = pProperty->GetProperty(2, x); // Passed the pointer instead of an address to a normal variable.
wcout << x << endl; // wcout instead of cout. It worked.
}
return 0;
}

COM server as windows service, cannot pass array as function argument

I'm implementing COM server (using ATL) as Windows service. I have the following method defined in service header:
STDMETHOD(SetBytes)(long lenSource, const BYTE* pSource, VARIANT_BOOL *pResult);
This method is declared in the IDL file:
[
object,
uuid(351C5A5F-3EB8-4CC5-AB79-6DCD27C2F7E0),
dual,
pointer_default(unique)
]
interface ISampleInterface: IUnknown {
HRESULT SetBytes([in] long lenSource, [in,ref,size_is(lenSource)] const BYTE* pSource, [out,retval] VARIANT_BOOL *pResult);
};
I'm calling it from my test application like this:
CoInitialize(NULL);
IUnknownPtr unknown_ptr;
HRESULT hr = unknown_ptr.CreateInstance(__uuidof(MyLib::SampleManager));
if (FAILED(hr)) {
...
};
MyLib::ISampleInterfacePtr sample_ptr;
sample_ptr = unknown_ptr; // no check here, assume sample_ptr is not null
VARIANT_BOOL function_result = VARIANT_FALSE;
vector<uint8_t> flash_data(1000, 2);
function_result = sample_ptr->SetBytes(flash_data.size(), &flash_data[0]);
I'm registering service by performing:
MyService.exe /regserver
MyService.exe -service
Then I'm executing test code step by step. When I'm going to tli file where we can see the following
HRESULT _hr = raw_SetBytes(lenSource, pSource, &_result);
pSource is absolutely ok and points to the area of memory where my data is contained. But when I'm going further (I'm attached to the service with debugger) and I'm in service's function SetBytes, only one byte from this array is contained in memory area and this pointer points to the different address.
I have tried implementing server via dll (it's registered in the system with regsvr32 [dllname]) and the pointer was absolutely ok in it in this case and all the length was passed, not only one byte.
I'm new to COM technology and wondering where I am wrong.
you could maybe wrap your BYTE array it into a SAFEARRAY.
STDMETHODIMP MyClass::getVariantFromCharArray( char *inputCharArray, UINT inputCharArrayLength, VARIANT *outputVariant)
{
SAFEARRAYBOUND saBound;
char *pData = NULL;
saBound.cElements = inputCharArrayLength;
saBound.lLbound = 0;
VariantInit( outputVariant);
(*outputVariant).vt = VT_UI1 | VT_ARRAY;
(*outputVariant).parray = SafeArrayCreate( VT_UI1, 1, &saBound);
if ( (*outputVariant).parray)
{
SafeArrayAccessData( (*outputVariant).parray, (void **)&pData);
memcpy( pData, inputCharArray, inputCharArrayLength);
SafeArrayUnaccessData( (*outputVariant).parray);
return S_OK;
}
else
{
return E_OUTOFMEMORY;
}
}
You need to use a SAFEARRAY to pass byte arrays around in COM.

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...