S
shehrzad
I am trying to implement an COM object (in ATL), that can be used in a
variety of contexts (Win32 console apps, MFC dialog apps, WinForms C#
or MC++ apps). This ActiveX control is a logger component writes a
message to a log file, calls OutputDebugString, and then fires an
event. The way I have my code structured is that I have an unmanaged
static library that wraps this COM object around a singleton:
class LoggerSingleton { // lots of implementation details omitted
public:
LoggerSingleton *getInstance();
ILogger *getActiveX() { return m_pInterface; }
void printf(char *fmt, ...); // calls m_pInterface->writeMsg(...)
private:
static LoggerSingleton *m_pInstance;
interface ILogger *m_pInterface; // from CoCreateInstance(...)
};
My idea was that in my Managed C++ WinForms app, I'll be able to log
messages by simply calling LoggerSingleton::getInstance()->printf(),
and likewise all of my unmanaged threads that are implented in static
libraries would do the same. Moreover, my thought was to use COM
interop to sink the events fired by the underlying control, so for
example whenever I sink a "new log message" event I can display it in a
Rich Text Edit box. The key thing here is I want to use interop
facilities so that it is .NET-esque, namely I want to use delegates to
set up my event sinks, as opposed to dropping down and using connection
points and such.
My problem is that from the .NET world I am having trouble creating a
CoClass object (defined in the Interop assembly) from an interface
pointer. The COM object has already been created by a thread in
unmanaged code by the time my WinForms app is ready to += its delegate.
It's relatively straightforward for me to get a managed reference to
an interface from an existing COM interface:
using System::Runtime::InteropServices;
Object ^iuobj =
Marshal::GetObjectForIUnknown(
(IntPtr)LoggerSingleton::getInstance()->getActiveX() );
// LoggerActiveXLib is generated for me when I
// "add reference" to my ActiveX object
// and the type library is imported into the project
LoggerActiveXLib::ILogger ^pLogger =
Marshal::CreateWrapperOfType(iuobj,
Type::GetTpyeFromCLSID(Guid(...)));
So this is all fine & dandy, my Managed C++ can invoke
pLogger->writeMsg(...) while my native threads are invoking the same
method (through that singleton). My problem is how do I sink the event
that the COM object exposes (through its CoClass) if I just have a
managed ref to a COM interface?
I can instantiate the "interop CoClass" very easily by doing something
like:
LoggerActiveXLib::LoggerClass ^pLoggerCoClass = gcnew
LoggerActiveXLib::LoggerClass();
again of course where LoggerClass is generated for me. I can then sink
events via
pLoggerCoClass->someEvent += gcnew
LoggerActiveXLib::_ILoggerEvents_EventHandler(...);
But then this is a whole new instantation of my ActiveX logger, and
hence when my unmanaged code writes log messages I won't get
notification of them. Is there some way for me to get a managed
reference to a CoClass Interop object from a managed reference to it's
COM interface Interop object? I attempted a bunch of things within the
Marshal class, to no avail, for example:
#using LoggerActiveXLib;
LoggerClass ^pLoggerCoClass = (LoggerClass ^)
Marshal::CreateWrapperOfType(iuobj,
Type::GetTpyeFromCLSID(Guid(...)));
and
LoggerClass ^pLoggerCoClass = (LoggerClass ^pLoggerCoClass)
Marshal::GetTypedObjectForIUnknown((IntPtr)LoggerSingleton::getInstance()->getActiveX()
Type::GetTpyeFromCLSID(Guid(...)));
In each of these cases, I got an exception with something to the effect
that "Unable to case COM object of type 'System.__ComObject' to class
type ..."
Failing miserably, I then figured that ok, since I can't seem to get
the CoClass object (which I just need so I can set up my event sinks,
implemented cleanly using .NET delegates) I'll just instantiate a
CoClass object and pass it down to this unmanaged code, and then the
unmanaged code will be none the wiser and I can get my delegates in the
managed code. I added a "setter" in my singleton, so from .NET land I
could do something like the following:
// instantiate CoClass
// add delegates to sink ActiveX events
IntPtr pInterface = Marshal::GetIunknownForObject(pLoggerCoClass );
LoggerSingleton::getInstance()->setActiveX( pInterface );
Surprisingly enough, this almost worked, except for one problem.
Now in the ATL connection point method, where the event is actually
fired, ->Invoke is coming back with an HRESULT of "Exception Occurred",
whenever my unmanaged threads write a log message. The actual writing
of a log message, i.e. invocation of the method through the COM
interface passed down from the Managed C++ app, works, but the event
throwing doesn't. Everything works if the Windows Form Managed C++
code writes a message.
This made me think it was some threading apartment thing, since it
works from one context but not the other. I did make sure I was
calling CoInitializeEx(NULL, COINIT,_MULTITHREADED) in all of my
threads, yet I still remain stymied.
In conclusion, if I could somehow set up ActiveX event sinks using
delegates given just an interface managed reference I'd be set (I don't
think this possible).
Barring that, how can I get a managed ref to the CoClass, because that
would also work out for me.
Lastly, how come if I explictly instantiate a CoClass object through
..NET interop and pass it down to the unmanaged world, unmanaged code
cannot fire events while managed code can? Do I perhaps need to "pin"
the interface pointer before it crosses the Managed to Native boundary?
Any help anyone can provide would be *most* appreciated.
Shehrzad Qureshi
variety of contexts (Win32 console apps, MFC dialog apps, WinForms C#
or MC++ apps). This ActiveX control is a logger component writes a
message to a log file, calls OutputDebugString, and then fires an
event. The way I have my code structured is that I have an unmanaged
static library that wraps this COM object around a singleton:
class LoggerSingleton { // lots of implementation details omitted
public:
LoggerSingleton *getInstance();
ILogger *getActiveX() { return m_pInterface; }
void printf(char *fmt, ...); // calls m_pInterface->writeMsg(...)
private:
static LoggerSingleton *m_pInstance;
interface ILogger *m_pInterface; // from CoCreateInstance(...)
};
My idea was that in my Managed C++ WinForms app, I'll be able to log
messages by simply calling LoggerSingleton::getInstance()->printf(),
and likewise all of my unmanaged threads that are implented in static
libraries would do the same. Moreover, my thought was to use COM
interop to sink the events fired by the underlying control, so for
example whenever I sink a "new log message" event I can display it in a
Rich Text Edit box. The key thing here is I want to use interop
facilities so that it is .NET-esque, namely I want to use delegates to
set up my event sinks, as opposed to dropping down and using connection
points and such.
My problem is that from the .NET world I am having trouble creating a
CoClass object (defined in the Interop assembly) from an interface
pointer. The COM object has already been created by a thread in
unmanaged code by the time my WinForms app is ready to += its delegate.
It's relatively straightforward for me to get a managed reference to
an interface from an existing COM interface:
using System::Runtime::InteropServices;
Object ^iuobj =
Marshal::GetObjectForIUnknown(
(IntPtr)LoggerSingleton::getInstance()->getActiveX() );
// LoggerActiveXLib is generated for me when I
// "add reference" to my ActiveX object
// and the type library is imported into the project
LoggerActiveXLib::ILogger ^pLogger =
Marshal::CreateWrapperOfType(iuobj,
Type::GetTpyeFromCLSID(Guid(...)));
So this is all fine & dandy, my Managed C++ can invoke
pLogger->writeMsg(...) while my native threads are invoking the same
method (through that singleton). My problem is how do I sink the event
that the COM object exposes (through its CoClass) if I just have a
managed ref to a COM interface?
I can instantiate the "interop CoClass" very easily by doing something
like:
LoggerActiveXLib::LoggerClass ^pLoggerCoClass = gcnew
LoggerActiveXLib::LoggerClass();
again of course where LoggerClass is generated for me. I can then sink
events via
pLoggerCoClass->someEvent += gcnew
LoggerActiveXLib::_ILoggerEvents_EventHandler(...);
But then this is a whole new instantation of my ActiveX logger, and
hence when my unmanaged code writes log messages I won't get
notification of them. Is there some way for me to get a managed
reference to a CoClass Interop object from a managed reference to it's
COM interface Interop object? I attempted a bunch of things within the
Marshal class, to no avail, for example:
#using LoggerActiveXLib;
LoggerClass ^pLoggerCoClass = (LoggerClass ^)
Marshal::CreateWrapperOfType(iuobj,
Type::GetTpyeFromCLSID(Guid(...)));
and
LoggerClass ^pLoggerCoClass = (LoggerClass ^pLoggerCoClass)
Marshal::GetTypedObjectForIUnknown((IntPtr)LoggerSingleton::getInstance()->getActiveX()
Type::GetTpyeFromCLSID(Guid(...)));
In each of these cases, I got an exception with something to the effect
that "Unable to case COM object of type 'System.__ComObject' to class
type ..."
Failing miserably, I then figured that ok, since I can't seem to get
the CoClass object (which I just need so I can set up my event sinks,
implemented cleanly using .NET delegates) I'll just instantiate a
CoClass object and pass it down to this unmanaged code, and then the
unmanaged code will be none the wiser and I can get my delegates in the
managed code. I added a "setter" in my singleton, so from .NET land I
could do something like the following:
// instantiate CoClass
// add delegates to sink ActiveX events
IntPtr pInterface = Marshal::GetIunknownForObject(pLoggerCoClass );
LoggerSingleton::getInstance()->setActiveX( pInterface );
Surprisingly enough, this almost worked, except for one problem.
Now in the ATL connection point method, where the event is actually
fired, ->Invoke is coming back with an HRESULT of "Exception Occurred",
whenever my unmanaged threads write a log message. The actual writing
of a log message, i.e. invocation of the method through the COM
interface passed down from the Managed C++ app, works, but the event
throwing doesn't. Everything works if the Windows Form Managed C++
code writes a message.
This made me think it was some threading apartment thing, since it
works from one context but not the other. I did make sure I was
calling CoInitializeEx(NULL, COINIT,_MULTITHREADED) in all of my
threads, yet I still remain stymied.
In conclusion, if I could somehow set up ActiveX event sinks using
delegates given just an interface managed reference I'd be set (I don't
think this possible).
Barring that, how can I get a managed ref to the CoClass, because that
would also work out for me.
Lastly, how come if I explictly instantiate a CoClass object through
..NET interop and pass it down to the unmanaged world, unmanaged code
cannot fire events while managed code can? Do I perhaps need to "pin"
the interface pointer before it crosses the Managed to Native boundary?
Any help anyone can provide would be *most* appreciated.
Shehrzad Qureshi