您的位置:首页 > 其它

使用COM方式实现不同语言之间的调用

2008-05-19 20:53 691 查看
转自某高人 :akirya的专栏 ,呵呵

原文出处:http://blog.csdn.net/akirya/archive/2008/02/17/2100501.aspx

---------------------------------正文:------------------------------

上一篇说道了使用DLL的方式实现混合使用,但是使用过程还有一些复杂,比如VB用使用字符串的一些操作就不得不使用了lstrlen这个API来完成.而且DLL的使用范围也不够广泛,而这次介绍如何使用COM方式进行多语言间调用,相对DLL方式这个容易使用.

按照惯例先使用C++的方式编写(我是使用VS2005)

先新建一个ATL工程,名字为ComCore点确定之后点完成,使用默认的工程配置.再添加一个类选择ATL Simple Object名字输入CComTest






点options,选上ConnectionPoints






点完成,就能看到向导生成的代码了.

切换到类视图(Class View) 之后选中IComTest右键,选择添加方法(Add Method).






输入函数名字参数类型点添加再点完成,只此已经成功添加一个方法

第二个方法添加如下,在选中参数类型的时候,将out选项选上.






第三个方法如下,除了将out选项选中,还将retval选项选中.






接下来在类视图里面选中_ICComTestEvents(在ComCoreLib中),右键选择添加方法,增加一个有两个参数的函数.






接下来选中CCComTest右键,添加->添加连接点(Add Connections Point)






将_ICComTestEvents这个增加到右边,如上图.点击完成

至此,我们已经将所需要的接口都添加上去.点编译即可.但我们只是提供了函数的接口,并没有实现,现在将实现添加上去.打开CComTest.cpp文件就会看到三个函数的空实现(我删掉了注释)

STDMETHODIMP CCComTest::OneFunction(VARIANT p_array)
{
return S_OK;
}

STDMETHODIMP CCComTest::Two(VARIANT* p_var)
{
return S_OK;
}

STDMETHODIMP CCComTest::Three(BSTR* p_ret)
{
return S_OK;
}

我们添加合适的代码上去先到CComTest.h给CCComTest增加一个私有的成员变量long len;在构造函数中初始化为0

#include<atlsafe.h>
#include<comutil.h>
#pragma comment(lib,"comsuppw.lib")
// CCComTest
STDMETHODIMP CCComTest::OneFunction(VARIANT p_array)
{
this->Fire_EventOne( _bstr_t("回调的哦") , p_array );//调用回调函数

if( (VT_INT==p_array.vt)|(VT_I4==p_array.vt) ) {//得到参数的内容,这里使用VARIANT ,当然也可以直接使用long类型来做参数
len = p_array.lVal;
return S_OK;
}else
return S_FALSE;
}

STDMETHODIMP CCComTest::Two(VARIANT* p_var)
{
SAFEARRAYBOUND bound[1]={ len };//传递一个数组,这里使用了SafeArray

CComSafeArray<BYTE> x( bound );
for( int i=0;i<len;i++){
x[i]=i;
}
p_var->vt = VT_ARRAY|VT_UI1;
x.CopyTo( &(p_var->parray) );
return S_OK;
}

STDMETHODIMP CCComTest::Three(BSTR* p_ret)
{
_bstr_t bstr=_bstr_t( _variant_t( len ) );//传递一个字符串类型
*p_ret = bstr.copy( true );
return S_OK;
}

这样整个测试的COM就算完成了,点编译即可编译出一个DLL,IDE会自动将这个DLL注册

C++调用方法(for VC)

还是在刚才的工程里面,增加一个win32 的console工程,使用默认的工程选项.

在stdafx.h中添加如下代码

#import "..ComCoreDebugComCore.dll" no_namespace //这里的路径COM的dll的路径.

#include <atlbase.h>
#include <atlcom.h>

一下是UseCom.cpp的代码,注意前面的#include"stdafx.h"别删掉

extern GUID const __IComTestEvents;
static _ATL_FUNC_INFO FunInfo = {CC_STDCALL, VT_EMPTY, 2 ,VT_BSTR,VT_VARIANT};

class Event:public IDispEventSimpleImpl<1, Event, &__IComTestEvents >
{
public:
BEGIN_SINK_MAP(Event)
SINK_ENTRY_INFO(1,__IComTestEvents,0x1,OnEventOne,&FunInfo)
END_SINK_MAP()
STDMETHOD(OnEventOne)( BSTR P1, VARIANT P2);
};

int _tmain(int argc, _TCHAR* argv[])
{
::CoInitialize(NULL);
{
ICComTestPtr ptr;
ptr.CreateInstance( __uuidof(CComTest) );

Event ev;
ev.DispEventAdvise( ptr );

_variant_t len( 256 );
ptr->OneFunction( len );
VARIANT var;
ptr->Two( &var );
SAFEARRAY* p = var.parray;
_bstr_t bstr = ptr->Three();
}
::CoUninitialize();
return 0;
}

