COM Component Design and Application (13)
Events and notifications (vc6.0)
Author: Instructor Yang
Download source code
I. Preface
A window is generated when my COM component is running. When you double-click the window, I need to notify the caller;
My COM component downloads a file on the network in thread mode. After I finish the task, I need to notify the caller;
My COM component completes a clock function. When the scheduled time is reached, I need to notify the caller;
............
In this book, there are many com events, notifications, connection points, and so on. I will introduce them twice (four times in total.
Ii. Notification Methods
When an event occurs within Party A, Party B must be notified to use the following methods:
Notification Method |
Simple Description |
Comment |
Direct Message |
Postmessage () Postthreadmessage () |
Send a message to a window or thread |
No matter when you execute it. |
Sendmessage () |
Immediate execution of the message Response Function |
If the message processing function is not executed, no response is returned. |
Sendmessage (wm_copydata ...) |
When sending messages, you can also include some custom data. |
It is commonly used, so it is listed separately. |
Indirect message |
Invalidaterect () Settimer () ...... |
The called function sends related messages. |
There are too many such functions. |
Callback Function |
Getopenfilename ()...... |
When the user changes the file selection, the callback function is executed. |
Hi! Dude, this is my phone number. Let's just say something. |
In the era of COM, the above methods are basically not fun, because...The COM component is running in a distributed environment.How can I send messages to your window from components running on the other side of the earth's computer? Of course not! (But again, for ActiveX components that can only run locally, you can also send window messages .)
The callback function is the basis for designing the com notification method. In essence, the callback function tells me the pointer of a function in advance. When necessary, I call the function directly. What does the callback function do? How does it do, I don't care at all. Okay. Let me ask you a question: what is the COM interface? An interface is actually a set of related functions (this definition is not rigorous, But you can understand it so well ). Therefore, instead of using the "callback function" in COM, we use the "Callback interface" (to be clear, we use a lot of encapsulated "callback function" sets) and callback interface, this is also called "receiver interface ".
Figure 1. The client transmits the receiver interface pointer to com. When an event occurs, com calls the receiver interface function to complete the notification.
The functions completed by the sample program are as follows:
Start the client component (simple11.ievent1.1) and obtain the interface pointer ievent1 *;
Call the interface method ievent1: advise () to pass a sink interface pointer (icallback *) inside the client to the component server;
Call ievent1: add () to calculate the sum of two integers;
However, the calculation result is returned to the client through icallback: fire_result () instead of using this function;
When the client no longer needs to accept the event, call ievent1: unadvise () to disconnect from the component.
3. component implementation steps
1. Create a workspace)
2. Create an ATL Project in the workspace ). In the example program, the project name is simple11. All default options are accepted.
3. In classview, run the right-click menu command new ATL object... to add the alt class.
3-1. Select objects for category on the left, and simpleobject for objects on the right (in fact, it is the default project)
3-2. Enter the component name in the name card. In the example, event1 (Note 1)
3-3. In the attribute attributes card, modify the interface type to custom M (note 2)
4. In classview, Select Interface (ievent1) and right-click the menu to add the function add method...
Figure 2. Add ([in] Long N1, [in] Long N2) an interface function)
Figure 3. added the advise ([in] icallback * pcallback, [out] Long * pdwcookie) function)
Figure 4. added the interface function unadvise ([in] Long dwcookie)
You should have noticed that in the add () function, there are no IDL attributes such as [out] and [retval]. Hey, because we didn't intend to use add () the function directly obtains the calculation result. Otherwise, how do I demonstrate the callback interface? :-) In addition, In the advise () function, we need to return an integer dwcookie. What is this? The principle is simple, because our components want to support multiple object callback connections at the same time. Therefore, when the client passes an interface to our component, I return a unique cookie number to indicate the identity. In the future, when the connection is disconnected, unadvise (), it needs to send the cookie ID number to me so that I can know who wants to disconnect it.
5. added the IDL definition of the callback interface icallback. Open the IDL file and enter it manually (the black text part is manually entered), and save:
Import "oaidl. IDL"; import "ocidl. IDL ";[Object, UUID (UID), // This IID can be generated using gudigen. EXE helpstring ("icallback interface"), pointer_default (unique)] interface icallback: iunknown {};[Object, // The following content is the same as the sample program. Of course, if it is your own program, there must be a difference. UUID (7e659bb0-fb79-4188-9661-65ca22b6a3e6), 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 (695c9bb2-2ae9-4232-8225-17ab8bd3babc), version (1.0), helpstring ("simple11 1.0 Type Library")] library simple11lib {importlib ("stdole32.tlb"); importlib ("stdole2.tlb"); [UUID (identifier), helpstring ("event1 class")] coclass event1 {[Default] interface ievent1; // manual input is required. If VB is used, the attribute [Source, default] is not available.[Source, default] interface icallback;};};
6. Added callback interface functions.
Figure 5. Add callback interface functions
In fact, it is the same as the previous method, as long as you do not select the wrong interface.
Figure 6. Add the interface function fire_result ([in] Long nresult)
After calculating the integer and the obtained result, we need to rely on this callback interface function to feedback it to the client.
7. Add a component to save the array of callback interface pointers.
As we have already said, this component is intended to support callback connections for multiple objects. Therefore, we need to use an array to save the connection. In classview, select the cevent1 class and add the member variable add member variable...
Figure 7. Add an array for saving icallback *
Of course, there are multiple 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 how to use STL, you can also implement it using containers such as vector. Note! Note! Note! In the constructor, do not forget to initialize the array element
Null.
8. Now, complete all the code below.
Stdmethodimp cevent1: add (long N1, long N2) {long nresult = N1 + N2; For (INT I = 0; I <10; I ++) {If (m_pcallback [I]) // If the callback interface is valid m_pcallback [I]-> fire_result (nresult); // an event is triggered/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]) // {m_pcallback [I] = pcallback is found; // save it to the array m_pcallback [I]-> addref (); // pointer counter + 1 * pdwcookie = I + 1; // cookie is the array subscript // + 1 to avoid using 0, because 0 indicates invalid return s_ OK ;}} return e_outofmemory; // more than 10 connections, insufficient memory} stdmethodimp cevent1: unadvise (long dwcookie) {If (dwcookie <1 | dwcookie> 10) // who did this? Return e_invalidarg; If (null = m_pcallback [dwcookie-1]) // The parameter is invalid or the return e_invalidarg parameter is invalid; m_pcallback [dwcookie-1]-> release (); // pointer counter-1m_pcallback [dwcookie-1] = NULL; // empty the array element return s_ OK ;}
Iv. Client implementation steps
After downloading the sample program, you can browse the client implementation program. Here I will only explain how the receiver is constructed:
Figure 8. derived receiver class csink from icallback
A csink class is derived from icallback. After confirmation, IDE will have a warning that it cannot find the icallback header file, because # import will generate XXXX for us only when compiling. tlh, xxxx. TLI files, which have the icallback statement.
Here icallback is a COM interface, so csink cannot be case-based. If you compile it, you will get an error (note 3). The report says that you have not implemented the virtual function. Then, we can implement all the virtual functions according to the error report:
// Stdmethodimp is a macro, which is equivalent to long _ stdcallstdmethodimp csink: QueryInterface (const struct _ guid & IID, void ** bp) {* bp= this; // no matter what interface you want, it is actually the return s_ OK of the object itself;} ulong _ stdcall csink: addref (void) {return 1;} // you can make a false one, because the ulong _ stdcall csink: release (void) {return 0 ;}// the object will not exit before the end of the program, because this object will not exit stdmethodimp csink: raw_fire_result (long nresult ){...... // display the calculation result in the window. Return s_ OK ;}
V. Summary
The COM component provides two basic methods to implement functions such as events and notifications. The callback interface method introduced today is very good, with fast speed, clear structure, and not complicated. The next back book introduces the connection point method (support connection points), which is actually not very good, slow speed (if it is a remote DCOM method, you should choose it with caution), complicated structure, the only benefit is that ATL wraps it, so it is relatively simple to implement. I don't want to introduce it, but I can't do it because the components that Microsoft supports a large number of events are implemented using connection points. I hate Microsoft (note 4 ).
Note 1: I have come up with several more examples. Therefore, the first name is event1. After writing it, I feel that the program is complicated and I will not continue to do it again.
NOTE 2: Of course, you can choose to use dual interface. Note that in the following steps, when you add a callback interface to modify the IDL file, you must use custom (derived from iunknown instead of idispatch.
NOTE 3: A pile of shit is often used to describe a pile of shit.
Note 4: Do not take jokes seriously from Microsoft comrades! I come to dinner with you.