C++ Optimization breaking OLE Automation program (non-MFC) - c++

I'm writing a program to parse a Word Document and export data out to an Excel Workbook using OLE Automation (the non-MFC way I guess). Works fine in Debug, not so in Release (specifically if optimization is enabled). The error is that the IDispatch::Invoke call failed, specifically:
0x80020004 DISP_E_PARAMNOTFOUND Parameter not found
I checked StackOverflow for some suggestions and the main one seems to be uninitialized variables. That might be what's going on, but I still don't understand this specific case. I've narrowed it down to a single function in my program Automation::Dispatch::Invoke which is responsible for finally calling IDispatch::Invoke. The arguments being passed into Automation::Dispatch::Invoke are correct so the problem is somewhere in its code.
Looking at the base code (from MSDN) that I adapted this from, I was able to get it working and narrow down the exact problem line. Below shows code that does not work, but the comments indicate the line that I moved to get it working (look for the 2 lines with a <--- Problem line comment). In Debug mode, the location of this line does not matter and it works in either spot.
My question is what does this fix, and why is it an issue to start with? Thank you and let me know if I can make the question more clear.
HRESULT Automation::Dispatch::Invoke(int cmd, std::string name, std::vector<VARIANT> values)
{
USES_CONVERSION;
HRESULT result;
/* Get DISPID for name passed */
DISPID dispID;
LPOLESTR nameOle=A2OLE(name.c_str());
result=pObjectInt->GetIDsOfNames(IID_NULL, &nameOle, 1, LOCALE_USER_DEFAULT, &dispID);
if (FAILED(result)) {
return result;
}
/* Reverse elements in values vector so they are invoked in the correct order */
std::reverse(values.begin(), values.end());
/* Allocate memory for object values */
VARIANT *pValues=new VARIANT[values.size() + 1];
for (unsigned int i=0; i < values.size(); ++i) {
pValues[i]=values[i];
}
/* Build DISPPARAMS */
DISPPARAMS dispParams= {NULL, NULL, 0, 0};
/* DISPID dispidNamed=DISPID_PROPERTYPUT; <--- PROBLEM LINE moved here makes it work */
dispParams.cArgs=values.size();
dispParams.rgvarg=pValues;
/* Handle special-case for property-puts */
if (cmd==DISPATCH_PROPERTYPUT) {
DISPID dispidNamed=DISPID_PROPERTYPUT; /* <--- PROBLEM LINE here */
dispParams.cNamedArgs=1;
dispParams.rgdispidNamedArgs=&dispidNamed;
}
/* Make the call */
if (cmd==DISPATCH_METHOD || cmd==DISPATCH_PROPERTYPUT) {
result=pObjectInt->Invoke(dispID, IID_NULL, LOCALE_SYSTEM_DEFAULT, cmd, &dispParams, NULL, NULL, NULL);
}
else {
VariantInit(&objectData);
result=pObjectInt->Invoke(dispID, IID_NULL, LOCALE_SYSTEM_DEFAULT, cmd, &dispParams, &objectData, NULL, NULL);
}
delete[] pValues;
return result;
}

In this code:
if (cmd==DISPATCH_PROPERTYPUT) {
DISPID dispidNamed=DISPID_PROPERTYPUT; /* <--- PROBLEM LINE here */
dispParams.cNamedArgs=1;
dispParams.rgdispidNamedArgs=&dispidNamed;
}
dispidNamed is a local variable to the code block it is in (i.e. the area delimited by { }).
After the } is reached it ceases to exist. Then rgdispidNamedArgs is a dangling pointer because it no longer points to a variable that exists.
You got unlucky in Debug mode that it didn't trigger an error sooner.

Related

Memory leak in CcomObject

