Memory leak in CcomObject - c++

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.

Related

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

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.

DecideBufferSize values seem to be ignored

I had the problem that when using a webcam as a source , the input sample was bigger than the size of the buffer provided by the allocator as you can see in the ASSERT statement in this code.
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;
hr = m_pAllocator->GetBuffer(&outsample, NULL, NULL, NULL);
if (FAILED(hr))
{
return hr;
}
BYTE* sampleBuffer = NULL;
BYTE* newBuffer = NULL;
long ulDataLen = sample->GetSize();
long datalenout = outsample->GetSize(); //this is always 92160
outsample->GetPointer(&newBuffer);
ASSERT(datalenout >= ulDataLen); //This fails
memcpy((void *)newBuffer, (void *)sampleBuffer, ulDataLen);
m_pInputPin->Receive(outsample);
outsample->Release();
sample->Release();
}
return hr;
//Forward to filter
}
So memcpy would definitely fails because you can't copy something into a buffer that is smaller than the data.
So I tried adjusting the buffersize in DecideBufferSize:
HRESULT MCMyOutputPin::DecideBufferSize(IMemAllocator *pAlloc, ALLOCATOR_PROPERTIES *pProps)
{
myLogger->LogDebug("On DecideBufferSIze", L"D:\\TEMP\\yc.log");
ALLOCATOR_PROPERTIES act;
HRESULT hr;
// by default we do something like this...
pProps->cbAlign = 1;
pProps->cBuffers = 30;
long buffersize = this->CurrentMediaType().lSampleSize * 3;
pProps->cbBuffer = 10 * 10 * 1000;
pProps->cbPrefix = 0;
hr = pAlloc->SetProperties(pProps, &act);
if (FAILED(hr)) return hr;
// make sure the allocator is OK with it.
if ((pProps->cBuffers > act.cBuffers) ||
(pProps->cbBuffer > act.cbBuffer) ||
(pProps->cbAlign > act.cbAlign))
return E_FAIL;
return NOERROR;
}
which gets ignored. The size of the sample returned by the alocator is always 92160.
I also made sure that the DecideBufferSize method gets actually called.
How do I set the size of the Buffer returned by Allocator->GetBuffer()?
MSDN states it pretty accurately:
Typically, the derived class will honor the input pin's buffer requirements, but it is not required to.
Buffer size decision is a matter of negotiation. Your setting requirements does not mean they will be accepted.
The size of the sample returned by the allocator is always 92160.
How do I set the size of the Buffer returned by Allocator->GetBuffer()?
What is wrong exactly with 92160, what makes you think it's invalid? You are looking for an answer to the wrong question. If you own allocator, then you set its buffer size. If you don't own and manage it, then you have to live with the size it already has.
I gave you MSDN link a few question ago, and it explains why increased size buffers are sometimes valid, and even more so - they are inevitable.

E_NOINTERFACE when calling CreateDXGIFactory1

I am relatively new to C++ in general, and very new to Windows development.
I am writing a program that uses the DXGI library - it compiles just fine, but when I run the executable, the HRESULT from the CreateDXGIFactory1 comes out as 0x80004002, or E_NOINTERFACE.
Am I missing some sort of library, or is there a deeper issue at play here?
The code I am using follows:
Output is "Error: 0x80004002".
//Initialize a UUID
GUID uuid;
HRESULT hCreateUUID = CoCreateGuid(&uuid);
//Convert the UUID to string
LPOLESTR stringUUID;
HRESULT hStringToUUID = StringFromCLSID(uuid, &stringUUID);
//Initialize the factory pointer
IDXGIFactory1* pFactory;
//Actually create it
HRESULT hCreateFactory = CreateDXGIFactory1(uuid, (void**)(&pFactory));
if (hCreateFactory == S_OK) {
printf("Factory creation was a success\n");
} else {
printf("ERROR: 0x%X\n", hCreateFactory);
}
You are passing a random, freshly created GUID. This makes no sense. You are supposed to pass the IID of the interface you wish to obtain - namely, __uuidof(IDXGIFactory1). The example in the documentation shows just that.

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.

what's wrong in my code related to COM?

