zoukankan      html  css  js  c++  java
  • VC使用MFC进行COM编程

    首先应当明确,MFC中是通过嵌套类而不是多重继承来实现COM接口的,通过接口映射机制将接口和实现该接口的嵌套类关联起来;MFC中提供一套简明的宏来实现嵌套类的定义.其次,MFC通过CCmdTarget类实现了IUnknown接口。

      本文首先描述创建一个COM服务器的步骤和核心代码.然后说明客户程序关键代码。

      此COM服务器实现一个TimeLogServer组件,为简明起见,此组件只有一个接口ITimeLog,通过ITimeLog的方法OutputLog可以将日志文本输出至日志文件。

      创建一个MFC DLL工程,选择支持Automation(当然本程序不一定是自动化服务器,在这里这样做好处在于自动实现了几个必要的输出函数如DllGetClassObject,DllRegisterServer等,否则要自己写)

      第一节 COM服务器

      一. 声明组件和接口

      1.写一个GUIDs.h,在GUIDs.h中声明组件和接口的GUID

    //声明组件GUID {A433E701-E45E-11d3-97B5-52544CBA7F28}
    //DEFINE_GUID(CLSID_TimeLogServer,
    //0xa433e701, 0xe45e, 0x11d3, 0x97, 0xb5, 0x52, 0x54, 0x4c, 0xba, 0x7f, 0x28);
    static const IID CLSID_TimeLogServer =
    {0xa433e701, 0xe45e, 0x11d3, {0x97, 0xb5, 0x52, 0x54, 0x4c, 0xba, 0x7f, 0x28}};
    // 声明接口GUID{A433E702-E45E-11d3-97B5-52544CBA7F28}
    //DEFINE_GUID(IID_ITimeLog,
    //0xa433e702, 0xe45e, 0x11d3, 0x97, 0xb5, 0x52, 0x54, 0x4c, 0xba, 0x7f, 0x28);
    static const IID IID_ITimeLog =
    {0xa433e702, 0xe45e, 0x11d3, {0x97, 0xb5, 0x52, 0x54, 0x4c, 0xba, 0x7f, 0x28}};

      2.写一个ITimeLogServer.h,在ITimeLogServer.h文件中声明组件和接口

    //ITimeLogServer.h
    #include ";GUIDs.h";
    //接口ITimeLog的声明
    DECLARE_INTERFACE_(ITimeLog,IUnknown)
    {
        STDMETHOD(OutputLog)(BSTR* varLogText)PURE;
    };

      说明:

      1.宏DEFINE_GUID将组件和接口的progid与GUID相关联.可以用guidgen.exe工具产生。

      2.宏DECLARE_INTERFACE_声明接口;该宏第一个参数为接口名,第二个参数为该接口的基类.声明没有基类的接口用DECLARE_INTERFACE宏。

      3.宏STDMETHOD声明接口中的方法.此方法的返回值为HRESULT.PURE被解释为";=0";,即此方法为纯虚函数.当方法的返回值不是HRESULT时,用宏STDMETHOD_(返回类型,函数名)(参数)PURE; 

    二.声明组件类CTimeLogServer和实现接口的嵌套类

      在ClassWizard中添加新类CTimeLogServer,其基类选择为CCmdTarget.修改其头文件TimeLogServer1.h,加上#include ";ITimeLogServer.h";;同时在类声明体中加上

    //声明实现ITimelog接口的嵌套类
        BEGIN_INTERFACE_PART(TimeLog,ITimeLog)//自动声明IUnknown接口的三个方法
        STDMETHOD(OutputLog)(BSTR* varLogText);
        END_INTERFACE_PART(TimeLog)
        //声明接口映射
        DECLARE_INTERFACE_MAP()
        //声明类厂
        DECLARE_OLECREATE(CTimeLogServer)

      三.实现类厂和接口映射

      在CTimeLogServer的实现文件中写入:

    //实现类厂
    IMPLEMENT_OLECREATE(CTimeLogServer,";TimeLogServer";,
    0xa433e701, 0xe45e, 0x11d3, 0x97, 0xb5, 0x52, 0x54, 0x4c, 0xba, 0x7f, 0x28);
    //映射接口到相应的嵌套类
    BEGIN_INTERFACE_MAP(CTimeLogServer,CCmdTarget)
    INTERFACE_PART(CTimeLogServer,IID_ITimeLog,TimeLog)
    END_INTERFACE_MAP()

      四.在组件的构造和析构函数中对全局对象计数

    CTimeLogServer::CTimeLogServer()
    {
        ::AfxOleLockApp();
    }

    CTimeLogServer::~CTimeLogServer()
    {
        ::AfxOleUnlockApp();
    }

      五.为嵌套类实现IUnknown接口

    //为嵌套类而实现IUnknown接口
    STDMETHODIMP_(ULONG)
    CTimeLogServer::XTimeLog::AddRef()
    {
        METHOD_PROLOGUE(CTimeLogServer,TimeLog)
        return pThis->;ExternalAddRef();
    }

    STDMETHODIMP_(ULONG)
    CTimeLogServer::XTimeLog::Release()
    {
        METHOD_PROLOGUE(CTimeLogServer,TimeLog)
        return pThis->;ExternalRelease();
    }

    STDMETHODIMP
    CTimeLogServer::XTimeLog::QueryInterface(REFIID riid,void**ppvObj)
    {
        METHOD_PROLOGUE(CTimeLogServer,TimeLog)
        return pThis->;ExternalQueryInterface(&;riid,ppvObj);
    }

      说明:虽然CCmdTarget类已经实现了IUnknown接口,但是还必须通过上述代码来将嵌套类的IUnknown映射到CCmdTarget支持的IUnknown接口.METHOD_PROLOGUEH宏的两个参数分别是实现组件对象的类和实现接口的嵌套类。

    六.实现ItimeLog接口的方法OutputLog

      注意本组件的功能是往日志文件中输入日志.

      1. 在组件类中添加一个文件指针:

     // Attributes
     public:
     protected:
          FILE* m_logfile;

      2. 初始化和退出

      首先在CTimeLogServer的构造函数中进行一些初始化:

    CTimeLogServer::CTimeLogServer()
    {
        ::AfxOleLockApp();
        CTime TimeStamp = CTime::GetCurrentTime();
        CString FileName;
        FileName.Format(_T(";%s.log";),TimeStamp.Format(";%Y%m%d";));
        m_logfile = fopen(FileName,_T(";a";));
        if(m_logfile)
        {
            fprintf(m_logfile,_T(";# # # # # # # # # # # # # # # # # /n";));
            fprintf(m_logfile,_T(";开始于:%s";),(LPCTSTR)TimeStamp.Format(";%Y年%m月%d日%H:%M %S";));
            fprintf(m_logfile,_T(";/n";));
        }
    }
    //然后在析构函数中关闭文件
    CTimeLogServer::~CTimeLogServer()
    {
        ::AfxOleUnlockApp();
        if(m_logfile)
        {
            CTime TimeStamp = CTime::GetCurrentTime();
            fprintf(m_logfile,_T(";/n";));
            fprintf(m_logfile,_T(";结束于:%s";),(LPCTSTR)TimeStamp.Format(";%Y年%m月%d日%H:%M %S";));
        fprintf(m_logfile,_T(";/n";));
            fprintf(m_logfile,_T(";# # # # # # # # # # # # # # # # #/n";));
            fclose(m_logfile);
        }
    }

      3. 实现接口ITimeLog方法

    //实现接口ITimeLog方法
    STDMETHODIMP
    CTimeLogServer::XTimeLog::OutputLog(BSTR* varLogText)
    {
        METHOD_PROLOGUE(CTimeLogServer,TimeLog)
        if(pThis->;m_logfile)
        {
            CTime TimeStamp = CTime::GetCurrentTime();
            CString NowTime = TimeStamp.Format(";%Y年%m月%d日%H:%M:%S";);
            CString LogText((LPCWSTR)*varLogText);
    fprintf(pThis->;m_logfile,";/n%s/n%s/n%";,NowTime,LogText);
            return NOERROR;
        }
        else
        {
    AfxMessageBox(";没有日志文件!";);
            return S_FALSE;
        }
    }

      七.完善组件服务器

      在应用对象CTimeLogServerApp的 实现文件中,处理Instance()和ExitInstance()

    BOOL CTimeLogServerApp::InitInstance()
    {
        ::AfxOleLockApp();
        // Register all OLE server (factories) as running. This enables the
        // OLE libraries to create objects from other applications.
        COleObjectFactory::RegisterAll();

        return TRUE;
    }
    int CTimeLogServerApp::ExitInstance()
    {
        // TODO: Add your specialized code here and/or call the base class
      ::AfxOleUnlockApp();
        return CWinApp::ExitInstance();
    }

      第二节 客户程序

      使用COM组件服务器的客户程序关键步骤是:初始化COM库,创建组件对象并获取IUnknown接口指针,查询接口并使用,释放组件。

      #include ";ITimeLogServer.h";
      //初始化COM库,对组件实例化
        HRESULT hResult;
      IUnknown* pIUnknown;
        hResult = ::CoInitialize(NULL);
        if(FAILED(hResult))
        {
            ::AfxMessageBox(";不能初始化COM库!";);
            return FALSE;
        }

      //创建组件实例
      pIUnknown = NULL;
        hResult = ::CoCreateInstance(CLSID_TimeLogServer,NULL,
            CLSCTX_INPROC_SERVER,IID_IUnknown,(void**)&;pIUnknown);
        if(FAILED(hResult))
        {
            pIUnknown = NULL;
            ::AfxMessageBox(";不能创建TimeLog对象!";);
            return FALSE;
        }
      //查询接口并使用
      if(pIUnknown!=NULL)
            {
                ITimeLog* pITimeLog;
    HResult=pIUnknown->;QueryInterface(IID_ITimeLog,(void**)&;pITimeLog);
                if(FAILED(hResult))
                {
                ::AfxMessageBox(";不能获取接口ITimeLog!";);
                    pIUnknown->;Release();
                    return;
                }
                BSTR bstrLogText;
                bstrLogText = m_logtext.AllocSysString();
                CString text((LPCWSTR)bstrLogText);
                ::AfxMessageBox(text);

                if(FAILED(pITimeLog->;OutputLog(&;bstrLogText)))
                {
                    ::AfxMessageBox(";日志输出出错!";);
                    pITimeLog->;Release();
                    return;
                }
                pITimeLog->;Release();
                ::AfxMessageBox(";日志已经写入!";);
            }
        //释放组件
        pIUnknown->;Release();
        pIUnknown = NULL;
          ::CoUninitialize();

  • 相关阅读:
    SSD
    NMS---非极大值抑制
    检测评价函数 IOU
    Ground Truth
    耿建超英语语法---状语从句
    联合索引创建时候的排序规则
    order by limit的原理
    mysql事务四种隔离级别
    为什么 Redis 快照使用子进程
    MYSQL查询~ 存在一个表而不在另一个表中的数据
  • 原文地址:https://www.cnblogs.com/tyjsjl/p/2156101.html
Copyright © 2011-2022 走看看