I was reading somewhere that 'every STA needs it's own message pump' .. does
this also apply to the scenario where an *inproc* object creates a bunch of
worker threads (and makes those threads STA's by calling CoInitalize(NULL))
.... I have a test app here that is an inproc control that does just this ..
spawns a bunch of worker threads, makes them into STA's and marshals
interfaces into them from the main STA .. and there's only one message pump
pumping.. out in the container app ... and it all seems to work just fine...
"mysteron" <email@example.com> wrote in message
>I was reading somewhere that 'every STA needs it's own message pump' ..
>does this also apply to the scenario where an *inproc* object creates a
>bunch of worker threads (and makes those threads STA's by calling
>CoInitalize(NULL)) ... I have a test app here that is an inproc control
>that does just this .. spawns a bunch of worker threads, makes them into
>STA's and marshals interfaces into them from the main STA .. and there's
>only one message pump pumping..
Not true. Each thread lives in its own STA, and each of those has a message
pump as well.
I assume you mean your worker threads are blocking on an
event or something waiting for work to come. Try running
Word and notice how long it takes to start. The problem is
it is a DDE server and DDE uses broadcast messages.
Since your threads are not dispathing their message queues,
the DDE broadcast hangs (until it times out) waiting for
responses from any top-level windows on a blocked thread.
Note DDE is not the only piece of code to broadcast
messages - open sysedit, edit win.ini and save and this
results in a WM_WININICHANGE message being broadcast
too. (At least that's my memory from Win9x... A quick
check in MSDN Library reveals this has been superseded
by WM_SETTINGCHANGE today, but the principle is the same.)
The moral is: "seems to work" does not equal "works".
Microsoft MVP [VC], MCSD
MVP VC FAQ: http://www.mvps.org/vcfaq
i'm having trouble seeing this hidden window in spy++ ... soes anyone know
if there are any issues that might be preventing me from peeking into this
window and watching the COM calls as they are invoked?
mysteron <firstname.lastname@example.org> wrote:
> i'm having trouble seeing this hidden window in spy++ ... soes anyone
> know if there are any issues that might be preventing me from peeking
> into this window and watching the COM calls as they are invoked?
It's a so called message-only window (see CreateWindow, HWND_MESSAGE).
These don't show in Spy++.
With best wishes,
With sufficient thrust, pigs fly just fine. However, this is not
necessarily a good idea. It is hard to be sure where they are going to
land, and it could be dangerous sitting under them as they fly
overhead. -- RFC 1925
I'm trying to find some decent documentation about how things work down at
the proxy/stub level in apartment threaded COM ... haven't found much so
Q: anyone know where I can find a really good detailed description of how
all that is implemented?
Q: in a simple scenario, two STA's in a single process, talking to each
other -what exactly is COM doing with message queues and worker threads
inside the proxy/stub? does it boil down to a simple sendmessage from one
thread to the other .. or is it a postmessage? My understanding is that it
is the latter.
Q: this isn't a COM question as such but i'd like your opinion on this ..
two processes A and B are running. They each create a window. At random
intervals they do a sendmessage to the opposite process. Isn't this a clear
invitation to deadlock on windows? Same of course if A and B are threads in
the same process.. right?
Microsoft MVP [VC], MCSD
MVP VC FAQ: http://www.mvps.org/vcfaq
"mysteron" <email@example.com> wrote in message
> i'm trying to find some decent documentation about how things work down at
> the proxy/stub level in apartment threaded COM ... haven't found much so
> Q: anyone know where I can find a really good detailed description of how
> all that is implemented?
If you manage to find one, I'd love to get the link as well...
> Q: in a simple scenario, two STA's in a single process, talking to each
> other -what exactly is COM doing with message queues and worker threads
> inside the proxy/stub? does it boil down to a simple sendmessage from one
> thread to the other .. or is it a postmessage? My understanding is that it
> is the latter.
Both can be used depending on the interface. Normally COM uses
PostMessage, unless the method specifies the [input_sync] attribute.
Very few interfaces use that, mostly ones dealing with OLE and
ActiveX Documents. Alas, it's very poorly documented, you'll be hard
pressed to find any mention of [input_sync] on Microsoft's site. I used
Google to find links.
> Q: this isn't a COM question as such but i'd like your opinion on this ..
> two processes A and B are running. They each create a window. At random
> intervals they do a sendmessage to the opposite process. Isn't this a
> clear invitation to deadlock on windows? Same of course if A and B are
> threads in the same process.. right?
No, there's no deadlock. IIRC, SendMessage spins a modal loop
after posting the message to the target thread's message queue.
I believe the loop does not dispatch all messages, but it does let
through other SendMessage calls. You should ask this on a UI
group where the experts are.
The rules of COM states that if an interface pointer is returned from a method (i.e. it is an [out] parameter, which means that the parameter would be typed something like ISomeInterface**), the method implimenter must AddRef() it and the client must later Release() it.
However, in your case, the "pInter" inerface pointer of MyEvent() is an [in] pointer which means that it is passed by value and not supplied as a reference. Hence, it should not be AddRef()'ed and the event handler should not call Release().
COM is a truly excellent programming model for the development of integrating components based on interfaces. Some of the fundamental principles of COM have their roots in Object-Oriented Philosophies. It is a great platform for the realization of Object-Oriented Development and Deployment.
One of COM's major contributions to the world of Windows Development is the awareness of the concept of separation of interface from implementation. This awareness has no doubt profoundly influenced the way programmers build systems today.
An extension of this fundamental concept is the notion of : one interface, multiple implementations. By this, we mean that at runtime, a COM client can choose to instantiate an interface from one of many different concrete implementations. Each concrete implementation can be written in any programming language that supports COM component development, e.g. C++, Visual Basic, Delphi, PowerBuilder, etc.
Via COM-interop, a .NET component can also be deployed as a COM component. This implies that a COM interface implementation can be developed in a .NET language like C#. And this is what I aim to expound on and demonstrate in this article : the implementation of a COM interface in C#.
I will assume that the reader already has prior knowledge on the following :
1. COM development in general and IDL in particular.
2. C++ and ATL.
3. C# and various .NET tools like tlbimp.exe and gacutil.exe.
The following is a general outline of how I intend to go about my explanations :
1. We will briefly recap IDL and then define a simple COM interface.
2. We will develope 2 separate concrete implementations of this interface : one in C++ and the other in C#.
3. We will write a client application that will instantiate both concrete implementations and call their methods.
Emphasis will be given to the C# implemetation. I will expound on how a C# component is transformed into a COM component usable by an unmanaged client.
A Short Refresher on IDL
The common practice in COM development is to start the definition of an interface using IDL (interface definition language). On this note, it is important to grasp the intended purpose of IDL and understand how it realizes OO principles. An IDL file is not just "another one of Microsoft's proprietary file types". It deserves deeper understanding.
Although I have stated that I wil assume the reader has prior knowledge of IDL, I will go on just a little anyway to explain here some of the more important concepts behind the IDL and the coclass (an IDL keyword) in particular :
A coclass is COM's (language independent) way of defining a class (class in the object-oriented sense).
An IDL file is what COM provides that allows developers to define such language independent object-oriented classes.
An IDL file is compiled by the MIDL compiler into a Type Library (.TLB file) which is a binary form of an IDL file meant to be processed by various language compilers (e.g. VB, VC++, Delphi, etc).
The end result of such .TLB processing is that the specific language compiler produces the language-specific constructs (VB classes for VB, C++ classes, various structs, macros and typedefs for VC++) that REPRESENT the coclass defined in the .TLB (and ultimately that which was defined in the originating .IDL).
When you look at an example coclass definition in an IDL :
it is declaring a COM class named MyObject which must implement an interface named IMyObject and which supports (not implement) the event interface _IMyObjectEvents.
This is conceptually equivalent to defining a C++ class like this :
class CSomeObject : public ISomeInterface
where ISomeInterface is a C++ virtual class.
It is up to the individual language compiler to produce whatever code (in the specific compiler's language) is necessary for its user to implement and ultimately produce a binary which can be deemed by COM to be of coclass MyObject.
Now, in languages like C++, we can use CoCreateInstance() in which we can specify the CLSID of the coclass and select the interface from that coclass that we want to use to interact with that coclass. Calling CoCreateInstance() like this :
is conceptually equivalent to the following C++ code :
ISomeInterface* pISomeInterface = NULL;
pISomeInterface = new CSomeObject();
In the first case, we are saying to the COM sub-system that we want to obtain a pointer to an object that implements the IMyObject interface and we want coclass CLSID_MyObject's particular implementation of this interface.
In the second case, we are saying that we want to create an instance of a C++ class that implements the interface ISomeInterface and we are using CSomeObject as that C++ class.
Do you see the equivalence ? A coclass, then, is an object-oriented class in the COM world. The main feature of the coclass is that it is (1) binary in nature and consequently (2) programming language-independent.
We begin our code development by defining a simple COM interface named ISimpleCOMObject. Using Visual Studio .NET, we create an ATL project named SimpleCOMObject.sln (complete source codes for this project is included in the downloadable sample codes).
This SimpleCOMObject.sln project will not contain any useful implementation codes. Its purpose is simply to allow us to automate the creation and maintenance of the ISimpleCOMObject interface. This is accomplished via the use of the ATL Wizards.
The project file folder also serves as the central point for the storage and generation of various .NET related resources (e.g. Primary Interop Assembly, Strong Name Key Files, etc). More on these later.
Listed below is a fragment of code taken from SimpleCOMObject.idl showing the ISimpleCOMObject interface :
ISimpleCOMObject contains one property (LongProperty) and one method (Method01()). We stipulate that ISimpleCOMObject is meant to be implemented by an object that takes a long value (LongProperty) and then displays its value when we call Method01(). The BSTR parameter "strMessage" is meant to be a short message which is to be displayed with the LongProperty value when Method01() is called.
We defined ISimpleCOMObject as a dual interface. Hence a client application can call its methods by using a vtable interface pointer or by using an IDispatch interface pointer. Although it is possible to define ISimpleCOMObject as deriving from IUnknown, I have chosen to derive it from IDispatch in order to ensure that parameters and return values for its methods be strictly automation-compatible (i.e. the types that can be stored in a VARIANT structure). This is done to keep things simple.
I will assume that the reader is already well-versed in the implementation of a COM interface using an unmanaged language like C++. It is the implementation in a .NET language like C# that I intend to explain in detail in this article. The use of automation-compatible types will help to keep this process as simple as possible.
Although SimpleCOMObject.sln contains no useful implementation for ISimpleCOMObject, we will nevertheless need to compile it. Once this project is compiled successfully, two important files will be produced :
1. A Type Library (SimpleCOMObject.tlb).
2. A DLL (SimpleCOMObject.dll).
These will be created in the Debug directory of the project's containing folder. If you have downloaded my sample codes, and have not modified anything so far, these files will be stored in the following path :
where is wherever you have copied the source codes zip file to.
The SimpleCOMObject.tlb will be used to produce something known as a Primary Interop Assembly. More on this in the next section.
The SimpleCOMObject.dll itself will contain the SimpleCOMObject.tlb (embedded as a binary resource). It is important as it will be registered (by the Visual Studio .NET IDE) as the Type Library for the coclasses and interfaces defined in SimpleCOMObject.idl.
This Type Library registration is done so that COM will know where to look for type information when it needs to perform marshalling for the interfaces defined in SimpleCOMObject.idl. This process is also known as Type Library Marshaling.
Neither IDL nor Automation define byte alignment for a struct. VB assumes 4-byte alignment, while #import in VC assumes 8-byte alignment. For most Automation structs this isn't an issue. However, if you use 8-byte types like double, this may make a difference:
For VB, the double field starts at the fourth byte of the struct, while for VC's #import it starts at the eight byte. This poses a significant problem. It can be solved by adding an inject_statement clause to #import:
By the way, I also checked up MFC's implementation codes for
COleDispatchDriver. It does the same thing, i.e. set
DISPPARAMS.rgdispidNamedArgs to DISPID_PROPERTYPUT and DISPPARAMS.cNamedArgs
to 1 before calling Invoke().
It seems that in some cases (going through the process of TLBs, .NET interop, etc.) the tag is used and sometimes the typedef name is used, and sometimes you get messed up if you have different tags because you've imported the same IDL file in two different places. So if you just use the same name for both you won't have weird problems later.
I guess everyone except me already knew this, but I just thought I'd pass it on in case someone didn't!
Recently, a CP member named LongIsland wrote the following post in the ATL forum :
--- START ---
Hello. I am new to ATL and had a serious problem, see I want to use interface A and interface B, both from MS. The bad thing is they all have method named "GetWindowHandle", what should I do ???!!!
I tried to modify one of them's header to just make it compile, but it just sucks, I think there must be a decent solution.
Thanks a lot ahead.
--- END ---
It was replied by Jörgen Sigvardsson. See next message.