Original: http://vckbase.com/index.php/wv/1244.html
First, preface
The runtime of my COM component generates a window that I need to notify the caller when the user double-clicks the window;
My COM component uses threads to download a file on the network, and when I have completed the task, I need to notify the caller;
My COM component completes the function of a clock, and when the scheduled time arrives, I need to notify the caller;
... ... ... ...
This get books back begins to say COM events, notifications, connection points ... This content is more, I divide two times (total four return) to introduce.
Ii. Methods of Notification
When an event occurs within a program party, it is necessary to notify party B, using only a few methods:
Notification method |
Simple description |
Comments |
Direct message |
PostMessage () PostThreadMessage () |
Send a message to a window or thread |
When you do it, I don't care. |
SendMessage () |
Execute message response function immediately |
The message handler function does not return after execution |
SendMessage (Wm_copydata ...) |
While sending messages, you can also take some custom data from the past |
It's more commonly used, so it's listed separately. |
Indirect message |
InvalidateRect () SetTimer () ...... |
The called function sends some related messages |
There's too many functions like that. |
callback function |
GetOpenFileName () ... |
Executes the callback function when the user changes the file selection |
Hey! Man, this is my phone, something just a word. |
COM era, these methods are basically unable to play around, because ... You think the COM component is running in a distributed environment , the other side of the world on the computer running components, how can you send messages to your window that? Of course not! (But then again, for an ActiveX component that can only be run locally, you can certainly send a window message.) )
Callback functions are the basis for designing COM notification methods. The callback function, in essence, tells me a pointer to a function in advance, and when I have to, I call the function directly, and the callback function does what, how to do it, I do not care at all. OK, let me ask you a question: What is COM's interface? An interface is actually a set of related functions (the definition is not rigorous, but you can understand that). Therefore, instead of using "callback functions" in COM rather than "callback interfaces" (more clearly, it is using a bunch of packaged "callback functions" sets), callback interfaces, we also call "receiver interfaces".
Figure one, the client passes the sink interface pointer to COM. When an event occurs, COM calls the sink interface function to complete the notification
The functionality that this sample program completes is:
Client Boot component (simple11.ievent1.1) and get interface pointer IEvent1 *;
The calling interface method Ievent1::advise () passes a receiver (sink) interface pointer (Icallback *) inside the client to the component server;
Call Ievent1::add () to calculate the number of two integers;
But the result of the calculation is not returned by the function, but is returned to the client by Icallback::fire_result ();
When the client no longer needs to accept the event, call Ievent1::unadvise () disconnects and the component is contacted.
Third, component implementation steps
1. Establish a solution
2. In the solution, build an ATL project. The project name in the sample program is called Simple12, which cancels "attributed" and the other accepts the default option.
3, select the project, execute the right mouse button menu command "Add \ Add Class".
3-1, left category Select ATL, right template select ATL Simple Object
3-2. In the name card, enter the component name. Event1 in the sample program (note 1)
3-3. In the option card, modify the interface type "Custom" (note 2)
4, select the IEnvent1 interface, the right mouse button menu "Add \ Add Method"
Figure two, add the interface function add ([in] long n1,[in] long N2)
Figure three, increase the interface function Advise ([in] icallback *pcallback,[out] Long *pdwcookie)
Figure four, increase the interface function Unadvise ([in] long Dwcookie)
You should notice that in the Add () function, there is no IDL attribute like [out], [retval], hehe, because we're not going to get the results directly from the Add () function. How else to demonstrate the callback interface AH:-) In addition, in function Advise (), you need to return an integer dwcookie, what is this? The rationale is simple, because our component wants to support callback connections for multiple objects at the same time. So when the client passes an interface to our component, I return it to its unique cookie number to represent the identity, and in the future when the connection is Unadvise (), it needs to give me the cookie ID number, so I know who wants to disconnect.
5, add the callback interface Icallback IDL definition. Open the IDL file and enter it manually (the bold part is entered manually), and then Save:
Import "Oaidl.idl"; import "Ocidl.idl"; [Object,uuid (DB72DF86-70E9-4ABC-B2F8-5E04062D3B2E),//This IID can be used Gudigen. EXE generates helpstring ("Icallback interface"), Pointer_default (unique)]interface icallback:iunknown{}; [object,//The following is the same as the sample program, of course, if you build your own program will certainly have a different uuid (DB72DF85-70E9-4ABC-B2F8-5E04062D3B2E), helpstring ("IEvent1 Interface "), pointer_default (unique)]interface ievent1:iunknown{[helpstring (" Method Add ")] HRESULT Add ([in] long N1, [ In] long N2); [HelpString ("Method Advise")] HRESULT Advise ([in] icallback * pcallback, [out] long * pdwcookie); [HelpString ("Method Unadvise")] HRESULT Unadvise ([in] long Dwcookie);}; [UUID (fba1e0f0-49cd-4b77-b9b1-4dc066af8a8e), version (1.0), helpstring ("Simple12 1.0 Type Library")]library simple11lib{ Importlib ("Stdole32.tlb"); Importlib ("Stdole2.tlb"); [UUID (53E00126-B1A0-4510-B9BC-75ED87CE2DB7), helpstring ("Event1 Class")]coclass Event1{[default] interface IEvent1 ;//need manual input, it is said that VB use, can not have [Source,default] attribute [source, default] interface Icallback; };};
6. Add Callback interface function
Figure V, adding callback interface functions
In fact, as in the previous method, just be careful not to choose the wrong interface is good.
Figure VI, increase the interface function Fire_result ([in] long Nresult)
We calculate the integer and, after getting the result, we have to rely on this callback interface function to feedback to the client.
7. Add an array of internal components to hold callback interface pointers
As we have just said, our component intends to support callback connections for multiple objects, so we will use an array to save. Since vc.net cannot use the wizard to add member variables in the form of an array, let's open the header file for the CEvent1 class by hand:
... private:icallback * m_pcallback[10];......
There are several ways to save an array. The sample program is relatively simple and defines a member array variable of 10 element spaces. If you have learned to use STL, you can also use vectors and other containers to achieve. Watch out! Attention! Attention! In the constructor, do not forget to initialize the array element to null.
8, OK, the following start to complete all the code
STDMETHODIMP Cevent1::add (Long N1, long N2) {Long nresult = n1 + n2;for (int i=0; i<10; i++) {if (M_pcallback[i])//IF Callback interface valid M_pcallback[i]->fire_result (nresult);//Send event/notification}return S_OK;} STDMETHODIMP Cevent1::advise (Icallback *pcallback, long *pdwcookie) {if (NULL = = pcallback)//actually give me a null pointer?! return e_invalidarg;for (int i=0; i<10; i++)//Find a location to save the interface pointer {if (NULL = = M_pcallback[i])//found {m_pcallback[i] = Pcall back;//Save to Array m_pcallback[i]->addref ();//Pointer counter +1*pdwcookie = i + 1;//cookie is array subscript//+1 is intended to avoid using 0 because 0 means invalid return s_ OK;}} Return e_outofmemory;//more than 10 connections, memory is not enough}stdmethodimp cevent1::unadvise (long Dwcookie) {if (dwcookie<1 | | dwcookie >10)//who did this? Random argument return e_invalidarg;if (NULL = = m_pcallback[DwCookie-1])//Parameter error, or the interface pointer is invalid return e_invalidarg;m_pcallback[Dwcoo Kie-1]->release ();//Pointer counter -1m_pcallback[dwCookie-1] = null;//Empty The array element of the subscript return S_OK;
Iv. Client Implementation Steps
After you download the sample program, go to the client implementation program. Here I'll just explain how the receiver is structured:
Figure VII Derivation of the receiver class from Icallback CSink
Here Icallback is COM interface, so csink can not be instantiated, if you go to compile, will get a lump (note 3) error, report that you did not implement the virtual function. Then, we can follow the error report to implement all the virtual functions:
STDMETHODIMP is a macro, equivalent to long __stdcallstdmethodimp csink::queryinterface (const struct _GUID &iid,void * * PPV) {*ppv= this;//no matter what interface you want, it is actually the object itself return S_OK;} ULONG __stdcall csink::addref (void) {return 1;} Just make a fake, because this object will not quit before the program ends. ULONG __stdcall Csink::release (void) {return 0;} Do a fake, because anyway, this object will not quit before the end of the program Stdmethodimp Csink::raw_fire_result (Long nresult) {...//display the calculation results in the window return S_OK;}
Five , Summary
There are two basic ways that COM components implement events and notify such functionality. The callback interface introduced today is very good, fast, the structure is clear, the implementation is not complicated; the next book introduces the connection point mode (Support Connection Points), the connection point method is not very good, slow (if it is remote DCOM way, to choose it carefully), complex structure, The only benefit is that ATL wraps it up, so it's easier to implement. Not introduced and not, because Microsoft has a large number of support event components are implemented with connection points, cough ... Nasty Microsoft (Note 4).
Note 1: It was supposed to give a few more examples, so the first is called Event1, can write, feel the program has been more complex, did not continue to do.
Note 2: Of course, you choose to use dual interface Dual also no problem. But notice that in the following steps, when adding the callback interface to modify the IDL file, we are going to use Custom (derived from IUnknown instead of IDispatch).
Note 3: A lump of a lump is often used to describe a pile of dog poop.
Note 4: Microsoft Comrades, play jokes don't take it seriously! I'm still on your dinner.
"Reprint" COM Component design and application (14)--Events and notifications (vc.net)