I am working on a memory leak and identified there is a leak in ummanaged code. Using UMDH, we found that the below code is having the leak:
+ 36608 ( 67704 - 31096) 651 allocs BackTraceAF008B48
+ 352 ( 651 - 299) BackTraceAF008B48 allocations
ntdll!RtlpCallInterceptRoutine+3F
ntdll!RtlpAllocateHeapInternal+9E0
MSVCR100!malloc+5B (f:\dd\vctools\crt_bld\self_64_amd64\crt\src\malloc.c, 89)
mfc100!operator new+3E
!ATL::CComObject<CCoreTimeoutObject>::CreateInstance+66 (c:\program files (x86)\microsoft visual studio 10.0\vc\atlmfc\include\atlcom.h, 2907)
!CMyClass::GetTimeoutObject+48 (cmyclass.cpp, 1813)
<no module>!???+0 : 7FFD68AC259D
STDMETHODIMP CMyClass::GetTimeoutObject(/* [out] */ IMyInterface **ppTimeoutObj)
{
ATLASSERT(ppTimeoutObj && !(*ppTimeoutObj));
CComObject<CCoreTimeoutObject> *pTimeoutObj = NULL;
HRESULT hr = CComObject<CCoreTimeoutObject>::CreateInstance(&pTimeoutObj);
if (SUCCEEDED(hr))
{
*ppTimeoutObj = static_cast<IMyInterface *>(pTimeoutObj);
(*ppTimeoutObj)->AddRef();
}
return hr;
}
EDIT: Adding the place where it is called.
This method is called from 2 places. One from managed code, and the other one from unmanaged code.
Managed code:
MYLib.IFlow mySvc = (MyLib.IFlow)MyServices.MySvcCOM;
mySvc.GetTimeoutObject(out _hardwareDoneRecieved);
But there is no code to make the _hardwareDoneReceived to be null. So I hope this is the leak here?
Unmanaged code:
STDMETHODIMP CSourceBase::get_CoreTimeoutObject( /* [retval][out] */ IUnknown **ppTimeoutObj)
{
return GetLocalSvc()->GetTimeoutObject(ppTimeoutObj);
}
I hope there is no leak in the calling part of unmanaged code!!
In the managed code, mySvc and _hardwareDoneRecieved should release their interfaces automatically when they go out of scope. There should be no leak there.
However, in the unmanaged code, I do see some problems.
You did not show the implementation of GetLocalSvc(), so that is a potential leak, depending on what it actually returns. For instance, if it returns an AddRef'ed interface, then get_CoreTimeoutObject() (and any other caller) will need to Release() it.
But more importantly, CMyClass::GetTimeoutObject() takes a IMyInterface** but CSourceBase::get_CoreTimeoutObject() is giving GetTimeoutObject() an IUnknown** instead, so you have a type mismatch that shouldn't even compile.
Either get_CoreTimeoutObject() should output a IMyInterface**:
STDMETHODIMP CSourceBase::get_CoreTimeoutObject( /* [retval][out] */ IMyInterface **ppTimeoutObj)
{
return GetLocalSvc()->GetTimeoutObject(ppTimeoutObj);
// or, if needed:
//
// CComPtr<IFlow> svc;
// svc.Attach(GetLocalSvc());
// return svc->GetTimeoutObject(ppTimeoutObj);
}
Or, it should call GetTimeoutObject() with a local IMyInterface* and then call QueryInterface() on it to get its IUnknown* (and then Release() the local).
STDMETHODIMP CSourceBase::get_CoreTimeoutObject( /* [retval][out] */ IUnknown **ppTimeoutObj)
{
CComPtr<IMyInterface> intf;
HRESULT hr = GetLocalSvc()->GetTimeoutObject(&intf);
if (SUCCEEDED(hr)) {
hr = intf->QueryInterface(IID_IUnknown, reinterpret_cast<void**>(ppTimeoutObj));
}
return hr;
// or, if needed:
//
// CComPtr<IFlow> svc;
// svc.Attach(GetLocalSvc());
// CComPtr<IMyInterface> intf;
// HRESULT hr = svc->GetTimeoutObject(&intf);
// if (SUCCEEDED(hr)) {
// hr = intf->QueryInterface(IID_IUnknown, reinterpret_cast<void**>(ppTimeoutObj));
// }
// return hr;
}
Also, you also did not show the code that is calling get_CoreTimeoutObject(), so we can't determine if that caller is leaking or not.

What is the different between system and Administrator account when call SHGetSpecialFolderLocation() function?