GUID const __IComTestEvents = { 0xD1D8E806,0x27B2,0x44E5
,{0xBA,0x8D,0x6D,0x5A,0xD4,0xF3,0xA3,0x06} };

STDMETHODIMP Event::OnEventOne( BSTR P1, VARIANT P2)
{
wchar_t sz[128]={0};
wsprintf( sz, _T("%s,%d") , P1 , P2.lVal );
MessageBoxW( 0 ,sz, 0, 0);
return S_OK;
}

编译运行就能看到调用的结果如何了,其中的__IComTestEvents数值是在ComTest工程idl文件中对应的数值. 可以在COM工程的IDL文件中找到,我是为了方便,直接将数值写过来的.

Delphi调用方法

新建一个控制台工程,然后在工程菜单(Porject)中选择导入类型库(Import Type Lib)

在列表框中找到ComTest 1.0 Type Lib(version 1.0)选择Create Unit

之后将将一下代码添加到工程里面,注意uses的时候别改变ComCoreLib_TLB 的路径,这是我机器上对应的路径.

program Project2;

{$APPTYPE CONSOLE}

uses
SysUtils,
ComCoreLib_TLB in '....ImportsComCoreLib_TLB.pas',ActiveX,Variants;

type
EventClass = class(TObject)
public
procedure EventOne(AS ender: TObject;const P1: WideString; P2: OleVariant);
end;

var
t:ICComTest;
p_ array:OleVariant;
p_ var:OleVariant;
i:integer;
c: array[0..254] of byte;
ev:TCComTest;
ec:EventClass;
{ EventClass }

procedure EventClass.EventOne(AS ender: TObject;const P1: WideString; P2: OleVariant);
begin
writeln( 'event', P1 ,' ' , P2 );
end;

begin
CoInitialize(nil);
t := CoCComTest.Create;

{设置回调}
ev := TCComTest.Create( nil );
ev.ConnectTo( t );
ec := EventClass.Create;
ev.OnEventOne := ec.EventOne;

p_ array := 255;
VarAsType( p_ array , varLongWord );
t.OneFunction( p_ array );
t.Two( p_ var );

writeln( VarArrayDimCount( p_ var ) );
for i:=VarArrayLowBound( p_ var , 1 ) to VarArrayHighBound( p_ var , 1 ) do
begin
c[i] := p_ var[i];
end;
writeln( t.Three );

readln;
CoUninitialize;
end.

其中代码中CoCComTest.Create;TCComTest.Create( nil );ICComTest;TCComTest;都是在生成的ComCoreLib_TLB 中寻找到的.

VB6调用方法

VB6的方法是最简单的了,project->reference,然后选择ComTest 1.0 Type Lib,然后就能在工程里面使用了.先新建一个模块

ComEventClass.模块代码如下

Dim WithEvents obj As ComCoreLib.CComTest 'WithEvents只能在class模块中使用
Option Explicit
Dim Number As Long
Dim buf As Variant
Public Sub test()
Number = 255
Set obj = CreateObject("ComCore.CComTest.1")
obj.OneFunction (Number)
Call obj.Two(buf) ' obj.Two buf 也可以 但obj.Two(buf)就不行
MsgBox "value" & buf(5)
MsgBox (obj.Three())
End Sub
Private Sub obj_EventOne(ByVal P1 As String, ByVal P2 As Variant)
MsgBox P1 & P2
End Sub

调用方法如下

Dim a As ComEventClass
Public Sub Main()
Set a = New ComEventClass
a.test
End Sub

C的调用方法

C的方法是最复杂的方法了,C++还好利用ATL的类能少写很多的代码.但C只能自己来实现,
尤其是连接点部分,需要自己完成这一些接口.不过这样的好处是对COM的让我连接点部分更加熟悉.首先需要将编写COM的时候所生成的两个文件拷贝过来ComCore.h和ComCore_i.c这里边包含了C调用的时候所需要的接口.将接口文件添加到工程里面,一下是工程的代码(扩展名为C),如果是VC的话,注意修改编译选项将编译器设置为C的,

#include<stdio.h>
#include<tchar.h>
#include<windows.h>
#define COBJMACROS //这个宏也是要定义的,并且一定在ComCore.h之前,
//这样就能够使用ICComTest_OneFunction这样的宏来调用的.
#include"ComCore.h"
void call();

