程序中和有js函数的网页交互,线程中调用 get_Script 就会错误的解决方法,由于COM的线程安全问题

Q: HOWTO: Post messages to a hidden window for raising events from an Apartment-threaded object employing worker threads

The reason this technique is useful is discussed in this FAQ. The basic principle is posting a custom Windows message to a hidden window created on the STA thread the COM object lives in and firing the event on that thread. The basic limitation is all event arguments must be [in] only, it cannot be used for acquiring data from the client sink(s). The main advantage is that unlike the other techniques, it does not block the worker thread while waiting for the client sink(s) to process the notifications.

While the theory is simple, there's a principle roadblock - the sheer amount of code to be written to support it. The purpose of this article is to provide a tiny framework for streamlining the process so it's less painful. In an ideal world, it'd be coming with a wizard that parses a type library and generates the whole supporting code. Some day this may become true, but for now, here's the framework in its current condition.

Note on terms: the process of packaging a method's call arguments and transferring them into another execution context to make the actual method call is called marshaling and is integral part of remote procedure calling (RPC). Since the task we solve is a variation on marshaling (do not confuse with COM marshaling though), the framework itself is called Message Marshal.

The basic architecture consists of three classes that operate together to transport the call from the originating thread to the desired thread where it is to be fired. The message marshal class is the main interface of the framework. You derive your ATL COM object class from one of the supplied CMessageMarshalXXX classes that best suits your purposes (discussed shortly), initialize it accordingly (typically in your FinalConstruct method), and call its MarshalMessage method to initiate the thread switching process. The message holder class is the engine behind the framework - it accepts the marshaling requests on the originating thread and dispatches them to the target thread to be fired to the client. Finally, the message class is the actual representation of the arguments of the method that has to be called (the event notification). It stores copies of all arguments and later uses them to make the actual call invocation in the client's sink. This is the framework's data abstraction class and the one the users of the framework write themselves.

The core of the framework is the message holder class - CMessageHolderSTA. It's mostly transparent to the code outside of the framework with one exception - it can be reused for multiple marshaling classes, as we'll discuss later. It's purpose is twofold - it provides storage for the marshaled messages during their transit from the originating call thread to the destination fire thread, and it also provides the internal signaling and signal handling to actually execute the call further along its chain on the target thread. In addition to CMessageMarshalSTA, another class is provided for us with free-threaded objects - CMessageMarshalMTA. Although it's not necessary to do thread switching as long as the originating thread also belongs to the MTA, this class solves the problem of the originating thread being blocked for the duration of the event notification call(s). Also, this class is useful if the originating thread has entered an STA, or does not belong to any apartment. Change the default argument of the message marshal class you are using if you intend on using this framework with free-threaded objects.

The message classes representing the arguments to a message call are provided by you. They must derive from CMessageBase and implement its pure virtual method Execute. The CMessageBase class provides simple abstraction for the actual method arguments. Your derived classes should contain members storing copies of all non-scalar arguments (like strings, arrays, structs...). Typically, each method would need a separate class for its marshaling. These classes are usually small, unless you have many complex arguments (which leads to more code for making the copies). The CMessageBase class provides basic reference counting by implementing IUnknown under the hood. It's further enhanced by the DECLARE_CREATE_DYN_MESSAGE macro which you'd put in each of your message classes. This macro provides a wrapper around CComObject::CreateInstance for constructing messages on the heap (it makes most sense to have them on the heap) and returns an AddRef-ed blank message. The method name is CreateMessage and it is static, so you'd use it like the following:

class CMyMessage : public CMessageBase{   ...DECLARE_CREATE_DYN_MESSAGE(CMyMessage)   ...};CMyMessage *pMsg = NULL;HRESULT hr = CMyMessage::CreateMessage(&pMsg);

A slight enhancement is the CMessageDelegate<> template class deriving from CMessageBase. It adds an owner field which is automatically populated when used in conjunction with the corresponding owner marshal classes. You'd likely want to pass in the ATL COM object class whose event you are marshaling for the owner template argument, since it must match the owner template argument of the marshal class. Use CMessageDelegate<> in place of CMessageBase for your messages' base class and you gain its m_pOwner field, which you'd typically use to forward the calls back to the data class' owner that originated the request. Typically, the Execute implementation would use m_pOwner to invoke the appropriate FireXXX method of the corresponding proxy class for the event interface generated by the ATL's Implement Connection Point wizard (since the ATL COM Object class derives from the proxy class).

