In-depth exploration of MFC and ATL2 MS COM development framework

zhaozj2021-02-16  79

1. In fact, the component class get the interface mapping table is the first GetInterfaceMap () static member function

Get an InterfaceMap structure variable, the second value of this variable is the entry of the interface mapping table.

The first value of InterfaceMap saves the _getBaseInterfaceMap function pointer of the base class.

When the interface is not found in the component class, pass _getBaseInterfaceMap

Get the foundation of InterfaceMap, thereby obtaining the interface mapping table inlet of the base class ....

This is continuously traced..

Through these macros, we implemented interface mapping tables and provided in the class level.

Method. This way, component classes can be locate the query and address of the interface through this table.

However, there is still a big problem here.

We said that any interface must implement AddRef, Release, QueryInterface.

(Note 2: In fact, the definition of the interface has changed after Idispatch appears, you can put any

The combination of methods and data is achieved as an interface.) ... You haven't seen anything about this here ..

Yes, there is still a "big conspiracy" .... About this

#define interface_part (THECLASS, IID, LOCALCLASS) /

{& IID, Offsetof (THECLASS, M_X ## localclass)}, /

// Fill the interface shot table

Stand out ....

In actual engineering, the default above LocalClass will be replaced by Dispatch.

What is DISPATCH? In fact, in CDK1.0, COM development does not have to be above these macros.

Where you can clearly see the essence of the problem, no way, the time has changed, and the situation in front of

Discuss it ...

The location of the Dispatch is specified in the nested class of the interface ...

Then there is no doubt that dispatch is nested. But you said, where is this?

In fact, in the ccmdtarget, there is such a piece:

Struct xdispatch

{

DWORD M_VTBL; // Place-Holder for iDispatch VTABLE

#ifndef _AFX_NO_NESTED_DERIVATION

SIZE_T M_NOFFSET;

#ENDIF

} m_xdispatch;

It will become a nested class that implements the interface.

How can this be a nested class? It does n'thing ... Simply structured ...

Internally, there is the same virtual table vTable assignment behavior, which is this behavior makes m_xdispatch thoroughly

Changed, this behavior of this behavior is EnableAutomation () in the component class constructor.

code show as below:

Void ccmdtarget :: enableautomation ()

{

Assert (GetDispatchMap ()! = Null); // Must Have Declare_Dispatch_map

// Construct an COLEDISPATCHIMPL Instance Just To Get To The VTable

ColedispatchImpl dispatch;

// vTable Pointer SHOULD BE Already Set To Same or Null

Assert (m_xdispatch.m_vtbl == null ||

* (DWORD *) & dispatch == m_xdispatch.m_vtbl);

// sizeof (ColedispatchImpl) Should Be Just A DWORD (VTable Pointer)

Assert (sizeof (m_xdispatch) == sizeof (COLEDISPATCHIMPL));

// copy the vtable (AND Other data) to make sure it is initialized

m_xdispatch.m_vtbl = * (dword *) & dispatch;

* (COLEDISPATCHIMPL *) & m_xdispatch = dispatch;}

Part 2 deep into ccmdtarget to see the implementation of the three elements of COM

The reason why us go deep into ccmdtarget

I don't think it is just to implement it.

We want to know a great scene of MFC to COM: polymerization

How is it implemented?

Don't say two words, put out the frame:

PUBLIC:

// data used when ccmdtarget is Made Ole Aware

Long m_dwref;

LPUNKNOWN M_POUTERUNKNOWN; // External Controlling Unknown if! = NULL

DWORD M_XINNERUNKNOWN; // Place-Holder for Inner Controlling Unknown

PUBLIC:

// Advanced Operations

Void enableaggregation (); // call to enable aggregation

Void externaldisconnect (); // forcibly disconnect

LPunknown getControllingunkNown ();

// Get Controlling iUnknown for aggregate cree

// THESE VERSIONS DO NOT DELEGATE TO M_POUTERUNKNOWN

DWORD INTERQUERYINTERFACE (const void *, lpvoid * ppvobj);

DWORD INTERNALADDREF ();

DWORD INTERNALESE ();

// THESE VERSIONS DELEGATE TO M_POUTERUNKNOWN

DWORD EXTERNALQUERYINTERFACE (const void *, lpvoid * ppvobj);

DWORD EXTERNALADDREF ();

DWORD EXTERNALRELEASE ();

//Mplementation Helpers

LPunknown GetInterface; Const Void *);

LPunkNown QueryAggregates (const void *);

// Advanced Overrideables for Implementation

Virtual bool oncreateaggregates ();

Virtual lpunknown getInterfaceHook (const void *);

From the above statement, you can find:

Here, two standard interface methods are declared.

ExternalXX corresponds to the entrustment IUnknown in the COM model

And INTERNALXX corresponds to non-entrusting IUNKNOWN

With these two interface methods, the achievement of aggregation is OK ....

For details on aggregation, for details, please refer to relevant special

Books, no more details here.

The foundation structure of the interface is completed by Part1 and Part 2.

Part3 -------- Class factory

Needless to say, COM objects are created in need of class.

-------------------------------------------------- -------------------------------------------------- -----

Declare_olecreate (csam) macroblock

-------------------------------------------------- ---------------------------

#define declare_olecreate (Class_name) /

PUBLIC: /

Static AFX_Data ColeObjectFactory Factory; /

// Define the class factory object ...

Static Afx_Data Const Guid Guid; /

// Component Class GUID

-------------------------------------------------- -------------------------------------------------- ------------

Implement_olecreate macroblock

-------------------------------------------------- ----------------

#define import_olecreate (Class_name, External_Name, L, W1, W2, B1, /

B2, B3, B4, B5, B6, B7, B8) /