I write two program, one is windows service run in system account(named xxService) and the other one is common application run in Administrator account(named xx);
They use the same code to get CSIDL_COMMON_DOCUMENTS directory. In most machine , they run well.
But some machine, the xxService can get the correct directory, the xx have failure in SHGetSpecialFolderLocation();
edit:
the program only run on Windows XP(sp3).
edit2:
Use SHGetFolderPathA() function to solve this problem.
My english is poor, everybody excuse me!
log:
[2964] [db](tid=1108)(pid=2964): SHGetSpecialFolderLocation() fail.hr=0x80070057, ierr=122
the detail error info:
//
// MessageId: E_INVALIDARG
//
// MessageText:
//
// One or more arguments are invalid
//
#define E_INVALIDARG _HRESULT_TYPEDEF_(0x80070057L)
ERROR_INSUFFICIENT_BUFFER
122 (0x7A)
The data area passed to a system call is too small.
code:
//C:\Users\Public\Documents
LPITEMIDLIST pidl;
LPMALLOC pShellMalloc;
HRESULT hr = S_FALSE;
hr = SHGetMalloc(&pShellMalloc);
if(SUCCEEDED(hr))
{
hr = SHGetSpecialFolderLocation(NULL,CSIDL_COMMON_DOCUMENTS,&pidl);
if(SUCCEEDED(hr))
{
if(!SHGetPathFromIDListW(pidl, strDbFilePath))
{
int ierr=GetLastError();
DebugMsgW((L"SHGetPathFromIDListW() fail., ierr=%d"), ierr);
}
DebugMsgW(L"DBpath=%s",strDbFilePath);
pShellMalloc->Free(pidl);
}
else
{
int ierr=GetLastError();
DebugMsgW((L"SHGetSpecialFolderLocation() fail.hr=0x%x, ierr=%d"), hr, ierr);
}
pShellMalloc->Release();
}
else
{
int ierr=GetLastError();
DebugMsgW((L"SHGetMalloc() fail.hr=0x%x, ierr=%d"), hr, ierr);
}
SHGetSpecialFolderLocation() (and any other function that returns an HRESULT) does not use GetLastError() to report error codes, since the HRESULT is the error code. Even SHGetPathFromIDList() is not documented as using GetLastError(), either. So the return value of GetLastError() is irrelevant in your example and needs to be removed to avoid confusion.
As for the E_INVALIDARG error, you are using a legacy function. CSIDL_COMMON_DOCUMENTS is known to fail in SHGetSpecialFolderLocation() on some systems. You need to use a newer function, such as SHGetFolderPath(), or SHGetKnownFolderPath() on Vista+.

Direct2d CreateBitmapfromWICBitmap giving assert error within ATL

I have a simple function that loads a Png file and returns it as a ID2D1Bitmap. But when it tries to call the CreateBitmapfromWicBitmap function, it gives a debug assert error. The funny thing is that I first made an imageload function in a seperate project, and it works fine in there. Both of these functions have the same code, while the second one is giving errors.
Here's the erroring code:
ID2D1Bitmap* Wnd::LoadPng(LPCWSTR Path) {
CComPtr<IWICBitmapDecoder> pDecoder;
CComPtr<IWICBitmapFrameDecode> pFrame;
CComPtr<ID2D1Bitmap> pBit;
CComPtr<IWICFormatConverter> pConv;
HRESULT Hr;
Hr = m_pWICFactory->CreateDecoderFromFilename(Path,NULL,GENERIC_READ,WICDecodeMetadataCacheOnDemand,&pDecoder);
if (SUCCEEDED(Hr)) {
Hr = m_pWICFactory->CreateFormatConverter(&pConv);
}
if (SUCCEEDED(Hr)) {
Hr = pDecoder->GetFrame(0,&pFrame);
}
if (SUCCEEDED(Hr)) {
Hr = pConv->Initialize(pFrame,GUID_WICPixelFormat32bppPBGRA,WICBitmapDitherTypeNone,0,0.f,WICBitmapPaletteTypeCustom);
}
if (SUCCEEDED(Hr)) {
Hr = m_pRT->CreateBitmapFromWicBitmap(pConv,0,&pBit);
}
return pBit;
}
The error happens in atlcomcli.h at line 182 in function _NoAddRefReleaseOnCComPtr.
I double-checked all headers and libraries and they're the same in both projects (With some extra headers in the second project).
Here's the code that WORKS:
CComPtr<IWICFormatConverter> Conv;
m_pWICFactory->CreateFormatConverter(&Conv);
CComPtr<IWICBitmapFrameDecode> Frame;
m_pDecoder->GetFrame(0,&Frame);
Frame->GetSize(&W,&H);
Conv->Initialize(Frame,GUID_WICPixelFormat32bppPBGRA,WICBitmapDitherTypeNone,0,0.f,WICBitmapPaletteTypeCustom);
CComPtr<ID2D1Bitmap> Bit;
Hr = m_pRT->CreateBitmapFromWicBitmap(Conv,0,&Bit);
m_pBitmap.push_back(Bit);
BitmapDecoder is predefined here, but it's exactly the same as in the first snippet.
------------------------------- FIXED ----------------------------
Third time I forgot to call the init function for my rendertarget.
The assertion failure warns you that you are trying to "use" a NULL interface pointer through CComPtr template. You should look up on the call stack which exactly line of your code you are at, and what variable holds NULL pointer which you expect to be non-NULL. Or otherwise just step through your code with debugger.