*****BLOCK_1****
if( strcmpi(appName.c_str(),MSSQL)==0 ||strcmpi(appName.c_str(),MSSQL2005)==0 )
{
if (FAILED(CoCreateInstance (CLSID_SQLDMOServer, NULL, CLSCTX_INPROC_SERVER,
IID_ISQLDMOServer, (LPVOID*)&m_pSQLServer))) {
DMOAvailable=false;
IDiscoverPtr pICalc;
HRESULT hRes=CoCreateInstance(Test::CLSID_SqlClass, NULL, CLSCTX_INPROC_SERVER,Test::IID_IDiscover, reinterpret_cast<void**> (&pICalc));
if(FAILED(hRes))
{
cout << "CoCreateInstance Failed on CLSID_SQLDMOServer\n";
return FALSE;
}
***BLOCK_2***
if((strcmpi(appName.c_str(),MSSQL2008)==0 || strcmpi(appName.c_str(),MSSQL2005)==0 ) && DMOAvailable==false )
{
HRESULT hr=CoInitialize(NULL);
IDiscoverPtr pICalc(__uuidof(SqlClass));
if(FAILED(CoCreateInstance(Test::CLSID_SqlClass, NULL, CLSCTX_INPROC_SERVER,
Test::IID_IDiscover, reinterpret_cast<void**> (&pICalc))))
{
cout<<" Loading SQLSMO failed This is because of SMO not Available "<<endl;
return FALSE;
}
}
*****BLOCK_3 ****
if((strcmpi(appName.c_str(),MSSQL2008)==0 && DMOAvailable==true))
{
HRESULT hr= CoInitialize(NULL);
cout<<"\nIn Init SqlServer DMO-true and SQL2008"<<endl;
HRESULT hRes=CoCreateInstance(Test::CLSID_SqlClass, NULL, CLSCTX_INPROC_SERVER,
Test::IID_IDiscover, reinterpret_cast<void**> (&pICalc));
if(FAILED(hRes))
{
printf(" Loading SQLSMO failed This is because of SMO not Available 0x%X\n",hRes)
return FALSE;
}
else
cout<<success;
}
return TRUE;
}
I have prepared the Test.dll in c# and in that i have a an interface IDiscover and a class SqlClass implementing that interface.I have Manually assigned the Guid like this
[System.Runtime.InteropServices.Guid("D4660088-308E-49fb-AB1A-77224F3FF851")]
public interface IDiscover
{
string getSqlInstances(string HostName);
string getDB(string SQLInstanceName);
string getDatabaseInfo(string SQLInstanceName, string DBName);
};
namespace Test
{
[System.Runtime.InteropServices.Guid("46A951AC-C2D9-48e0-97BE-91F3C9E7B065")]
public class SqlClass:IDiscover
{
}
}
I also make COMVisible=true;
and register the class using RegAsm.exe Test.dll/tlb:Test.tlb /codebase
and imported the tlb in one cpp file as #import c:...\Test.tlb named_guids
This is working fine in my Machine And also in My virtual Machine for any case.if i gave sql2005 it works and i gave sql2008 it working.
and in some other machine it shows that errror code as 0x80004002 only when entering into 3rd block.if it enters 1st block and 2nd block its working fine in other machine also.what happening in 3rd block i am not understanding plzzzzzzzz help me in this regard...
Sharptooth u can plzz go through this one .....
When working with COM, your assemblies should be "Release Builds". Make sure that's the case before digging around any further.
These two statements:
IDiscoverPtr pICalc(__uuidof(SqlClass));
HRESULT hRes=CoCreateInstance(Test::CLSID_SqlClass, NULL, CLSCTX_INPROC_SERVER, Test::IID_IDiscover, reinterpret_cast<void**> (&pICalc));
do exactly the same thing, so you don't need to run them in sequence.
The first one translates the HRESULT into an exception of type _com_error. I'd try something like:
IDiscoverPtr pDiscover;
HRESULT hr = pDiscover.CreateInstance(__uuidof(SqlClass));
if (FAILED(hr))
{
printf("SQL DMO is not avilable. Error code: 0x%X\n", hr);
}
Could you post back what the error code is?
The IDiscoverPtr constructor will throw an exception if it can't instantiate the COM object. The most likely reason is that the COM object is not registered in the registry of that machine (regasm was not run for the .NET assembly that implements the IDiscover).
I have seen the error 0x80004002 E_NOINTERFACE if you expose a property of type DateTime on the com visible type you are trying to call CreateInstance() on. I've also noticed that you have to Rebuild the project that imports the tlb file instead of a Build if the tlb file changes for example.