The final piece of the framework is the message marshal class. Its purpose is to bind to a suitable message holder that does the actual marshaling. Four marshal classes are provided with the framework. Two of them contain a message holder and can be used standalone - they are also called host classes. The other two accept an external holder and can only be used in conjunction with a host. These are useful when building an object hierarchy of tightly integrated objects - the root of the hierarchy provides a single holder that is used by any subobjects that need to report events. The reason is simple - the resource encapsulated by the message holder (a hidden window for an STA, a kernel thread for MTA) is expensive and you don't want to allocate many of these unnecessarily. Both the hosts and the lightweight marshalers come in two flavors: a basic variant works with messages derived from CMesageBase directly, whereas the owner variant is used in conjunction with CMessageDelegate<> to automatically setup its owner field.

An extra feature useful for loosely coupled object hierarchies is message tagging. The idea is simple - if a subobject goes out of scope and is destroyed before its parent (the one that contains the holder), it can requests all of the messages issued by its marshaler to be cancelled. This facility is only available on the holder directly. To support it, pass the same non-zero tag (your this pointer for example, cast to DWORD_PTR) with each message sent to your marshaler.

Observant readers will notice the additional class CTagPtrQueue<> used in the implementation of CMessageHolderBase (the base class for the STA and the MTA flavors). It's a class I've developed for this and other reasons, it has a lot of functionality not used here, though since it's a template, the unused features won't ever be compiled. You can use it as it is, and even for your own purposes, or you may rewrite it in your own way (using STL for example). It's a thread-safe queue with extras.

The last piece of the puzzle is your class deriving from the chosen message marshal. As was mentioned before, typically this is your ATL COM object class, and you need to pass it as the Owner template argument of the marshal class. A well organized class would provide as many methods as the events it will be firing, whose sole purpose is to create the appropriate message, initialize it with the method arguments and pass it down to the message marshal. These are the replacement methods for the FireXXX methods from the proxy class generated by the ATL Implement Connection Point wizard, and you'll be calling them to the same effect from your worker thread code. Just like the proxy class, these can be combined into another proxy class which you'd derive from in your ATL COM object class. For example:

// The message classtemplate<class Owner>class CMshlPxy__EventGenEvents_Event : public CMessageDelegate< Owner >{public:   HRESULT Init(long nNumber, BSTR bstrText, double dblValue)   {      m_nNumber  = nNumber;      m_bstrText = bstrText;      m_dblValue = dblValue;      return (bstrText != NULL) && (m_bstrText == NULL) ? E_OUTOFMEMORY : S_OK;   }   virtual void Execute()   {      static_cast<Owner*>(this)->FireEvent(m_nNumber, m_bstrText, m_dblValue);   }   DECLARE_CREATE_DYN_MESSAGE(CMshlPxy__EventGenEvents_Event< Owner >)protected:   long m_nNumber;   CComBSTR m_bstrText;   double m_dblValue;};// The marshaling proxy classtemplate<class T>class CMshlPxy__EventGenEvents{public:   HRESULT Event(long Number, BSTR Text, double Value)   {      // Create the marshal message with our owner as the template argument      CMshlPxy__EventGenEvents_Event< T > *pMessage = NULL;      HRESULT hr = CMshlPxy__EventGenEvents_Event< T >::CreateMessage(&pMessage);      if (SUCCEEDED(hr)) {         hr = pMessage->Init(Number, Text, Value);      }      // Send the message to the marshal class      //  (base of our derived class passed as a template argument)      if (SUCCEEDED(hr)) {         hr = static_cast<T*>(this)->MarshalMessage(pMessage) ? S_OK : E_FAIL;      }      // Get rid of our reference on the message      if (pMessage != NULL) {         pMessage->Release();      }      return hr;   }};

To support the framework, your class has to initialize and shutdown the marshal class via its InitMarshal and DoneMarshal methods. Typically, this is done in your FinalConstruct and FinalRelease methods. InitMarshal of the lightweight flavor marshals is also the place to pass in the external holder, whereas the host marshals expose the GetHolder method so their holder can be shared with other lightweight marshal classes.

This concludes the overview of the tiny Message Marshal framework. MessageMarshal.zip contains the core files of the framework. EventGen.zip is a sample (sans the project files) demonstrating the use of the framework. The framework can be used beyond the scenario detailed here - transporting events from a worker thread to be fired to the client's sinks. Any such applications are left to the readers.

原文地址:https://www.cnblogs.com/SuperXJ/p/1651707.html