How do you run external programs with parameters without the cmd window showing up in Windows?

I just asked a question earlier today because I wanted to run an executable file that takes parameters from my C++ code and it wasn't working.
It works now, but I'm still having problems since I thought I was going the right way about this, but it seems like what I want to accomplish can't be done the way I'm approaching it...
This is my corrected code from my other question:
#include <stdlib.h>
#include <conio.h>
int main (){
system("\"\"C:\\Users\\Adam\\Desktop\\pdftotext\" -layout \"C:\\Users\\Adam\\Desktop\\week 4.pdf\"\"");
_getch();
}
which is me running "pdftotext -layout myfile.pdf" as if I was running it from a CMD window.
The thing is, I don't actually want the cmd to show up since I have a GUI interface on top of it and I want to display a nicer progress bar instead of seeing the windows pop-up for every file I need to parse.
I looked around and either I don't understand what I'm reading since I'm relatively new to C++, or I just didn't find what I was looking for. I found that using CreateProcess, I should be able to do this, but after copying some code I found somewhere else, the cmd window pops-up anyway.
I'd like it if someone could give me the name of a function I could use to accomplish something like this or if someone could give some example code for this small case in the code I posted since I'm not sure I understand everything as I should, being new to C++ and all.
Edit: As requested in a comment, the code for CreateProcess that I tried is what I found at this url:
http://www.goffconcepts.com/techarticles/development/cpp/createprocess.html
Which is (with my own parameters that I think should go there):
#include <windows.h>
#include <string>
#include <conio.h>
size_t ExecuteProcess(std::wstring FullPathToExe, std::wstring Parameters, size_t SecondsToWait)
{
size_t iMyCounter = 0, iReturnVal = 0, iPos = 0;
DWORD dwExitCode = 0;
std::wstring sTempStr = L"";
/* - NOTE - You should check here to see if the exe even exists */
/* Add a space to the beginning of the Parameters */
if (Parameters.size() != 0)
{
if (Parameters[0] != L' ')
{
Parameters.insert(0,L" ");
}
}
/* The first parameter needs to be the exe itself */
sTempStr = FullPathToExe;
iPos = sTempStr.find_last_of(L"\\");
sTempStr.erase(0, iPos +1);
Parameters = sTempStr.append(Parameters);
/* CreateProcessW can modify Parameters thus we allocate needed memory */
wchar_t * pwszParam = new wchar_t[Parameters.size() + 1];
if (pwszParam == 0)
{
return 1;
}
const wchar_t* pchrTemp = Parameters.c_str();
wcscpy_s(pwszParam, Parameters.size() + 1, pchrTemp);
/* CreateProcess API initialization */
STARTUPINFOW siStartupInfo;
PROCESS_INFORMATION piProcessInfo;
memset(&siStartupInfo, 0, sizeof(siStartupInfo));
memset(&piProcessInfo, 0, sizeof(piProcessInfo));
siStartupInfo.cb = sizeof(siStartupInfo);
if (CreateProcessW(const_cast<LPCWSTR>(FullPathToExe.c_str()),
pwszParam, 0, 0, false,
CREATE_DEFAULT_ERROR_MODE, 0, 0,
&siStartupInfo, &piProcessInfo) != false)
{
/* Watch the process. */
dwExitCode = WaitForSingleObject(piProcessInfo.hProcess, (SecondsToWait * 1000));
}
else
{
/* CreateProcess failed */
iReturnVal = GetLastError();
}
/* Free memory */
delete[]pwszParam;
pwszParam = 0;
/* Release handles */
CloseHandle(piProcessInfo.hProcess);
CloseHandle(piProcessInfo.hThread);
return iReturnVal;
}
int main(void){
ExecuteProcess(L"C:\\Users\\Adam\\Desktop\\pdftotext", L"-layout \"C:\\Users\\Adam\\Desktop\\week 4.pdf\"", 0);
_getch();
}
I'm a little bit overwhelmed since it uses some things I've never used before, but I think I understand the core (keyword: think). It doesn't solve my problem, though, because the cmd shows up and by retesting it I actually noticed that the cmd doesn't even run the .exe and doesn't give me an error message.
I hope this bit of code helps... I didn't want to look into it further since it seemed like I wasn't even going in the right direction.
Use CreateProcess instead of system.
--EDIT--
the code for CreateProcess that I tried is what I found at this url:
The code is a mess, I'd advise to avoid that url in future.
At the end of "CreateProcess" article is a link named "Creating Processes", which contains simpler example that is easier to read. Use it as a starting point.
After adding the following lines for siStartupInfo, cmd window won't pop up any more with my own test *.exe.
siStartupInfo.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
siStartupInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
siStartupInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
siStartupInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
siStartupInfo.wShowWindow = SW_HIDE;
But I have another problem. As I try to run some other executable, whose command line would be
TEST.exe <input-file> output-file
in a cmd window, WaitForSingleObject() return 258, and GetLastError() return 1813 ("The specified resource type cannot be found in the image file.").
See system() and CreateProcess() / CreateProcessW() for more details.
Any ideas would be highly appreciated!
The only way I found how to execute an external program is:
system("start C:\file path\ file");
The only problem is that the file or directory can't have spaces.