int main(int argc, char * argv[])
{
CoInitialize(NULL);
call();
CoUninitialize();
return 0;
}
HRESULT STDMETHODCALLTYPE QueryInterface(_ICComTestEvents * This,REFIID riid,void **ppvObject)
{
*ppvObject = (void*)This;//这是重要的一点,COM会通过这个函数得到_ICComTestEvents的函数指针表.
//之前是IUnkonwn,因为这是连续的一个空间,所以直接返回首地址即可.
return S_OK;
}
ULONG STDMETHODCALLTYPE AddRef(_ICComTestEvents * This)
{
return 1;//栈上变量,返回任意值都可以.自己保证生存周期
}
ULONG STDMETHODCALLTYPE Release(_ICComTestEvents * This)
{
return 1;//栈上变量,返回任意值都可以.自己保证生存周期
}

HRESULT STDMETHODCALLTYPE Invoke (_ICComTestEvents * This,DISPID dispIdMember,REFIID riid,LCID lcid,WORD wFlags,
/* [out][in] */ DISPPARAMS *pDispParams,
/* [out] */ VARIANT *pVarResult,
/* [out] */ EXCEPINFO *pExcepInfo,
/* [out] */ UINT *puArgErr)
{
if( 1 == dispIdMember ){//dispIdMember函数调用序号,这个可以在idl文件中看到
wchar_t sz[128]={0};
BSTR p1 = pDispParams->rgvarg[1].bstrVal;
long p2 = pDispParams->rgvarg[0].lVal;

wsprintf( sz, _T("%s,%d") , p1 , p2 );
MessageBoxW( 0 ,sz, 0, 0);
}
return S_OK;
}

void call()
{
VARIANT len={VT_EMPTY};
VARIANT var={VT_EMPTY};
ICComTest* pComTest = 0;
IConnectionPointContainer *pCPC = 0;
IConnectionPoint* pCP= 0 ;
_ICComTestEvents* pEvent = 0;
SAFEARRAY* pArray = 0 ;
BSTR bstr = 0;
DWORD dwCookie=0;
_ICComTestEventsVtbl _pFun={QueryInterface,AddRef,Release,0,0,0,Invoke};//初始化连接点对象的成员函数
//其中前三个和最后一个Invoke是必须的,COM对象会调用前三个进行接口查询,调用最后一个进行时间通知
_ICComTestEvents _event={ &_pFun };//初始化连接点对象

CoCreateInstance( &CLSID_CComTest , NULL , CLSCTX_ALL , &IID_ICComTest , (void**)&pComTest );//得到ICComTest接口

ICComTest_QueryInterface( pComTest , &IID_IConnectionPointContainer , (void**)&pCPC );//得到连接点容器
IConnectionPointContainer_FindConnectionPoint( pCPC , &DIID__ICComTestEvents , &pCP );//得到连接点
IConnectionPoint_Advise(pCP , (IUnknown *)&_event , &dwCookie );//挂接连接点

len.vt = VT_I4;
len.lVal = 250;
ICComTest_OneFunction( pComTest , len );//函数调用,激发连接点事件

ICComTest_Two( pComTest ,&var );
pArray = var.parray;
ICComTest_Three( pComTest , &bstr);
SysFreeString( bstr );
IConnectionPoint_Unadvise( pCP , dwCookie );//断开连接点

IConnectionPointContainer_Release( pCPC );//释放资源
IConnectionPoint_Release( pCP );
ICComTest_Release( pComTest );
}

附注
COM工程中的IDL文件

// ComCore.idl : IDL source for ComCore
//

// This file will be processed by the MIDL tool to
// produce the type library (ComCore.tlb) and marshalling code.

import "oaidl.idl";
import "ocidl.idl";

[
object,
uuid(EC7A5E88-BD84-4C7A-989B-F5724E3BD3B8),
dual,
nonextensible,
helpstring("ICComTest Interface"),
pointer_default(unique)
]
interface ICComTest : IDispatch{
[id(1), helpstring("method OneFunction")] HRESULT OneFunction(VARIANT p_array);
[id(2), helpstring("method Two")] HRESULT Two([out] VARIANT* p_var);
[id(3), helpstring("method Three")] HRESULT Three([out,retval] BSTR* p_ret);
};
[
uuid(66DE6EF7-034D-4691-A65B-C8CE496D0644),
version(1.0),
helpstring("ComCore 1.0 Type Library")
]
library ComCoreLib
{
importlib("stdole2.tlb");
[
uuid(D1D8E806-27B2-44E5-BA8D-6D5AD4F3A306),
helpstring("_ICComTestEvents Interface")
]
dispinterface _ICComTestEvents
{
properties:
methods:
[id(1), helpstring("method EventOne")] HRESULT EventOne(BSTR p1, VARIANT p2);
};
[
uuid(6E99DDF6-9BE7-4CC4-A754-E4618C730901),
helpstring("CComTest Class")
]
coclass CComTest
{
[default] interface ICComTest;
[default, source] dispinterface _ICComTestEvents;
};
};
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: