Aggregation: a real aggregate ActiveX Control

Source: Internet
Author: User
In fact, I personally think that using an aggregate method to integrate ActiveX controls is not very interesting, but it is still practical to use inclusiveness. However, many people asked on msdn, and they wanted to look at the process of aggregating an ActiveX control, so they spent some time using MFC.

1. Create a common MFC ActiveX Control tagc

2. by default, the control is derived from the COleControl, so you already have your own ActiveX control implementation and corresponding interface ing. In this way, when QueryInterface is used, all the standard interfaces (IOleObject, IViewObject, IPersist, and so on) of the returned ActiveX control are their own, rather than those of the control to be aggregated. I personally think there are at least two ways to modify it. One is to overload GetInterfaceHook and force the standard interface of ActiveX control to be obtained from the aggregated control, the other is to change the control to be derived from csf-target. This document uses the second method.

A. comment out the unique functions and
Macro/END_PROPPAGEIDS attribute page: BEGIN_EVENT_MAP/END_EVENT_MAP event ing macro, BEGIN_MESSAGE_MAP/END_MESSAGE_MAP message ing macro and notify/dispatch ing macro. Of course, they also have their declaration DECLARE_XXXX.

B. Change all COleControl to c1_target.

C. Add member variables
Const IID * m_piidPrimary; // IID for control automation
Const IID * m_piidEvents; // IID for control events
And member functions
Void CTagcCtrl: InitializeIIDs (const IID * piidPrimary, const IID * piidEvents)
{
M_piidPrimary = piidPrimary;
M_piidEvents = piidEvents;

EnableTypeLib ();
}
This is because m_piidPrimary may be used, and it is better to call EnableTypeLib.
In this case, the InitializeIIDs (& IID_DTagc, & IID_DTagcEvents) in the constructor do not need to be commented out.

D. Add AfxOleLockApp (); and AfxOleUnlockApp () to the constructor and destructor respectively, and EnableAggregation () to enable the aggregation of our controls.

CTagcCtrl: CTagcCtrl ()
{
InitializeIIDs (& IID_DTagc, & IID_DTagcEvents );

// TODO: Initialize your control's instance data here.
M_lpAggrInner = NULL;

EnableAggregation ();

AfxOleLockApp ();
}

CTagcCtrl ::~ CTagcCtrl ()
{
// TODO: Cleanup your control's instance data here.
AfxOleUnlockApp ();
}

E. Save the IUnknown interface pointer of the control to be aggregated as a member variable of the control for use at any time.
LPUNKNOWN m_lpAggrInner;

F. Load OnCreateAggregates to create the aggregate control. Here we use the TreeView control.

BOOL CTagcCtrl: OnCreateAggregates ()
{
CLSID clsid;
: CLSIDFromProgID (L "MSComctlLib. TreeCtrl.2", & clsid );
LPUNKNOWN pn = GetControllingUnknown ();
CoCreateInstance (clsid,
Pn, CLSCTX_INPROC_SERVER,
IID_IUnknown, (LPVOID *) & m_lpAggrInner );
If (m_lpAggrInner = NULL)
Return FALSE;
Return TRUE;
}

G. Reload OnFinalRelease and release the aggregated control.
Void CTagcCtrl: OnFinalRelease ()
{
// TODO: Add your specialized code here and/or call the base class
If (m_lpAggrInner! = NULL ){
M_lpAggrInner-> Release ();
M_lpAggrInner = NULL;
}
Csf-target: OnFinalRelease ();
}

H. Add the interface ing declaration DECLARE_INTERFACE_MAP () in. h, and add the interface ing definition in. cpp. Here we only use INTERFACE_AGGREGATE to add the aggregation interface. The problem will be seen below.
BEGIN_INTERFACE_MAP (CTagcCtrl, c1_target)
INTERFACE_AGGREGATE (CTagcCtrl, m_lpAggrInner)
END_INTERFACE_MAP ()

I. If it is compiled and tested now, the Test will be successful in ActiveX Control Test Container, but not in VB.
The final cause is that GetControllingUnknown () in the preceding code in VB returns NULL.
In this way, when the request (QueryInterface) involves some standard interfaces of our control, such as IOleObject, the interface can be found because of the ing table of the aggregation interface, however, the external Unknown interface pointer in the aggregated controller is NULL (because pn = GetControllingUnknown () = NULL ). In this way, a problem occurs.
The problem here is that when the inner aggregate control AddRef is to add its own m_dwRef instead of the m_dwRef of the external interface (that is, our control), but for the control container, it requests the interfaces of our controls (it does not know whether our controls are aggregated or not, but it only requests an interface). What we want to add is the m_dwRef of our controls, the results are messy.