Struggling with debug asserts with mem alloc for SP_DEVICE_INTERFACE_DETAIL_DATA

I'm struggling with some debug asserts when my test program exits. The purpose is to get the device path to a disk drive through using the Setup API. This I'm doing. I'm following the rule described here for SetupDiGetDeviceInterfaceDetail i.e. calling SetupDiGetDeviceInterfaceDetail() to determine the size needed for the structure, and allocating memory for the structure and calling SetupDiGetDeviceInterfaceDetail() again.
This process works and I'm getting the data that I need. What is not working correctly is, when the program exits, or I delete the memory directly, I get a debug assertion. The assert window has the file where the problem was found, dbgdel.cpp, on line 52. The problem: "Expression: _BLOCK_TYPE_IS_VALID(pHead->nBlockUse)".
I'm not sure what the problem is. If I remove all of the code I'm using and new up an SP_DEVICE_INTERFACE_DETAIL_DATA object without all of the Setup API calls, the call to delete works. Basically, here's what I'm doing:
HDEVINFO hDevs = SetupDiGetClassDevs(&DiskClassGuid, NULL, NULL, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT);
SP_DEVICE_INTERFACE_DATA devInterfaceData = {sizeof(SP_DEVICE_INTERFACE_DATA)};
DWORD size(0);
SetupDiEnumDeviceInterfaces(hDevs, NULL, &DiskClassGuid, 0, &devInterfaceData);
PSP_DEVICE_INTERFACE_DETAIL_DATA pDetails(NULL);
SetupDiGetDeviceInterfaceDetail(hDevs, &devInterfaceData, pDetails, 0, &size, NULL);
pDetails = reinterpret_cast<PSP_DEVICE_INTERFACE_DETAIL_DATA>(new BYTE[sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA) + size]);
// zero allocated memory
pDetails->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
SetupDiGetDeviceInterfaceDetail(hDevs, &devInterfaceData, pDetails, size, NULL, NULL);
delete[] pDetails;
When the program exits, or the delete[] is called, the assert that mentioned earlier shows up. Please explain to me what I'm doing wrong.
Thanks,
Andy
UPDATE:
Forgot to add the definition of SP_DEVICE_INTERFACE_DETAIL_DATA. That can be found here. However, it looks like this:
typedef struct _SP_DEVICE_INTERFACE_DETAIL_DATA {
DWORD cbSize;
TCHAR DevicePath[ANYSIZE_ARRAY];
} SP_DEVICE_INTERFACE_DETAIL_DATA, *PSP_DEVICE_INTERFACE_DETAIL_DATA;