TN038: การใช้งาน IUnknown MFC/OLE

หัวใจของ OLE 2 อยู่ "OLE Component Object Model" หรือ COM. COM กำหนดมาตรฐานสำหรับวัตถุฟลอริวิธีสื่อสารอื่น ซึ่งรวมถึงรายละเอียดของอะไร "วัตถุ" ดูเหมือนว่า รวมทั้งวิธีอบรมวิธีการบนวัตถุ COM กำหนดคลาส base ที่มาทั้งหมด COM เข้าเรียน คลาสพื้นฐานนี้คือIUnknown ถึงแม้ว่าอินเทอร์เฟสIUnknownถูกอ้างถึงเป็นคลาส c ++ COM ไม่เฉพาะกับภาษาหนึ่ง — มันสามารถถูกใช้ใน C, PASCAL หรือภาษาอื่น ๆ ที่สนับสนุนเค้าโครงแบบไบนารีของวัตถุ COM?

OLE หมายถึงการเรียนทั้งหมดที่มาจากIUnknownเป็น "อินเทอร์เฟซ" นี้เป็นการสำคัญ distinction ตั้งแต่ "อินเทอร์เฟซ" เช่นIUnknownดำเนินด้วยไม่มีการใช้งาน มันเพียงแค่กำหนดโพรโทคอลซึ่งวัตถุสื่อ ไม่เกี่ยวกับการทำงานเหล่านั้นใช้งาน นี่คือเหตุผลสำหรับระบบซึ่งช่วยให้เพิ่มความยืดหยุ่นสูง เป็นงานของ MFC ใช้ลักษณะการทำงานเริ่มต้นสำหรับโปรแกรม MFC/c ++?

ทำความเข้าใจเกี่ยวกับการใช้งานของ MFC IUnknownคุณต้องเข้าใจว่าอินเทอร์เฟซนี้คืออะไร มีกำหนดรุ่นของ IUnknown สมัยใหม่ด้านล่าง:

 คลาส IUnknown
{
สาธารณะ:
 nbsp  เสมือน hresult ใน QueryInterface(REFIID iid, void** ppvObj) = 0
    เสมือน ULONG AddRef() = 0
    เสมือน ULONG Release() = 0
}(&N)

หมายเหตุnbsp  รายละเอียดบางอย่างจำเป็นเรียกแบบแผนของ เช่น__stdcallจะออกไปทางซ้ายสำหรับภาพประกอบนี้(&N)?

ฟังก์ชันสมาชิกAddRefและปล่อยควบคุมจัดการหน่วยความจำของวัตถุ COM ใช้โครงร่างการตรวจนับการอ้างอิงในการติดตามวัตถุ วัตถุที่มีการอ้างอิงไม่ตรงกับที่คุณเขียนใน c ++ อ้างอิงแทน COM วัตถุอยู่เสมอถึงผ่านตัวชี้อยู่ เมื่อต้องการปล่อยวัตถุเมื่อเจ้าเสร็จเรียบร้อยแล้วใช้งาน วัตถุปล่อยสมาชิกถูกเรียก (เหมือนที่แสดงเป็นการใช้ตัวดำเนินการลบ เท่าที่จะทำได้สำหรับวัตถุดั้งเดิม c ++) การอ้างอิงที่กลไกการตรวจนับที่อนุญาตให้สำหรับการอ้างอิงหลายวัตถุเดียวเพื่อที่ได้รับการจัดการ การใช้งานของAddRefและออกรักษาจำนวนที่ใช้ในการอ้างอิงบนวัตถุ — วัตถุนั้นจะไม่ถูกลบจนกว่าจำนวนของการอ้างอิงถึงศูนย์?

AddRefและปล่อยจะค่อนข้างตรงไปตรงมาจากยืนใช้งาน นี่คือการใช้งาน trivial:

ULONG CMyObj::AddRef() {nbsp  กลับ ++ m_dwRef 
}

ULONG CMyObj::Release() {ถ้า (-m_dwRef == 0) {
        ลบนี้ 
        ส่งกลับค่า 0
    }
    กลับ m_dwRef
}(&N)

ฟังก์ชันQueryInterfaceสมาชิกเป็นที่น่าสนใจเพิ่มเติม ในขณะที่คุณสามารถจินตนาการได้ ไม่น่าสนใจมากมีฟังก์ชันสมาชิกเท่านั้นคือAddRefและปล่อยวัตถุ — มันก็จะดีจะบอกวัตถุเพื่อทำสิ่งต่าง ๆ เพิ่มเติมมากกว่าIUnknownแสดง นี่คือที่อยู่QueryInterfaceประโยชน์ สมุดรายวันนี้อนุญาตให้คุณได้รับการ "ติดต่อ" แตกต่างกันบนวัตถุเดียวกัน อินเทอร์เฟซเหล่านี้มักจะมาจากIUnknownและเพิ่มการทำงานเพิ่มเติม โดยการเพิ่มฟังก์ชันใหม่ของสมาชิก อินเทอร์เฟซ COM ไม่เคยมีสมาชิกตัวแปรที่ประกาศไว้ในอินเทอร์เฟซ และฟังก์ชันทั้งหมดของสมาชิกจะถูกประกาศให้เป็นเสมือนบริสุทธิ์ ตัวอย่างเช่น,

คลา IPrintInterface: IUnknown สาธารณะ
{
สาธารณะ:
 nbsp  เสมือนโมฆะ PrintObject() = 0
}(&N)

ได้รับการ IPrintInterface ถ้าคุณมีเฉพาะการIUnknownโทรIUnknown::QueryInterface IIDของ IPrintInterface ใช้ การIIDเป็นหมายเลขที่ 128-บิตที่ระบุอินเทอร์เฟซ มีการIIDสำหรับแต่ละอินเทอร์เฟซที่กำหนดคุณหรือ OLE ถ้า ตัวชี้ไปยังวัตถุIUnknownพังก์ รหัสเรียก IPrintInterface จากนั้นอาจเป็น