AFX_DATADEF COLEOBJECTFAACTORY CLASS_NAME :: Factory (class_name :: Guid, /

Runtime_class (class_name), false, _t (external_name)); /

/ / What to pay attention to is external_name: progid

AFX_COMDAT const AFX_DATADEF GUID CLASS_NAME :: Guid = /

{L, W1, W2, {B1, B2, B3, B4, B5, B6, B7, B8}}; /

// Give the CLSID of the component class to the member variable GUID of the component class.

-------------------------------------------------- -------------------------------------------------- -------

Implement_olecreate (CSAM, "MFCcom.sam",

0x43d242f9, 0x4f7e, 0x4cbb, 0xae, 0xDa, 0x77, 0x8d, 0xa1, 0x16, 0xD0, 0xD9)

Description: We know that when you create a component class object, you first get the current state by the app core.

Remove the class factory table from the middle, get the corresponding class factory object pointer according to CLSID. It is here

Associate information such as class and CLSID, ProID.

PART4 ------- Automation

In fact, automation is also a great subject. Automation technology enhances the environmental adaptability of components.

For automation components in the MFC, because the default interface is DISPINTERFACE, it is

Accessions use distribution means. Unlike your consciousness, always keep in mind through vtable [_index]

To access the method. In the process of using the component, first get the idispatch interface, then call IDispatch

Method GetidsOfNames gets the token (ID) of the method of Programmers hopes, finally passed

Idispatch's method invoke is executed. This technology allows scripts and macro environments to use COM

Objects, but for environments with pre-compilation, it will make the performance of the component system

Discount. Because it is more than one intermediate layer repeatedly called.

About Dispatch support, basically constructing the principle of thinking with

The above INTERFACE.

-------------------------------------------------- -------------------------------------------------- -------------------

Declare_dispatch_map ()

Begin_Dispatch_map (csam, ccmdtarget) [. CPP]

DISP_PROPERTY_NOTIFY (CSAM, "Fook", M_fook, OnfookChanged, VT_R4)

DISP_FUNCTION (CSAM, "Post", POST, VT_R4, VTS_NONE)

END_DISPATCH_MAP ()

Mobility ------------------------------------------------ -------------------- # IFDEF_AFXDLL

#define declare_dispatch_map () /

PRIVATE: /

Static const AFX_DISPMAP_ENTRY _DISPATCHENTRIES []; /

Static uint _dispatchentrycount; /

Static DWORD _DWSTOCKPROPMASK; /

protected: /

Static AFX_DATA const AFX_DISPMAP DispatchMap; /

Static const AFX_DISPMAP * PASCAL _GETBASPASPATCHMAP (); /

Virtual const AFX_DISPMAP * getDispatchMap () const; /

#ELSE

#define declare_dispatch_map () /

PRIVATE: /

