分享
 
 
 

连接 COM 与.NET 的桥梁(二)——COM 服务器的 P/Invoke 方式

王朝vc·作者佚名  2006-05-23
窄屏简体版  字體: |||超大  

连接 COM 与.NET 的桥梁(二)

COM 服务器的 P/Invoke 方式

作者:caeser2

下载源代码

一、COM 服务器 -- COM 客户端

这是传统的 COM 知识,如果对这部分内容不清楚,可以去看

杨老师 的个人专栏,那里有非常棒的教程,我就不在这里废话了^_^

我不细说可并不代表这部分不重要,恰恰相反,如果读者对这部分很熟悉,就会发现后面所有的内容在形式上几乎都是模仿传统的COM调用。二、COM 服务器 -- .net 客户端

嗯,这才是重点。下图是这部分的原理。每个COM对象都会有且只有一个运行库可调用包装(RCW)代理,而不管它有多少个引用。

在没有公开接口(或者根本就没有)的情况下

这种情况用到的操作就是P/Invoke。我们至少要知道如下二个内容:

DLL文件的名称

将要调用的函数的名称或者序号;

然后需要做如下二个步骤:

在.net程序中标识它,必须是静态的、外部的

C++得这样:extern "C";像调用普通函数那样调用它;

对于参数要注意:

如果是结构或类,注意内部成员必须定义为public,才能公开

可以应用一些属性来实现“个性化”,详见下面的“个性化”属性代码;

如果要调用的函数有很多,或者想将这个函数成为托管类的成员,可以使用包装类:

直接在现有类内声明 DLL 函数;

使函数相互隔离,易于查找,可以分别为每个 DLL 函数创建一个类;

为了形成逻辑分组并减少系统开销,可以将一组相关的 DLL 函数写进一个类;

我们一起来看示例代码,首先来个简单的调用步骤演示,调用Win32API提供的 MessageBox():

//1.先写好要用到的命名空间

using namespace System::Runtime::InteropServices;

typedef IntPtr HWND; //就是非托管类型 void * 啦,win32平台上是4个字节,所以也可以写成int

//2.利用DllImport属性(即DllImportAttribute属性类)"#import"导入DLL文件,并标识调用函数

[DllImport("user32", EntryPoint="MessageBoxA")]

//3.创建原型,请读者注意数据类型的变化

extern "C" int MsgBox(HWND hWnd,String* pText,String* pCaption,unsigned int uType);

//4.调用

MsgBox(this-Handle,"hello","hi",0);

//5.包装类这样写,很简单,就不写进提供下载的示例代码了^_^

public __gc class SDKMsgBox: {

public:

[DllImport("user32", EntryPoint="MessageBoxA")]

extern "C" int MsgBox(HWND hWnd,String* pText,String* pCaption,unsigned int uType);

.......

}

如果传递的值是数组、结构或者类,就没这么简单了,需要自定义封装(即Marshal,进行自定义类型转换)

//对于数组,只需定义一下封送方法即可,就不写入供下载的示例代码了

extern "C" void SendArray(

[MarshalAs(UnmanagedType::LPArray)]Array<int> list,

int length

);

/*

对于结构,比如,User32.dll中的这个函数

BOOL PtInRect(const RECT *lprc, POINT pt);

RECT和Point是两个结构

注意,下面Point声明为__value而非__gc,因为.net v1.1的封送处理会出现问题(可能是笔者失误,

也可能是.net v1.1的Bug),不能自动将托管指针所指向的内容复制到非托管堆中(不是指__box打包

功能哦),所以实际使用中没有像上例的参数(String *)那样使用托管指针。

*/

//StructLayout即StructLayoutAttribute属性类,是用来定义对象的内存布局的

//Sequential表示对象的成员按照定义的顺序进行内存布局

[StructLayout(LayoutKind::Sequential)]

public __value struct Point {

public:

int x;

int y;

};

//Explicit表示对象的成员按照FieldOffset(即FieldOffsetAttribute属性类)指定的位置进行内存布局

[StructLayout(LayoutKind::Explicit)]

public __gc struct Rect {

public:

[FieldOffset(0)] int left;//FieldOffset()中的数字是内存布局,

[FieldOffset(4)] int top;//一定不能写了,这里都是int,所以每次都+4

[FieldOffset(8)] int right;

[FieldOffset(12)] int bottom;

};

[DllImport("User32.dll")]

extern "C" bool PtInRect(const Rect& r, Point p);//第1个参数定义成托管则规定1级间接寻址要使用引用

/*

如果COM服务器方的参数里有char *text,最好如下定义属性

[StructLayout(LayoutKind::Sequential,CharSet=CharSet::Ansi)]

... ( ... , String *text);

其它的类型依此类推

*/

}

类的传递方法没什么好说的,自然是和结构的传递方法相同。但有一点要注意,传递类时通常要有至少1级间接寻址,即指针(和上例中的Rect一样)。

/*

比如,Kernel32.dll中的这个函数

void GetSystemTime(SYSTEMTIME* SystemTime);

把SYSTEMTIME看成类,结构和类本来就是“同根生”嘛^_^

*/

[StructLayout(LayoutKind::Sequential)]

public __gc class MySystemTime {

public:

unsigned short wYear;

unsigned short wMonth;

unsigned short wDayOfWeek;

unsigned short wDay;

unsigned short wHour;

unsigned short wMinute;

unsigned short wSecond;

unsigned short wMilliseconds;

};

[DllImport("Kernel32.dll")]

extern "C" void GetSystemTime(MySystemTime& st);

如果想使用回调函数,那就更麻烦了,需要用到委托/事件机制来接收消息。

using namespace System::Runtime::InteropServices;

//定义一个委托

__delegate bool CallBack(int hwnd, int lParam);

[DllImport("user32")]

extern "C" int EnumWindows(CallBack* x, int y); //参数CallBack从函数指针变成了委托,其实它们大同小异

//回调函数,在调试窗口显式窗口句柄

bool Report(int hwnd, int lParam) {

System::Diagnostics::Trace::WriteLine(hwnd.ToString(),"Window handle is:");

return true;

};

//使用

//实例化一个委托myCallBack

CallBack* myCallBack = new CallBack(this, &EnumReport::Report);

EnumWindows(myCallBack, 0); //将函数指针(实例化的委托)传给COM服务器,COM服务器会自动调用它返回结果

如果想使用回调接口或连接点,看清本节的标题啦,根本就没有接口,怎么做啊?呵呵。

好啦,P/Invoke 差不多能干的就这些啦,下面我罗列了一些有用的表格。

几个常用的 Win32 API DLL

可用的属性,通常使用 DllImportAttribute( [DllImport(...)] ) 来设置值

本节大多内容可以在MSDN2003以上版本的“使用非托管 DLL 函数”中找到。“个性”化封送处理(仅COM服务器--.net客户端)请参见“用平台调用封送数据”,平台调用即P/Invoke。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有