IPrintInterface * pPrint = NULL
ถ้า (พังก์-gtQueryInterface(IID_IPrintInterface, (void**) & pPrint) == NOERROR)
{
    pPrint - > PrintObject()
    pPrint - > Release()   
        / / ชี้ออกมาผ่าน QueryInterface
}

ที่ดูเหมือนง่ายค่อนข้าง แต่อย่างไรจะคุณใช้วัตถุสนับสนุนอินเทอร์เฟซทั้ง IPrintInterface และIUnknown ในกรณีนี้ มันง่ายเนื่องจาก IPrintInterface ได้รับมาโดยตรงจากIUnknown — โดยนำ IPrintInterface, IUnknownโดยอัตโนมัติสนับสนุน ตัวอย่างเช่น:

 คลา CPrintObj: CPrintInterface สาธารณะ
{
 nbsp  เสมือน hresult ใน QueryInterface(REFIID iid, void** ppvObj)
    เสมือน ULONG AddRef()
    เสมือน ULONG Release()
    PrintObject() โมฆะเสมือน
}(&N)

การใช้งานของAddRefและออกจะตรงเหมือนกันเป็นผู้ดำเนินการข้างต้น CPrintObj::QueryInterfaceจะมีลักษณะดังนี้

Hresult ใน CPrintObj::QueryInterface(REFIID iid, void FAR* FAR* ppvObj)
{
 nbsp  ถ้า (iid == IID_IUnknown || iid == IID_IPrintInterface)
    {
        * ppvObj =นี้
        AddRef()
        กลับ NOERROR
    }
    กลับ ResultFromScode(E_NOINTERFACE)
}(&N)

เช่นที่คุณเห็น หากเป็นที่รู้จักการให้รหัสอินเทอร์เฟซ (IID) ตัวชี้จะถูกส่งกลับวัตถุ มิฉะนั้น ข้อผิดพลาดเกิดขึ้น โปรด สังเกตว่า การประสบความสำเร็จQueryInterfaceผลโดยนัยที่AddRef แน่นอน คุณจะยังมีการใช้CEditObj::Print ที่เป็นธรรมดา เพราะ IPrintInterface โดยตรงมาจากอินเทอร์เฟสIUnknown อย่างไรก็ตาม ถ้าคุณต้องการสนับสนุนอินเทอร์เฟซที่แตกต่างกันสอง ทั้งสองมาจากIUnknownพิจารณาต่อไป

คลา IEditInterface: IUnkown สาธารณะ
{
สาธารณะ:
 nbsp  เสมือนโมฆะ EditObject() = 0
}(&N)

แม้ว่าจะมีหลายวิธีที่แตกต่างกันการใช้คลาสสนับสนุนทั้ง IEditInterface และ IPrintInterface รวมถึงการใช้ c ++สืบทอดหลาย หมายเหตุนี้จะมุ่งในการใช้ชั้นซ้อนกันการใช้ฟังก์ชันนี้?

คลาส CEditPrintObj
{
สาธารณะ:
 nbsp  CEditPrintObj()

Hresult ใน QueryInterface(REFIID iid, void**)
    ULONG AddRef()
    ULONG Release()
    DWORD m_dwRef

คลา CPrintObj: IPrintInterface สาธารณะ
    {
    สาธารณะ:
        CEditPrintObj * m_pParent
        เสมือน hresult ใน QueryInterface(REFIID iid, void** ppvObj)
        เสมือน ULONG AddRef()
        เสมือน ULONG Release()
    } m_printObj

คลา CEditObj: IEditInterface สาธารณะ
    {
    สาธารณะ:
        CEditPrintObj * m_pParent
        เสมือน ULONG QueryInterface(REFIID iid, void** ppvObj)
        เสมือน ULONG AddRef()
        เสมือน ULONG Release()
    } m_editObj
}(&N)

การใช้งานทั้งหมดที่อยู่ด้านล่าง:

CEditPrintObj::CEditPrintObj()
{
 nbsp  m_editObj.m_pParent =นี้
    m_printObj.m_pParent =นี้
}

ULONG CEditPrintObj::AddRef() {กลับ ++ m_dwRef
}

CEditPrintObj::Release()
{
    ถ้า (-m_dwRef == 0)
    {
        ลบนี้
        ส่งกลับค่า 0
    }
    กลับ m_dwRef
}

Hresult ใน CEditPrintObj::QueryInterface(REFIID iid, void** ppvObj)
{
    ถ้า (iid == IID_IUnknown || iid == IID_IPrintInterface)
    {
        * ppvObj = & m_printObj
        AddRef()
        กลับ NOERROR
    }
    else if (iid == IID_IEditInterface)
    {
        * ppvObj = & m_editObj
        AddRef()
        กลับ NOERROR
    }
    กลับ ResultFromScode(E_NOINTERFACE)
}

ULONG CEditPrintObj::CEditObj::AddRef() {กลับ m_pParent - > AddRef() 
}

ULONG CEditPrintObj::CEditObj::Release() {กลับ m_pParent - > Release() 
}

Hresult ใน CEditPrintObj::CEditObj::QueryInterface (
    REFIID iid โมฆะ ** ppvObj) {กลับ m_pParent - > QueryInterface (iid, ppvObj); 
}

ULONG CEditPrintObj::CPrintObj::AddRef() {กลับ m_pParent - > AddRef() 
}

ULONG CEditPrintObj::CPrintObj::Release() {กลับ m_pParent - > Release() 
}

Hresult ใน CEditPrintObj::CPrintObj::QueryInterface (
    REFIID iid โมฆะ ** ppvObj) {กลับ m_pParent - > QueryInterface (iid, ppvObj); 
}