The final reason is that ActiveX Control Test Container aggregates our controls by means of aggregation. Instead of calling GetControllingUnknown (), it directly uses its internal source code, it can be found that it actually has m_pOuterUnknown.
LPUNKNOWN c1_target: GetControllingUnknown ()
{
If (m_pOuterUnknown! = NULL)
Return m_pOuterUnknown; // aggregate of m_pOuterUnknown

LPUNKNOWN lpUnknown = (LPUNKNOWN) GetInterface (& IID_IUnknown );
Return lpUnknown; // return our own IUnknown implementation
}
When you comment out EnableAggregation (); In the constructor, you can find that our controls cannot be created in ActiveX Control Test Container.

J. the final reason is that the interface ing table does not have our own control interface (including IUnknown, which is interesting. This is the c1_target of MFC, it uses the package class to implement various interfaces). Here, we first use m_xInnerUnknown in c1_target as the IUnknown interface, so that GetInterface can find an IUnknown interface and do not know if it is OK, I did not analyze it carefully, but it seems that there is no problem (of course, EnableAggregation () still needs to be removed from the annotation to continue calling. Otherwise, m_xInnerUnknown is 0 and there is still no interface ).
BEGIN_INTERFACE_MAP (CTagcCtrl, c1_target)
INTERFACE_PART (CTagcCtrl, IID_IUnknown, InnerUnknown)
INTERFACE_AGGREGATE (CTagcCtrl, m_lpAggrInner)
END_INTERFACE_MAP ()

3. So far, a dummy control has been successfully created. It has only one IUnknown interface, and all interfaces are provided by the control aggregated by it. However, this is clearly not the purpose. We hope to extend the functions of the aggregated controls.
Of course, we can add interfaces for our controls to expand.
However, ActiveX controls provide their functions through the Dispatch interface. What should we do? Our own controls have their own IDispatch interface, the aggregated control also has its own IDispatch interface. How can the functions of these two Dispatch interfaces be combined?
It's easy to implement our IDispatch interface again. First, call our default IDispatch interface to implement it. If the correct dispid is not found, then call the IDispatch interface of the aggregated control.
I originally wanted to overload the COleDispatchImpl to implement it. However, some incredible errors have occurred and I am too lazy to check them. I just changed it to a new package class to provide IDispatch, call m_xDispatch (that is, COleDispatchImpl) of cve-target to provide us with the default IDispatch implementation and the IDispatch interface of the aggregated control.

A. Use BEGIN_INTERFACE_PART/END_INTERFACE_PART macro in. h to add the package class declaration
BEGIN_INTERFACE_PART (Dispatchx, IDispatch)
INIT_INTERFACE_PART (CTagcCtrl, Dispatchx)
STDMETHOD (GetTypeInfoCount) (UINT *);
STDMETHOD (GetTypeInfo) (UINT, LCID, LPTYPEINFO *);
STDMETHOD (GetIDsOfNames) (REFIID, LPOLESTR *, UINT, LCID, DISPID *);
STDMETHOD (Invoke) (DISPID, REFIID, LCID, WORD, DISPPARAMS *, LPVARIANT,
Lp1_info, UINT *);
END_INTERFACE_PART (Dispatchx)

B. Implement the XDispatchx class

Ulong far export CTagcCtrl: XDispatchx: AddRef ()
{
METHOD_PROLOGUE (CTagcCtrl, Dispatchx)
Return pThis-> ExternalAddRef ();
}

Ulong far export CTagcCtrl: XDispatchx: Release ()
{
METHOD_PROLOGUE (CTagcCtrl, Dispatchx)
Return pThis-> ExternalRelease ();
}

Hresult far export CTagcCtrl: XDispatchx: QueryInterface (
REFIID iid, void FAR * ppvObj)
{
METHOD_PROLOGUE (CTagcCtrl, Dispatchx)
Return (HRESULT) pThis-> ExternalQueryInterface (& iid, ppvObj );
}

STDMETHODIMP CTagcCtrl: XDispatchx: GetTypeInfoCount (UINT * pctinfo)
{
METHOD_PROLOGUE_EX _ (CTagcCtrl, Dispatchx)
* Pctinfo = pThis-> GetTypeInfoCount ();
Return S_ OK;
}

