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

简介:
转载:VC知识库  http://www.vckbase.com/document/viewdoc/?id=1622


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


作者:caeser2

下载源代码

一、COM 服务器 --> COM 客户端
这是传统的 COM 知识,如果对这部分内容不清楚,可以去看 杨老师 的个人专栏,那里有非常棒的教程,我就不在这里废话了^_^
我不细说可并不代表这部分不重要,恰恰相反,如果读者对这部分很熟悉,就会发现后面所有的内容在形式上几乎都是模仿传统的COM调用。

二、COM 服务器 --> .net 客户端
嗯,这才是重点。下图是这部分的原理。每个COM对象都会有且只有一个运行库可调用包装(RCW)代理,而不管它有多少个引用。


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



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

  1. DLL文件的名称
  2. 将要调用的函数的名称或者序号;
    然后需要做如下二个步骤:
    • 在.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。


本文转自peterzb博客园博客,原文链接:http://www.cnblogs.com/peterzb/archive/2009/07/29/1534222.html,如需转载请自行联系原作者。

目录
相关文章
|
13天前
|
监控 网络安全 调度
Quartz.Net整合NetCore3.1,部署到IIS服务器上后台定时Job不被调度的解决方案
解决Quartz.NET在.NET Core 3.1应用中部署到IIS服务器上不被调度的问题,通常需要综合考虑应用配置、IIS设置、日志分析等多个方面。采用上述策略,结合细致的测试和监控,可以有效地提高定时任务的稳定性和可靠性。在实施任何更改后,务必进行充分的测试,以验证问题是否得到解决,并监控生产环境的表现,确保长期稳定性。
27 1
|
17天前
|
网络协议 Unix Linux
一个.NET开源、快速、低延迟的异步套接字服务器和客户端库
一个.NET开源、快速、低延迟的异步套接字服务器和客户端库
|
16天前
|
IDE 网络安全 开发工具
IDE之vscode:连接远程服务器代码(亲测OK),与pycharm链接服务器做对比(亲自使用过了),打开文件夹后切换文件夹。
本文介绍了如何使用VS Code通过Remote-SSH插件连接远程服务器进行代码开发,并与PyCharm进行了对比。作者认为VS Code在连接和配置多个服务器时更为简单,推荐使用VS Code。文章详细说明了VS Code的安装、远程插件安装、SSH配置文件编写、服务器连接以及如何在连接后切换文件夹。此外,还提供了使用密钥进行免密登录的方法和解决权限问题的步骤。
144 0
IDE之vscode:连接远程服务器代码(亲测OK),与pycharm链接服务器做对比(亲自使用过了),打开文件夹后切换文件夹。
|
17天前
|
IDE 网络安全 开发工具
IDE之pycharm:专业版本连接远程服务器代码,并配置远程python环境解释器(亲测OK)。
本文介绍了如何在PyCharm专业版中连接远程服务器并配置远程Python环境解释器,以便在服务器上运行代码。
127 0
IDE之pycharm:专业版本连接远程服务器代码,并配置远程python环境解释器(亲测OK)。
|
24天前
|
存储 网络协议 Java
【网络】UDP回显服务器和客户端的构造,以及连接流程
【网络】UDP回显服务器和客户端的构造,以及连接流程
48 2
|
14天前
|
Apache 数据中心 Windows
将网站迁移到阿里云Windows系统云服务器,访问该站点提示连接被拒绝,如何处理?
将网站迁移到阿里云Windows系统云服务器,访问该站点提示连接被拒绝,如何处理?
|
15天前
|
弹性计算 安全 Windows
通过远程桌面连接Windows服务器提示“由于协议错误,会话将被中断,请重新连接到远程计算机”错误怎么办?
通过远程桌面连接Windows服务器提示“由于协议错误,会话将被中断,请重新连接到远程计算机”错误怎么办?
|
15天前
|
存储 弹性计算 安全
阿里云第七代云服务器ECS性能、适用场景与价格参考
阿里云第七代云服务器ECS(Elastic Compute Service)作为阿里云最新一代的高性能计算产品,凭借其基于最新硬件架构和虚拟化技术的全面升级,在计算能力、存储性能、网络传输速度以及灵活性等多个方面实现了显著提升。这一代云服务器旨在为用户提供更为强大、稳定且可定制的云端基础设施服务,广泛适用于从基础的Web托管到复杂的高性能计算等多种应用场景。
|
14天前
|
弹性计算 网络安全
阿里云国际OpenAPI多接口快速管理ECS服务器教程
阿里云国际OpenAPI多接口快速管理ECS服务器教程
|
3天前
|
存储 弹性计算 NoSQL
"从入门到实践,全方位解析云服务器ECS的秘密——手把手教你轻松驾驭阿里云的强大计算力!"
【10月更文挑战第23天】云服务器ECS(Elastic Compute Service)是阿里云提供的基础云计算服务,允许用户在云端租用和管理虚拟服务器。ECS具有弹性伸缩、按需付费、简单易用等特点,适用于网站托管、数据库部署、大数据分析等多种场景。本文介绍ECS的基本概念、使用场景及快速上手指南。
17 3