สังเกตส่วนใหญ่ใช้งานIUnknownถูกวางลงในคลาCEditPrintObjแทนที่จะทำซ้ำรหัส CEditPrintObj::CEditObj และ CEditPrintObj::CPrintObj นี้ลดจำนวนของรหัส และเลี่ยงบัก จุดสำคัญที่นี่เป็นที่จากอินเทอร์เฟส IUnknown โทรQueryInterfaceเรียกอินเทอร์เฟซใด ๆ วัตถุอาจสนับสนุน และจากแต่ละอินเทอร์เฟซดังกล่าว จะสามารถทำเช่นเดียวกัน ซึ่งหมายความ ว่าQueryInterfaceฟังก์ชันทั้งหมดพร้อมใช้งานจากแต่ละอินเทอร์เฟซต้องทำงานแบบเดียวกัน เพื่อให้วัตถุฝังตัวเหล่านี้เพื่อเรียกใช้งานใน "วัตถุภายนอก" ชี้หลังถูกใช้ (m_pParent) M_pParent ชี้ถูกเตรียมใช้งานระหว่างการกำหนด CEditPrintObj แล้ว คุณจะใช้ CEditPrintObj::CPrintObj::PrintObject และ CEditPrintObj::CEditObj::EditObject ด้วย ไม่น้อยของรหัสถูกเพิ่มเพื่อเพิ่มคุณลักษณะหนึ่ง — สามารถแก้ไขวัตถุ โชคดี มันค่อนข้างแปลกปลอมสำหรับอินเทอร์เฟซการมีฟังก์ชันสมาชิกเดียวเท่านั้น (แม้ว่าจะเกิดขึ้น) และในกรณีนี้ EditObject และ PrintObject จะมักจะถูกรวมเข้าไปในอินเทอร์เฟซแบบเดียว?

ที่เป็นคำอธิบายมากมายและรหัสสำหรับสถานการณ์เช่นวิมากมาย คลาสที่ MFC/OLE ให้เป็นทางเลือกที่ง่ายขึ้น ใช้งาน MFC ใช้เทคนิคเช่นเดียวกับข้อความของ Windows ถูกห่อ ด้วยแผนที่ความ สิ่งอำนวยความสะดวกนี้เรียกว่าแผนที่ของอินเทอร์เฟซและมีการกล่าวถึงในส่วนถัดไป?

แผนที่ติดต่อ MFC

MFC/OLE รวมถึงการใช้งานของ "อินเทอร์เฟซ Maps" คล้ายกับ "แผนที่ของข้อความ" และ "ส่งแผนที่" ของ MFC ในแนวคิดและการดำเนินการ คุณลักษณะหลักของ MFC ของอินเทอร์เฟซ Maps มีดังนี้:

นอกจากนี้ แผนที่ติดต่อสนับสนุนคุณลักษณะขั้นสูงต่อไปนี้:

สำหรับข้อมูลเพิ่มเติมเกี่ยวกับสไม่ ดูการOLE Programmer's Reference?

MFC ของอินเทอร์เฟซสำหรับแผนที่สนับสนุนคือ rooted ในคลาCCmdTarget CCmdTarget "ได้ที่" อ้างอิงนับรวมทั้งฟังก์ชันสมาชิกทั้งหมดเกี่ยวข้องกับการใช้งานIUnknown (การนับจำนวนการอ้างอิงตัวอย่างอยู่ในCCmdTarget) การสร้างคลาที่สนับสนุน OLE COM คุณชั้นที่ได้สืบทอดมาจากCCmdTargetและใช้แมโครต่าง ๆ ตลอดจนสมาชิกฟังก์ชันCCmdTargetการใช้อินเทอร์เฟซที่ต้องการ การใช้งานของ MFC ใช้ชั้นซ้อนกันเพื่อกำหนดอินเทอร์เฟซสำหรับแต่ละการใช้งานเหมือนกับตัวอย่างข้างบน นี่คือทำได้ง่ายขึ้น ด้วยการใช้งานมาตรฐานของ IUnknown รวมทั้งหมายเลขของแมโครที่ตัดบางส่วนของรหัสซ้ำ?

อินเทอร์เฟซพื้นฐานเกี่ยวกับแผนที่

การใช้คลาสที่ใช้อินเทอร์เฟซของ MFC แผนที่ทำตามขั้นตอนเหล่านี้:

  1. สืบทอดมาชั้นโดยตรง หรือโดยอ้อมจากCCmdTarget.

  2. ใช้ฟังก์ชันDECLARE_INTERFACE_MAPในข้อกำหนดของคลาสที่ได้รับ?

  3. สำหรับแต่ละอินเทอร์เฟซ ที่คุณต้องการสนับสนุน ใช้แมโคBEGIN_INTERFACE_PARTและEND_INTERFACE_PARTในข้อกำหนดของคลาส?

  4. ในแฟ้มใช้งาน ใช้แมโคBEGIN_INTERFACE_MAPและEND_INTERFACE_MAPเพื่อกำหนดแผนผังของอินเทอร์เฟซของคลาส?

  5. สำหรับแต่ละ IID สนับสนุน ใช้แมโคINTERFACE_PARTระหว่างแมโคBEGIN_INTERFACE_MAPและEND_INTERFACE_MAPให้แมป IID ที่เฉพาะ "ส่วน" ของของคลาส?

  6. ใช้คลาสที่ซ้อนที่เป็นตัวแทนของอินเทอร์เฟซคุณสนับสนุน?

  7. ใช้แมโคMETHOD_PROLOGUEในการเข้าถึงไซต์แม่CCmdTarget-มาวัตถุ?

  8. AddRef, ReleaseและQueryInterfaceสามารถมอบหมายในการใช้งานCCmdTargetของฟังก์ชันเหล่านี้ (ExternalAddRef, ExternalReleaseและExternalQueryInterface)?

ไม่สามารถนำมาใช้ตัวอย่าง CPrintEditObj ข้างบนเป็นดังนี้:

