萨博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”,有属性和接口方法如下:
[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),在客户端调用步骤如下:
{
// 加载程序集
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
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") );
}
}