News:

MASM32 SDK Description, downloads and other helpful links
MASM32.com New Forum Link
masmforum WebSite

Connecting com interface

Started by ToutEnMasm, February 19, 2009, 09:37:35 PM

Previous topic - Next topic

ToutEnMasm


I am writing a browser for com,that can retrieve the CLSID associated with the interface.
To do this,he need to know manything.For example.
Quote
invoke CoInitializeEx,NULL,COINIT_APARTMENTTHREADED
He must also know if the connection can be made by cocreateinstance or by progid.
Most of the needed informations are in the registry.
Some caller need a proxy before calling an interface (Idon't know how to make a proxy)
and more ...and more
---------------------------------------------------------------------------------------------------
What i search his informations,code,that help making connections.
Thanks for answer.

donkey

Most of this information is available in through ITypeLib for a specific class, however you might want to read this...

http://www.codeguru.com/cpp/com-tech/activex/apts/print.php/c5529

A proxy is simply a class that you can call members of another interface through without having to instantiate that interface directly. For example you create a class that uses an instance of IPersistFile, that class can be a Proxy for IPersistFile since you can call its methods through your class, your class would usually just have stubs for the target interfaces methods. In this case it might have a stub for IPersistFile::Save in it's VTable and when it is called it creates an instance of IPersistFile and calls that method.
"Ahhh, what an awful dream. Ones and zeroes everywhere...[shudder] and I thought I saw a two." -- Bender
"It was just a dream, Bender. There's no such thing as two". -- Fry
-- Futurama

Donkey's Stable

baltoro

ToutEnMasm,
I don't know if this will help,...I've been reading Don Box's "Essential COM" lately, and he has a chapter on the Standard Marshaling Architecture. COM uses the Object Remote Procedure Call protocol for all cross-apartment access (all remoting). All remote COM activation calls (like CoCreateInstance or CoGetClassObject) use a Proxy and Stub architecture that the COM library manages. You probably know all that. Anyway, I was googling around and found this site: http://www.hsc.fr/ressources/articles/win_net_srv/chap_msrpc.html. I don't speak French, but most of the articles at this site are in FRENCH.
Also, there is a reasonabvly good explanation of the COM+ Standard Marshaling here: http://www.codeguru.cn/VC&MFC/InsideCOM+/ch15a.htm and a description of the COM+ Network Protocol here: http://www.codeguru.cn/VC&MFC/InsideCOM+/ch19a.htm. Both are in English.
Baltoro

ToutEnMasm

Hello,
Thanks for answer that are useful.
I break my teeth (french expression)  in this sort of CLSID
Quote
Windows Registry Editor Version 5.00

[HKEY_CLASSES_ROOT\CLSID\{042C3298-4790-4E9C-98C8-CC65629CBB8C}]
@="Cyberlink RipMVR Setting"

[HKEY_CLASSES_ROOT\CLSID\{042C3298-4790-4E9C-98C8-CC65629CBB8C}\InprocServer32]
@="C:\\Program Files\\CyberLink\\PowerProducer\\RipMVR.dll"
"ThreadingModel"="Both"

cocreateinstance  connect OK with the Iunknown Interface,THE problem is when I want to release the interface,I got only a crash.
Quote
.data
CLSID_RipMVR GUID <042C3298h,4790h,4E9Ch,<98h,0C8h,0CCh,65h,62h,9Ch,0BBh,8Ch>>
IID_Iunknown GUID <0H,0H,0H,<0C0H,0H,0H,0H,0H,0H,0H,046H>>
.code
invoke CoInitializeEx,NULL, COINIT_APARTMENTTHREADED
.
.
...cocreateinstance ... CLSID_RipMVR .... IID_Iunknown
return S_OK
But ...
Release go to crash

That i want is,just connect to the object,test if he can connect or not to one interface (Iunknown is useful in this case) and Release without problem.
If there is too much protections to do this,test is not made.

Help please ?







baltoro

COM Threading Models are an endless source of confusion. I think the only person on the planet that isn't confused by it is Don Box,...anyway,...
You should read: http://msdn.microsoft.com/en-us/library/ms809971.aspx, this is an explanation of COM Threading Models at MSDN.
A number of things to keep in mind:   
When you call CoInitializeEx with the COINT_APARTMENTTHREADED, you are entering a newly created STA (Single-Threaded Apartment). This is incompatible with the registry value for the Class's threading mode (ThreadingModel="Both", means that the object can execute in either a Single-Threaded or Multi-Threaded apartment)l. When that happens the COM library creates a proxy. Here, I quote directly from Don Box: 
"Each thread that calls CoInitializeEx with COINIT_APARTMENTTHREADED executes in a private apartment that no other thread can enter. If the client's apartment is incompatible with the CLSID's Threading Model, then in-process activation requests for that CLSID will force COM to silently instantiate the object in a distinct apartment, and a proxy will be returned to the client. Implementors that mark their classes as either ThreadingModel="Free" or ThreadingModel="Both" are making the statement that instances of their class may run in the MTA, which means that a single instance of the class may be accessed concurrently. If a class is marked ThreadingModel="Both" and an activation request is made from an STA-based thread, the object will live in an STA. This means that the worker threads (which will run in the MTA) must access the object using interapartment method calls,..."
Baltoro

baltoro

...And, if this wasn't confusing enough already,...it gets worse,...
More background information, from Don Box (Essential COM, 1998):   
"COM allows interface pointers to be passed across apartment boundaries using a technique called marshaling. Note that because COM deals exclusively with interface pointers, not the objects themselves, this marshaled state does not represent the state of the object, but rather the serialized state of an apartment-independent reference to the object. These marshaled object references simply contain connection establishment information that is completely independent of the object's state. Normally, interface pointers are marshaled implicitly as part of the normal operation of COM. When an in-process activation request is made for a class with an incompatible threading model, COM implicitly marshals the interface from the object's apartment and unmarshals a proxy in the client's apartment. When method calls are performed on proxies, any interface pointers that are passed as method parameters will be marshaled in order to make the object references available in both the client's and the object's apartments."     
You should read this MSDN reference : http://msdn.microsoft.com/en-us/library/ms678428(VS.85).aspx to CoMarshalInterface.   
Don Box explains further: 
"CoMarshalInterface takes an interface pointer on input and writes the serialized representation of the pointer to a caller provided byte stream. This byte stream can then be passed to another apartment, where the CoUnmarshalInterface API function uses the byte stream to return an interface pointer that is semantically equivalent to the original object yet can be legally accessed in the apartment that executes the CoUnmarshalInterface call. CoMarshalInterface also allows the caller to specify the marshaling semantics using the following marshal flags:   

typedef enum tagMSHLFLAGS {   
MSHLFLAGS_NORMAL,                      //  marshal once, unmarshal once   
MSHLFLAGS_TABLESTRONG,             //  marshal once, unmarshal many   
MSHLFLAGS_TABLEWEAK,                //  marshal once, unmarshal many   
MSHLFLAGS_NOPING = 4,                //  supress distributed garbage collection   
} MSHLFLAGS;   
 

...At this point, you're probably wondering, where is this MANIAC going with this INSANELY LENGTHY, SEEMINGLY RANDOM TECHNICAL EXPLANATION,....
Well, as it turns out, for various reasons, the external reference count that the proxy holds (and transmits with the marshaled object header byte stream) and the actual AddRef/Release reference count that the object may implement can differ significantly. COM can be maddening in this respect.
Baltoro

baltoro

...at this point, I'm guessing that you are probably thoroughly disgusted with COM,...
...but, there's more,...alot more,...in fact, it gets pretty exciting at this point, because, you now know how to solve your problem.
... and, if you need more information, there's an entire chapter on 'Standard Marshaling Architecture' that attempts to describe COM proxies and object reference counts.   
"When an object does not implement the IMarshal interface, all references to the object will be standard marshaled. When (the COM Library calls) CoMarshalInterface and determines that an object wishes to use standard narshaling, a special COM object called the stub manager is created." It goes on and on,...
...you could save me alot of typing by getting yourself a copy of Don Box's book. It is by far the best reference on the subject. I've worn my copy out.

Title: "ESSENTIAL COM",
Author: Don Box   
Copyright: 1998   
Publisher: Addison-Wesley Longman, Inc.   
ISBN: 0-201-63446-5 
Paperback, 440 pages   

Baltoro

ToutEnMasm

Hello,
Quote
I'm guessing that you are probably thoroughly disgusted with COM,...
No,No I am just searching a way to solve this and follow with interest this post.
The stub manager will be responsible of the trouble ?
Perhaps searching a sample ...

baltoro

#8
Actually, I'm a little confused by this section (Lifecycle Management and Marshaling) in Don Box's book, because I've never encountered the situation in real life. But, I'll try to summarize. (Quotes are Don Box.)   
"The stub manager is created the first time CoMarshalInterface is called on an object identity. The stub manager holds outstanding references to the object it represents, and the stub manager stays alive as long as there is at least one outstanding external reference to the stub. These external references are usually proxies, although marshaled object references are counted as well, as they represent potential proxies. When all external references ti the stub manager are destroyed, the stub manager deletes itself and releases all references it holds to the actual object. This default behavior exactly simulates the normal in-process semantics of AddRef and Release."   
Box then goes on to describe in excruciating detail the operation of COM's distributed garbage collection algorithm and the RPC messages associated with it.
"The stub manager keeps track of how many external references are outstanding. When the stub is created , this count begins at zero. When a call to CoMarshalInterface is made using MSHLFLAGS_NORMAL, this count is increased by some number 'n' that is written to the marshaled object reference. When the proxy manager unmarshals the reference, it adds 'n' to its count of held references. If CoUnmarshalInterface is called on a proxy manager to pass a copy of the reference to another apartment, the proxy manager is free to give out some number of references in order to initialize the second proxy. If a proxy has only one remaining reference, it must go back to the stub manager to request additional references."    
"It is often useful to store marshaled interface references in a central location that can be accessed by one or more clients. The Running Object Table used by some moniker implementations is the canonical example of this. If the marshaled interface pointer were to be created using MSHLFLAGS_NORMAL, then one client could ever unmarshal the object reference. If multiple clients are expected to unmarshal the object reference, then the reference needs to be marshaled using either MSHLFLAGS_TABLESTRONG or MSHLFLAGS_TABLEWEAK. In either case, the marshaled object reference can be unmarshaled mukltiple times."   
Then, Don Box goes on to describe the difference in reference counting methods that the stub manager implements when either the MSHLFLAGS_TABLESTRONG or MSHLFLAGS_TABLEWEAK Marshaling Flags are used. This stuff is incredibly tedious and seems slightly ridiculous to me, and if you think it's important, I can provide more Don Box. Box mentions a COM function, CoLockObjectExternal, http://msdn.microsoft.com/en-us/library/ms680592(VS.85).aspx by which you can access the external reference count of the stub manager, and either increment or decrement the stub manager's external reference count. Another COM function, CoDisconnectObject, http://msdn.microsoft.com/en-us/library/ms680756(VS.85).aspx allows object implementors to explicitly destroy the stub manager irrespective of the number of outstanding object references. Both CoLockObjectExternal and CoDisconnectObject must be called from within the process of the actual object and cannot be called on a proxy.   
Baltoro

baltoro

Also, it's entirely possible that the problem is something alot simpler.
You should definately change your CoInitializeEx flag to COINIT_MULTITHREADED. Don Box, again: "If the client's apartment is compatible with the CLSID's threading model,then all in-process activation requests for that CLSID will instantiate the object directly in the apartment of the client This is by far the most efficient scenario, as no intermediate proxy is needed."
" Note that to pass a pointer from a thread executing in an MTA to another thread executing in the same apartment, no marshaling calls are required." 
In the section of his book where he describes IUnknown reference counting and its pitfalls, he lists a set of rules that apply to clients using COM interface pointers; these are common situations that require calls to the Release method: (1) Prior to overwriting a non-null local variable or data member, (2) Prior to leaving the scope of a non-null local variable, (3) When callee overwrites an [in, out] parameter of a method or function whose initial value is non-null. Note that [out] parameters are assumed to be null on input and must never be released by the callee, (4) Prior to overwriting a non-null data memberof an object, (5) Prior to leaving the destructor of an object that has a non-null interface pointer as a data member. 
Also, maybe this is helpful: "If a DLL is unloaded while there are outstanding references to class objects, subsequent calls to even the Release method would cause the client process to crash." This is for an In-Process Server.
Also, you might find that using class monikers to activate your COM class interfaces will be more useful. http://msdn.microsoft.com/en-us/library/ms688618(VS.85).aspx
...Hope you figure it out,...
Baltoro

ToutEnMasm

Hello,
The answer is here:
Quote
Also, you might find that using class monikers to activate your COM class interfaces will be more useful

It is what i made,with

Quote
UseMoniker PROC pclsid:DWORD
         Local bindOpts:BIND_OPTS2
         Local  retour:DWORD
         ZEROLOCALES retour
   mov retour,-1   ;CreateClassMoniker or CreateBindCtx failed
   mov bindOpts.cbStruct,sizeof bindOpts
   invoke CreateBindCtx,NULL,addr ppvIBindCtx
   .if eax == S_OK
      ;IBindCtx GetBindOptions,addr bindOpts      
      invoke CreateClassMoniker,pclsid,addr ppvIMoniker
      .if eax == S_OK
         IMoniker BindToObject,ppvIBindCtx,NULL,addr IID_IUnknown,addr ppvItest
         .if eax == S_OK && ppvItest != 0
            Itest Release
         .endif
         IMoniker Release
      .endif
      IBindCtx Release
   .endif
FindeUseMoniker:
         mov eax,retour
         ret
UseMoniker endp

I put the adress of the CLSID in pclsid , it is the one (see upper post) that crashes at Release and follow it with the debugger.
connect S_OK
Release  ................ don't crash

using class monikers  is the more secure method to connect COM interface,explain is contained in the SDK.

Quote
IMoniker::BindToObject
Uses the moniker to bind to the object it identifies. The binding process involves finding the object, putting it into the running state if necessary, and supplying the caller with a pointer to a specified interface on the identified object.

Many thanks for help

baltoro

...sorry for all the misdirection.
I have crashed a DirectX application by calling Release as the application is terminating (processing the WM_CLOSE message), and the application crashes, like yours. Eliminating the call to Release keeps the application from crashing, but, it is unknown why the reference count is already zero and the DLL has apparently unloaded.
I assume that you are enumerating through your registry's CLSIDs, and this only happens occasionally. As far as I know, there is no straightforward way to check the reference count.
...anyway, good luck with your project.
Baltoro

ToutEnMasm

Hello,
Don't be sorry,it was ways to explore before finding the good.
But now I have made a test of browsing the many clsid in my registry,I can tell you that the moniker can do manything not explain in the SDK.
Some interfaces that was bad installed have displayed a dialog box to finish the work and I haven't get a single crash,I have made no test with CLSID not installed (filtered) .
Another thing is that there isn't use of the CLSTX constant to connect.That is better,vista have a list of ten of those constants and it will be not easy to find the good one or the good group.
Try it in your application that have crashed,and you will surely have a good surprise.


baltoro

It's REALLy EMBARRASSING to admit this,...but, I've never actually used Monikers in my code projects (although the intent was there). I've read about them and am aware of how useful they are, especially in a network environment.
In Don Box's book, he devotes about a dozen pages to describing the various types and typical usage. As he says in the book's forward: "Monikers are more powerful than you think." Also, he says that the IMoniker inrerface is one of the more complex COM interfaces.
You ought to investigate the ROT, or Running Object Table, which "is a facility of the SCM (Service Control Manager) that maps arbitrary monikers onto running instances on the local host machine". The COM API GetRunningObjectTable (http://msdn.microsoft.com/en-us/library/ms695276(VS.85).aspx) returns an IRunningObjectTable interface pointer into the SCM. Item Monikers and File Monikers are used with the ROT for convient access to registered COM Objects.
This might provide you with more information; it's the section on Monikers from the original Microsoft COM Specification:
(http://groups.csail.mit.edu/medg/ftp/emjordan/COM/CH11%20MON.DOC).
This COM API might also be useful: MkParseDisplayName (http://msdn.microsoft.com/en-us/library/ms691253(VS.85).aspx)
And, finally, Box describes how Monikers can be composited to allow object hierarchies to be navigated based on a textual description of a path. I can't say that I really understand how this works specifically. Box leaves alot of information about the algorithms used internally by the various Moniker types unstated.

What amazes me is that you are writing a browser for COM classes in ASSEMBLY. If I tried to do that in C++, using Visual Studio, I'd be having a hell of a time. I would develop some bizarre dementia, and would be running around with my hair on fire.
Baltoro

ToutEnMasm


Making some test,I have made it better.
At this time of my experiment , all interfaces don't accept to connect with the monikers method.
But,when the method failed,there is no crash.This a good thing that allow to write a secure method to connect to interfaces.
The GetInterfaceFromProgId proc call UseMoniker to finish the work.In case of failed connection,the normal cocreateinstance is called.All tests are made with the Iunknown interface.
I have added the CLSTX values in the BindOptions,but I am not sure of what i am doing.


Quote
;pProgId pointeur sur une chaine ProgId "Microsoft.ISAM.OLEDB.1.1"
;pIID pointeur sur un GUID IID_ nommant l'interface
GetInterfaceFromProgId PROC pProgId:DWORD,pIID:DWORD
   LOCAL wszProgID[MAX_PATH]:word
   Local resultat:SDWORD
   LOCAL ppv:Dword,retour,pchaine
   LOCAL clsid   :GUID
   mov retour,0
   invoke MultiByteToWideChar,CP_ACP,MB_PRECOMPOSED,pProgId,-1,addr wszProgID,sizeof wszProgID
   invoke CLSIDFromProgID,addr wszProgID,addr clsid
   .if eax == S_OK
      invoke UseMoniker,addr clsid,pIID
      mov resultat,eax
      .if resultat > 0
         PuPo retour,eax
      .endif         
   .endif
   mov eax,retour
   RET
GetInterfaceFromProgId endp


;################################################################
UseMoniker PROC pclsid:DWORD,pIID:DWORD
         Local bindOpts:BIND_OPTS2
         Local ppv:DWORD
         Local  retour:DWORD
         ZEROLOCALES retour
   mov retour,-1   ;CreateClassMoniker failed
   mov bindOpts.cbStruct,sizeof bindOpts
   IBindCtx GetBindOptions,addr bindOpts
   mov bindOpts.dwClassContext,CLSCTX_INPROC_SERVER OR CLSCTX_INPROC_HANDLER OR CLSCTX_LOCAL_SERVER
   IBindCtx SetBindOptions,addr bindOpts
   ;ppvIBindCtx condition de départ      
   invoke CreateClassMoniker,pclsid,addr ppvIMoniker
   .if eax == S_OK
      mov eax,pIID
      IMoniker BindToObject,ppvIBindCtx,NULL,pIID,addr ppv
      .if eax == S_OK
         ;le clsid est gérable comme ça
         .if  ppv != 0
            PuPo retour,ppv            
         .endif
      .else
         ;non traité par BindToObject
         invoke CoCreateInstance,pclsid,NULL,
               CLSCTX_INPROC_SERVER OR CLSCTX_INPROC_HANDLER OR CLSCTX_LOCAL_SERVER,
               pIID,addr ppv
         .if eax == S_OK
            PuPo retour,ppv
         .else
            mov retour,0
         .endif       
      .endif
      IMoniker Release
   .endif
FindeUseMoniker:
         mov eax,retour
         ret
TraiteErreur:
   ;WinError.h
   .if eax == E_OUTOFMEMORY
      mov edx,0   
   .elseif eax == MK_E_NOOBJECT
      mov edx,0   
   .elseif eax == MK_E_EXCEEDEDDEADLINE
      mov edx,0   
   .elseif eax == MK_E_CONNECTMANUALLY
      mov edx,0   
   .elseif eax == MK_E_INTERMEDIATEINTERFACENOTSUPPORTED
      mov edx,0   
   .elseif eax == STG_E_ACCESSDENIED
      mov edx,0   
   .elseif eax == CLASS_E_CLASSNOTAVAILABLE
      mov edx,0
   .else
      mov edx,1
   .endif
retn
UseMoniker endp

;################################################################