IOleInPlaceSiteWindowless::AdjustRect not working? - com

I am using IOleInPlaceSiteWindowless::AdjustRect to properly capture and release the mouse in a windowless ActiveX control hosted in IE:
LRESULT CD3DControl::OnMouseMove(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& /*bHandled*/)
{
CRect rc(CPoint(lParam), CSize(0, 0));
HRESULT hr = m_spInPlaceSite->AdjustRect(rc);
bool isInside = hr == S_OK;
TRACE("AdjustRect 0x%X, isInside=%d %d %d %d %d\n",
hr, isInside, rc.top, rc.left, rc.bottom, rc.right);
if (m_spInPlaceSite->GetCapture() == S_FALSE)
{
if (isInside)
{
hr = m_spInPlaceSite->SetCapture(TRUE);
TRACE("SetCapture(TRUE) 0x%X\n", hr);
}
}
else if (!isInside)
{
hr = m_spInPlaceSite->SetCapture(FALSE);
TRACE("SetCapture(FALSE) 0x%X\n", hr);
}
return 0;
}
When the mouse enters my control's rect things work great and the control captures the mouse. However, when my mouse leaves the control's area, AdjustRect still returns S_OK. It also returns S_OK if the mouse hovers over a div that covers part of my control.
These results are not consistent with the AdjustRect documentation.
To debug this further, I re-wrote OnMouseMove:
LRESULT CD3DControl::OnMouseMove(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& /*bHandled*/)
{
CRect rc(0, 0, 2000, 2000);
HRESULT hr = m_spInPlaceSite->AdjustRect(&rc);
bool isInside = hr == S_OK;
TRACE("AdjustRect 0x%X, isInside=%d %d %d %d %d\n",
hr, isInside, rc.top, rc.left, rc.bottom, rc.right);
return 0;
}
In this case, AdjustRect also returns S_OK, but the rectangle isn't adjusted at all! It is still (0,0)x(2000,2000).

For OnMouseOut on windowless controls I'm usually using TrackMouseEvent on the container hwnd and monitor WM_MOUSELEAVE and WM_MOUSEMOVE.
Also, keep in mind when authoring windowless controls that some containers refuse windowless instantiation so your controls turn into full blown "windowful" controls there. Most notably MS Access is such a beast. In this case you never get a call on IOleInPlaceObjectWindowless::OnWindowMessage because you have you own hwnd.

The amazing Igor Tandetnik answered my question here:
http://social.msdn.microsoft.com/Forums/en-US/ieextensiondevelopment/thread/b8586255-0321-450e-9c8a-090e47ce13c4/
Evidently the function simply isn't implemented. IE should return E_NOTIMPLEMENTED
-Erik

Related

Text Services Framework failed to set global compartment value as VT_BSTR

I wrote a test application (.exe) for inter-process communication using TSF global compartment and the following code works correctly when the variant type is VT_I4, but for VT_BSTR the ITfCompartment::SetValue return S_FALSE and the OnChange callback is not fired on the text service (an IME).
The S_FALSE for ITfCompartment::SetValue is not even documented on MSDN and I guess that means the operation succeeded but has no effect.
Can anyone offer some ideas as to how to solve this problem? Thanks!
ITfThreadMgr *pThreadMgr;
if (FAILED(CoCreateInstance(CLSID_TF_ThreadMgr, nullptr, CLSCTX_INPROC_SERVER, IID_ITfThreadMgr, (void **)&pThreadMgr)))
{
return;
}
if (FAILED(pThreadMgr->Activate(&m_tfClientID)))
{
return;
}
ITfCompartmentMgr *pCompartmentMgr;
if (pThreadMgr->GetGlobalCompartment(&pCompartmentMgr) != S_OK)
{
return;
}
ITfCompartment *pCompartment;
if (pCompartmentMgr->GetCompartment(TheGlobalCompartmentGUID, &pCompartment) != S_OK)
{
pCompartment = nullptr;
pCompartmentMgr->Release();
return;
}
VARIANT varValue;
varValue.vt = VT_BSTR;
varValue.bstrVal = SysAllocString(L"abc");
//varValue.vt = VT_I4;
//varValue.lVal = 1;
HRESULT hr = pCompartment->SetValue(m_tfClientID, &varValue);
if (hr != S_OK)
{
OutputDebugString(L"SetValue failed");
}
pCompartment->Release();
pCompartmentMgr->Release();
The short answer is that you can only store integers in global compartments. Marshaling a string or object is not possible given how TSF global compartments work (it runs below the COM marshaling layer).