คลา CPrintEditObj: CCmdTarget สาธารณะ
{
สาธารณะ:
 nbsp  / / ข้อมูลสมาชิกและสมาชิกฟังก์ชันสำหรับ CPrintEditObj ไปที่นี่

/ / อินเทอร์เฟซ Maps
ได้รับการป้องกัน:
    DECLARE_INTERFACE_MAP()

BEGIN_INTERFACE_PART (EditObj, IEditInterface)
        STDMETHOD_ (โมฆะ EditObject)()
    END_INTERFACE_PART(EditObj)

BEGIN_INTERFACE_PART (PrintObj, IPrintInterface)
        STDMETHOD_ (โมฆะ PrintObject)()
    END_INTERFACE_PART(PrintObj)
}(&N)

การประกาศข้างต้นสร้างคลาสที่ได้รับมาจากCCmdTarget แมโคDECLARE_INTERFACE_MAPบอกกรอบว่า คลาสนี้จะมีแผนที่อินเทอร์เฟซแบบกำหนดเอง นอกจากนี้ แมโคBEGIN_INTERFACE_PARTและEND_INTERFACE_PARTกำหนดชั้นซ้อนกัน ในกรณีนี้ ด้วยชื่อ CEditObj และ CPrintObj (X ถูกใช้เฉพาะในการแยกความแตกต่างชั้นซ้อนกันจากชั้นส่วนกลางที่เริ่มต้น ด้วย "C" และชั้นของอินเทอร์เฟซที่เริ่มต้น ด้วย "I") สร้างสมาชิกซ้อนกันสองชั้นเหล่านี้: m_CEditObj, m_CPrintObj และตามลำดับ แมโคประกาศAddRef, Releaseและ ฟังก์ชันQueryInterfaceโดยอัตโนมัติ ดังนั้น คุณประกาศเฉพาะฟังก์ชันเฉพาะในอินเทอร์เฟซนี้: EditObject และ PrintObject (OLE แมโคSTDMETHODจะใช้เช่นเพื่อที่จะให้ตามความเหมาะสมของเป้าหมายแพลตฟอร์_stdcallและคำสำคัญเสมือน)?

การใช้แผนที่อินเทอร์เฟซสำหรับคลาสนี้:

BEGIN_INTERFACE_MAP (CPrintEditObj, CCmdTarget)
 nbsp  INTERFACE_PART (CPrintEditObj, IID_IPrintInterface, PrintObj)
    INTERFACE_PART (CPrintEditObj, IID_IEditInterface, EditObj)
END_INTERFACE_MAP()(&N)

นี้เชื่อม IID IID_IPrintInterface กับ m_CPrintObj และ IID_IEditInterface กับ m_CEditObj ตามลำดับ ใช้งานCCmdTarget QueryInterface (CCmdTarget::ExternalQueryInterface) ใช้แผนที่นี้จะกลับตัวชี้ไปที่ m_CPrintObj และ m_CEditObj เมื่อการร้องขอ ไม่จำเป็นต้องรวมรายการสำหรับIID_IUnknown กรอบจะใช้อินเทอร์เฟสแรกในแผนที่ (ในกรณีนี้ m_CPrintObj) เมื่อมีการร้องขอIID_IUnknown?

แม้ว่าแมโคBEGIN_INTERFACE_PARTโดยอัตโนมัติประกาศAddRefเผยแพร่และฟังก์ชันQueryInterfaceสำหรับคุณ คุณยังต้องการการปฏิบัติงาน:

ULONG ไกลส่งออก CEditPrintObj::XEditObj::AddRef()
{
 nbsp  METHOD_PROLOGUE (CEditPrintObj, EditObj)
    กลับ pThis - > ExternalAddRef()
}

ULONG ไกลส่งออก CEditPrintObj::XEditObj::Release()
{
    METHOD_PROLOGUE (CEditPrintObj, EditObj)
    กลับ pThis - > ExternalRelease()
}

Hresult ในส่งออก CEditPrintObj::XEditObj::QueryInterface (ไกล
    REFIID iid โมฆะ * ไกลไกล * ppvObj)
{
    METHOD_PROLOGUE (CEditPrintObj, EditObj)
    กลับ (hresult ใน) pThis - > ExternalQueryInterface (& iid, ppvObj);
}

โมฆะส่งออกไกล CEditPrintObj::XEditObj::EditObject()
{
    METHOD_PROLOGUE (CEditPrintObj, EditObj)
    / / รหัส "แก้ไข" วัตถุ สิ่งที่หมายถึง...
}

การใช้งานสำหรับ CEditPrintObj::CPrintObj จะคล้ายคลึงกับข้อกำหนดข้างต้นสำหรับ CEditPrintObj::CEditObj แม้ว่ามันจะเป็นไปได้ในการสร้างแมโครที่สามารถใช้ฟังก์ชันเหล่านี้ (เป็นแท้ ก่อนหน้านี้ในการพัฒนา MFC/OLE นี้เป็นกรณี), สร้างโดยอัตโนมัติ จะกลายเป็นยากที่จะกำหนดจุดแบ่งเมื่อแมโครที่สร้างบรรทัดมากกว่าหนึ่งบรรทัดของรหัส ด้วยเหตุนี้ รหัสนี้จะถูกขยายด้วยตนเอง?

โดยใช้กรอบการใช้งานของข้อความ มีแผนที่มีหมายเลขของสิ่งที่ไม่จำเป็นต้องทำ:

นอกจากนี้ กรอบใช้แมปข้อความภายใน นี้อนุญาตให้คุณสืบทอดมาจากคลาสที่กรอบ พูดCOleServerDocที่สนับสนุนอินเทอร์เฟซบางเรียบร้อยแล้ว และให้ใช้แทนหรือเพิ่มการอินเทอร์เฟซที่มาจากกรอบ นี้ถูกเปิดใช้งาน โดยข้อเท็จจริงว่า กรอบทั้งหมดสนับสนุนสืบทอดอินเทอร์เฟซแมปจากคลาสพื้นฐาน — นั่นคือเหตุผลทำไมBEGIN_INTERFACE_MAPใช้เป็นพารามิเตอร์ของสองชื่อของคลาสพื้นฐาน?

