萨博39战斗机:COM调用方法

来源:百度文库 编辑:九乡新闻网 时间:2024/10/04 03:46:52

一:动态调用:

早绑定就是在编译期已经确定了类型,编译后代码执行效率很高;晚绑定是在运行期才确定类型。晚绑定需要在运行期确定类型,所以效率比较低,但是带来了很大的灵活性。本人认为动态调用算是晚绑定的具体形式了,也就是不需要头文件和Lib,而调用dll里面的函数。
目前的基于ATL开发的插件体系软件多采用前者,即需要idl编译出的.h和_i.c文件,这种方式有时候比较死板,缺少动态机制。其实采用下面介绍的任何一种技术都可以构造出自己的插件体系,并且可以设计的很灵活。
以下是我所了解的三种动态调用方法(例子基于VS2005):
1、DLL函数调用:(获取函数地址)
假设DllFunc.dll里面有一个函数:int Add(int a, int b),在客户端调用步骤如下:

 //  加载动态库 
     hModule  =  LoadLibrary(_T( " DllFunc.dll " ));
     if (hModule)
     {
         //  获取函数地址 
         fnAdd  =  (LPFunc)GetProcAddress(hModule,  " Add " );
         if (fnAdd)
         {
             //  调用函数 
             iRet  =  fnAdd( 2 ,  4 );
            printf( " Result is %d " , iRet);
        } 
         //  释放动态库 
         FreeLibrary(hModule);
        hModule  =  NULL;
    }


2、COM组件功能调用(IDispatch的Invoke)
假设有一个组件,ProgID为“ATLFunc.MyMath.1”,有属性和接口方法如下:

interface IMyMath : IDispatch{
    [propget, id(1), helpstring("属性 Result")] HRESULT Result([out, retval] LONG* pVal);
    [propput, id(1), helpstring("属性 Result")] HRESULT Result([in] LONG newVal);
    [id(2), helpstring("方法Add")] HRESULT Add(LONG a, LONG b,[out, retval] LONG* pC);
};

在客户端调用步骤如下:

// 初始化COM库
    CoInitialize(NULL);
    // 根据ProgID创建组件
    hr = pIMyMath.CoCreateInstance(_T("ATLFunc.MyMath.1"));
    if(SUCCEEDED(hr))
    {
        // 获取Add函数的DISPID
        hr = pIMyMath->GetIDsOfNames(IID_NULL, &szMember, 1, LOCALE_USER_DEFAULT, &dispid);
        if(hr == S_OK)
        {
            pvarsIn = new CComVariant[2];
            pvarsIn[0].vt = VT_I4;
            pvarsIn[0].intVal = 2;
            pvarsIn[1].vt = VT_I4;
            pvarsIn[1].intVal = 4;
            DISPPARAMS dispParam = {pvarsIn, NULL, 2, 0 };
            // 调用Add函数
            VariantInit(&vtOut);
            hr = pIMyMath->Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &dispParam, &vtOut, NULL, NULL);
            if(SUCCEEDED(hr))
            {
                printf("%d", vtOut.intVal);
            }
            VariantClear(&vtOut);
            delete[]pvarsIn;
            pvarsIn = NULL;
        }
        // 其实如果我们知道接口方法或者属性的DISPID,可以直接通过DISPID来调用
        VariantInit(&vtOut);
        DISPPARAMS dispParam = {NULL, NULL, 0, 0 };
        hr = pIMyMath->Invoke(1, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYGET, &dispParam, &vtOut, NULL, NULL);
        if(SUCCEEDED(hr))
        {
            printf("%d", vtOut.intVal);
        }
        VariantClear(&vtOut);
    }
    // 在CoUninitialize之前释放COM组件,否则导致COM库卸载后释放组件会出问题
    pIMyMath = NULL;
    // 释放COM库
    CoUninitialize();


3、.NET组件功能调用:(反射技术)

假设一CLR类库程序集中有一个类MyMath,含有方法int Add(int a, int b),在客户端调用步骤如下:

try
    {
        // 加载程序集
        pAssembly = Assembly::Load(L"CLRFunc");
        // 获取程序集中CLRFunc.MyMath类型
        pType = pAssembly->GetType(L"CLRFunc.MyMath", true, true);
        // 获取方法信息
        pMI = pType->GetMethod(L"Add");
        // 创建CLRFunc.MyMath类型实例
        pObject = Activator::CreateInstance(pType);
        p[0] = 2;
        p[1] = 4;
        // 调用方法
        iRet = safe_cast(pMI->Invoke(pObject, p));
        System::Console::WriteLine(iRet);
    }
    catch (Exception^ exp)
    {
        System::Console::WriteLine(exp->Message);
    }


所谓的插件技术不过是在主程序上指定一套接口,所有遵循接口的可加载模块都是插件。主程序可以采用上面的方法去加载任意的dll,调用方法功能,只要满足就是插件,这样插件体系不再局限于COM接口级别,一个插件可以采用以上三种形式去实现。 

 

二:其他基本的方法:

 

void CUse1Dlg::OnBnClickedButton1()

{

 ::CoInitialize( NULL );

 

 IUnknown * pUnk = NULL;

 IFun* pFun = NULL;

 HRESULT hr;

 

 try

 {

  hr= ::CoCreateInstance(

  CLSID_Fun,

  NULL,

  CLSCTX_INPROC_SERVER, // 以进程内组件 DLL 方式加载

  IID_IUnknown,   // 想要取得 IUnknown接口指针

  (LPVOID *) &pUnk);

  if(FAILED( hr ) ) throw( _T("没注册吧?") );

 

  hr= pUnk->QueryInterface( // 从 IUnknown 得到其它接口指针

  IID_IFun,    // 想要取得 IFun 接口指针

  (LPVOID *)&pFun );

  if(FAILED( hr ) ) throw( _T("居然没有接口?") );

 

 long nSum;

  hr= pFun->Add( 1, 2, &nSum ); // IFun::Add()

  if(SUCCEEDED( hr ) )

  {

  CString sMsg;

  sMsg.Format( _T("1 + 2 = %d"), nSum );

  AfxMessageBox( sMsg );

  }

 

 BSTR s1 = ::SysAllocString( L"Hello" );

 BSTR s2 = ::SysAllocString( L" world" );

 BSTR s3 = NULL;

 

  hr= pFun->Cat( s1, s2, &s3 ); // IFun::Cat()

  if(SUCCEEDED( hr ) )

  {

  CString sMsg( s3 );

  AfxMessageBox( sMsg );

  }

 

  //IFun::Cat() 最后一个参数是 [out] 方向属性,因此需要调用者释放

  if(s3 ) ::SysFreeString( s3 );

 }

 catch( LPCTSTR lpErr )

 {

 AfxMessageBox( lpErr );

 }

 

 if(pUnk ) pUnk->Release();

 if(pFun ) pFun->Release();

 

 ::CoUninitialize();

}

 

// 包含使用 CComBSTR 需要的头文件

#include

 

void CUse1Dlg::OnBnClickedButton2()

{

 ::CoInitialize( NULL );

 

 IFun* pFun = NULL;

 HRESULT hr;

 

 try

 {

  hr= ::CoCreateInstance(

  CLSID_Fun,

  NULL,

  CLSCTX_INPROC_SERVER,

  IID_IFun,    // 不再经过IUnknown, 直接得到 IFun 接口指针

  (LPVOID *) &pFun);

  if(FAILED( hr ) ) throw( _T("没注册吧或 没有接口?"));

 

 long nSum;

  hr= pFun->Add( 1, 2, &nSum );

  if(SUCCEEDED( hr ) )

  {

  CString sMsg;

  sMsg.Format( _T("1 + 2 = %d"), nSum );

  AfxMessageBox( sMsg );

  }

 

 CComBSTR s1( "Hello" ); // 不再使用 API 方式操作 BSTR

 CComBSTR s2( " world" ); // 使用 CComBSTR 比较简单,并且

 CComBSTR s3;    // 最大的好处是,不用咱们自己来释放

 

  hr= pFun->Cat( s1, s2, &s3 );

  if(SUCCEEDED( hr ) )

  {

  CString sMsg( s3 );

  AfxMessageBox( sMsg );

  }

 }

 catch( LPCTSTR lpErr )

 {

 AfxMessageBox( lpErr );

 }

 

 if(pFun ) pFun->Release();

 

 ::CoUninitialize();

}

 