Static const AFX_DISPMAP_ENTRY _DISPATCHENTRIES []; /

Static uint _dispatchentrycount; /

Static DWORD _DWSTOCKPROPMASK; /

protected: /

Static AFX_DATA const AFX_DISPMAP DispatchMap; /

Virtual const AFX_DISPMAP * getDispatchMap () const; /

#ENDIF

-------------------------------------------------- ----

#ifdef _AFXDLL

#define begin_dispatch_map (Theclass, BaseClass) /

Const Afx_Dispmap * pascal theclass :: _ getBaseDispatchMap () /

{RETURN & BASECLASS :: DispatchMap;} /

Const Afx_Dispmap * Theclass :: getDispatchmap () const /

{RETURN & THECLASS :: DispatchMap;} /

AFX_COMDAT const AFX_DISPMAP THECLASS :: DispatchMap = /

{& THECLASS :: _ getBasedispatchMap, & theclass :: _ dispatchentries [0], /

& theclass :: _ dispatchentrycount, & thermlass :: _ dwstockpropmask}; /

AFX_COMDAT uint theclass :: _ dispatchentrycount = (uint) -1; /

AFX_COMDAT DWORD THECLASS :: _ DWSTOCKPROPMASK = (DWORD) -1; /

AFX_COMDAT const AFX_DISPMAP_ENTRY THECLASS :: _ dispatchentries [] = /

