Com example (3)
-- Component code
This article focuses on this series of articles. The interfaces previously designed are only auxiliary, and have nothing to do with the com thread model. Because the imodule and imodulesite interfaces implemented by the customer and components are packaged with interface transmission, the interface packaging function of MFC is used for implementation. Therefore, all interfaces are run in the sta suite, in addition, the main client thread can be used to deliver messages in the component window.
Assume that the remote component method is called to implement the business logic. Due to the relationship between remote and large data volume operations, a thread is initiated for each data operation (such as search) initiated on the interface, then, call the remote method in this thread. It is decided to use a class packaging task to implement the itask interface to provide the Task Management Service, and the thread function task is of course the static member function of the itask implementation class ctask. Because ctask is an internal object and does not require CLSID, in this example, the instance is obtained directly through the static member function createinstance.
For the purpose of demonstration, two methods are proposed here to consider how to present the progress.
1. If the remote component method is synchronous, the calling thread will be suspended due to the delay in returning the remote component method, so the progress cannot be expressed. At this point, you can enable a timer (timer) to notify itaskmanager of the progress by the previous ctask at a fixed time, and set an upper limit. After the timer is exceeded, it will not be timed again, the progress will not be increased until the method is returned or the itaskmanager is terminated. If it is asynchronous, the job of initiating a thread mentioned above will be done by the COM Runtime Library (hereinafter referred to as COM, it makes no sense here, and it must be valid on win2000 and later versions of COM, which means that the client must run on win2000 and later versions of the operating system, this is not a good proposal.
II. assume that itask is implemented by a remote service component, rather than providing packaging for an internal object as mentioned above, every piece of code passes through the remote service component method, it calls itasknotted to set the progress, and CALLS itasknotted to end the task at the end of the method.
In the first case above, the ctask is not necessary in the MTA suite-it will only be called by the customer's main thread (whether it is a task initiated by the itaskmanager implementer or the department component operation interface, ). Therefore, it should be an apartment component, implemented using MFC. However, as this example mainly demonstrates the access call between suites, it is still designed as a free component here (even if this is not necessary) and implemented using ATL, to demonstrate how to call data across suites. However, because it is directly created through the static member function createinstance, there is no CLSID and related registry items to indicate that it is a free component. Because it is created in the customer's main thread, that is, the customer's main thread obtains its direct pointer, it exists in the sta suite related to the customer's main thread (even if written as required by the free component ).
For the termination of the initiator thread (ctask: Task static member function), if the initiator thread is associated with the MTA thread (that is, coinit_multithreaded at the beginning of the thread calls coinitializeex as the parameter ), the business component is a free component and will exist in the MTA. Unfortunately, the method for initiating a thread is to directly call the business component, that is, the initiator thread is also the executor of the business component method (assuming that the business component is a local component ). Unfortunately, if there is an endless loop or long-running code in the business component method, and it does not provide any termination method (such as waiting for an event in a short time ), the only way to terminate a call is to forcibly terminate the thread, and thus the release of the service component cannot be called correctly, resulting in low efficiency of COM (couninitialize is not called, com ). If you use a proxy call (instead of a direct pointer), you can use icancelmethodcils to cancel the pending call-that is, the call to the business component in the initiating thread. This is an interface provided by COM, so that customers can cancel synchronous calls. This is a two-way job. If the component has been occupying the thread resources, it will remain the same as the previous one, and it must be forcibly terminated, however, this provides at least one way to reply to the execution of the initiating thread and then call couninitialize to exit. The disadvantage is that, like the previous asynchronous call, it must be the com published by the operating system of Win2000 and later versions. In this example, the latter is used, so the initiating thread uses coinitialize to enter the sta instead of the MTA to obtain the proxy Object Pointer, rather than the direct pointer, and thus the call can be canceled (if you are interested, try again and set the ctask:: replace coinitialize in a task with coinitializeex to enter the MTA. The task will terminate and fail ). However, since this thread does not generate any COM component object, that is, this sta suite does not contain any object, so there is no need to write a message loop.
In the second case, assume that cocreateinstance is called in each call thread to obtain a brand new instance of the business component, and then call the method on it, in fact, it will only be called by the calling thread, which is not necessary in the MTA suite. This method is very compatible with the programming model provided by MTS. By registering business components as MTS components, You can implement the object pool function and enable the instant activation feature (just-in-time activation) the loss caused by each call to cocreateinstance is almost zero, but the program structure is very simple and does not require complicated logic.
However, this example is not written as an MTS component, and the above advantages are the disadvantages of this article. It is impossible to demonstrate how multithreading handles service component calls. Therefore, a global business component object is provided to make it a free component, in addition, every time a call thread is initiated, itasknotted is used to notify the progress of the correct thread task (only one thread is actually notified-the main client thread. Here, we only explain how to use the correct proxy object for notification. However, since the service component is in the MTA suite and only one thread will be notified, therefore, it is not necessary to retain the neutral form of itasknotify *, that is, the proxy objects of each call thread are the same in the sense, and there may be slight differences in implementation, it depends on the implementation of the COM Runtime Library. However, in this demonstration, itasknotted * is saved in neutral format *).
In the second case, for thread termination (see the code below), I just make the business logic a waitforsingleobject event to timely respond to the termination event and simulate the time consumption, this is certainly not the case in reality. If the actual business code is a loop, you can wait for an event (or call the icancelmethodcils method) for a short time in each loop ), if there is no event, the loop continues (in this case, it is best to monitor the value of a global variable rather than waiting for an event ). If it is not a loop but a very time-consuming operation, and the operation does not provide any interrupted interface, it can only be forcibly terminated.
In this example, the preceding two methods are implemented: task1 and task2 in the Business Component Interface. The service component is a free-type in-process component. Because there are many codes, only the business component code in the second case and the code that initiates the call in the department component are listed here. If you want to obtain other relevant source code, download it in COM sample (4.
In the second case, call the initiating code
......
// Column set iexample1logic interface pointer
Istream * pstream = NULL;
If (failed (: codecomalinterthreadinterfaceinstream (iid_iexample1logic,
M_plogic,
& Pstream )))
{
M_perror-> reportharderror (_ wfile __,
_ Line __,
L "The iexample1logic interface of the column set failed! ");
Return;
}
// Initiate a thread
Handle hthread = createthread (null, 0, task2, pstream, 0, null );
If (! Hthread)
{
Pstream-> release ();
M_perror-> reportharderror (_ wfile __,
_ Line __,
L "task thread creation failed! ");
Return;
}
: Closehandle (hthread );
......
Second, the call thread code
Static DWORD winapi task2 (lpvoid pparam)
{
Assert (pparam );
// Bind this thread to the MTA suite
: Coinitializeex (null, coinit_multithreaded );
// Scatter set iexample1logic interface pointer
Iexample1logic * plogic = NULL;
If (failed (: cogetinterfaceandreleasestream (
Reinterpret_cast <istream *> (pparam ),
Iid_iexample1logic,
Reinterpret_cast <void **> (& plogic ))))
Return static_cast <DWORD> (-1 );
// Execute the task
Plogic-> task2 ();
// Release resources
Plogic-> release ();
: Couninitialize ();
Return 0;
}
Business Components in the second case
Business Component Interface Definition
[
Object,
UUID (348ff439-26b3-495d-85c1-4464caa98cd9 ),
Pointer_default (unique)
]
Interface iexample1logic: iunknown
{
Hresult task1 ();
Hresult task2 ();
};
[
Object,
UUID (348ff439-26b3-495d-85c2-4464caa98cd9 ),
Pointer_default (unique)
]
Interface isettaskmanager: iunknown
{
Hresult settaskmanager ([in] itaskmanager * pmanager );
};
Header file code of the itask implementation class
Class cexample1logic;
Class atl_no_vtable ctask:
Public ccomobjectrootex <ccommultithreadmodel>,
Public itask
{
// ATL definition macro
Declare_not_aggregatable (ctask)
Declare_protect_final_construct ()
Begin_com_map (ctask)
Com_interface_entry (itask)
End_com_map ()
// Member variable
Protected:
Float m_rate; // current progress
DWORD m_cookie; // cookie returned by itaskmanager: addtask
Handle m_hevent; // wait for the event and cooperate with cexample1logic
// Structure and Structure
Public:
Ctask (): m_rate (0.0f ),
M_cookie (static_cast <DWORD> (-1 )),
M_hevent (null)
{
// Do nothing
}
~ Ctask ()
{
: Closehandle (m_hevent );
}
// Interface implementation
Public:
// Itask
Stdmethod (getprocessrateoftask) (float * prate );
Stdmethod (terminatetask )();
// Friends
Friend class cexample1logic;
};
Source code of the itask implementation class
Stdmethodimp ctask: getprocessrateoftask (float * prate)
{
If (! Prate)
Return e_invalidarg;
* Prate = m_rate;
Return s_ OK;
}
Stdmethodimp ctask: terminatetask ()
{
If (! M_hevent)
Return e_fail;
Return: setevent (m_hevent )? S_ OK: e_fail;
}
Header file Code of Business Components
Class atl_no_vtable cexample1logic:
Public ccomobjectrootex <ccommultithreadmodel>,
Public ccomcoclass <cexample1logic, & clsid_example1logic>,
Public iexample1logic,
Public isettaskmanager
{
// ATL definition macro
Public:
Declare_protect_final_construct ()
Declare_registry_resourceid (idr_example1logic)
Begin_com_map (cexample1logic)
Com_interface_entry (iexample1logic)
Com_interface_entry (isettaskmanager)
End_com_map ()
// Structure and Structure
BLIC:
Cexample1logic (): m_cookie (static_cast <DWORD> (-1 ))
{
: Initializecriticalsection (& m_cs );
}
~ Cexample1logic ();
// Member variable
Protected:
DWORD m_cookie; // git cookie of itaskmanager
Critical_section m_cs; // used to protect m_taskmanagercookie
// Interface implementation
Public:
// Iexample1logic
Stdmethod (task1 )();
Stdmethod (task2 )();
// Isettaskmanager
Stdmethod (settaskmanager) (itaskmanager * pmanager );
};
Source File Code of the Business Component
Extern iglobalinterfacetable * g_pgit;
Cexample1logic ::~ Cexample1logic ()
{
: Deletecriticalsection (& m_cs );
Atlassert (g_pgit );
// Cancel the original m_cookie
If (m_cookie! = Static_cast <DWORD> (-1 ))
Atlverify (succeeded (
G_pgit-> revokeinterfacefromglobal (m_cookie )));
}
/// // Iexample1logic /////////// ////////////
Stdmethodimp cexample1logic: task1 ()
{
// Sleep for a period of time to simulate a long call
For (DWORD I = 0; I <30; I ++)
{
Sleep (300 );
If (: cotestcancel () = rpc_e_call_canceled)
Return e_abort;
}
Return s_ OK;
}
Stdmethodimp cexample1logic: task2 ()
{
// Get the correct itaskmanager proxy through git
Itasknotted * p1_y = NULL;
Itaskmanager * pmanager = NULL;
: Entercriticalsection (& m_cs );
If (m_cookie! = Static_cast <DWORD> (-1 ))
{
Atlassert (g_pgit );
// The work must be completed no matter whether getinterfacefromglobal is successful or not.
If (succeeded (g_pgit-> getinterfacefromglobal (
M_cookie,
Iid_itaskmanager,
Reinterpret_cast <void **> (& pmanager ))))
Pmanager-> QueryInterface (iid_itasknotted,
Reinterpret_cast <void **> (& pnotify ));
}
: Leavecriticalsection (& m_cs );
// Start the task
Static DWORD Index = 1;
Itask * ptask = NULL;
Ccomobject <ctask> * ptaskobject = NULL;
If (failed (ccomobject <ctask>: createinstance (& ptaskobject )))
Return e_fail;
Atlverify (succeeded (ptaskobject-> QueryInterface (
Iid_itask,
Reinterpret_cast <void **> (& ptask ))));
DWORD cookie = static_cast <DWORD> (-1 );
Wchar temp [10];
Wsprintf (temp, l "Task 2% d", index ++ );
Ptaskobject-> m_hevent =: createevent (null, true, false, null );
If (pmanager & failed (pmanager-> addtask (temp, ptask, & cookie )))
Cookie = static_cast <DWORD> (-1 );
Saferelease (pmanager );
// Sleep each time to simulate the completion of each step
If (: waitforsingleobject (ptaskobject-> m_hevent, 4000) = wait_object_0)
Goto terminate;
Ptaskobject-> m_rate = 0.3f;
If (pnotify)
Pnotify-> processratechange (cookie );
If (: waitforsingleobject (ptaskobject-> m_hevent, 4000) = wait_object_0)
Goto terminate;
Ptaskobject-> m_rate = 0.6f;
If (pnotify)
Pnotify-> processratechange (cookie );
If (: waitforsingleobject (ptaskobject-> m_hevent, 4000) = wait_object_0)
Goto terminate;
Ptaskobject-> m_rate = 0.9f;
If (pnotify)
Pnotify-> processratechange (cookie );
If (pnotify)
{
If (cookie! = Static_cast <DWORD> (-1 ))
Pnotify-> taskover (cookie );
Pnotify-> release ();
}
Ptask-> release ();
Return s_ OK;
Terminate:
If (pnotify)
{
If (cookie! = Static_cast <DWORD> (-1 ))
Pnotify-> taskover (cookie );
Pnotify-> release ();
}
Ptask-> release ();
Return rpc_e_call_canceled;
}
/// // Isettaskmanager ////////// //////////////
Stdmethodimp cexample1logic: settaskmanager (itaskmanager * pmanager)
{
If (! Pmanager)
Return e_pointer;
Pmanager-> addref ();
Atlassert (g_pgit );
// Cancel the original m_cookie
: Entercriticalsection (& m_cs );
If (m_cookie! = Static_cast <DWORD> (-1 ))
Atlverify (succeeded (
G_pgit-> revokeinterfacefromglobal (m_cookie )));
// Register a new m_cookie
If (failed (g_pgit-> registerinterfaceinglobal (pmanager,
Iid_itaskmanager,
& M_cookie )))
: Interlockedexchange (reinterpret_cast <long *> (& m_cookie ),
Static_cast <DWORD> (-1 ));
: Leavecriticalsection (& m_cs );
Pmanager-> release ();
Return s_ OK;
}