2

 

void CUse2Dlg::OnBnClickedButton3() //CComPtr 使用举例

{

 //COM 初始化以 AfxOleInit() 函数调用,放在了 CUse2App::InitInstance() 中了。

 

 CComPtr < IUnknown > spUnk;  // 定义 IUnknown 智能指针

 CComPtr < IFun > spFun;   // 定义 IFun 智能指针

 HRESULT hr;

 

 try

 {

  // 可以用 CLSID 启动组件,也可以用ProgID

  hr= spUnk.CoCreateInstance( CLSID_Fun );

  if(FAILED( hr ) ) throw( _T("没有注册组件吧?") );

 

  hr= spUnk.QueryInterface( &spFun );

  if(FAILED( hr ) ) throw( _T("居然没有接口?") );

 

 long nSum;

  hr= spFun->Add( 1, 2, &nSum );

  if(SUCCEEDED( hr ) )

  {

  CString sMsg;

  sMsg.Format( _T("1 + 2 = %d"), nSum );

  AfxMessageBox( sMsg );

  }

 

 CComBSTR s1( "Hello" ), s2( " world" ), s3;

  hr= spFun->Cat( s1, s2, &s3 );

  if(SUCCEEDED( hr ) )

  {

  CString sMsg( s3 );

  AfxMessageBox( sMsg );

  }

 }

 catch( LPCTSTR lpErr )

 {

 AfxMessageBox( lpErr );

 }

 

 // 智能接口指针的最大好处是,我们不用负责释放

}

 

void CUse2Dlg::OnBnClickedButton4() //CComQIPtr 使用举例

{

 CComPtr < IUnknown > spUnk;  // 智能指针 IUnknown

 CComQIPtr < IFun > spFun;  // 智能指针 IFun

 HRESULT hr;

 

 try

 {

  // 使用 ProgID 启动组件

  hr= spUnk.CoCreateInstance( L"Simple2.fun.1" );

  if(FAILED( hr ) ) throw( _T("没有注册吧?") );

 

 spFun = spUnk; // CComQIPtr 会帮我们自动调用QueryInterface

  if(!spFun ) throw( _T("居然没有接口?") ); // 成功与否可以判断非NULL

 

 long nSum;

  hr= spFun->Add( 1, 2, &nSum );

  if(SUCCEEDED( hr ) )

  {

  CString sMsg;

  sMsg.Format( _T("1 + 2 = %d"), nSum );

  AfxMessageBox( sMsg );

  }

 

 CComBSTR s1( "Hello" ), s2( " world" ), s3;

  hr= spFun->Cat( s1, s2, &s3 );

  if(SUCCEEDED( hr ) )

  {

  CString sMsg( s3 );

  AfxMessageBox( sMsg );

  }

 }

 catch( LPCTSTR lpErr )

 {

 AfxMessageBox( lpErr );

 }

 

}

 

void CUse2Dlg::OnBnClickedButton5()  // 不再经过 IUnknown

{

 CComQIPtr < IFun, &IID_IFun >spFun;  // 定义 IFun 智能指针

 HRESULT hr;

 

 try

 {

  hr= spFun.CoCreateInstance( L"Simple2.fun.1" );

  if(FAILED( hr ) ) throw( _T("没有注册组件或 没有找到接口"));

 

 long nSum;

  hr= spFun->Add( 1, 2, &nSum );

  if(SUCCEEDED( hr ) )

  {

  CString sMsg;

  sMsg.Format( _T("1 + 2 = %d"), nSum );

  AfxMessageBox( sMsg );

  }

 

 CComBSTR s1( "Hello" ), s2( " world" ), s3;

  hr= spFun->Cat( s1, s2, &s3 );

  if(SUCCEEDED( hr ) )

  {

  CString sMsg( s3 );

  AfxMessageBox( sMsg );

  }

 }

 catch( LPCTSTR lpErr )

 {

 AfxMessageBox( lpErr );

 }

}

 

