What is the difference between the two lines of code below:
CComPtr< IInterface > m_interface;
IInterface* m_interface;
I know that CComPtr help eliminate memory leaks, but I am getting inconsistent results. When declaring the pointer with CComPtr< IInterface > m_interface;
and using the interface in my C# code there are no errors, however using the Interface in VC++ I get an unhandled exception error, even if I comment out the instance creation of IInterface.
I am pretty sure the problem is in here somewhere:
STDMETHODIMP CSomeClass::get_IClass(IClass** var)
{
return m_class_var->QueryInterface(var);
}
STDMETHODIMP CSomeClass::putref_IClass(IClass* var)
{
m_class_var = var;
return S_OK;
}
When I declare the interface pointer with: IInterface* m_interface;
I get a RPC_E_SERVERFAULT error when testing the Interface in C# and have to explicitly call GC.Collect() to avoid the error being thrown after instantiation of a few objects. When testing the Interface in VC++ the error is consistent however when it occurs is different. If I comment out the instance creation of IInterface the code runs fine, however when I try to create an instance I get same error as before, just a vague unhandled exception error. What am I doing wrong here?
CComPtr is a smart pointer designed to do the 'right' thing when used with COM idioms.
Your code for get_IClass looks good, but putref_IClass needs to call AddRef on the IClass as you're storing it. If you used CComPtr that would happen automatically.
You'll need to add more details about your VC++ unhandled exception.
IInstance* m_instance is a simple pointer to an IInstance object. You have to manage the lifetime of this pointer yourself. You don't new and delete COM objects like you would ordinary objects. Instead, the operating system allocates the object when you call the WINAPI function `CoCreateInstance' :
// instantiate the CoClass which implements IInstance...
IInstance* instance = 0;
HRESULT hr = CoCreateInstance(__uuidof(mylibrary::MyCoClass), 0, CLSCTX_INPROC_SERVER, __uuidof(mylib::IInstance), &instance);
: :
// We're done, so release the object...
instance->Release();
instance = 0;
Each COM object implements reference counting. When the last reference to an object has been Release()ed, the COM object destroys itself.
Using CComPtr<> simplifies how you manage the lifetime of the COM objects. It is a smart pointer similar in nature to std::auto_ptr or Boost's shared_ptr, but it works with COM objects. Typically when using a CComPtr you would call the CreateInstance member function rather than calling the WINAPI function, and you would not explicitly call Release when you are done. Just let the CComPtr go out of scope, and when it's destructor is called it will call Release for you:
void function()
{
// instantiate the CoClass which implements IMyInterface...
CComPtr<IInstance> instance;
instance.CoCreateInstance(__uuidof(mylibrary::MyCoClass));
: :
// We're done, so release the object...
// dont have to do anything, it will be released when function() exits
}
CComPtr< IInterface > m_interface is an object. Whereas IInterface* m_interface is a pointer.
The first will have its destructor called when it goes out of scope and I think (a long time since I used it) it will automatically call m_interface ->Release().
The latter is a pointer to an interface and you have to manage when m_interface->Release() is called.
Can you confirm that the COM object is not being released before access?
Related
I have a C# assembly that does some work and sends the results of the work back to a C++ core. I am trying to use Reflection to pass it back since the C# assembly runs on a different thread than the one it was initialized by from the C++ core. I have tried using the COM interface as the parameter type.
IDL:
HRESULT SendEvent([in] IEventData *pEventData);
C#:
WECOInspectionCoreIDL.IEventData eventData = new EventData() as WECOInspectionCoreIDL.IEventData;
var parameters = new object[1];
parameters[0] = eventData;
_piInspectionCore.GetType().InvokeMember("SendEvent", BindingFlags.InvokeMethod, null, _piInspectionCore, parameters);
This gets the error "0x80020005 Type mismatch" in the atlcom.h hRes = m_pInfo->Invoke(...) call which apparently eventually gets converted to "Unable to cast COM object of type 'System.__ComObject' ... No such interface supported.
I've also tried making the parameter an IDispatch* and then the call goes through to C++, but it doesn't seem to be the real object.
IDL:
HRESULT SendEvent([in] IDispatch *pEventData);
C++:
STDMETHODIMP CInspectionCore::SendEvent(IDispatch *pEventData)
{
IEventData *pIEventData = (IEventData *)pEventData;
Even calling pIEventData->GetIDsOfNames() fails.
Is there a way to pass a COM object created in C# to C++ when the C# object is called from a different C++ thread?
With COM, you should never cast a COM interface into another COM interface like this:
STDMETHODIMP CInspectionCore::SendEvent(IDispatch *pEventData)
{
IEventData *pIEventData = (IEventData *)pEventData; // wrong!
}
Instead you must use QueryInterface, this works fine:
STDMETHODIMP CInspectionCore::SendEvent(IDispatch *pEventData)
{
IEventData* pIEventData;
HRESULT hr = pEventData->QueryInterface(&pIEventData);
if (FAILED(hr)) // etc.
}
In some cases (in fact often), raw casting may work which can give the false impression that it's ok.
In your case, it doesn't work because you use different threads which creates implicit proxies (COM apartments, etc.). You can see that if you breakpoint in SendEvent have a look at the call stack when it's called, it's all COM marshaling stuff.
I have unmanaged class. In that class I have a managed object. Now I want a pin_ptr for the managed object in my unmanged class. And when I try to do this I get this error "error C3265: cannot declare a managed _pinnedProject in an unmanaged ProjectWrapper How can I achieve this?
Here is the code.
class ProjectWrapper
{
private:
msclr::auto_gcroot<Project^> _project; // --> This works fine,
pin_ptr<msclr::auto_gcroot<Project^>> _pinnedProject; // ---> This gives error
public:
ProjectWrapper()
{
_project = gcnew Project();
pin_ptr<msclr::auto_gcroot<Project^>> anotherPinnedProject = &_project; // --> This one works,
//_pinnedProject = _project; // --> I want to use this instead of above live,
}
}
The MSDN article about pin_ptr<> is not shy about telling you why this cannot work:
Pinning pointers can only be declared as non-static local variables on the stack.
Pinning pointers cannot be used as:
function parameters
the return type of a function
a member of a class
the target type of a cast.
Which is all for a rather good reason, this kind of object pinning is very efficient. It doesn't require an explicit call into the CLR at all, the pin is discovered when the garbage collector walks the stack looking for roots. That requires the object reference to be a local variable, the compiler emits it in the MSIL with the [pinned] attribute. Also exposed in the C# language with the fixed keyword.
So, no can do, nor should you pursue this. Pinning an object for a long time is very detrimental to GC, it is a rock in the road and prevents heap segments from getting recycled. You should only pin at the exact moment in time that you need the pointer to be stable, that moment only occurs inside of the code of the function that uses the pointer.
If you want to wish this problem away then you need to fall back to the heavy pin. That requires GCHandle::Alloc(), passing GCHandleType::Pinned. You get the pointer you need from AddrOfPinnedObject(), release with the Free() method.
I am attempting to aggregate a .NET object within a COM object (the outer unknown) using the following code:
Object obj = ... // some ComVisible .NET object
var comObj = (IMyComInterface)Activator.CreateInstance(Type.GetTypeFromProgID("some.progid"));
var unknown = Marshal.GetIUnknownForObject(comObj);
var innerUnknown = Marshal.CreateAggregatedObject(unknown, obj);
// This is where the ExecutionEngineException is thrown.
var aggObj = Marshal.GetObjectForIUnknown(innerUnknown);
// Make outer object aware of our dotnet object, say.
comObj.DotNetObj = aggObj;
Marshal.Release(unknown);
...
Is there anything "obvious" wrong with this code? This exception is leaving me with very little to go on. Also annoying is that the exception does not always happen, although it does more often than not.
Is there another way to achieve this? (ie marshal the innerUnknown IntPtr to my COM object)
NOTE: I have to target version 2 of the framework and have not tried version 4, and am, as far as I know, fully up to date.
To answer the question the reason it is failing is that the innerUnknown is the wrong unknown. The method is intended to get the active RCW for a COM IUnknown, but this IUnknown is a Com-callable wrapper for a Managed object.
In any case, the outer object needs the innerUnknown pointer. If you gave it the one you are trying to you would get a stack overflow in QueryInterface.
In your example above, the .Net object thinks it has been aggregated by the COM object, but the COM object isn't aware. Assuming it is your COM object, you need to give it innerUnknown, and then implement a delegating IUnknown (if you haven't already). You don't need to call GetObjectForIUnknown.
You are kind of doing it backwards however. The usual way is for the COM object to call CoCreateInstance passing itself as the outer unknown. The runtime then calls CreateAggregatedObject for you.
If you want the .Net object to aggregate the COM object, you should inherit from the RCW. This will create the COM object as an aggregated object passing your .Net inner unknown to CoCreateInstance. But it has to be an object which supports aggregation.
If you want the COM object to aggregate the .Net object, you have to use a COM object which does that. Typical examples are ADO aggregating OLEDB providers, ADSI aggregating ADSI extensions, WMI (I think). But it has to be supported by the object, you can't tell any old object to aggregate you. Usually it occurs by the other object calling CoCreateInstance, which is handled by the .Net runtime, which will call CreateAggregatedObject for you, and pass the innerUnknown back to the caller.
I'm currently working on an upgrade to a project that extensively uses COM / MFC / (who knows how many other technologies). As part of the upgrade, we're trying to move as much functionality as we can into managed C# code, but unfortunately some stuff just can't move (for reasons I won't go into). One of those things is a section of code that grossly abuses COM, and returns pointers in the HRESULTs that later get cast to various other COM-interface implementing objects. I've tried the following code to convert an HRESULT into a pointer that I can then get an interface from :
MyComInterfaceInCS myObj = null;
try
{
world.GetTD_MyComInterfaceInCS();
}
catch (COMException comException)
{
int pointerValue = Marshal.GetHRForException(comException);
IntPtr myObjPointer = new IntPtr(pointerValue);
myObj = (MyComInterfaceInCS) Marshal.GetObjectForIUnknown(myObjPointer);
}
.... but, no COM exception gets thrown, and I'm guessing it's because the pointer is not a negative value, and hence isn't technically a COM error. Is there any way to configure COM on the object to throw a COM exception on anything BUT S_OK (0) ?
Two thoughts:
Find and kill (or severely maim)
whoever implemented this library
Implement a wrapper in managed C++
as you will need to get at the
method call's HResult directly and I
cannot think of a way to do that
using interop.
EDIT
Another option is to declare the COM interfaces in C# such that the signature of each method returns an HRESULT and uses [out retval] for return values. This will alow you to get and examine the HRESULT of all method calls, not just ones that throw a COMException.
By default COM interop typeimport "fixes up" the method signatures such that the HRESULT return is removed on the managed side and the interop layer throws exceptions for E_FAIL etc, essentially throwing away S_OK, S_FALSE and the like.
This article has some explanation and the PreserveSig attriubte documentation has some additoinal detail.
This will likely require that you declare all of the COM interfaces manually rather than using tlbimp but you may be able to get tlbimp to preserve the signature of the COM methods.
This would allow you to stay purely in C# but in your shoes I'd go with managed C++ as it is just easier to interact with COM in this non-standard fashion from there.
I am writing an application that needs to record video using DirectShow - to do this, I am using the interop library DirectShowLib, which seems to work great.
However, I now have the need to get a callback notification as samples are written to a file, so I can add data unit extensions. According to the msdn documentation, in C++ this is done by implementing the IAMWMBufferPassCallback interface, and passing the resulting object to the SetNotify method of a pin's IAMWMBufferPass interface.
So, I created a small class that implements the IAMWMBufferPassCallback interface from DirectShowLib:
class IAMWMBufferPassCallbackImpl : IAMWMBufferPassCallback
{
private RecordingPlayer player;
public IAMWMBufferPassCallbackImpl(RecordingPlayer player)
{
this.player = player;
}
public int Notify(INSSBuffer3 pNSSBuffer3, IPin pPin, long prtStart, long prtEnd)
{
if (player.bufferPin == pPin && !player.firstBufferHandled)
{
player.firstBufferHandled = true;
//do stuff with the buffer....
}
return 0;
}
}
I then retrieved the IAMWMBufferPass interface for the required pin, and passed an instance of that class to the SetNotify method:
bufferPassCallbackInterface = new IAMWMBufferPassCallbackImpl(this);
IAMWMBufferPass bPass = (IAMWMBufferPass)DSHelper.GetPin(pWMASFWriter, "Video Input 01");
hr = bPass.SetNotify(bufferPassCallbackInterface);
DsError.ThrowExceptionForHR(hr);
No exception is thrown, indicating that the SetNotify method succeeded.
Now, the problem is, that the Notify method in my callback object never gets called. The video records without a problem, except for the fact that the callback is not getting executed at all.
Is it a problem with the way I am doing the interop?
I usually work with delegates for function pointers in unsafe C# code.
The only trick is that you need to make sure that the reference to the delegate does not get garbage-collected, as the reference in the unsafe code is not used when counting the references inside the garbage collector. Thus the function either needs to be either static or the object which holds the reference needs an additional artificial reference to ensure its availability.
This is a tutorial for using delegates on MSDN.
The way you are doing the interop looks totally fine. I looked at the MSDN docs and it also looks like you are going the right direction, so I don't know why it wouldn't make the callback.
Once you do get it doing the callback, however, you might want to make sure to call Marshal.ReleaseComObject(pNSSBuffer3). Sometimes sample buffers aren't released for processing until all other references have been removed. This is the case with the SampleGrabber, so might be the same for this.
You should probably be using a delegate:
delegate int NotifyDelegate(INSSBuffer3 pNSSBuffer3, IPin pPin, long prtStart, long prtEnd);
void run(){
bufferPassCallbackInterface = new IAMWMBufferPassCallbackImpl(this);
IAMWMBufferPass bPass = (IAMWMBufferPass)DSHelper.GetPin(pWMASFWriter, "Video Input 01");
NotifyDelegate d = new NotifyDelegate(bufferPassCallbackInterface.Notify);
hr = bPass.SetNotify(d);
DsError.ThrowExceptionForHR(hr);
}
However, I'm not quite sure how your implementing an unmanaged interface on a managed class. An INSSBuffer3* in C++ does not directly correlate to new INNSBuffer3() in C#.
Edit:
In response to your comment, the SetNotify function is defined as
HRESULT SetNotify(
[in] IAMWMBufferPassCallback *pCallback
);
So, it requires a pointer to an implementation of IAMWMBufferPassCallback.
MSDN says that pCallback is pCallback a
Pointer to the application's
IAMWMBufferPassCallback interface.
The delegate is (an attempt to be) the managed equivalent to a pointer to IAMWMBufferPassCallback. A call back is, generally, a method, not an object. So, passing in an instance of the delegate should work for you. Another similar example is with InternetSetStatusCallback, where you pass a pointer to an implementation of InternetStatusCallback.
I would try it, if it doesn't work, why not try a library where someone has already done all the hard work? DirectShowNet may be an option (I've never used it).