Hi everyone
I just wanted to know how to deal with COM Object
I made a simple example by VB, Then I made another example by MASM32, linking programs..
The purpose of this work is to know how to link with the Com Object with Events.
It didn't work with me.
To see what happens I added some modifications to procedures "QueryInterface,AddRef,Release,..."
When I call the procedure "FindConnectionPoint.Advise" I found that the Class send requests to QueryInterface
What is exactly required to success of the procedure Advise?
Quote from: mabdelouahab on April 16, 2015, 05:50:24 PM
I just wanted to know how to deal with COM Object
the answer to that is quite involved, to say the least
furthermore, to set off reading about COM is difficult
they use a lot of terminology that doesn't make sense unless you already know about COM :lol:
i'll try to cover a few points to get you started....
first, before using COM in a program, you must CoInitialize (there are different versions of this in some cases)
when done, CoUninitialize must be called
i generally put these at the beginning and end of the program
start: INVOKE CoInitialize,NULL
;rest of program
INVOKE CoUninitialize
INVOKE ExitProcess,0
next, access to functions are implemented through what are essentially "vector tables"
by now, i'm sure you're familiar with PROTOtypes
well - the vectors in a COM interface table each have different prototypes, in a manner of speaking
qWord has provided us with a very nice macro to help us
there are others, but i like qWord's because using it is a little easier to understand
;METHOD macro by qWord
METHOD MACRO name,args:VARARG
LOCAL _type1,_type2
_type1 TYPEDEF PROTO args
_type2 TYPEDEF PTR _type1
EXITM <name _type2 ?>
ENDM
now, in the past, i have used an IDispatch interface, so i already have the interface structures made
in this case, it's for the IShellDispatch interface
IShellDispatch is an IDispatch interface
and, IDispatch interface is an IUnkown interface
that probably seems very confusing - lol
i think it's safe to say that most COM interfaces are of the type IUnknown
that means, among other things, that the first few functions are of a common set
notice that qWord's METHOD macro is used to prototype a structure of vectors
;IUnknown interface vTable (structure)
IUnknown STRUCT
METHOD(QueryInterface, _this:LPVOID,riid:LPVOID,ppvObj:LPVOID)
METHOD(AddRef, _this:LPVOID)
METHOD(Release, _this:LPVOID)
IUnknown ENDS
now, to make more sense of what i was saying before,
the IDispatch interface can be made by simply referencing IUnknown
;IDispatch interface vTable (structure)
IDispatch STRUCT
IUnknown <>
METHOD(GetTypeInfoCount, _this:LPVOID,pctinfo:LPVOID)
METHOD(GetTypeInfo, _this:LPVOID,iTInfo:UINT,lcid:LCID,ppTInfo:LPVOID)
METHOD(GetIDsOfNames, _this:LPVOID,riid:LPVOID,rgszNames:LPOLESTR,cNames:UINT,lcid:LCID,rgDispId:LPVOID)
METHOD(dInvoke, _this:LPVOID,dispIdMember:DWORD,riid:LPVOID,lcid:LCID,wFlags:DWORD,pDispParams:LPVOID,pVarResult:LPVOID,pExcepInfo:LPVOID,puArgErr:LPVOID)
IDispatch ENDS
all IDispatch interfaces share these common elements
IShellDispatch may now reference IDispatch
in the case of VARIANT structures, i have broken them down into individual DWORD's
this makes functions with VARIANT arguments easier to use in assembly language
;IShellDispatch interface vTable (structure)
; vDir VARIANT struct = vtDir:DWORD,vD0:DWORD,vstrDir:LPVOID,vDV0:DWORD
;vRootFolder VARIANT struct = vtRoot:DWORD,vR0:DWORD,vstrRoot:LPVOID,vRV0:DWORD
IShellDispatch STRUCT
IDispatch <>
METHOD(Application, _this:LPVOID,ppid:LPVOID)
METHOD(Parent, _this:LPVOID,ppid:LPVOID)
METHOD(NameSpace, _this:LPVOID,vtDir:DWORD,vD0:DWORD,vstrDir:LPVOID,vDV0:DWORD,ppsdf:LPVOID)
METHOD(BrowseForFolder, _this:LPVOID,Hwnd:DWORD,lpTitle:LPVOID,Options:DWORD,vtRoot:DWORD,vR0:DWORD,vstrRoot:LPVOID,vRV0:DWORD,ppsdf:LPVOID)
METHOD(Windows, _this:LPVOID,ppid:LPVOID)
METHOD(Open, _this:LPVOID,vtDir:DWORD,vD0:DWORD,vstrDir:LPVOID,vDV0:DWORD)
METHOD(Explore, _this:LPVOID,vtDir:DWORD,vD0:DWORD,vstrDir:LPVOID,vDV0:DWORD)
METHOD(MinimizeAll, _this:LPVOID)
METHOD(UndoMinimizeALL, _this:LPVOID)
METHOD(FileRun, _this:LPVOID)
METHOD(CascadeWindows, _this:LPVOID)
METHOD(TileVertically, _this:LPVOID)
METHOD(TileHorizontally, _this:LPVOID)
METHOD(ShutdownWindows, _this:LPVOID)
METHOD(Suspend, _this:LPVOID)
METHOD(EjectPC, _this:LPVOID)
METHOD(SetTime, _this:LPVOID)
METHOD(TrayProperties, _this:LPVOID)
METHOD(Help, _this:LPVOID)
METHOD(FindFiles, _this:LPVOID)
METHOD(FindComputer, _this:LPVOID)
METHOD(RefreshMenu, _this:LPVOID)
METHOD(ControlPanelItem, _this:LPVOID,bstrDir:LPVOID)
IShellDispatch ENDS
in many cases - using the features provided requires accessing yet another interface level
for example, if we want to use an IShellDispatch::NameSpace Folder object, we need another interface
it, too, is an IDispatch interface, so....
;IShellDispatch::NameSpace Folder object interface vTable (structure)
; vItem VARIANT struct = vtItem:DWORD,vI0:DWORD,vdwItem:DWORD,vIV0:DWORD
;vOptions VARIANT struct = vtOpt:DWORD,vO0:DWORD,vdwOpt:LPVOID,vOV0:DWORD
Folder STRUCT
IDispatch <>
METHOD(getTitle, _this:LPVOID,pbs:LPVOID)
METHOD(Application, _this:LPVOID,ppid:LPVOID)
METHOD(Parent, _this:LPVOID,ppid:LPVOID)
METHOD(ParentFolder, _this:LPVOID,ppsf:LPVOID)
METHOD(Items, _this:LPVOID,ppid:LPVOID)
METHOD(ParseName, _this:LPVOID,bstrName:LPVOID,ppid:LPVOID)
METHOD(NewFolder, _this:LPVOID,bstrName:LPVOID,vtOpt:DWORD,vO0:DWORD,vdwOpt:LPVOID,vOV0:DWORD)
METHOD(MoveHere, _this:LPVOID,vtItem:DWORD,vI0:DWORD,vdwItem:DWORD,vIV0:DWORD,vtOpt:DWORD,vO0:DWORD,vdwOpt:LPVOID,vOV0:DWORD)
METHOD(CopyHere, _this:LPVOID,vtItem:DWORD,vI0:DWORD,vdwItem:DWORD,vIV0:DWORD,vtOpt:DWORD,vO0:DWORD,vdwOpt:LPVOID,vOV0:DWORD)
METHOD(GetDetailsOf, _this:LPVOID,vtItem:DWORD,vI0:DWORD,vdwItem:DWORD,vIV0:DWORD,iColumn:DWORD,pbs:LPVOID)
Folder ENDS
a note about GUID's (including CLSID's and IID's)
there is a structure defined in Hutch's windows.inc, called "GUID"
.DATA
ALIGN 4
CLSID_Shell GUID <13709620h,0C279h,11CEh,<0A4h,9Eh,44h,45h,53h,54h,0,0>>
IID_IShellDispatch GUID <0D8F015C0h,0C278h,11CEh,<0A4h,9Eh,44h,45h,53h,54h,0,0>>
IID_Folder GUID <0BBCBDE60h,0C3FFh,11CEh,<83h,50h,44h,45h,53h,54h,0,0>>
IID_FolderItem GUID <0FAC32C80h,0CBE4h,11CEh,<83h,50h,44h,45h,53h,54h,0,0>>
IID_FolderItems GUID <744129E0h,0CBE5h,11CEh,<83h,50h,44h,45h,53h,54h,0,0>>
IID_IShellFolder GUID <214E6h,0,0,<0C0h,0,0,0,0,0,0,46h>>
i'll let you chew on all that before we actually use the interfaces :P
a simplified example of using a COM interface...
1) CoInitialize
2) CoCreateInstance
this gives you an instance of the interface, in the form of a pointer to a vector table
you store this pointer and use it whenever a call is made
3) call the function or "method"
you pass a pointer, and it's contents - lol
4) Release - when you're done with the instance, release it
5) CoUninitialize
now, that's a simple example - in most cases, it's much more involved
we want to keep it simple to help you understand the sequence of events
.DATA?
ALIGN 4
pvShell LPVOID ?
.CODE
Start: INVOKE CoInitialize,NULL
;notice that pvShell is filled by this call
;normally, we would examine the return value to verify success before proceeding
;again, we're going to keep it simple
INVOKE CoCreateInstance,offset CLSID_Shell,NULL,CLSCTX_INPROC_SERVER,offset IID_IShellDispatch,offset pvShell
;now, to make a call, we need the "THIS" pointer content
;in this example, we make a NameSpace call that actually creates another interface instance (pvFolder)
;we aren't going to use that interface (or release it) - we just want to demonstrate an interface invoke
mov edx,pvShell
mov ecx,[edx]
INVOKE IShellDispatch.NameSpace[ecx],edx,VT_BSTR,0,offset bstrPathTarget,0,offset pvFolder
;when we're done using the interface, we Release it (this is a standard IUnknown procedure)
mov edx,pvShell
mov ecx,[edx]
INVOKE IShellDispatch.Release[ecx],edx
;now, we want to exit, so....
INVOKE CoUninitialize
INVOKE ExitProcess,0
Hello my teacher dave.
I thank you for the valuable information
But my question is much simpler
Why not work with me Advise correctly; IConnectionPoint.Advise doesn't work with me
In the first of the program I call the method "Class1. IsMe2(a As String)" without Events، And then I called it with Events
the method "Class1. IsMe2(a As String)" will show a message and then launch Events
Public Sub IsMe2(a As String)
MsgBox "Is Me sec[" & a & " ]"
RaiseEvent Ev01
End Sub
To link events, I made mrthod AdviseEvents, which links ConnectionPoint then call Advise to correlate Events
I have the problem of expressing the problem
i've never played with connection points
it will take me some time to do some reading and understand it better :t
https://msdn.microsoft.com/en-us/library/windows/desktop/ms694318%28v=vs.85%29.aspx (https://msdn.microsoft.com/en-us/library/windows/desktop/ms694318%28v=vs.85%29.aspx)
i should be able to get back to this later today
I'm sorry... ; I forgot to add REG.BAT :biggrin:
To work my attachment, open "\vbexmple" and then run Reg.bat to register the Library
Then execute the example in "\TestEvents"
Hi, MABDELOUAHAB,
I read through the posts in this thread, and I read through the source code that you provided.
Unfortunately, I'm not experienced in programming with Visual Basic, so, I didn't understand the VB source code.
But, neither prj001 nor prj008 (in the vbexample directory) launched on my machine. And, there is no compiled executable in the TestEvents directory (although there is a compiled Obj file).
I don't quite see the point of this whole exercise, though. The Visual Basic language and, presumably, the VB compiler, are not really adequate for handling COM objects. COM evolved from C++, and, it's fairly complicated with Visual Studio, using the C++ language. I have heard unsubstantiated rumors, though, that it is possible to instantiate a COM object and execute its methods in a Visual Basic program. But, you're doing it the hard way.
Based on what I've seen with the example you've provided, I can't even tell if the vbexamples, prj001 and prj008 have linked correctly. Nothing happens when I try to run these programs.
If it were me, I'd attempt writing the whole project in MASM assembly language first. At least the MASM Forum members could understand the source code, and diagnose the problem.
In reading through the assembly language portion of your project,...I can see that you've got problems. Maybe you should rethink the whole approach.
MASM Forum member, japheth, is famous for his assembly language versions of simple COM servers and various other COM components. In fact, he used to have a website dedicated to COM and assembly language,...and a number of other very useful utilities,...but, his website no longer exists. I have a number of his example projects that I can upload here if you are interested. He structured his COM interfaces in an entirely different way than you did,
...although you do seem to understand the principles.
Here is MSDNs section about: Component Object Model (COM) (https://msdn.microsoft.com/en-us/library/windows/desktop/ms680573(v=vs.85).aspx)
...And you might find this helpful: Translating to Visual Basic (Translating COM Object Syntax for Programming Languages) (https://msdn.microsoft.com/en-us/library/windows/desktop/ms682260(v=vs.85).aspx)
Hi Zen;
prj001 is ActiveX EXE Component; not executable, but it wait until the the creation of objects (Jast an TypeLibrary), Just registered by REG.Bat
prj001 Contains an object CLASS1 implement 2 dispatch _Class1 (Default) and __Class1( sink objects)
If I wanted that I only work on CLASS1 without events I've been using (masm) "CreateClass Proc", Then I call any method of CLASS1 like "Class1. IsMe2" ;******************************************** Class1. IsMe2(a As String)
lea ecx, bs
push ecx
mov edx, Inst_Class1
push edx
mov edx,[edx]
CALL dword ptr [edx+40] ;IsMe2
But if I wanted to work on CLASS1 withe Events It must be create an interface "__Class1 struct" with "CreateInterface proc" then advise it with IConnectionPoint.Advise For the host Object call procedures of this interface when an events is raised
in my attachment , do not use anything in " "\vbexmple"", just use REG to register the typelibrary
then return to the masm programe "\TestEvents", and run it to see what hapen
a lot of material to cover - i'm afraid i'm a bit overwhelmed - lol
however, i did disassemble the prj008.exe file
and, i used the resource hacker to examine the resources
much of the COM code resides in the MSVBVM60.DLL file :(
MABDELOUAHAB,
This is your 'reg' file. Tell me what is supposed to happen when I execute this file.
prj001.exe /RegServer
...You know,...the way you've structured this whole project is just annoying. Do you really think I want to register a COM server (that doesn't work) on my machine ???
Regsvr32 (https://technet.microsoft.com/en-us/library/bb490985.aspx)
How to register an ActiveX control (.ocx) manually (https://support.microsoft.com/en-us/kb/146219/)
Zen
prj001.exe is just an example, Give me any example of a Com Typelibrary contains an Object with Events I'm working on it, My goal is to know how it works IConnectionPoint.Advise.
MABDELOUAHAB,
Do you know what the OLE/COM Object Viewer (https://msdn.microsoft.com/en-us/library/d0kh9f4c.aspx) is ? When you have correctly registered a COM server, the type library, and the COM class, and the various COM interfaces are displayed in the OLE/COM Object Viewer. If I used the Regsvr32 utility to register your COM component, and it worked correctly (which it doesn't), your COM component could be accessed by the OLE/COM Object Viewer and the information about your type library and any registered COM classes and interfaces would be displayed on the OLE/COM Object Viewer interface window.
MABDELOUAHAB,
Maybe this will help. It's the example I mentioned in my original post, contributed by Japheth. It's written completely in MASM assembly language.
Quote from: Zen on April 18, 2015, 06:12:00 AM
MABDELOUAHAB,
Maybe this will help. It's the example I mentioned in my original post, contributed by Japheth. It's written completely in MASM assembly language.
Zen I Thanks for the help but the example does not contain any events.
I found another very nice example in MASM, Contains an object
IMyCom2, store Value and read it (get_Value/set_Value), and then fire an event
XMax if the value is greater than 100
I worked with him without Events, But when I want to link to the event, gave me the same problem in the first example
CreateClass OK
IMyCom2. Set_Value 107 OK
IMyCom2. Get_Value m_Value=107 OK Press any key to continue ...
QueryInterface IConnectionPointContainer OK
FindConnectionPoint OK
_QIf<000000003,00000h,00000h<0C0h,000h,000h,000h,000h,000h,000h,046h>>_QIf
_QIf<0ECC8691B,0C1DBh,04DC0h<085h,05Eh,065h,0F6h,0C5h,051h,0AFh,049h>>_QIf
_QIf<000000003,00000h,00000h<0C0h,000h,000h,000h,000h,000h,000h,046h>>_QIf
_QIf<00000001B,00000h,00000h<0C0h,000h,000h,000h,000h,000h,000h,046h>>_QIf
_QIf<000000000,00000h,00000h<0C0h,000h,000h,000h,000h,000h,000h,046h>>_QIf2
AddRef
_QIf<000000018,00000h,00000h<0C0h,000h,000h,000h,000h,000h,000h,046h>>_QIf
_QIf<0334D391F,00E79h,03B15h<0C9h,0FFh,0EAh,0C6h,05Dh,0D0h,07Ch,042h>>_QIf
_QIf<000000040,00000h,00000h<0C0h,000h,000h,000h,000h,000h,000h,046h>>_QIf
_QIf<0334D391F,00E79h,03B15h<0C9h,0FFh,0EAh,0C6h,05Dh,0D0h,07Ch,042h>>_QIf
_QIf<094EA2B94,0E9CCh,049E0h<0C0h,0FFh,0EEh,064h,0CAh,08Fh,05Bh,090h>>_QIf
_QIf<0334D391F,00E79h,03B15h<0C9h,0FFh,0EAh,0C6h,05Dh,0D0h,07Ch,042h>>_QIf
_QIf<077DD1250,0139Ch,02BC3h<0BDh,095h,090h,00Ah,0CEh,0D6h,01Bh,0E5h>>_QIf
_QIf<000000019,00000h,00000h<0C0h,000h,000h,000h,000h,000h,000h,046h>>_QIf
_QIf<04C1E39E1,0E3E3h,04296h<0AAh,086h,0ECh,093h,08Dh,089h,06Eh,092h>>_QIf
Release
Does one tell me what is the meaning of the values of the riid sent to queryinterface Or what should I do to connect events ( MyEventClass in this example)
Example needs some files, I've sent with attachments (com.zip)
Don't forget reg.bat in "\AsmEvent"
You probably need to implement IDispatch.Invoke. In the first example, Invoke is called with the DispId of the Ev1 function, but since Invoke returns E_NOTIMPL, nothing else happens*
It can be implemented using Microsoft's helper function (https://msdn.microsoft.com/en-us/library/windows/desktop/ms221366(v=vs.85).aspx), but it needs an ITypeInfo for an implementation of the interface. So the ASM app would need its own typelibrary as well as the one in the VB app.
It can be implemented manually but that involves looking up the function to call from the DispId, unpacking the parameters from the VARIANT array, calling the function, then cleaning up afterwards.
* I translated your ASM app to C++, and that's what happened
adeyblue
Quote from: adeyblue on April 18, 2015, 10:35:35 AM
You probably need to implement IDispatch.Invoke. In the first example, Invoke is called with the DispId of the Ev1 function, but since Invoke returns E_NOTIMPL, nothing else happens*
The problem is that my program does not exceed the
" IConnectionPoint.Advise" We have yet to call events, Either TypeInfo and IDispatch.Invoke they are easy to handle.
I noticed that when the program calls the advise, my Interface.
QueryInterface receives riid:
<000000003,00000h,00000h<0C0h,000h,000h,000h,000h,000h,000h,046h>>;<0ECC8691B,0C1DBh,04DC0h<085h,05Eh,065h,0F6h,0C5h,051h,0AFh,049h>>;......
First is
IID_IMarshal, Second:
IID_INoMarshal; IID_IIdentityUnmarshal
I tried to accept the first (), then I sent new instance of IMarshal object, He sends a request to
IMarshal.GetMarshalSizeMax, All this happens in the background And Before passing
IConnectionPoint.Advise ;I don't have enough information
In the example MSDN inserted C++ Event Sinks Sample (https://msdn.microsoft.com/en-us/library/windows/desktop/ms701155%28v=vs.85%29.aspx) , Called
CoCreateFreeThreadedMarshalerDoes anyone have information on what happens in the background?
this is kind of "advanced" COM, from what i understand
we had a similar issue when we were trying to use COM to zip or unzip a file
we figured out how to write the zip/unzip code, but we haven't really figured out how to know when it's done :P
i think this is where COM "apartments" come in - and i haven't got that far in the learning curve
so far, i haven't seen too much on events in COM, in the way of ASM code
when you write the code in visual basic, the compiler does a lot of stuff in the background that the programmer doesn't see
MABDELOUAHAB,
Here is an excellent article that explains COM Events: Understanding COM Event Handling, CodeProject (http://www.codeproject.com/Articles/9014/Understanding%20COM%20Event%20Handling)
Scroll down to the section: Connection Points and Connection Point Containers
If you read that article, you will come to this statement:
Quote from: Understanding COM Event Handling Visual Basic applications can only handle ActiveX object events through sinks which are dispinterface-based. This is natural both because of the fact that Visual Basic cannot handle non-dispinterface-based events, and also because of the need to handle events fired from any and all ActiveX objects.
The central point behind this is that while event interfaces need not be dispinterface-based (their methods can be of any signature, the only mandate is that the event interface must also derive from IUnknown), Visual Basic is not able to internally anticipate the design of these custom event interfaces and to generate Sinks for them.
Furthermore, the types of method return values and parameters must be confined to those that Visual Basic is able to understand and internally process. Hence, the only way to standardize the handling of event interfaces is to require that they be derived from IDispatch, and that the return and parameter types be from a wide but limited ranged set. This set of types is known as the automation-compatible-types.
...So,...apparently, ADEYBLUE was correct. (http://www.conceptart.org/forums/images/ca_smilies/membermade/wtf.gif)
Also, you may find this MSDN reference section informative: COM Interop (Visual Basic) (https://msdn.microsoft.com/en-us/library/6bw51z5z.aspx)
Quote from: DAVE !!!...when you write the code in visual basic, the compiler does a lot of stuff in the background that the programmer doesn't see,...
...Dave is right about this. Seriously,...Dave is
ALWAYS right,...except,...of course,...when it concerns Device Contexts,...lol,...:biggrin:
Note: The impression that I get is that Visual Basic syntax is
FUNKY (in the Robin Williams sense),...
For those of you who are NOT Visual Basic programmers: Visual Basic Joke, Raymond Chen (http://blogs.msdn.com/b/oldnewthing/archive/2005/09/30/475688.aspx)
Quote from: Zen on April 19, 2015, 03:17:03 AMHere is an excellent article that explains COM Events: Understanding COM Event Handling, CodeProject (http://www.codeproject.com/Articles/9014/Understanding-COM-Event-Handling)
Excellent article indeed :t
If I had unlimited time and no other projects, I would give it a try.
...So,...I don't see any reason,...given all this pertinent information,...why MABDELOUAHAB's project cannot be correctly implemented,...
(http://1.bp.blogspot.com/-RP6Rv-x72Z0/T_5a4VferNI/AAAAAAAAAtI/YJ6oy7TGv1E/s1600/lost_emoticon.png)
Quote from: Zen on April 19, 2015, 05:11:29 AM
...So,...I don't see any reason,...given all this pertinent information,...why MABDELOUAHAB's project cannot be correctly implemented,...
Me neither. Not trivial but certainly feasible, especially with that rarely well-written article :t
dave jj2007 Zen Thank you all
Quote from: Zen on April 19, 2015, 03:17:03 AMHere is an excellent article that explains COM Events: Understanding COM Event Handling, CodeProject (http://www.codeproject.com/Articles/9014/Understanding-COM-Event-Handling)
I am engaged in the project after I read this topic ; But he did not talk about my problem
Am not alone suffered from a problem, see:Advise of Connection Point calls QueryInterface with IID_IMarshal (http://stackoverflow.com/questions/8444551/advise-of-connection-point-calls-queryinterface-with-iid-imarshal)
//Get the pointer to CSink's IUnknown pointer
hr = pSink->QueryInterface (IID_IUnknown,(void **)&pSinkUnk);
if ( !SUCCEEDED(hr) )
{
return hr;
}
// It is assumed that this should call the QueryInterface with IID__IAdHocPresenceEvents
// Do not understand why this calls the QueryInterface with IID_IMarshal
// Later this becomes reason for not getting event at ClientSink::OnAdHocPresenceQuery
hr = pCntPoint->Advise(pSinkUnk, &dwAdvise);
if ( !SUCCEEDED(hr) )
{
return hr;
}
MABDELOUAHAB,
The project that you are attempting to develop is difficult, without a doubt. When I am coding something that is somewhat beyond my capabilities, I just take my time, read and then reread the documentation as many times as necessary, until I understand it thoroughly,...and, then, maybe rethink my approach. Create several different versions to test your techniques. You'll eventually get it,...I can see that you have a fundamental comprehension of the mechanics of COM. I think, exposing your COM object correctly in your Visual Basic application will be the most difficult part of the project.
Just to give you an idea of the effort required: we had a thread last year where one of the MASM Forum members (vertograd) attempted to expose a .NET Framework component from within a MASM assembly language application, (.NET Framework is quite similar to COM, conceptually),...and, it took him a good month to get it to work correctly. This didn't even require any data marshalling, or Interop code. And, that wasn't as difficult a project as yours.
The problem with posting your questions here is that nobody on the MASM Forum has ever attempted something like this (...and lived to tell). So, we're throwing alot of information your way that may not be actually useful.
What I'm wondering is: Why even implement a Visual Basic generated Active X control ? You could write this project entirely in MASM assembly language,...or, combine a MASM generated component with a C or C++ generated component, (or, better yet,...write the entire thing in C++)...or, any one of a number of combinations that would be easier, cleaner, and more compatible with existing Windows Operating Systems.
...Also,...what compiler are you using for the Visual Basic component ? Are you aware of the inherent limitations imposed by the compiler's operation ?
my problem is not in the Visual basic Server, my problem is in the client
because in my secend attachment, (http://masm32.com/board/index.php?topic=4153.msg44134#msg44134 (http://masm32.com/board/index.php?topic=4153.msg44134#msg44134)) the server and the client is written completely in MASM. and give the same problem, the same problem
I'm not urgent, but I want to clarify the problem only
Hi mabdelouahab
In the ObjAsm32 package, there is a project called OCX_LED, that is designed to interface with VB6. AFAIR it implements ConnectionPoints, ConnectionPointContainer, PropertyPages, and so on. Maybe it helps...
Regards, Biterider
I found that if de server object is de type InprocServer32 (Dll) like 2 example (AsmEvent):
invoke CoInitializeEx, 0,COINIT_APARTMENTTHREADED ;COINIT_MULTITHREADED
My method QueryInterface receives rIId_ofeventclass place IID_IMarshal spam, and then everything goes correctly
I will see other type like LocalServer32 (exe) like vb example
MABDELOUAHAB,
The CodeProject has a large number of articles about COM programming: COM / COM+ (http://www.codeproject.com/KB/COM/)
Here is the section for: COM/DCOM/COM+ - Beginners (http://www.codeproject.com/KB/COM/#Beginners)
These articles are excellent:
Introduction to COM - What It Is and How to Use It (http://www.codeproject.com/Articles/633/Introduction-to-COM-What-It-Is-and-How-to-Use-It)
Introduction to COM Part II - Behind the Scenes of a COM Server (http://www.codeproject.com/Articles/901/Introduction-to-COM-Part-II-Behind-the-Scenes-of-a)
The COM Macro-Architecture Topology (http://www.codeproject.com/Articles/1268/The-COM-Macro-Architecture-Topology)
COM Macro Architecture Topology - Servers (http://www.codeproject.com/Articles/1267/COM-Macro-Architecture-Topology-Servers)
COM Macro Architecture Topology - Clients (http://www.codeproject.com/Articles/1266/COM-Macro-Architecture-Topology-Clients)
COM IDs & Registry Keys in a Nutshell (http://www.codeproject.com/Articles/1265/COM-IDs-Registry-keys-in-a-nutshell)
There is also a whole series of articles (for C language programmers) by Jeff Glatt:
COM in Plain C (http://www.codeproject.com/Articles/13601/COM-in-plain-C)
COM in Plain C, Part Two (http://www.codeproject.com/Articles/13862/COM-in-plain-C-Part)
COM in Plain C, Part Three (http://www.codeproject.com/Articles/14037/COM-in-plain-C-Part)
COM in Plain C, Part Four (http://www.codeproject.com/Articles/14117/COM-in-plain-C-Part)
COM in Plain C, Part Five (http://www.codeproject.com/Articles/14183/COM-in-plain-C-Part)
COM in Plain C, Part Six (http://www.codeproject.com/Articles/14905/COM-in-plain-C-Part)
COM in Plain C, Part Seven (http://www.codeproject.com/Articles/15037/COM-in-plain-C-Part)
COM in Plain C, Part Eight (http://www.codeproject.com/Articles/17038/COM-in-plain-C-part)
...There are also a number of articles about using COM in ActiveX components, Visual Basic wrappers, connection points, and all kinds of other useful stuff,...
For instance: COM Connection Points (http://www.codeproject.com/Articles/3541/COM-Connection-Points). and, ActiveX EXE Wrappers (http://www.codeproject.com/Articles/16499/ActiveX-EXE-Wrappers)
...And,...if you are serious about COM programming,...there is no better reference than: Essential COM, Don Box (http://books.google.com/books/about/Essential_COM.html?id=kfRWvKSePmAC)
(http://books.google.com/books/content?id=kfRWvKSePmAC&printsec=frontcover&img=1&zoom=1&edge=curl&imgtk=AFLRE71Sd_1lGwgjtHIkd69UZyM-QeaXb-WDWWXUnPB3CRmTf3AHc1jMgWRZS438gyqKB7QmLtCQ9SlDOP3YjYU8a4zNodlhq0d1Yd7pZgHgaulBirP4ycHlLnhVwPY-kA8eAxKdC2FB)
Oh yeah, I had changed it to ApartmentThreaded. Must've forgotten I did that before I started adding printfs and stuff to the other code.
MABDELOUAHAB,
Here is an internet archive (the Wayback machine) of Japheth's Site: Japheth's Site, COM & Assembly (http://web.archive.org/web/20141003032346/http://www.japheth.de/)
...Scroll down the left side Menu to Miscellaneous, COM & Assembly,...
All his original MASM COM Source Code examples are located there and downloadable,...
I have found some answers, but I'm trying scattered work Like (http://stackoverflow.com/questions/7477511/how-to-implement-imarshal-for-custom-marshalling-interface-from-out-of-proc-serv)
QuoteThe question is a bit not specific, so the answer will outline the main steps taking place.
Given is an interface pointer on the server side and the pointer needs to get marshaled into foreign apartment. The server COM object implements custom marshaler supposed to be picked up. The client expects the interface pointer to a proxy obtained through custom marshaling.
Server Side
COM queries the COM object in question for IMarshal interface - this succeeds, our object indeed implements it
COM calls IMarshal::GetUnmarshalClass to get CLSID of the class responsible for unmarshaling on client side; this might be the same CLSID of the server object , this could be proxy class CLSID, or proxy factory class CLSID - the idea is that COM requests this via method call, so object is free to return whatever seems appropriate
COM might call IMarshal::GetMarshalSizeMax to prepare a buffer for marshaling data
COM calls IMarshal::MarshalInterface to marhsal the data
Client Side
COM starts here getting eventually external request suggesting that some magic is necessary to spawn a proxy object and obtain COM interface pointer to be given to the controlling/caller application
COM has on hands CLSID of the unmarshaler class and data from the marshaler
COM instantiates a class using the CLSID
COM calls IMarhsal::UnmarshalInterface on the created class and provides IID it eventually wants to obtain
Note that on the last step above COM has the following:
or
QuoteNow back to original question, what is marshaling doing here at all? You are initializing an MTA thread and you are creating apartment threaded COM object CLSID_UserNotification there. COM creates the notification API in a side STA thread for you and then it tries to pass your callback there to match threading. It has to ask your COM object whether it does marshaling itself, or it needs this thing thing to be supplied. Your object has to say it does not marshal itself and it has no idea about it. This is what is going on. Make it COINIT_APARTMENTTHREADED and you will see a different picture.
I've added the IMarshal concluded that
When I called for Advice: QueryInterface receives iid_IMarshal
I resend the IMarshal Instance
Then automatically calls the
IMarshal.GetUnmarshalClass, then
IMarshal.GetMarshalSizeMax After all this, the program stops
Quote
CreateClass OK
CreateInterface
QueryInterface IConnectionPointContainer OK
FindConnectionPoint OK
QueryInterface iidClass :
<000000003,00000h,00000h<0C0h,000h,000h,000h,000h,000h,000h,046h>>QIf5
IMarshal@GetMarshalSizeMax iidClass:
<000000000,00000h,00000h<0C0h,000h,000h,000h,000h,000h,000h,046h>>
CoGetStandardMarshal OK
IMarshal.GetMarshalSizeMax pSize= 258
IMarshal@Release
QueryInterface iidClass :
<0ECC8691B,0C1DBh,04DC0h<085h,05Eh,065h,0F6h,0C5h,051h,0AFh,049h>>
QueryInterface iidClass :
<000000003,00000h,00000h<0C0h,000h,000h,000h,000h,000h,000h,046h>>QIf5
IMarshal@GetUnmarshalClass iidClass :
<000000000,00000h,00000h<0C0h,000h,000h,000h,000h,000h,000h,046h>>
IMarshal@GetMarshalSizeMax iidClass:
<000000000,00000h,00000h<0C0h,000h,000h,000h,000h,000h,000h,046h>>
CoGetStandardMarshal OK
IMarshal.GetMarshalSizeMax pSize= 258
QueryInterface proc lpME:dword ,riid:DWORD,ppvObj:DWORD
LOCAL ff
invoke crt_wprintf,cfm$("\n QueryInterface iidClass :\n ")
invoke PrintGUID,riid
.if rv(IsEqualGUID,addr IID_IUnknown ,riid)
invoke crt_wprintf,cfm$("_QIf2")
mov eax,lpME
mov ecx,ppvObj
mov dword ptr [ecx],eax
mov eax,S_OK
jmp exQI
.endif
.if rv(IsEqualGUID,addr IID_IDispatch,riid)
invoke crt_wprintf,cfm$("_QIf3")
mov eax,lpME
mov ecx,ppvObj
mov dword ptr [ecx],eax
mov eax,S_OK
jmp exQI
.endif
.if rv(IsEqualGUID,addr IID___Class1,riid)
invoke crt_wprintf,cfm$("_QIf4")
mov eax,lpME
mov ecx,ppvObj
mov dword ptr [ecx],eax
mov eax,S_OK
jmp exQI
.endif
.if rv(IsEqualGUID,addr IID_IMarshal,riid)
invoke crt_wprintf,cfm$("QIf5")
.if !Inst_InterfaceIMarshal
mov ecx,rv(CreateInterface@IMarshal)
mov Inst_InterfaceIMarshal,ecx
.endif
lea eax,Inst_InterfaceIMarshal
mov ecx,ppvObj
mov [ecx],eax
mov eax,S_OK
jmp exQI
.endif
; .if rv(IsEqualGUID,addr IID_INoMarshal,riid)
; invoke crt_wprintf,cfm$("QIf7")
; lea eax,Inst_ICF ; IClassFactory
; mov ecx,ppvObj
; mov dword ptr [ecx],eax
; mov eax,S_OK
; jmp exQI
; .endif
mov eax,E_NOINTERFACE
exQI:
ret
QueryInterface endp
IMarshal_QIP typedef proto IMarshal_QIP :DWORD,:DWORD,:DWORD
IMarshal_QRP typedef proto IMarshal_QRP :DWORD
IMarshal_RLP typedef proto IMarshal_RLP :DWORD
IMarshal_GetUnmarshalClassProto typedef proto IMarshal_GetUnmarshalClassProto :DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD
IMarshal_GetMarshalSizeMaxProto typedef proto IMarshal_GetMarshalSizeMaxProto :DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD
IMarshal_MarshalInterfaceProto typedef proto IMarshal_MarshalInterfaceProto :DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD
IMarshal_UnmarshalInterfaceProto typedef proto IMarshal_UnmarshalInterfaceProto :DWORD,:DWORD,:DWORD,:DWORD
IMarshal_ReleaseMarshalDataProto typedef proto IMarshal_ReleaseMarshalDataProto :DWORD,:DWORD
IMarshal_DisconnectObjectProto typedef proto IMarshal_DisconnectObjectProto :DWORD,:DWORD
IMarshal_QIPP typedef ptr IMarshal_QIP
IMarshal_QRPP typedef ptr IMarshal_QRP
IMarshal_RLPP typedef ptr IMarshal_RLP
IMarshal_GetUnmarshalClassVal typedef ptr IMarshal_GetUnmarshalClassProto
IMarshal_GetMarshalSizeMaxVal typedef ptr IMarshal_GetMarshalSizeMaxProto
IMarshal_MarshalInterfaceVal typedef ptr IMarshal_MarshalInterfaceProto
IMarshal_UnmarshalInterfaceVal typedef ptr IMarshal_UnmarshalInterfaceProto
IMarshal_ReleaseMarshalDataVal typedef ptr IMarshal_ReleaseMarshalDataProto
IMarshal_DisconnectObjectVal typedef ptr IMarshal_DisconnectObjectProto
IMarshal STRUCT
QueryInterface IMarshal_QIPP 0
AddRef IMarshal_QRPP 0
Release IMarshal_RLPP 0
GetUnmarshalClass IMarshal_GetUnmarshalClassVal 0
GetMarshalSizeMax IMarshal_GetMarshalSizeMaxVal 0
MarshalInterface IMarshal_MarshalInterfaceVal 0
UnmarshalInterface IMarshal_UnmarshalInterfaceVal 0
ReleaseMarshalData IMarshal_ReleaseMarshalDataVal 0
DisconnectObject IMarshal_DisconnectObjectVal 0
IMarshal ENDS
.data
pMarshal dd 0
.code
IMarshal@QueryInterface proc lpME:dword ,riid:DWORD,ppvObj:DWORD
invoke crt_wprintf,cfm$("\n ICP_QIf")
invoke PrintGUID,riid
mov eax,lpME
mov ecx,ppvObj
mov dword ptr [ecx],eax
mov eax,S_OK
ret
IMarshal@QueryInterface endp
IMarshal@AddRef proc lpME:dword
invoke crt_wprintf,cfm$("\n IMarshal@AddRef ")
inc RefCount
mov eax,RefCount
ret
IMarshal@AddRef endp
IMarshal@Release proc lpME:dword
invoke crt_wprintf,cfm$("\n IMarshal@Release")
dec RefCount
mov eax,RefCount
ret
IMarshal@Release endp
IMarshal@GetUnmarshalClass proc lpME :DWORD,riid:DWORD,pv:DWORD,dwDestContext:DWORD,pvDestContext:DWORD,mshlflags:DWORD,pClsid:DWORD
LOCAL dwRegister
LOCAL dd__
;jmp stest2
lea eax,pclsismarsh
mov ecx,pClsid
m2m dword ptr [ecx],dword ptr [eax]
m2m dword ptr [ecx+4],dword ptr [eax+4]
m2m dword ptr [ecx+8],dword ptr [eax+8]
m2m dword ptr [ecx+12],dword ptr [eax+12]
invoke crt_wprintf,cfm$("\n IMarshal@GetUnmarshalClass iidClass :\n ")
invoke PrintGUID,riid
jmp IMarshal@GetUnmarshalClassexit
;************************************************************** 1 test
stest2:
mov pMarshal,0
invoke CoGetStandardMarshal,addr IID___Class1,NULL, dwDestContext,pvDestContext, mshlflags,addr pMarshal
.if pMarshal
invoke crt_wprintf,cfm$("\n CoGetStandardMarshal OK ")
mov ecx,pMarshal
mov edx,ecx
mov ecx,[ecx]
invoke [ECX].IMarshal.GetUnmarshalClass,edx,addr IID___Class1, NULL, dwDestContext, pvDestContext, mshlflags, pClsid
.if !eax
invoke crt_wprintf,cfm$("\n GetUnmarshalClass OK ")
.endif
mov ecx,pMarshal
mov edx,ecx
mov ecx,[ecx]
invoke [ECX].IMarshal.Release,edx
.else
invoke crt_wprintf,cfm$("\n CoGetStandardMarshal ___________ Fail ")
.endif
IMarshal@GetUnmarshalClassexit:
ret
IMarshal@GetUnmarshalClass endp
.data
h dd 0
m_spIStream dd 0
cbMax dq 0
iLNull dq 0
uLLen dq 0
spIPersistStm dd 0
.code
IMarshal@GetMarshalSizeMax proc lpME:DWORD,riid:DWORD,pv:DWORD,dwDestContext:DWORD,pvDestContext:DWORD,mshlflags:DWORD,pSize:DWORD
LOCAL lff
local ulSize,hGlobal,ppStreamReceiver_
mov lff,0
invoke crt_wprintf,cfm$("\n IMarshal@GetMarshalSizeMax iidClass: \n ")
invoke PrintGUID,riid
invoke crt_wprintf,cfm$("\n")
invoke CoGetStandardMarshal,addr IID___Class1,NULL, dwDestContext,pvDestContext, mshlflags,addr pMarshal
.if pMarshal
invoke crt_wprintf,cfm$("\n CoGetStandardMarshal OK ")
mov ecx,pMarshal
mov edx,ecx
mov ecx,[ecx]
invoke [ECX].IMarshal.GetMarshalSizeMax,edx,addr IID___Class1, pv, dwDestContext, pvDestContext, mshlflags, pSize
.if !eax
mov eax,pSize
mov edx,[eax]
invoke crt_wprintf,cfm$("\n IMarshal.GetMarshalSizeMax pSize= %d "),edx
.endif
.else
invoke crt_wprintf,cfm$("\n CoGetStandardMarshal Fail ")
.endif
IMarshal@GetMarshalSizeMaxexit:
mov eax,S_OK ;E_FAIL
ret
IMarshal@GetMarshalSizeMax endp
IMarshal@MarshalInterface proc lpME:DWORD,pStm:DWORD,riid:DWORD,pv:DWORD,dwDestContext:DWORD,pvDestContext:DWORD,mshlflags:DWORD
invoke crt_wprintf,cfm$("\n\n IMarshal@MarshalInterface ")
ret
IMarshal@MarshalInterface endp
IMarshal@UnmarshalInterface proc lpME:DWORD,pStm:DWORD,riid:DWORD,ppv:DWORD
invoke crt_wprintf,cfm$("\n IMarshal@UnmarshalInterface")
mov eax,E_NOTIMPL
ret
IMarshal@UnmarshalInterface endp
IMarshal@ReleaseMarshalData proc lpME:dword ,pStm:DWORD
invoke crt_wprintf,cfm$("\n\n IMarshal@ReleaseMarshalData ")
ret
IMarshal@ReleaseMarshalData endp
IMarshal@DisconnectObject proc lpME:dword ,dwReserved:DWORD
invoke crt_wprintf,cfm$("\n\n IMarshal@DisconnectObject")
ret
IMarshal@DisconnectObject endp
CreateInterface@IMarshal proc
invoke LocalAlloc,LMEM_FIXED or LMEM_ZEROINIT ,sizeof IMarshal
mov edi,eax
mov dword ptr [edi] ,OFFSET IMarshal@QueryInterface
add edi,4
mov dword ptr [edi] ,OFFSET IMarshal@AddRef
add edi,4
mov dword ptr [edi] ,OFFSET IMarshal@Release
add edi,4
mov dword ptr [edi] ,OFFSET IMarshal@GetUnmarshalClass
add edi,4
mov dword ptr [edi] ,OFFSET IMarshal@GetMarshalSizeMax
add edi,4
mov dword ptr [edi] ,OFFSET IMarshal@MarshalInterface
add edi,4
mov dword ptr [edi] ,OFFSET IMarshal@UnmarshalInterface
add edi,4
mov dword ptr [edi] ,OFFSET IMarshal@ReleaseMarshalData
add edi,4
mov dword ptr [edi] ,OFFSET IMarshal@DisconnectObject
ret
CreateInterface@IMarshal endp
For illustration
I added to the QueryInterface:
.if rv(IsEqualGUID,addr IID_IMarshal,riid)
invoke crt_wprintf,cfm$("QIf5")
.if !Inst_InterfaceIMarshal
mov ecx,rv(CreateInterface@IMarshal)
mov Inst_InterfaceIMarshal,ecx
.endif
lea eax,Inst_InterfaceIMarshal
mov ecx,ppvObj
mov [ecx],eax
mov eax,S_OK
jmp exQI
.endif
With the addition of IMarshal inc
MABDELOUAHAB,
Can you post your current source (and maybe a compiled executable) for your client ???
...So, that we can understand what you've got that actually WORKS ???
OK Zen
dont forget \vbexmple\REG.bat
Hi mabdelouahab,
is a VB installation necessary for your example?
Gunther
I Add masm server example, Choose which Server you want to , and If you choose masm server , run "\AsmEvent\REG.bat"
That's fine. Thank you.
Gunther
MABDELOUAHAB,
I was looking at your server code (which presumably compiles into AsmEvent.dll, located in the AsmEvent directory), including the DEF file:
; MyCom2.def : Declares the module parameters.
LIBRARY "AsmEvent.DLL"
EXPORTS
DllCanUnloadNow @1 PRIVATE
DllGetClassObject @2 PRIVATE
DllRegisterServer @3 PRIVATE
DllUnregisterServer @4 PRIVATE
I ran the DUMPBIN utility on the AsmEvent DLL with the /EXPORTS option, and, yes, it told me that AsmEvent.dll does export DllGetClassObject (also, DllRegisterServer, DllUnregisterServer, and DllCanUnloadNow). I'm assuming that AsmEvent.dll is an In-Proc server, because it's a DLL (In-Proc servers must be DLLs). But, without the implementation of DllRegisterServer, I can't be sure.
...Anyway, I couldn't find the implementation of these exported functions.
If DLL doesn't export DllGetClassObject, all activation calls will fail.This quote is from: The Rules of the Component Object Model, MSDN (https://msdn.microsoft.com/en-us/library/ms810016.aspx)
QuoteServer Rules
In-process servers must export DllGetClassObject and DllCanUnloadNow.
In-process servers must support COM self-registration.
In-process and local servers should put an OLESelfReg string in their file version information.
In-process servers should export DllRegisterServer and DllUnRegisterServer.
Local servers should support the /RegServer and /UnRegServer command-line switches.
This quote is from: Introduction to COM Part II - Behind the Scenes of a COM Server (http://www.codeproject.com/Articles/901/Introduction-to-COM-Part-II-Behind-the-Scenes-of-a)
Quote from: Introduction to COM Part II - Behind the Scenes of a COM ServerAn in-proc server must meet two criteria before it can be used by the COM library:
It must be registered properly under the HKEY_CLASSES_ROOT\CLSID key.
It must export a function called DllGetClassObject().
Here is a good description of: How Does COM Activation Work Anyway?, Larry Osterman's Blog (http://blogs.msdn.com/b/larryosterman/archive/2004/10/12/241420.aspx) (The article's hyperlinks are dead, but, you can Google them.)
DllGetClassObject Entry Point, MSDN (https://msdn.microsoft.com/en-us/library/ms680760(v=vs.85).aspx)
Create Class (which is in the client code directory, TestEvents.inc, looks like this)
CreateClass Proc
invoke CoCreateInstance,addr sCLSID_Class1,NULL,CLSCTX_LOCAL_SERVER,addr IID_IUnknown,addr Inst_IUnknown
.if eax == S_OK
LEA ECX, Inst_Class1
PUSH ECX
lea eax ,IID__Class1
PUSH EAX
MOV EDX,Inst_IUnknown
PUSH EDX
MOV EDX,[EDX]
CALL DWORD PTR [EDX]
.if !eax
invoke crt_wprintf,cfm$("\n CreateClass OK ")
MOV EAX,TRUE
.else
invoke crt_wprintf,cfm$("\n CreateClass fail ")
MOV EAX,FALSE
.endif
.else
MOV EAX,FALSE
.endif
ret
CreateClass endp
This routine is called in TestEvent.asm. Presumably, TestEvent.asm will be compiled into the client exe.
You know, it's confusing,...all these directories have files with the same names (yes, I know, the paths are different).
MABDELOUAHAB,
You will notice in the above call to CoCreateInstance (https://msdn.microsoft.com/en-us/library/windows/desktop/ms686615(v=vs.85).aspx), that the CLSCTX_LOCAL_SERVER member of the CLSCTX Enumeration (https://msdn.microsoft.com/en-us/library/windows/desktop/ms693716(v=vs.85).aspx) is specified, indicating that the server is: an Out-Of-Process Server (The EXE code that creates and manages objects of this class runs on same machine but is loaded in a separate process space.)
The following is from: COM Clients and Servers, MSDN (https://msdn.microsoft.com/en-us/library/windows/desktop/ms683835(v=vs.85).aspx)
QuoteThere are two main types of servers, in-process and out-of-process. In-process servers are implemented in a dynamic linked library (DLL), and out-of-process servers are implemented in an executable file (EXE). Out-of-process servers can reside either on the local computer or on a remote computer.
Informative reading: COM Server Responsibilities, MSDN (https://msdn.microsoft.com/en-us/library/windows/desktop/ms690101(v=vs.85).aspx)
...So,...what is it ??? An In-Proc Server or an Out-Of-Process Server ??? I think that neglecting these kinds of critically important details in your current project, is why MASM Forum members are reluctant to provide suggestions and help for your 'problem',...
If you don't know, just ask,...there a number of extremely-knowledgeable, World-Class COM programmers that belong to this Forum,...
...Heck,...even DAVE knows quite a bit about COM,...:icon_eek:
...Here's how Japheth implements DllGetClassObject (from, SimpleServer.asm, in the zip file, Simple Server Example, Japheth.zip (http://masm32.com/board/index.php?topic=4153.msg44120#msg44120)):
;--------------------------------------------------------------
;--- DllGetClassObject: scans object table to see if requested
;--- CLSID is in there. If yes, creates an IClassFactory object
;--------------------------------------------------------------
DllGetClassObject PROC public uses esi rclsid:ptr CLSID,riid:ptr IID,ppReturn:ptr LPCLASSFACTORY
mov eax, ppReturn
mov DWORD PTR [eax], 0
mov esi, offset ObjectMap
mov ecx, OBJECTMAPITEMS
.while (ecx)
push ecx
invoke IsEqualGUID, rclsid, [esi].ObjectEntry.pClsId
pop ecx
.break .if (eax)
add esi, sizeof ObjectEntry
dec ecx
.endw
.if (!ecx)
mov eax, CLASS_E_CLASSNOTAVAILABLE
jmp done
.endif
invoke Create@CClassFactory, esi
.if (eax == NULL)
mov eax, E_OUTOFMEMORY
jmp done
.endif
mov esi,eax
invoke vf(esi,IClassFactory,QueryInterface),riid,ppReturn
push eax
invoke vf(esi,IClassFactory,Release)
pop eax
done:
ret
DllGetClassObject ENDP
...That file also has example implementations of: DllRegisterServer, DllUnregisterServer, and, DllCanUnloadNow.
...Here are some interesting blog articles from Raymond Chen:
How Can I Tell Whether a DLL Has Been Registered? (http://blogs.msdn.com/b/oldnewthing/archive/2011/11/10/10235540.aspx)
Registration-Free COM the Old-Fashioned Way (http://blogs.msdn.com/b/oldnewthing/archive/2012/04/06/10291324.aspx)
...And, you should probably read these too (from Larry Osterman's Blog):
So What Exactly IS COM Anyway ?, Larry Osterman (http://blogs.msdn.com/b/larryosterman/archive/2004/10/15/242989.aspx)
Minimal COM Object Registration, Larry Osterman (http://blogs.msdn.com/b/larryosterman/archive/2006/01/05/509731.aspx)
What Registry Entries Are Needed To Register a COM Object ?, Larry Osterman (http://blogs.msdn.com/b/larryosterman/archive/2006/01/11/511647.aspx)
COM Registration If You Need a Typelib, Larry Osterman (http://blogs.msdn.com/b/larryosterman/archive/2006/01/09/510856.aspx)
What Are These "Threading Models" and Why Do I Care?, Larry Osterman (http://blogs.msdn.com/b/larryosterman/archive/2004/04/28/122240.aspx)
COM Registration for Cross Process Access, Larry Osterman (http://blogs.msdn.com/b/larryosterman/archive/2006/01/06/510168.aspx)
When Do You Need an APPID in your COM Registration ?, Larry Osterman (http://blogs.msdn.com/b/larryosterman/archive/2006/01/04/509436.aspx)
...OK,...sorry,...that last section is just overkill,...
Hi Zen
I thank you for helping me,
Just for clarification, I found that information available, clear, but there is always an error.
I focused on errors only, in order to know the real problem
Before speaking to the real problem, I want to point out what I have learned
When the Client forwards request to the server to associate a connection point, The server sends the request to the proxy, either iid of custom interface or iid_IMarshal ,
* iid of custom interface In the case of single Thread,
* IID_IMarshal In the case of Multi-Thread,In this case the system intervenes by COMBASE.DLL to solve the problem between disputants
The Client must send our Instance of IMarshal, then COM sends 3 consecutive requests
1.IMarshal::GetUnmarshalClass is called to obtain the CLSID of the proxy object.
2.IMarshal::GetMarshalSizeMax is called to determine the size of the marshaling packet that the interface needs.
The system allocates the memory and then creates a stream object that wraps the memory buffer.
3.IMarshal::MarshalInterface is called to tell the object to marshal the interface pointer into the stream.
Here detailing locations Will That Be Custom or Standard Marshaling? (http://thrysoee.dk/InsideCOM+/ch14c.htm) or How to marshal interfaces across apartments in Visual C++ (https://support.microsoft.com/en-us/kb/206076)(for more information)
I go back to my problem
I used VS2012 Debuger to access error, I discovered that ...(Focused with me please)
Error in which debt program stops Is that the Parametre sent to the function overlap, Sometimes at the programme ، and Sometimes at the COMBASE.DLL
(https://googledrive.com/host/0B5q3H3sB5u6QQ0JBeWt2dGhjRXc)
this is mov edx,pSize
mov dword ptr [edx],255
In
IMarshal@GetMarshalSizeMax proc lpME:DWORD, riid:DWORD,pv:DWORD,dwDestContext:DWORD,pvDestContext:DWORD,mshlflags:DWORD,pSize:DWORD
The procedure receives the value of pSize 0
(https://googledrive.com/host/0B5q3H3sB5u6QVFdvSTYtc1hUNFE)
this is IStream::Write
but pStream = addr of IID_INoMarshal
I think that the Align
I've translated the proceedings being conducted by the COMBASE.DLL in the BackGround:
PMarshal proc pUnknown,riid, dwDestContext, pvDestContext, dwFlags
; AddRef the incoming pointer to verify that it is safe.
;******************* pUnknown::AddRef
MOV EDX, pUnknown
PUSH EDX
MOV EDX,[EDX]
CALL DWORD PTR [EDX+4] ; pUnknown::AddRef
; Do you support custom marshaling?
;******************* pUnknown::QueryInterface
PUSH OFFSET pMarshal_
PUSH OFFSET IID_IMarshal
MOV EDX, pUnknown
PUSH EDX
MOV EDX,[EDX]
CALL DWORD PTR [EDX] ; pUnknown::QueryInterface
; I guess not, so we'll use standard marshaling.
.if eax == E_NOINTERFACE
invoke CoGetStandardMarshal,riid, pUnknown, dwDestContext, pvDestContext, dwFlags, addr pMarshal_
.endif
; OK, what's the CLSID of your proxy?
mov ecx,pMarshal_
mov edx,ecx
mov ecx,[ecx]
invoke [ECX].IMarshal.GetUnmarshalClass,edx,riid, pUnknown, dwDestContext, pvDestContext, dwFlags,addr clsid
; How much space do you need?
mov ecx,pMarshal_
mov edx,ecx
mov ecx,[ecx]
invoke [ECX].IMarshal.GetMarshalSizeMax,edx,riid, pUnknown, dwDestContext, pvDestContext, dwFlags,addr packetSize
; Allocate that memory.
mov ecx,packetSize
add ecx,sizeof CLSID
mov pMem ,rv(GlobalAlloc,GHND, ecx );
; Turn it into a stream object.
invoke CreateStreamOnHGlobal,pMem, FALSE, addr pStream_
.if !eax
invoke crt_wprintf,cfm$("\n .... CreateStreamOnHGlobal OK \n")
.endif
; Write the CLSID of the proxy into the stream.
invoke WriteClassStm,pStream_,addr clsid
.if !eax
invoke crt_wprintf,cfm$("\n .... WriteClassStm OK \n")
.endif
; Marshal that interface into the stream.
mov ecx,pMarshal_
mov edx,ecx
mov ecx,[ecx]
invoke [ECX].IMarshal.MarshalInterface,edx,pStream_,riid, pUnknown, dwDestContext, pvDestContext, dwFlags
push eax
; Release everything in sight.
MOV EDX, pStream_
PUSH EDX
MOV EDX,[EDX]
CALL DWORD PTR [EDX+8] ; pStream::Release
MOV EDX, pMarshal_
PUSH EDX
MOV EDX,[EDX]
CALL DWORD PTR [EDX+8] ; pMarshal::Release
MOV EDX, pUnknown
PUSH EDX
MOV EDX,[EDX]
CALL DWORD PTR [EDX+8] ; pUnknown::Release
pop eax
ret
PMarshal endp
Without the use of any communication with the server
And without any new CoCreateInstanceof objects
When you create new interface and use CoMarshalInterface the com is the same with you and produce the same errors
invoke CoInitializeEx, NULL,COINIT_MULTITHREADED ;COINIT_APARTMENTTHREADED ;
invoke CoCreateGuid,addr pclsismarsh
mov Inst_InterfaceMASM,rv(CreateInterfaceMASM)
mov Inst_ICF,rv(CreateInterface@IClassFactory)
invoke CoRegisterClassObject,addr pclsismarsh,addr Inst_ICF ,CLSCTX_LOCAL_SERVER, REGCLS_MULTIPLEUSE,addr dwReg__
invoke GlobalAlloc,GMEM_MOVEABLE,ecx
mov hGlobal,eax
invoke CreateStreamOnHGlobal, hGlobal, TRUE,addr ppStreamReceiver_
invoke CoMarshalInterface ,ppStreamReceiver_, addr IID___Class1, addr Inst_Interface, MSHCTX_LOCAL, NULL, MSHLFLAGS_NORMAL
Quote from: Zen on April 24, 2015, 06:33:20 AM
MABDELOUAHAB,
I was looking at your server code (which presumably compiles into AsmEvent.dll, located in the AsmEvent directory), including the DEF file:
; MyCom2.def : Declares the module parameters.
LIBRARY "AsmEvent.DLL"
EXPORTS
DllCanUnloadNow @1 PRIVATE
DllGetClassObject @2 PRIVATE
DllRegisterServer @3 PRIVATE
DllUnregisterServer @4 PRIVATE
I ran the DUMPBIN utility on the AsmEvent DLL with the /EXPORTS option, and, yes, it told me that AsmEvent.dll does export DllGetClassObject (also, DllRegisterServer, DllUnregisterServer, and DllCanUnloadNow). I'm assuming that AsmEvent.dll is an In-Proc server, because it's a DLL (In-Proc servers must be DLLs). But, without the implementation of DllRegisterServer, I can't be sure.
....
Not important, because CoCreateInstanceEx locate COM component DLL (From registry) then load it into memory using LoadLibrary, then locate DllGetClassObject function exported from the DLL using GetProcAddress Win32 API, Call DllGetClassObject and obtain IClassFactory interface of your component class factory
Quote from: Zen on April 25, 2015, 03:29:30 AM
MABDELOUAHAB,
You will notice in the above call to CoCreateInstance (https://msdn.microsoft.com/en-us/library/windows/desktop/ms686615(v=vs.85).aspx), that the CLSCTX_LOCAL_SERVER member of the CLSCTX Enumeration (https://msdn.microsoft.com/en-us/library/windows/desktop/ms693716(v=vs.85).aspx) is specified, indicating that the server is: an Out-Of-Process Server (The EXE code that creates and manages objects of this class runs on same machine but is loaded in a separate process space.)
The following is from: COM Clients and Servers, MSDN (https://msdn.microsoft.com/en-us/library/windows/desktop/ms683835(v=vs.85).aspx)
QuoteThere are two main types of servers, in-process and out-of-process. In-process servers are implemented in a dynamic linked library (DLL), and out-of-process servers are implemented in an executable file (EXE). Out-of-process servers can reside either on the local computer or on a remote computer.
Informative reading: COM Server Responsibilities, MSDN (https://msdn.microsoft.com/en-us/library/windows/desktop/ms690101(v=vs.85).aspx)
...So,...what is it ??? An In-Proc Server or an Out-Of-Process Server ??? I think that neglecting these kinds of critically important details in your current project ...
Zen
I haven't neglected this, and explained that if case single Thread the QueryInterface will receive IID of EventClass, and in the case of multiple thread
The QueryInterface receives IID_IMarshal Because the program required that Marshal our Interface to send it
And Excuse me for my english
Quote from: Zen on April 25, 2015, 03:29:30 AM
..., is why MASM Forum members are reluctant to provide suggestions and help for your 'problem',...
If you don't know, just ask,...there a number of extremely-knowledgeable, World-Class COM programmers that belong to this Forum,...
...Heck,...even DAVE knows quite a bit about COM,...
Always I'm getting help from Forum members, Thank everyone of course :biggrin:
If someone can continue with me, I suggested that I restored from zero, and does not add anything to the project until I explained
First: I make the very simplest server software does not contain only 1 function and 1 event
Open VB New Project an Select DLL ActiveX , In Class1 add:
Public Event EventForTest()
Public Sub MethodForTest()
MsgBox "Hi Is Me ..." ' invoke MessageBox ...
RaiseEvent EventForTest ' FireEvents EventForTest
End Sub
Rename Class1 to ClsForTest and rename Project1 to DllForTest
In FILE Menu select "Create DllForTest.DLL" and save it
VB will automatically made Active X Dll contains the following:_ClsForTest Dispatsh ;__ClsForTest (Event Dispatsh ) ; Inc file is as follows (Generate by me):
.data
CLSID_ClsForTest GUID <0B6E21F7Ch,0AC81h,040EDh,<089h,0F7h,0D9h,034h,05Fh,027h,051h,066h>>
IID__ClsForTest GUID <06B81DDC9h,0881Bh,0486Fh,<087h,08Bh,03Ch,0BCh,0A3h,005h,09Dh,090h>>
Inst__ClsForTest dd 0
_ClsForTest struct
QueryInterface dd 0
AddRef dd 0
Release dd 0
GetTypeInfoCount dd 0
GetTypeInfo dd 0
GetIDsOfNames dd 0
_Invoke dd 0
MethodForTest dd 0
_ClsForTest ends
.code
_ClsForTest@Release proc
MOV EDX, Inst__ClsForTest
PUSH EDX
MOV EDX,[EDX]
CALL DWORD PTR [EDX+8] ;Call ClsForTest.Release
ret
_ClsForTest@Release endp
_ClsForTest@MethodForTest proc
MOV EDX, Inst__ClsForTest
PUSH EDX
MOV EDX,[EDX]
CALL DWORD PTR [EDX+28] ;Call ClsForTest.MethodForTest
ret
_ClsForTest@MethodForTest endp
_ClsForTest@CreateClass Proc
LOCAL __IUnknown
invoke CoCreateInstance,addr CLSID_ClsForTest,NULL,CLSCTX_ALL,addr IID_IUnknown,addr __IUnknown
.if eax == S_OK
LEA ECX, Inst__ClsForTest
PUSH ECX
lea eax ,IID__ClsForTest
PUSH EAX
MOV EDX,__IUnknown
PUSH EDX
MOV EDX,[EDX]
CALL DWORD PTR [EDX] ;QueryInterface
.if !eax
invoke crt_wprintf,cfm$("\n CreateClass _ClsForTest OK ")
MOV EAX,TRUE
.else
invoke crt_wprintf,cfm$("\n CreateClass _ClsForTest fail ")
MOV EAX,FALSE
.endif
.else
MOV EAX,FALSE
.endif
ret
_ClsForTest@CreateClass endp
Go back to Masm, make the following
create New Consol :
__UNICODE__ equ 1
include \masm32\include\masm32rt.inc
.data
IID_IUnknown GUID <000000000H,00000H,00000H,<0C0H,000H,000H,000H,000H,000H,000H,046H>>
include Cls_ClsForTest.inc
.code
Start:
invoke CoInitializeEx, NULL,COINIT_MULTITHREADED ;COINIT_APARTMENTTHREADED ;
.if rv( _ClsForTest@CreateClass); Create New ClsForTest
invoke _ClsForTest@MethodForTest ;ClsForTest.MethodForTest
invoke _ClsForTest@Release ; ClsForTest.Release
.endif
invoke CoUninitialize
exit
End Start
add the INC file of DllFor test to Proj, Must register DllForTest.dll " REGSVR32 "DllForTest.dll" "
Run and I will complete
at here I work without EVENT
if I want to work with events we must create an interface at my application then advise it to the Server DLL
I generate Inc File for the Event Clss (__ClsForTest) , add it to the project
.data
IID___ClsForTest GUID <03D369F24h,0B1BFh,04816h,<0BEh,0A2h,0B1h,0AFh,0A2h,0FFh,098h,053h>>
Inst__ClsForTest dd 0
___ClsForTest@RefCount dd 0
__ClsForTest struct ;;;;;;;; Events
QueryInterface dd 0
AddRef dd 0
Release dd 0
GetTypeInfoCount dd 0
GetTypeInfo dd 0
GetIDsOfNames dd 0
_Invoke dd 0
EventForTest dd 0
__ClsForTest ends
.code
__ClsForTest@QueryInterface proc lpME:dword ,riid:DWORD,ppvObj:DWORD
invoke crt_wprintf,cfm$("\n __ClsForTest@QueryInterface")
.if rv(IsEqualGUID,addr IID_IUnknown ,riid)
mov eax,lpME
mov ecx,ppvObj
mov dword ptr [ecx],eax
mov eax,S_OK
jmp __ClsForTest@QueryInterfaceExit
.endif
.if rv(IsEqualGUID,addr IID___ClsForTest,riid)
mov eax,lpME
mov ecx,ppvObj
mov dword ptr [ecx],eax
mov eax,S_OK
jmp __ClsForTest@QueryInterfaceExit
.endif
mov eax,E_NOINTERFACE
__ClsForTest@QueryInterfaceExit:
ret
__ClsForTest@QueryInterface endp
__ClsForTest@AddRef proc lpME:dword
invoke crt_wprintf,cfm$("\n __ClsForTest@AddRef")
inc ___ClsForTest@RefCount
mov eax,___ClsForTest@RefCount
ret
__ClsForTest@AddRef endp
__ClsForTest@Release proc lpME:dword
invoke crt_wprintf,cfm$("\n __ClsForTest@Release")
dec ___ClsForTest@RefCount
mov eax,___ClsForTest@RefCount
ret
__ClsForTest@Release endp
__ClsForTest@GetTypeInfoCount proc lpME:dword ,pctinfo:DWORD
invoke crt_wprintf,cfm$("\n __ClsForTest@GetTypeInfoCount")
mov eax,E_NOTIMPL
ret
__ClsForTest@GetTypeInfoCount endp
__ClsForTest@GetTypeInfo proc lpME:dword ,itinfo:DWORD,lcid:DWORD,pptinfo:DWORD
invoke crt_wprintf,cfm$("\n __ClsForTest@GetTypeInfo")
mov eax,E_NOTIMPL
ret
__ClsForTest@GetTypeInfo endp
__ClsForTest@GetIDsOfNames proc lpME:dword ,riid:DWORD,rgszNames:DWORD,cNames:DWORD,lcid:DWORD,rgdispid:DWORD
invoke crt_wprintf,cfm$("\n __ClsForTest@GetIDsOfNames")
mov eax,E_NOTIMPL
ret
__ClsForTest@GetIDsOfNames endp
__ClsForTest@_Invoke proc lpME:dword ,dispidMember:SDWORD,riid:DWORD,lcid:DWORD,wFlags:WORD,pdispparams:DWORD,pvarResult:DWORD,pexcepinfo:DWORD,puArgErr:DWORD
invoke crt_wprintf,cfm$("\n __ClsForTest@_Invoke %d ") ,dispidMember
mov ecx,dispidMember
cmp ecx,1
jne @F
push lpME
call __ClsForTest@MethodForTest
jmp __ClsForTest@_InvokeExit
@@: ;cmp ecx,2
;jne @F
; push lpME
; call __ClsForTest@MethodForTest
; jmp __ClsForTest@_InvokeExit...............
__ClsForTest@_InvokeExit:
xor eax,eax
ret
__ClsForTest@_Invoke endp
__ClsForTest@MethodForTest proc lpME:dword
invoke crt_wprintf,cfm$("\n __ClsForTest@MethodForTest")
invoke MessageBox,0,chr$(" __ClsForTest@MethodForTest Event Fired OK"),0,0
ret
__ClsForTest@MethodForTest endp
__ClsForTest@CreateInterface proc
invoke LocalAlloc,LMEM_FIXED or LMEM_ZEROINIT ,sizeof __ClsForTest
mov edi,eax
mov dword ptr [edi] ,OFFSET __ClsForTest@QueryInterface
add edi,4
mov dword ptr [edi] ,OFFSET __ClsForTest@AddRef
add edi,4
mov dword ptr [edi] ,OFFSET __ClsForTest@Release
add edi,4
mov dword ptr [edi] ,OFFSET __ClsForTest@GetTypeInfoCount
add edi,4
mov dword ptr [edi] ,OFFSET __ClsForTest@GetTypeInfo
add edi,4
mov dword ptr [edi] ,OFFSET __ClsForTest@GetIDsOfNames
add edi,4
mov dword ptr [edi] ,OFFSET __ClsForTest@_Invoke
add edi,4
mov dword ptr [edi] ,OFFSET __ClsForTest@MethodForTest
ret
__ClsForTest@CreateInterface endp
__ClsForTest@Advise proc PUBLIC
LOCAL pIConnectionPointContainer,m_pIConnectionPoint,m_dwCookie,dwRegister
xor ecx,ecx
mov pIConnectionPointContainer,ecx
mov m_pIConnectionPoint,ecx
mov m_dwCookie,ecx
.if !Inst__ClsForTest
mov Inst__ClsForTest,rv(__ClsForTest@CreateInterface)
.endif
;------------------------------------------ Class.QueryInterface IConnectionPointContainer
LEA ECX, pIConnectionPointContainer
PUSH ECX
lea eax ,IID_IConnectionPointContainer
PUSH EAX
MOV EDX,Inst_ClsForTest
PUSH EDX
MOV EDX,[EDX]
CALL DWORD PTR [EDX];QueryInterface
.if pIConnectionPointContainer
invoke crt_wprintf,cfm$("\n QueryInterface IConnectionPointContainer OK ")
;------------------------------------------ IConnectionPointContainer.FindConnectionPoint
LEA ECX, m_pIConnectionPoint
PUSH ECX
lea eax ,IID___ClsForTest
PUSH EAX
MOV EDX,pIConnectionPointContainer
PUSH EDX
MOV EDX,[EDX]
CALL DWORD PTR [EDX+16];FindConnectionPoint
.if m_pIConnectionPoint
MOV EDX,pIConnectionPointContainer
PUSH EDX
MOV EDX,[EDX]
CALL DWORD PTR [EDX+8];release
invoke crt_wprintf,cfm$("\n FindConnectionPoint OK")
;------------------------------------------ FindConnectionPoint.Advise
LEA ECX, m_dwCookie
PUSH ECX
lea ecx,Inst__ClsForTest
PUSH ecx
MOV EDX,m_pIConnectionPoint
PUSH EDX
MOV EDX,[EDX]
CALL DWORD PTR [EDX+20];Advise
.if eax == S_OK
invoke crt_wprintf,cfm$("\n Advise OK ")
.else
invoke crt_wprintf,cfm$("\n Advise Failer eax:=0x%Xh"),eax
.endif
.else
invoke crt_wprintf,cfm$("\n FindConnectionPoint Failer")
.endif
.else
invoke crt_wprintf,cfm$("\n IConnectionPointContainer Failer")
.endif
ret
__ClsForTest@Advise endp
and after invoke _ClsForTest@MethodForTest : add
invoke __ClsForTest@Advise
then add another invoke _ClsForTest@MethodForTest for test if the event is fired
The final code as follows: __UNICODE__ equ 1
include \masm32\include\masm32rt.inc
.data
IID_IUnknown GUID <000000000H,00000H,00000H,<0C0H,000H,000H,000H,000H,000H,000H,046H>>
IID_IConnectionPointContainer GUID <0B196B284h,0BAB4h,0101Ah,<0B6h,09Ch,000h,0AAh,000h,034h,01Dh,007h>>
include Ev_ClsForTest.inc
include Cls_ClsForTest.inc
.code
Start:
invoke CoInitializeEx, NULL,COINIT_MULTITHREADED ;COINIT_APARTMENTTHREADED ;
.if rv( _ClsForTest@CreateClass); Create New ClsForTest
invoke _ClsForTest@MethodForTest ;ClsForTest.MethodForTest
invoke __ClsForTest@Advise
invoke _ClsForTest@MethodForTest ;ClsForTest.MethodForTest
invoke _ClsForTest@Release ; ClsForTest.Release
.endif
invoke CoUninitialize
inkey
exit
End Start
Switch between COINIT_MULTITHREADED and COINIT_APARTMENTTHREADED at CoInitializeEx; then tell me what's going on
At here everything is working fine; We have succeeded in working with InprocServer DLL
So
I created the same ٍVB project, But this time with EXE Active X
Alert: I added event 2
Public Event EventForTest()
Public Event EventForTest2()
Public Sub MethodForTest()
MsgBox "Hi Is Me ..."
RaiseEvent EventForTest2
RaiseEvent EventForTest
End Sub
And I created a second Masm project for working with the new VB project , But the strange thing this time everything well
Quote
CreateClass _ClsForTest OK
QueryInterface IConnectionPointContainer OK
FindConnectionPoint OK
__ClsForTest@QueryInterface
__ClsForTest@QueryInterface
__ClsForTest@QueryInterface
__ClsForTest@QueryInterface
__ClsForTest@QueryInterface
__ClsForTest@AddRef
__ClsForTest@QueryInterface
__ClsForTest@QueryInterface
__ClsForTest@QueryInterface
__ClsForTest@QueryInterface
__ClsForTest@QueryInterface
__ClsForTest@QueryInterface
__ClsForTest@QueryInterface
__ClsForTest@QueryInterface
__ClsForTest@QueryInterface
__ClsForTest@Release
__ClsForTest@QueryInterface
__ClsForTest@AddRef
Advise OK
__ClsForTest@QueryInterface
__ClsForTest@QueryInterface
__ClsForTest@Release
__ClsForTest@AddRef
__ClsForTest@_Invoke 2
__ClsForTest@MethodForTest
__ClsForTest@_Invoke 1
__ClsForTest@MethodForTest
__ClsForTest@Release
__ClsForTest@Release
__ClsForTest@Release
__ClsForTest@QueryInterface
__ClsForTest@QueryInterface
__ClsForTest@ReleasePress any key to continue ...
Don't forget Reg.Bat to register exe server
What happens to me
This time everything worked
Everything went correctly
What we neglect in the past :redface:
Dave , Zen ,adeyblue ,jj2007 , Biterider ,Gunther .. Thank you all :t, Forum members, Thank everyone
Hi mabdelouahab,
Quote from: mabdelouahab on April 26, 2015, 04:53:20 AM
Dave , Zen ,adeyblue ,jj2007 , Biterider ,Gunther .. Thank you all :t, Forum members, Thank everyone
it was our pleasure to help you.
Gunther