{/

#ELSE

#define begin_dispatch_map (Theclass, BaseClass) /

Const Afx_Dispmap * Theclass :: getDispatchmap () const /

{RETURN & THECLASS :: DispatchMap;} /

AFX_COMDAT const AFX_DISPMAP THECLASS :: DispatchMap = /

{& Baseclass :: DispatchMap, & Theclass :: _ dispatchentries [0], / & theclass :: _ dispatchentrycount, & thermlass :: _ dwstockpropmask}; /

AFX_COMDAT uint theclass :: _ dispatchentrycount = (uint) -1; /

AFX_COMDAT DWORD THECLASS :: _ DWSTOCKPROPMASK = (DWORD) -1; /

AFX_COMDAT const AFX_DISPMAP_ENTRY THECLASS :: _ dispatchentries [] = /

{/

#ENDIF

#define end_dispatch_map () /

{VTS_NONE, DISPID_UNKNOWN, VTS_NONE, VT_VOID, /

(AFX_PMSG) NULL, (AFX_PMSG) NULL, (SIZE_T) -1, AfxDispcustom}}; /

The way the idea is the same as the above interface map. Here is not detailed here.

About Automation Question, I think two days, specialize in writing an article.

Because Automation is indeed a wide range of technologies, what can I say?

Difficult, but there are many details, and the general discussion is unclear.

The structure that appears in the above macro is needed:

Struct AFX_DISPMAP_ENTRY

{

LPCTSTSTR LPSZNAME; // Method or attribute name

Long ldispid; // Interface distribution ID (May Be Dispid_Unknown)

LPCSTR LPSZPARAMS; // Parameters

Word vt; // Return value type or attribute type

AFX_PMSG PFN; // Normal Member On OR, ONGET

AFX_PMSG PFNSET; // Special Member for Onset

Size_t npropoffset; // offset, this place is the core

AFX_DISPMAP_FLAGS FLAGS; // Flags (E.G. Stock / Custom)

}

Struct AFX_DISPMAP

{

#ifdef _AFXDLL

Const AFX_DISPMAP * (PASCAL * PFNGETBASEMAP) ();

#ELSE

Const AFX_DISPMAP * PBASEMAP;

#ENDIF

Const AFX_DISPMAP_ENTRY * LPENTRIES;

Uint * LpenTryCount;

DWORD * LPSTOCKPROPMASK; / / Do you use a spare name

}

In summary, even when the component class finds an interface, the method or change attribute is executed in the way, it is not performed directly.

Part 5 ------- Component Terrace: Several core functions

Naturally, the use of components is not always, starting from the call interface query function,

The program is started to find and create the process:

NO1.

ColeObjectFactory :: registerall ();

When the application is initialized, call. Where is the purpose? None is the registered factory.

(This place, I think it is loaded with all kinds of works.

Because you can directly use the class factory pointer in the pointer chain directly)

NO2.

AFX_MANAGE_STATE (AFXGETSTATICModuleState ());

In the core of the CWINAPP object developed by MFC

In the component, AFX_Manage_State (AFXGETSTAICMODULESTATE ());

You must call before the specific operation because it masters all the information.

In ATL, CCOMMODEL objects, coordinated inside.

Everything is scheduled by the CCOMMODEL object.

NO3.

AFXDLLGETCLASSOBJECT (RCLSID, RIID, PPV); you want to get the desired interface pointer, which is necessary to get the steps of the class pointer.

Getting the factory pointer is AFXDLLGETCLASSOBJECT (RCLSID, RIID, PPV);

Completed, it must be called before use

AFX_MANAGE_STATE (AFXGETSTATICModuleState ());

To monitor the status,

code show as below:

Scode AFXAPI AFXDLLGETCLASSOBJECT (Refclsid Rclsid, Refiid Riid, LPVOID * PPV)

{

* ppv = NULL;

DWORD LDATA1 = rclsid.data1;

// Search Factories Defined in the Application

AFX_MODULE_STATE * PModuleState = AFXGETMODULESTATE ();

AfxlockGlobals (crit_ObjectFactoryList);

For (ColeObjectFactory * pFactory = PModuleState-> m_factorylist;

Pfactory! = null; pfactory = pfactory-> m_pnextfactory)

{

IF (pfactory-> m_bregistered! = 0 &&

LDATA1 == PFactory-> m_clsid.data1 &&

((DWORD *) & rclsid) [1] == ((DWORD *) & pfactory-> m_clsid) [1] &&

((DWORD *) & rclsid) [2] == ((DWORD *) & pfactory-> m_clsid) [2] &&

((DWORD *) & rclsid) [3] == ((DWORD *) & pfactory-> m_clsid) [3])

{

// Found Suitable Class Factory - Query for Correct Interface

Scode SC = PFactory-> InternalQueryInterface (& RIID, PPV);

AFXUNLOCKGLOBALS (crit_ObjectFactoryList);

Return SC;

}

}

AFXUNLOCKGLOBALS (crit_ObjectFactoryList);

#ifdef _AFXDLL

AfxlockGlobals (crit_dynlinklist);

// Search Factories Defined in Extension DLLS

For (cdynlinklibrary * pdll = pmodulestate-> m_libraryList; pdll! = null;

PDLL = PDLL-> M_PNEXTDLL)

{

FOR (PFactory = PDLL-> m_factorylist;

Pfactory! = null; pfactory = pfactory-> m_pnextfactory)

{

IF (pfactory-> m_bregistered! = 0 &&

LDATA1 == PFactory-> m_clsid.data1 &&

((DWORD *) & rclsid) [1] == ((DWORD *) & pfactory-> m_clsid) [1] && ((dword *) & rclsid) [2] == ((DWORD *) & pfactory-> m_clsid) [2 ] &&

((DWORD *) & rclsid) [3] == ((DWORD *) & pfactory-> m_clsid) [3])

{

// Found Suitable Class Factory - Query for Correct Interface

Scode SC = PFactory-> InternalQueryInterface (& RIID, PPV);

AfXunlockGlobals (crit_dynlinklist);

Return SC;

}

}

}

AfXunlockGlobals (crit_dynlinklist);

#ENDIF

// Factory Not Registered - Return Error

Return Class_e_classNotavailable;

}

Code brief description:

First, when the component exports the function DllgetClassObject via CLSID and IID, first

The program environment will be monitored, then call AFXDLLGETCLASSOBJECT, and AfxdllgetClassObject

The internal operation is like this, which will be maintained in the global information maintained (

There are also registered types of customs, resource handles, etc., according to CLSID search

The corresponding class is searching in the extended DLL referenced by this DLL.

OK! Basically, the MFC's basic support of COM is through the above macro.

Realize ...

-------------------------------------------------- ----------------------------------

Treatment of ATL

-------------------------------------------------- ----------------------------------

Solemnly declare:

Allow copy, modification, delivery, or other behavior

However, it is not allowed for any commercial use.

Written at 1/4/2003

Last modified: 1/4/2003

By Tomhornson (@) Hotmail.com

-------------------------------------------------- ----------------------------------

转载请注明原文地址:https://www.9cbs.com/read-14697.html

New Post(0)