หมายเหตุnbsp  ได้โดยทั่วไปไม่สามารถนำการใช้งานของ MFC ใช้งานภายในของอินเทอร์เฟซการ OLE โดยสืบทอดเฉพาะทางที่ฝังตัวของอินเทอร์เฟซที่จากรุ่น MFC นี้เป็นไปไม่ได้เนื่องจากการใช้งานแมโคMETHOD_PROLOGUEเพื่อเข้าถึงการประกอบด้วยCCmdTarget-วัตถุที่ได้รับแสดงถึงการคงออฟเซตของวัตถุฝังตัวจากการCCmdTarget-มาวัตถุ ซึ่งหมายความว่า ตัวอย่าง สืบทอดคุณไม่สามารถมา XMyAdviseSink การฝังตัวจากการใช้งานของ MFC ในCOleClientItem::XAdviseSinkเนื่องจาก XAdviseSink อาศัยการอยู่ตรงข้ามกับเฉพาะจากด้านบนของวัตถุCOleClientItem(&N)?

อย่างไรก็ตาม คุณสามารถ มอบหมายในการใช้งานทั้งหมดของฟังก์ชันที่คุณต้องการให้ทำงานดีฟอลต์ของ MFC MFC สามารถทำได้ในการใช้ MFC งานIOleInPlaceFrame (XOleInPlaceFrame) ในชั้นเรียนCOleFrameHook (มัน delegates กับ m_xOleInPlaceUIWindow สำหรับฟังก์ชันหลายฟังก์ชัน) การออกแบบนี้ถูกเลือกเพื่อลดขนาดของวัตถุที่ใช้อินเทอร์เฟซหลาย runtime มันกำจัดความต้องการด้านหลังตัวชี้ (เช่นการ m_pParent ด้วยวิธีถูกใช้ในส่วนก่อนหน้า)?

รวมและอินเทอร์เฟซ Maps

นอกจากการสนับสนุนวัตถุ COM stand-alone, MFC ยังสนับสนุนได้อีกด้วย รวมตัวเองเป็นซับซ้อนเกินกว่าที่หัวข้อการอภิปรายที่นี่ อ้างอิงถึงOLE โปรแกรมเมอร์ของการอ้างอิงสำหรับข้อมูลเพิ่มเติมเกี่ยวกับสไม่ หมายเหตุนี้จะอธิบายถึงการสนับสนุนสำหรับการรวมที่อยู่ภายในกรอบและติดต่อกับแผนที่เพียงแค่?

มีสองวิธีที่ใช้ได้: (1) ใช้วัตถุ COM ที่สนับสนุนสไม่ และ (2) การใช้วัตถุที่สามารถถูกรวม ด้วยอีก ความสามารถเหล่านี้สามารถถูกเรียกว่า "ใช้วัตถุรวม" และ "ทำวัตถุ aggregatable" MFC สนับสนุนทั้งสอง?

ใช้วัตถุรวม

การใช้วัตถุรวม ความต้องการมีเป็น บางวิธีผูกมัดรวมเข้าไปในกลไก QueryInterface กล่าวอีกนัยหนึ่ง วัตถุรวมต้องทำงานเหมือนเป็นส่วนหนึ่งของวัตถุของคุณแม่ ดังนั้นวิธีไม่เสมอนี้เข้าไปในกลไกการแมปของอินเทอร์เฟซของ MFC นอกจากการแมโครINTERFACE_PARTที่วัตถุที่ซ้อนกันถูกแมปไปยัง IID คุณยังสามารถประกาศวัตถุรวมเป็นส่วนหนึ่งของคลาสCCmdTargetมาของคุณ เมื่อต้องการทำเช่นนั้น แมโคINTERFACE_AGGREGATEถูกใช้ นี้ช่วยให้คุณระบุเป็นสมาชิก (ซึ่ง ตัวแปรต้องเป็นตัวชี้ไปยังIUnknownหรือมาคลาส), ซึ่งจะถูกรวมเข้าไปในกลไกการแมปที่อินเทอร์เฟซ ถ้าตัวชี้ไม่ใช่ NULL เมื่อเรียกว่าCCmdTarget::ExternalQueryInterfaceกรอบจะเรียกฟังก์ชันของวัตถุรวมQueryInterfaceสมาชิกโดยอัตโนมัติIIDร้องขอไม่ได้เป็นหนึ่งของ s IIDดั้งเดิมได้รับการสนับสนุน โดยCCmdTargetวัตถุ?

เมื่อต้องการใช้แมโค INTERFACE_AGGREGATE ทำตามขั้นตอนเหล่านี้:

  1. ประกาศแบบสมาชิก (การIUnknown *) ซึ่งตัวแปรจะประกอบด้วยตัวชี้ไปยังวัตถุรวม?

  2. รวมแมโครที่มีINTERFACE_AGGREGATEในแผนที่ของอินเทอร์เฟซ ซึ่งหมายถึงตัวแปรสมาชิกตามชื่อ?

  3. ที่บางจุด (โดยปกติระหว่างCCmdTarget::OnCreateAggregates), เตรียมใช้งานตัวแปรของสมาชิกกับสิ่งอื่นที่ไม่ใช่ค่า NULL?

ตัวอย่างเช่น,

คลา CAggrExample: CCmdTarget สาธารณะ
{
สาธารณะ:
 nbsp  CAggrExample()

ได้รับการป้องกัน:
    LPUNKNOWN m_lpAggrInner
    เสมือน BOOL OnCreateAggregates()

DECLARE_INTERFACE_MAP()
    / / แมโคส่วนอินเทอร์เฟซ "ดั้งเดิม" อาจใช้ที่นี่
};

CAggrExample::CAggrExample()
{
    m_lpAggrInner = NULL
}