Problems connecting to the input pins of GMFBridge Sink Filter

I am experiencing a strange problem when trying to use the GMFBridge filter with the output of an Euresys UxH264 card.
I am trying to integrate this card into our solution, that relies on GMFBridge to handle the ability of continuous capture to multiple files, performing muxing and file-splitting without having to stop the capture graph.
This card captures video and audio from analog inputs. It provides a DirectShow filter exposing both a raw stream of the video input and a hardware-encoded H.264 stream. The audio stream is provided as an uncompressed stream only.
When I attempt to directly connect any of the output pins of the Euresys source filters to the input pins of the GMFBridge Sink, they get rejected, with the code VFW_E_NO_ALLOCATOR. (In the past I have successfully connected both H.264 and raw audio streams to the bridge).
Grasping at straws, I plugged in a pair of SampleGrabber filters between the Euresys card filters and the bridge sink filter, and -just like that- the connections between sample grabbers and sink were accepted.
However, I am not getting any packets on the other side of the bridge (the muxing graph). I inspected the running capture graph with GraphStudioNext and somehow the sample grabbers appear detached from my graph, even though I got successful confirmations when I connected them to the source filter!.
Here's the source code creating the graph.
void EuresysSourceBox::BuildGraph() {
HRESULT hRes;
CComPtr<IGraphBuilder> pGraph;
COM_CALL(pGraph.CoCreateInstance(CLSID_FilterGraph));
#ifdef REGISTER_IN_ROT
_rotEntry1 = FilterTools::RegisterGraphInROT(IntPtr(pGraph), "euresys graph");
#endif
// [*Video Source*]
String^ filterName = "Ux H.264 Visual Source";
Guid category = _GUIDToGuid((GUID)AM_KSCATEGORY_CAPTURE);
FilterMonikerList^ videoSourceList = FilterTools::GetFilterMonikersByName(category, filterName);
CComPtr<IBaseFilter> pVideoSource;
int monikerIndex = config->BoardIndex; // a filter instance will be retrieved for every installed board
clr_scoped_ptr<CComPtr<IMoniker>>^ ppVideoSourceMoniker = videoSourceList[monikerIndex];
COM_CALL((*ppVideoSourceMoniker->get())->BindToObject(NULL, NULL, IID_IBaseFilter, (void**)&pVideoSource));
COM_CALL(pGraph->AddFilter(pVideoSource, L"VideoSource"));
// [Video Source]
//
// [*Audio Source*]
filterName = "Ux H.264 Audio Encoder";
FilterMonikerList^ audioSourceList = FilterTools::GetFilterMonikersByName(category, filterName);
CComPtr<IBaseFilter> pAudioSource;
clr_scoped_ptr<CComPtr<IMoniker>>^ ppAudioSourceMoniker = audioSourceList[monikerIndex];
COM_CALL((*ppAudioSourceMoniker->get())->BindToObject(NULL, NULL, IID_IBaseFilter, (void**)&pAudioSource));
COM_CALL(pGraph->AddFilter(pAudioSource, L"AudioSource"));
CComPtr<IPin> pVideoCompressedOutPin(FilterTools::GetPin(pVideoSource, "Encoded"));
CComPtr<IPin> pAudioOutPin(FilterTools::GetPin(pAudioSource, "Audio"));
CComPtr<IBaseFilter> pSampleGrabber;
COM_CALL(pSampleGrabber.CoCreateInstance(CLSID_SampleGrabber));
COM_CALL(pGraph->AddFilter(pSampleGrabber, L"SampleGrabber"));
CComPtr<IPin> pSampleGrabberInPin(FilterTools::GetPin(pSampleGrabber, "Input"));
COM_CALL(pGraph->ConnectDirect(pVideoCompressedOutPin, pSampleGrabberInPin, NULL)); // DOES NOT FAIL!!
CComPtr<IBaseFilter> pSampleGrabber2;
COM_CALL(pSampleGrabber2.CoCreateInstance(CLSID_SampleGrabber));
COM_CALL(pGraph->AddFilter(pSampleGrabber2, L"SampleGrabber2"));
CComPtr<IPin> pSampleGrabber2InPin(FilterTools::GetPin(pSampleGrabber2, "Input"));
COM_CALL(pGraph->ConnectDirect(pAudioOutPin, pSampleGrabber2InPin, NULL)); // DOES NOT FAIL!!
// [Video Source]---
// |-->[*Bridge Sink*]
// [Audio Source]---
CComPtr<IPin> pSampleGrabberOutPin(FilterTools::GetPin(pSampleGrabber, "Output"));
CComPtr<IPin> pSampleGrabber2OutPin(FilterTools::GetPin(pSampleGrabber2, "Output"));
CreateGraphBridge(
IntPtr(pGraph),
IntPtr(pSampleGrabberOutPin),
IntPtr(pSampleGrabber2OutPin)
);
// Root graph to parent object
_ppCaptureGraph.reset(new CComPtr<IGraphBuilder>(pGraph));
}
COM_CALL is my HRESULT checking macro, it will raise a managed exception if the result is other than S_OK. So the connection between pins succeeded, but here is how the graph looks disjointed when it is running:
So, I have three questions:
1) What could VFW_E_NO_ALLOCATOR mean in this instance? (the source filter can be successfully connected to other components such as LAV Video decoder or ffdshow video decoder).
2) Is there a known workaround to circumvent the VFW_E_NO_ALLOCATOR problem?
3) Is it possible that a filter gets disconnected at runtime as it seems to be happening in my case?
I found a reference by Geraint Davies giving a reason as to why the GMFBridge sink filter may be rejecting the connection.
It looks as though the parser is insisting on using its own allocator
-- this is common with parsers where the output samples are merely pointers into the input samples. However, the bridge cannot implement
suspend mode without using its own allocator, so a copy is required.
With this information, I decided to create an ultra simple CTransformFilter filter that simply accepts the allocator offered by the bridge and copies to the output sample whatever comes in from the input sample. I am not 100% sure that what I did was right, but it is working now. I could successfully plug-in the Euresys card as part of my capture infrastructure.
For reference, if anyone experiences something similar, here is the code of the filter I created:
class SampleCopyGeneratorFilter : public CTransformFilter {
protected:
HRESULT CheckInputType(const CMediaType* mtIn) override { return S_OK; }
HRESULT GetMediaType(int iPosition, CMediaType* pMediaType) override;
HRESULT CheckTransform(const CMediaType *mtIn, const CMediaType *mtOut) override { return S_OK; }
HRESULT DecideBufferSize(IMemAllocator *pAlloc, ALLOCATOR_PROPERTIES *pProp) override;
HRESULT Transform(IMediaSample *pSource, IMediaSample *pDest) override;
public:
SampleCopyGeneratorFilter();
};
//--------------------------------------------------------------------------------------------------------------------
SampleCopyGeneratorFilter::SampleCopyGeneratorFilter()
: CTransformFilter(NAME("SampleCopyGeneratorFilter"), NULL, GUID_NULL)
{
}
HRESULT SampleCopyGeneratorFilter::GetMediaType(int iPosition, CMediaType* pMediaType) {
HRESULT hRes;
ASSERT(m_pInput->IsConnected());
if( iPosition < 0 )
return E_INVALIDARG;
CComPtr<IPin> connectedTo;
COM_CALL(m_pInput->ConnectedTo(&connectedTo));
CComPtr<IEnumMediaTypes> pMTEnumerator;
COM_CALL(connectedTo->EnumMediaTypes(&pMTEnumerator));
AM_MEDIA_TYPE* pIteratedMediaType;
for( int i = 0; i <= iPosition; i++ ) {
if( pMTEnumerator->Next(1, &pIteratedMediaType, NULL) != S_OK )
return VFW_S_NO_MORE_ITEMS;
if( i == iPosition )
*pMediaType = *pIteratedMediaType;
DeleteMediaType(pIteratedMediaType);
}
return S_OK;
}
HRESULT SampleCopyGeneratorFilter::DecideBufferSize(IMemAllocator *pAlloc, ALLOCATOR_PROPERTIES *pProp) {
HRESULT hRes;
AM_MEDIA_TYPE mt;
COM_CALL(m_pInput->ConnectionMediaType(&mt));
try {
BITMAPINFOHEADER* pBMI = HEADER(mt.pbFormat);
pProp->cbBuffer = DIBSIZE(*pBMI); // format is compressed, uncompressed size should be enough
if( !pProp->cbAlign )
pProp->cbAlign = 1;
pProp->cbPrefix = 0;
pProp->cBuffers = 4;
ALLOCATOR_PROPERTIES actualProperties;
COM_CALL(pAlloc->SetProperties(pProp, &actualProperties));
if( pProp->cbBuffer > actualProperties.cbBuffer )
return E_FAIL;
return S_OK;
} finally{
FreeMediaType(mt);
}
}
HRESULT SampleCopyGeneratorFilter::Transform(IMediaSample *pSource, IMediaSample *pDest) {
HRESULT hRes;
BYTE* pBufferIn;
BYTE* pBufferOut;
long destSize = pDest->GetSize();
long dataLen = pSource->GetActualDataLength();
if( dataLen > destSize )
return VFW_E_BUFFER_OVERFLOW;
COM_CALL(pSource->GetPointer(&pBufferIn));
COM_CALL(pDest->GetPointer(&pBufferOut));
memcpy(pBufferOut, pBufferIn, dataLen);
pDest->SetActualDataLength(dataLen);
pDest->SetSyncPoint(pSource->IsSyncPoint() == S_OK);
return S_OK;
}
Here is how I inserted the filter in the capture graph:
CComPtr<IPin> pAACEncoderOutPin(FilterTools::GetPin(pAACEncoder, "XForm Out"));
CComPtr<IPin> pVideoSourceCompressedOutPin(FilterTools::GetPin(pVideoSource, "Encoded"));
CComPtr<IBaseFilter> pSampleCopier;
pSampleCopier = new SampleCopyGeneratorFilter();
COM_CALL(pGraph->AddFilter(pSampleCopier, L"SampleCopier"));
CComPtr<IPin> pSampleCopierInPin(FilterTools::GetPin(pSampleCopier, "XForm In"));
COM_CALL(pGraph->ConnectDirect(pVideoSourceCompressedOutPin, pSampleCopierInPin, NULL));
CComPtr<IPin> pSampleCopierOutPin(FilterTools::GetPin(pSampleCopier, "XForm Out"));
CreateGraphBridge(
IntPtr(pGraph),
IntPtr(pSampleCopierOutPin),
IntPtr(pAACEncoderOutPin)
);
Now, I still have no idea why inserting the sample grabber instead did not work and resulted in detached graphs. I corroborated this quirk by examining the graphs with Graphedit Plus too. If anyone can offer me an explanation, I would be very grateful indeed.