STDMETHODIMP CTagcCtrl: XDispatchx: GetTypeInfo (UINT itinfo, LCID lcid,
LPTYPEINFO * pptinfo)
{
METHOD_PROLOGUE_EX _ (CTagcCtrl, Dispatchx)

Return (LPDISPATCH) (& pThis-> m_xDispatch)-> GetTypeInfo (
Itinfo,
Lcid,
Pptinfo );
/* ASSERT_POINTER (pptinfo, LPTYPEINFO );
If (itinfo! = 0)
Return E_INVALIDARG;

IID iid;
If (! PThis-> GetDispatchIID (& iid ))
Return E_NOTIMPL;

Return pThis-> GetTypeInfoOfGuid (lcid, iid, pptinfo );*/
}
STDMETHODIMP CTagcCtrl: XDispatchx: GetIDsOfNames (
REFIID riid, LPOLESTR * rgszNames, UINT cNames, LCID lcid, DISPID * rgdispid)
{
METHOD_PROLOGUE_EX _ (CTagcCtrl, Dispatchx)
HRESULT hr = (LPDISPATCH) (& pThis-> m_xDispatch)-> GetIDsOfNames (
Riid,
RgszNames,
CNames,
Lcid,
Rgdispid );
If (rgdispid [0] = DISPID_UNKNOWN)
{
LPDISPATCH pd;
If (pThis-> m_lpAggrInner ){
PThis-> m_lpAggrInner-> QueryInterface (IID_IDispatch, (void **) & pd );
If (pd ){
HRESULT hr = pd-> GetIDsOfNames (riid, rgszNames, cNames, lcid, rgdispid );
Pd-> Release ();
Return hr;
}
}
}
Return hr;

}

STDMETHODIMP CTagcCtrl: XDispatchx: Invoke (
DISPID dispid, REFIID riid, LCID lcid,
WORD wFlags, DISPPARAMS * pDispParams, LPVARIANT pvarResult,
Lp1_info p1_info, UINT * puArgErr)
{
METHOD_PROLOGUE_EX _ (CTagcCtrl, Dispatchx)
Const AFX_DISPMAP_ENTRY * pEntry = pThis-> GetDispEntry (dispid );
If (pEntry = NULL)
{
LPDISPATCH pd;
If (pThis-> m_lpAggrInner ){
PThis-> m_lpAggrInner-> QueryInterface (IID_IDispatch, (void **) & pd );
If (pd ){
HRESULT hr = pd-> Invoke (dispid, riid, lcid, wFlags, pDispParams, pvarResult,
P1_info, puArgErr );
Pd-> Release ();
Return hr;
}
}
Return DISP_E_MEMBERNOTFOUND;
}

Return (LPDISPATCH) & (pThis-> m_xDispatch)-> Invoke (
Dispid,
Riid,
Lcid,
WFlags,
PDispParams,
PvarResult,
P1_info,
PuArgErr );
}

In addition, reload GetDispatchIID so that GetTypeInfo can be called successfully.

BOOL CTagcCtrl: GetDispatchIID (IID * pIID)
{
If (m_piidPrimary! = NULL)
* PIID = * m_piidPrimary;

Return (m_piidPrimary! = NULL );
}

C. Add EnableAutomation to the constructor and assign m_xDispatch to the correct vtbl of COleDispatchImpl. The constructor is as follows:

CTagcCtrl: CTagcCtrl ()
{
InitializeIIDs (& IID_DTagc, & IID_DTagcEvents );

// TODO: Initialize your control's instance data here.
M_lpAggrInner = NULL;

EnableAutomation ();

EnableAggregation ();

AfxOleLockApp ();
}

D. Cancel the comments of the dispatch interface and the definition macro

// Dispatch maps
// {AFX_DISPATCH (CTagcCtrl)
Afx_msg void Hello ();
//} AFX_DISPATCH
DECLARE_DISPATCH_MAP ()

BEGIN_DISPATCH_MAP (CTagcCtrl, c1_target)
// {AFX_DISPATCH_MAP (CTagcCtrl)
DISP_FUNCTION (CTagcCtrl, "Hello", Hello, VT_EMPTY, VTS_NONE)
//} AFX_DISPATCH_MAP
DISP_FUNCTION_ID (CTagcCtrl, "AboutBox", DISPID_ABOUTBOX, AboutBox, VT_EMPTY, VTS_NONE)
END_DISPATCH_MAP ()
Here, Hello is a control method defined by me, which is listed together.

E. Add the IDispatch interface to the interface ing table.
BEGIN_INTERFACE_MAP (CTagcCtrl, c1_target)
INTERFACE_PART (CTagcCtrl, IID_IUnknown, InnerUnknown)
INTERFACE_PART (CTagcCtrl, IID_IDispatch, Dispatchx)
INTERFACE_AGGREGATE (CTagcCtrl, m_lpAggrInner)
END_INTERFACE_MAP ()