BOOL CAggrExample::OnCreateAggregates()
{
    / / สายขึ้นรวมกับไม่รู้จักควบคุมที่ถูกต้อง
    m_lpAggrInner = CoCreateInstance (CLSID_Example
        GetControllingUnknown(), CLSCTX_INPROC_SERVER
        IID_IUnknown (LPVOID *) & m_lpAggrInner);
    ถ้า (m_lpAggrInner == NULL)
        ส่งกลับค่า FALSE
    / / เลือก สร้างวัตถุอื่น ๆ รวมที่นี่
    ส่งกลับ TRUE
}

BEGIN_INTERFACE_MAP (CAggrExample, CCmdTarget)
    / / ดั้งเดิม "INTERFACE_PART" รายการไปที่นี่
    INTERFACE_AGGREGATE (CAggrExample, m_lpAggrInner)
END_INTERFACE_MAP()

m_lpAggrInnerถูกเตรียมใช้งานในการกำหนดให้เป็น NULL กรอบจะละเว้นตัวแปรค่า NULL สมาชิกในการใช้งานตามค่าเริ่มต้นQueryInterface OnCreateAggregatesดีจริง สร้างวัตถุของคุณรวมอยู่ คุณจะต้องเรียกว่าชัดเจนหากคุณกำลังสร้างวัตถุภายนอกของ MFC ใช้งานCOleObjectFactory เหตุผลสำหรับการสร้างผลในCCmdTarget::OnCreateAggregatesเช่นเดียวกับการใช้งานของCCmdTarget::GetControllingUnknownจะมีแจ้งเมื่อสร้างวัตถุ aggregatable ได้กล่าวถึง?

เทคนิคนี้จะให้วัตถุของคุณทั้งหมดของอินเทอร์เฟซที่สนับสนุนการรวมวัตถุบวกของอินเทอร์เฟซมาตรฐาน ถ้าคุณต้องเซตย่อยของอินเทอร์เฟซที่รวมการสนับสนุนเท่านั้น คุณสามารถแทนที่CCmdTarget::GetInterfaceHook นี้ช่วยให้คุณมากต่ำระดับ hookability คล้ายกับQueryInterface โดยปกติแล้ว คุณต้องอินเทอร์เฟซทั้งหมดที่รวมการสนับสนุน?

ทำให้การใช้งานวัตถุ Aggregatable

สำหรับวัตถุจะ aggregatable การใช้งานAddRef, ReleaseและQueryInterfaceต้องมอบให้เป็น "ไม่ควบคุมรู้จักกัน" กล่าวอีกนัยหนึ่ง มันเป็นส่วนหนึ่งของวัตถุ มันต้องการมอบAddRef, ReleaseและQueryInterfaceวัตถุอื่น ยัง มาจากIUnknown นี้ "ไม่ควบคุมรู้จัก" คือการให้วัตถุ เมื่อสร้าง กล่าวคือ ให้มาเพื่อการใช้งานCOleObjectFactory นำมาบังคับใช้นี้ดำเนินตามค่าใช้จ่ายในจำนวนเล็กน้อย และในบางกรณีไม่เป็น เพื่อทำ MFC ให้นี้ก็ได้ การเปิดใช้งานวัตถุให้ aggregatable คุณโทรCCmdTarget::EnableAggregationจากพารามิเตอร์ของวัตถุ?

ถ้าวัตถุใช้ผล คุณยังต้องแน่ใจว่าได้ผ่านถูกต้อง "ไม่ควบคุมรู้จัก" ไปยังวัตถุที่รวมกัน มักจะชี้IUnknownนี้จะถูกส่งไปวัตถุเมื่อสร้างการรวม พารามิเตอร์ pUnkOuter เป็น "ไม่ควบคุมรู้จัก" สำหรับวัตถุที่สร้างขึ้นด้วยCoCreateInstanceตัวอย่าง ชี้ "ไม่รู้จักการควบคุม" ที่ถูกต้องสามารถเรียกได้ โดยการเรียกCCmdTarget::GetControllingUnknown ค่าส่งกลับจากฟังก์ชันที่ อย่างไรก็ตาม ไม่ถูกต้องในระหว่างที่กำหนด ด้วยเหตุนี้ มันจะแนะนำให้ คุณสร้างผลของคุณเท่านั้นในการแทนที่ของCCmdTarget::OnCreateAggregatesค่าส่งกลับจากGetControllingUnknownน่าเชื่อถือ แม้ว่าจะสร้างขึ้นจากการใช้งานCOleObjectFactory?

ยังเป็นสำคัญวัตถุใช้นับจำนวนการอ้างอิงที่ถูกต้องเมื่อมีการเพิ่ม หรือการปล่อยการอ้างอิงที่เทียมนับ เพื่อให้แน่ใจว่า เป็นกรณีนี้ เสมอโทรExternalAddRefและExternalReleaseแทนของInternalReleaseและInternalAddRef มันหายากโทรInternalReleaseหรือInternalAddRefในคลาสไม่สนับสนุน?

วัสดุอ้างอิง

การใช้งานขั้นสูงของ OLE เช่นกำหนดอินเทอร์เฟซของคุณเอง หรือการแทนที่การใช้งานของกรอบของอินเทอร์เฟซการ OLE ต้องใช้กลไกแผนที่อินเทอร์เฟซพื้นฐาน?

ส่วนนี้อธิบายแต่ละแมโครและ APIs ซึ่งใช้ในการนำคุณลักษณะขั้นสูงเหล่านี้?

CCmdTarget::EnableAggregation — อธิบายฟังก์ชัน

โมฆะ EnableAggregation()

หมายเหตุnbsp  เรียกใช้ฟังก์ชันนี้ในการกำหนดของชั้นได้รับถ้าคุณต้องการให้สนับสนุน OLE ได้สำหรับวัตถุชนิดนี้ นี้เตรียมใช้งาน IUnknown พิเศษที่จำเป็นสำหรับวัตถุ aggregatable(&N)?

CCmdTarget::ExternalQueryInterface — อธิบายฟังก์ชัน

DWORD ExternalQueryInterface (const โมฆะ ไกล * lpIID, LPVOID ไกล * ppvObj);