C++/CLI Runtime Error: "object reference not set to an instance of an object"

1st: I have already read dozens, if not close to a hundred other threads on SO (and other websites) about "object reference not set to an instance of an object", (I get the impression it's apparently a common error) but I just don't seem to "get" it. So, sorry if this is a simple error or dumb question, but I'm new to C++/CLI, I've been stuck on this for quite a while now, and I'm completely stumped. It's possible my specific version of this question has been answered elsewhere, but I either can't find it, or I did find it and don't understand enough to know what actually needs fixing or how to fix it. =(
I'm getting a runtime error (crash):
"Unhandled Exception: System.NullReferenceException: Object reference not set to an instance of an object.
at CreateEmployees(Int16 retCode, Void* hStmt, List`1 employee, Int32 numRows) in c:\directory\filename.cpp:line 385
at main() in c:\directory\filename.cpp:line 472
at _mainCRTStartup()
Press any key to continue . . ."
Here is line 472:
List<Employee^>^ employee; // Line 471
CreateEmployees(retCode, hStmt, employee, numRows); // Line 472
Here is the block with line 385:
void CreateEmployees(SQLRETURN retCode, SQLHANDLE hStmt, List<Employee^>^ employee, SQLLEN numRows)
{
for (int i = 0; i < numRows; i++)
{
Employee^ temp = CreateNewEmployee(retCode, hStmt); // Line 384
employee->Add(temp); // Line 385
Console::WriteLine("Successfully created Employee {0}, Employee ID: {1}", i, employee[i]->getEmployeeId());
retCode = SQLFetch(hStmt);
}
}
Here is the code called on Line 384:
Employee^ CreateNewEmployee(SQLRETURN retCode, SQLHANDLE hStmt)
{
int EmployeeId;
int DeptId;
String^ FirstName;
String^ LastName;
String^ Street;
String^ Phone;
System::String^ bufN;
char buf[256];
SQLINTEGER numBytes;
for (int i = 1; i <= 6; i++)
{
retCode = SQLGetData(
hStmt,
i, // COLUMN NUMBER of the data to get
SQL_C_CHAR, // DATA TYPE that you expect to receive
buf, // BUFFER to put the data that you expect to receive
255, // BUFFER size in bytes (-1 for null terminator)
&numBytes // SIZE in bytes of data returned
);
if (CHECK(retCode, "SqlGetData", false))
{
// Print the data we got.
bufN = gcnew String((char *)buf);
if (i == 1)
{
std::string s = msclr::interop::marshal_as<std::string>(bufN);
EmployeeId = std::stoi(s, nullptr, 0);
}
else if (i == 2)
{
FirstName = bufN;
}
else if (i == 3)
{
LastName = bufN;
}
else if (i == 4)
{
Street = bufN;
}
else if (i == 5)
{
Phone = bufN;
}
else if (i == 6)
{
std::string s = msclr::interop::marshal_as<std::string>(bufN);
DeptId = std::stoi(s, nullptr, 0);
}
}
}
Employee^ temp(gcnew Employee(EmployeeId, DeptId, FirstName, LastName, Street, Phone));
return temp;
}
Standard warning: While it's certainly possible to write the main body of your application in C++/CLI, or even write the GUI in C++/CLI using WinForms, it is not recommended. C++/CLI is intended for interop scenarios: where C# or other .Net code needs to interface with unmanaged C++, C++/CLI can provide the translation between the two. Because of that, C++/CLI has all of the complexities of C++, all of the complexities of C#, and some complexities of its own. For primary development, it is recommended to use C# with either WinForms or WPF if you want managed code, or C++ with MFC if you want unmanaged.
Now, that said:
List<Employee^>^ employee;
At this point, employee is null, because you haven't assigned anything. (By the way, if it's a list, the variable name should probably be plural: "employees".)
CreateEmployees(retCode, hStmt, employee, numRows);
OK, you're passing the null reference to the CreateEmployees method. Perfectly legal.
void CreateEmployees(SQLRETURN retCode, SQLHANDLE hStmt, List<Employee^>^ employee, SQLLEN numRows)
{
employee->Add(temp);
}
employee is still null. You need to initialize the list before adding things to it.
There's two possible fixes here.
Fix 1: Initialize before calling the method.
List<Employee^>^ employees = gcnew List<Employee^>();
Fix 2: Passing in a list to receive the result of a method is not the standard way to do things in managed land. Switch the return value of the method to return a new list.
List<Employee^>^ CreateEmployees(SQLRETURN retCode, SQLHANDLE hStmt, SQLLEN numRows)
{
List<Employee^>^ result = gcnew List<Employee^>();
for (int i = 0; i < numRows; i++)
{
...
result->Add(temp);
}
return result;
}

DirectShow's PushSource filters cause IMediaControl::Run to return S_FALSE

I'm messing around with the PushSource sample filter shipped with the DirectShow SDK and I'm having the following problem:
When I call IMediaControl::Run(), it returns S_FALSE which means "the graph is preparing to run, but some filters have not completed the transition to a running state". MSDN suggests to then call IMediaControl::GetState() and wait for the transition to finish.
And so, I call IMediaControl::GetState(INFINITE, ...) which is supposed to solve the problem.
However, to the contrary, it returns VFW_S_STATE_INTERMEDIATE even though I've specified an infinite waiting time.
I've tried all three variations (Bitmap, Bitmap Set and Desktop) and they all behave the same way, which initially lead me to believe there is a bug in there somewhere.
However, then, I tried using IFilterGraph::AddSourceFilter to do the same and it did the same thing, which must mean it's my rendering code that is the problem:
CoInitialize(0);
IGraphBuilder *graph = 0;
assert(S_OK == CoCreateInstance(CLSID_FilterGraph, 0, CLSCTX_INPROC_SERVER, IID_IGraphBuilder, (void**)&graph));
IBaseFilter *pushSource = 0;
graph->AddSourceFilter(L"sample.bmp", L"Source", &pushSource);
IPin *srcOut = 0;
assert(S_OK == GetPin(pushSource, PINDIR_OUTPUT, &srcOut));
graph->Render(srcOut);
IMediaControl *c = 0;
IMediaEvent *pEvent;
assert(S_OK == graph->QueryInterface(IID_IMediaControl, (void**)&c));
assert(S_OK == graph->QueryInterface(IID_IMediaEvent, (void**)&pEvent));
HRESULT hr = c->Run();
if(hr != S_OK)
{
if(hr == S_FALSE)
{
OAFilterState state;
hr = c->GetState(INFINITE, &state);
assert(hr == S_OK );
}
}
long code;
assert(S_OK == pEvent->WaitForCompletion(INFINITE, &code));
Anyone knows how to fix this?
IBaseFilter *pushSource = 0;
graph->AddSourceFilter(L"sample.bmp", L"Source", &pushSource);
AddSourceFilter adds a default source filter, I don't think it will add your pushsource samplefilter.
I would recommend to add the graph to the ROT, so you can inspect it with graphedit.
And what happens if you don't call GetState()?
hr = pMediaControl->Run();
if(FAILED(hr)) {
/// handle error
}
long evCode=0;
while (evCode == 0)
{
pEvent->WaitForCompletion(1000, &evCode);
/// other code
}
Open GraphEditPlus, add your filter, render its pin and press Run. Then you'll see states of each filter separately, so you'll see what filter didn't run and why.

What is the correct way to cast when using ATL and IUnknownPtr?

During the modification of an existing ATL COM object I came across an article from the "The Old New Thing" blog called "The ways people mess up IUnknown::QueryInterface" and there was a discussion in the comments section that started when one of the respondents (Norman Diamond) pointed out that that in one of the article's examples that the cast to void** was incorrect.
However when I try and correct my code to do the casting properly I end up with a memory leak.
The example was as follows:
IShellFolder *psf = some object;
IUnknown *punk = NULL;
psf->QueryInterface(IID_IUnknown, (void**)&punk);
Norman said
punk is not a void*. punk is an IUnknown*.
void** is not a universal pointer type. void* is a universal pointer type, and char* and relatives are grandparented in to be equivalent in that way, but void** is not.
If you want to obey the calling convention and avoid horrible deaths, you have to do this:
IUnknown *punk;
void *punkvoid;
psf->QueryInterface(IID_IUnknown, &punkvoid);
punk = (IUnknown *)punkvoid;
Lots of other MSDN contributors made the same identical mistake.... some people might say that it works in all VC++ implementations to date, but that doesn't make it correct code, and it's still violating the calling convention.
In light of this I went to change my old code - which was as follows:
#include <comdef.h>
...
HRESULT FinalConstruct()
{
if (m_dwROTCookie != 0)
return E_FAIL;
//Check whether there already is an instance of the Object
IUnknownPtr pUnk = NULL;
if (GetActiveObject(CLSID_Object, NULL, &pUnk) == S_OK)
{
TRACE_WARNING("An instance of Object already exists in the current context");
return S_OK;
}
HRESULT hr = QueryInterface(IID_IUnknown, reinterpret_cast<void **>(&pUnk));
hr = RegisterActiveObject(pUnk, CLSID_Object, ACTIVEOBJECT_WEAK, m_dwROTCookie);
if (FAILED(hr))
return hr;
hr = CoLockObjectExternal(pUnk, TRUE, TRUE);
pUnk = NULL;
ATLASSERT(m_dwRef == 2);
return hr;
}
I then changed it as follows:
HRESULT FinalConstruct()
{
if (m_dwROTCookie != 0)
return E_FAIL;
//Check whether there already is an instance of the Object
IUnknownPtr pUnk = NULL;
if (GetActiveObject(CLSID_Object, NULL, &pUnk) == S_OK)
{
TRACE_WARNING("An instance of Object already exists in the current context");
return S_OK;
}
void* pUnkVoid = NULL;
HRESULT hr = QueryInterface(IID_IUnknown, &pUnkVoid);
if (SUCCEEDED(hr)
{
pUnk = reinterpret_cast<IUnknown*>(pUnkVoid);
hr = RegisterActiveObject(pUnk, CLSID_Object, ACTIVEOBJECT_WEAK, m_dwROTCookie);
if (FAILED(hr))
return hr;
hr = CoLockObjectExternal(pUnk, TRUE, TRUE);
pUnk = NULL;
}
ATLASSERT(m_dwRef == 2);
return hr;
However now my application has a memory leak from this the COM Object
You likely have a memory leak because you call GetActiveObject() and QueryInterface() which upon success increment the reference count on the object, but don't call Release() later to decrement the reference count.
Mmm, I think that rather than assigning the void* to pUnk I should be using:
pUnk.Attach(reinterpret_cast<IUnknown*>(pUnkVoid));