You can also use GetInterfaceHook to add the IDispatch interface as follows:
LPUNKNOWN CTagcCtrl: GetInterfaceHook (const void * piid)
{
If (_ AfxIsEqualGUID (IID_IDispatch, * (IID *) piid) |
_ AfxIsEqualGUID (* m_piidPrimary, * (IID *) piid )){
Return & m_xDispatchx;
}
Return NULL;
}
Here * m_piidPrimary is IID_DTagc, so we can also obtain the IDispatch interface through IID_DTagc.

F. This is basically the case. Compile and test it.

G. although it is normal, If you Test in ActiveX Control Test Container, you will find that only the Dispatch methods and attributes of the aggregate Control are available. Why, this is because ActiveX Control Test Container obtains the ITypeInfo interface pointer by querying IProvideClassInfo of the Control to obtain the type information. Obviously, the IProvideClassInfo interface pointer here comes from the aggregated Control. The type information is naturally the type information of the aggregated control. Although changing the type information of a widget is not helpful (because the type information of the aggregated widget cannot be displayed), change it.

Add the package class declaration XProvideClassInfo in the same way
BEGIN_INTERFACE_PART (ProvideClassInfo, IProvideClassInfo2)
INIT_INTERFACE_PART (CTagcCtrl, ProvideClassInfo)
STDMETHOD (GetClassInfo) (LPTYPEINFO * ppTypeInfo );
STDMETHOD (GetGUID) (DWORD dwGuidKind, GUID * pGUID );
END_INTERFACE_PART (ProvideClassInfo)

Implementation Package

//////////////////////////////////////// /////////////////////////////////////
// CTagcCtrl: XProvideClassInfo

STDMETHODIMP _ (ULONG) CTagcCtrl: XProvideClassInfo: AddRef ()
{
METHOD_PROLOGUE_EX _ (CTagcCtrl, ProvideClassInfo)
Return (ULONG) pThis-> ExternalAddRef ();
}

STDMETHODIMP _ (ULONG) CTagcCtrl: XProvideClassInfo: Release ()
{
METHOD_PROLOGUE_EX _ (CTagcCtrl, ProvideClassInfo)
Return (ULONG) pThis-> ExternalRelease ();
}

STDMETHODIMP CTagcCtrl: XProvideClassInfo: QueryInterface (
REFIID iid, LPVOID * ppvObj)
{
METHOD_PROLOGUE_EX _ (CTagcCtrl, ProvideClassInfo)
Return (HRESULT) pThis-> ExternalQueryInterface (& iid, ppvObj );
}

STDMETHODIMP CTagcCtrl: XProvideClassInfo: GetClassInfo (
LPTYPEINFO * ppTypeInfo)
{
METHOD_PROLOGUE_EX (CTagcCtrl, ProvideClassInfo)

CLSID clsid;
PThis-> GetClassID (& clsid );

Return pThis-> GetTypeInfoOfGuid (GetUserDefaultLCID (), clsid, ppTypeInfo );
}

STDMETHODIMP CTagcCtrl: XProvideClassInfo: GetGUID (DWORD dwGuidKind,
GUID * pGUID)
{
METHOD_PROLOGUE_EX _ (CTagcCtrl, ProvideClassInfo)

If (dwGuidKind = GUIDKIND_DEFAULT_SOURCE_DISP_IID)
{
IProvideClassInfo2 * pinfo = NULL;
If (pThis-> m_lpAggrInner! = NULL ){
PThis-> m_lpAggrInner-> QueryInterface (IID_IProvideClassInfo2, (void **) & pinfo );
Pinfo-> GetGUID (dwGuidKind, pGUID );
Pinfo-> Release ();
}
Else {
* PGUID = * pThis-> m_piidEvents;
}
Return NOERROR;
}
Else
{
* PGUID = GUID_NULL;
Return E_INVALIDARG;
}
}

Add interface ing
BEGIN_INTERFACE_MAP (CTagcCtrl, c1_target)
INTERFACE_PART (CTagcCtrl, IID_IUnknown, InnerUnknown)
INTERFACE_PART (CTagcCtrl, IID_IDispatch, Dispatch)
INTERFACE_PART (CTagcCtrl, IID_IProvideClassInfo, ProvideClassInfo)
INTERFACE_PART (CTagcCtrl, IID_IProvideClassInfo2, ProvideClassInfo)
INTERFACE_AGGREGATE (CTagcCtrl, m_lpAggrInner)
END_INTERFACE_MAP ()

If you Test in ActiveX Control Test Container, you can find that the attributes and methods of your Control are listed. In VB, attributes and methods of their own controls are always displayed. It is estimated that VB directly obtained from typelib.

G. Unfortunately, I still cannot think of how to integrate the TypeLib of the two controls. I still don't know about odl and idl for a long time. You can use a tool to reverse export odl attributes and method code text from typelib, or call CreateTypeLib to generate your own TypeLib. I'm too lazy to get it, so that's it.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.