3

 

void CUse3Dlg::OnBnClickedButton6()  // 智能指针的释放

{

 //::CoInitialize( NULL );  // 如果在这里进行 COM 初始化,要注意智能指针的释放

 

 CComQIPtr < IFun, &IID_IFun > spFun;

 

 HRESULT hr = spFun.CoCreateInstance( CLSID_Fun);

 ASSERT( SUCCEEDED( hr ) );

 // 为了简单起见,不再使用if 判断 HRESULT 了。IFun::Add() 也没有调用

 

 CComBSTR s1( "Hello" ), s2( "world" ), s3;

 hr =spFun->Cat( s1, s2, &s3 );

 ASSERT( SUCCEEDED( hr ) );

 CString sMsg( s3 );

 AfxMessageBox( sMsg );

 

// spFun->Release(); // 大错特错!!!

 //spFun.Release(); // 正解

 

 //::CoUninitialize();

}

 4

 

void CUse4Dlg::OnBnClickedButton7()  // #import 的使用

{

/**********************************************************

1. 在CUse4App::InitInstance()中使用 AfxOleInit() 进行初始化

2. 打开 stdafx.h 文件,插入 #import,后编译

   产生 .tlh 和 .tlh 的智能指针包装

3. #import 使用了 no_namespace 表示不使用命名空间

4. 智能指针的包装形式是:IxxxPtr,xxx 表示接口名

***********************************************************/

 IFunPtr spFun;

 HRESULT hr = spFun.CreateInstance(L"Simple2.fun.1" ); // 使用 ProgID

// HRESULT hr = spFun.CreateInstance(__uuidof( Fun ) ); // 使用 CLSID

 ASSERT( SUCCEEDED( hr ) );

 

 try

 {

 long nSum = spFun->Add( 1, 2 );

 

 CString sMsg;

 sMsg.Format( _T("1+2=%d"), nSum );

 AfxMessageBox( sMsg );

 

 _bstr_t sCat = spFun->Cat( _T("Hello"), _T("world") );

 AfxMessageBox( sCat );

 }

 catch( _com_error &e )

 {

  // 在这里可以取得详细的错误信息

  // 以后在介绍ISupportErrorInfo 接口时详细说明

// e.Description();

// e.ErrorMessage();

// e.ErrorInfo();

// ......

  e;// 由于没有使用 e, 加上这行只是为了取消编译警告

 AfxMessageBox( _T("Error") );

 }

}

 

 

 5

 

void CUse5Dlg::OnBnClickedButton8()

{

 // 由于在 stdafx.h 中 #import 的时候,没有使用no_namespace 因此要使用命名空间

 // 命名空间叫Simple2Lib ,这个名称是组件 IDL 文件 Library 指定的

 try

 {

  // 这次使用智能指针的构造函数启动组件,书写简单。

  // 但也有缺点,因为如果失败的话,不知道错误原因

 

// Simple2Lib::IFunPtr spFun( L"Simple2.fun.1" );    // ProgID 方式

 Simple2Lib::IFunPtr spFun( __uuidof(Simple2Lib::Fun) );  // CLSID 方式

 

 long nSum = spFun->Add( 1, 2 );

 

 CString sMsg;

 sMsg.Format( _T("1+2=%d"), nSum );

 AfxMessageBox( sMsg );

 

 _bstr_t sCat = spFun->Cat( _T("Hello"), _T("world") );

 AfxMessageBox( sCat );

 }

 catch( _com_error &e )

 {

  e;

 AfxMessageBox( _T("Error") );

 }

}