lpIID

ชี้การไกลเพื่อการ IID (อาร์กิวเมนต์แรกไปยัง QueryInterface)

ppvObj

ตัวชี้การ IUnknown การ * (QueryInterface อาร์กิวเมนต์ที่สอง)

หมายเหตุnbsp  เรียกใช้ฟังก์ชันนี้ในการใช้งานของ IUnknown สำหรับอินเทอร์เฟซสำหรับแต่ละชั้นของคุณใช้การ ฟังก์ชันนี้มีการใช้มาตรฐานข้อมูลงาน QueryInterface ตามแผนที่ของอินเทอร์เฟซของวัตถุ มันจำเป็นในการทอดค่าส่งคืน HRESULT ถ้าวัตถุนั้นจะรวม ฟังก์ชันนี้จะเรียก "IUnknown ควบคุม" แทนการใช้อินเทอร์เฟซภายในแผนที่(&N)?

CCmdTarget::ExternalAddRef — อธิบายฟังก์ชัน

DWORD ExternalAddRef()

หมายเหตุnbsp  เรียกใช้ฟังก์ชันนี้ในการใช้งานของคุณ IUnknown::AddRef สำหรับอินเทอร์เฟซสำหรับแต่ละชั้นของคุณใช้การ ค่าส่งคืนเป็นจำนวนการอ้างอิงใหม่บนวัตถุ CCmdTarget ถ้าวัตถุนั้นจะรวม ฟังก์ชันนี้จะเรียก "IUnknown ควบคุม" แทนที่เป็นของการควบคุมการนับจำนวนการอ้างอิงภายใน(&N)?

CCmdTarget::ExternalRelease — อธิบายฟังก์ชัน

DWORD ExternalRelease();

หมายเหตุnbsp  เรียกใช้ฟังก์ชันนี้ในการใช้งานของคุณ IUnknown::Release สำหรับอินเทอร์เฟซสำหรับแต่ละชั้นของคุณใช้การ ค่าส่งกลับระบุว่า จำนวนการอ้างอิงใหม่บนวัตถุ ถ้าวัตถุนั้นจะรวม ฟังก์ชันนี้จะเรียก "IUnknown ควบคุม" แทนที่เป็นของการควบคุมการนับจำนวนการอ้างอิงภายใน(&N)?

DECLARE_INTERFACE_MAP — คำอธิบายของแมโคร

DECLARE_INTERFACE_MAP

หมายเหตุnbsp  ใช้แมโครนี้ในชั้นใด ๆ มาจากCCmdTargetที่จะมีการแมปของอินเทอร์เฟซ ใช้แบบเดียวกับDECLARE_MESSAGE_MAPในมาก เรียกแมโคนี้ควรถูกวางในนิยามคลาส มักจะอยู่ในส่วนหัว (&H) แฟ้ม ชั้นกับDECLARE_INTERFACE_MAPต้องกำหนดแผนที่อินเทอร์เฟซในการใช้งานแฟ้ม (CPP) กับแมโคBEGIN_INTERFACE_MAPและEND_INTERFACE_MAP?

BEGIN_INTERFACE_PART และ END_INTERFACE_PART — คำอธิบายของแมโคร

BEGIN_INTERFACE_PART (localClass, iface);

END_INTERFACE_PART (localClass)

localClass

ชื่อของระดับชั้นที่ใช้อินเทอร์เฟซ

iface

ชื่อของอินเทอร์เฟซสำหรับคลาสนี้ใช้

หมายเหตุnbsp  สำหรับติดต่อกับแต่ละชั้นของคุณจะใช้ คุณจำเป็นต้องมีคู่BEGIN_INTERFACE_PARTและEND_INTERFACE_PART แมโครเหล่านี้กำหนดมาจากอินเทอร์เฟซสำหรับ OLE ที่คุณกำหนดตลอดจนตัวแปรฝังตัวสมาชิกของคลาสที่ ชั้นภายในเครื่อง AddRef, ReleaseและQueryInterfaceสมาชิกมีประกาศโดยอัตโนมัติ คุณต้องรวมการประกาศสำหรับฟังก์ชันสมาชิกอื่น ๆ ซึ่งเป็นส่วนของอินเทอร์เฟซที่ถูกนำมาใช้ (ประกาศดังกล่าวจะถูกวางไว้ระหว่างแมโคBEGIN_INTERFACE_PARTและEND_INTERFACE_PART )(&N)?

Ifaceอาร์กิวเมนต์เป็นอินเทอร์เฟซสำหรับ OLE ที่คุณต้องการใช้ เช่นIAdviseSink, IPersistStorage (หรืออินเทอร์เฟซแบบกำหนดเองของคุณเอง)?

อาร์กิวเมนต์localClassคือชื่อของคลาท้องถิ่นที่จะกำหนด มี ' X' โดยอัตโนมัติจะถูกซึ่งชื่อ ตั้งชื่อแบบนี้ถูกใช้เพื่อหลีกเลี่ยงการชนกับชั้นส่วนกลางมีชื่อเดียวกัน นอกจากนี้ หน้าชื่อของการฝังตัวสมาชิก เหมือนกับชื่อlocalClassนั้นยกเว้น โดย 'm_x'?

ตัวอย่างเช่น:

BEGIN_INTERFACE_PART (MyAdviseSink, IAdviseSink)
 nbsp STDMETHOD_(void,OnDataChange) (LPFORMATETC, LPSTGMEDIUM);
   STDMETHOD_(void,OnViewChange) (DWORD ยาว);
   STDMETHOD_(void,OnRename)(LPMONIKER)
   STDMETHOD_(void,OnSave)()
   STDMETHOD_(void,OnClose)()
END_INTERFACE_PART(MyAdviseSink)(&N)

จะกำหนดชั้นท้องถิ่นเรียกว่า XMyAdviseSink มาจาก IAdviseSink และสมาชิกของคลาสซึ่งมันถูกประกาศเรียกว่า m_xMyAdviseSink.Note:

หมายเหตุnbsp  จำเป็นอย่างยิ่งบรรทัดที่ขึ้นต้นด้วยSTDMETHOD_จะถูกคัดลอกจาก OLE2H และแก้ไขเล็กน้อย คัดลอกแฟ้มจาก OLE2H สามารถลดข้อผิดพลาดที่ไม่ยากที่จะแก้ไข(&N)?

BEGIN_INTERFACE_MAP และ END_INTERFACE_MAP — คำอธิบายของแมโคร

BEGIN_INTERFACE_MAP (theClass, baseClass)

END_INTERFACE_MAP

theClass

คลาแผนที่อินเทอร์เฟซจะมีการกำหนดไว้

baseClass

ชั้นเรียนที่theClassมาจาก?

หมายเหตุ: แมโคBEGIN_INTERFACE_MAPและEND_INTERFACE_MAPใช้ในการใช้งานแฟ้มจริง กำหนดอินเทอร์เฟซสำหรับแผนที่ สำหรับแต่ละอินเทอร์เฟซที่ถูกนำมาใช้ มีอย่าง น้อยหนึ่งINTERFACE_PARTแมโค invocations สำหรับการรวมแต่ละที่ชั้นใช้ ไม่มีเรียกแมโคINTERFACE_AGGREGATEหนึ่ง?

INTERFACE_PART — คำอธิบายของแมโคร

INTERFACE_PART (theClass, iid, localClass)

theClass

ชื่อของคลาสซึ่งประกอบด้วยอินเทอร์เฟซสำหรับแผนที่?

iid

IIDซึ่งจะถูกแมปไปยังระดับชั้นฝังตัว?

localClass

ชื่อของระดับชั้นภายใน (น้อยกว่า ' X')

หมายเหตุnbsp  แมโครนี้ถูกใช้ระหว่างแมโคBEGIN_INTERFACE_MAPและแมโคEND_INTERFACE_MAPสำหรับแต่ละอินเทอร์เฟซของวัตถุจะสนับสนุน สมุดรายวันนี้อนุญาตให้คุณแมป IID การให้กับสมาชิกของคลาสที่ระบุ โดยใช้theClassและlocalClass 'M_x' จะถูกเพิ่มลงในlocalClassโดยอัตโนมัติ หมายเหตุการที่ มากกว่าหนึ่งIIDอาจเกี่ยวข้องกับสมาชิกเดียวกัน นี้มีประโยชน์มากเมื่อคุณจะใช้เฉพาะกับ "สุดมา" ติดต่อ และต้องการให้อินเทอร์เฟซทั้งหมดระดับกลางสำหรับทั้ง ตัวอย่างที่ดีนี้เป็นอินเทอร์เฟซสำหรับIOleInPlaceFrameWindow ลำดับชั้นของมีลักษณะดังนี้(&N):

IUnknown
 nbsp  IOleWindow
        IOleUIWindow
            IOleInPlaceFrameWindow(&N)

ถ้าวัตถุใช้IOleInPlaceFrameWindowไคลเอ็นต์อาจQueryInterfaceบนใด ๆ ของอินเทอร์เฟซเหล่านี้: IOleUIWindow, IOleWindowหรือIUnknownนอกจากอินเทอร์เฟซ "ได้รับมากที่สุด" IOleInPlaceFrameWindow (หนึ่งคุณจะใช้งานจริง) การจัดการนี้ คุณสามารถใช้แมโครINTERFACE_PARTมากกว่าหนึ่งการแมปอินเทอร์เฟซพื้นฐานที่แต่ละอินเทอร์เฟซสำหรับIOleInPlaceFrameWindow

ในแฟ้มข้อกำหนดของคลาส:

BEGIN_INTERFACE_PART (CMyFrameWindow, IOleInPlaceFrameWindow)

ในการใช้งานแฟ้มคลาส:

BEGIN_INTERFACE_MAP (CMyWnd, CFrameWnd)
 nbsp  INTERFACE_PART (CMyWnd, IID_IOleWindow, MyFrameWindow)
    INTERFACE_PART (CMyWnd, IID_IOleUIWindow, MyFrameWindow)
    INTERFACE_PART (CMyWnd, IID_IOleInPlaceFrameWindow, MyFrameWindow)
END_INTERFACE_MAP(&N)

กรอบจะดูแลของ IUnknown เนื่องจากมักจำเป็น?

INTERFACE_PART — คำอธิบายของแมโคร

INTERFACE_AGGREGATE (theClass, theAggr)

theClass

ชื่อของคลาสซึ่งประกอบด้วยอินเทอร์เฟซสำหรับแผนที่,

theAggr

ชื่อของตัวแปรสมาชิกซึ่งจะถูกรวม?

หมายเหตุnbsp  แมโครนี้ถูกใช้เพื่อบอกกรอบว่า ชั้นใช้วัตถุที่รวมกัน มันต้องปรากฏระหว่างแมโคBEGIN_INTERFACE_PARTและEND_INTERFACE_PART รวมวัตถุคือ วัตถุแยก มาจากIUnknown โดยใช้การรวมและแมโคINTERFACE_AGGREGATEคุณสามารถสร้างอินเทอร์เฟซทั้งหมดที่สนับสนุนการรวมปรากฏขึ้นเพื่อสนับสนุนโดยตรง โดยวัตถุ อาร์กิวเมนต์theAggrเป็นเพียงชื่อของตัวแปรเป็นสมาชิกของคลาสของคุณที่ได้รับมาจากIUnknown (โดยตรง หรือโดยอ้อม) แมโครINTERFACE_AGGREGATEทั้งหมดต้องเป็นไปตามแมโคINTERFACE_PARTวางไว้ในแผนที่มีอินเทอร์เฟซ(&N)?

หมายเหตุด้านเทคนิคตามหมายเลข|nbsp หมายเหตุด้านเทคนิคตามประเภท(&N)

Index