背景
在Windows Mobile和Wince(Windows Embedded CE)产品开发中,有时候会使用C++封装一些共用代码,例如在我们项目中使用了C++封装了一个对USB通信的公开代码库,这些共用代码编译成静态库,其他模式使用的时候,只需要include头文件,链接lib库就可以了,但是.NET Compact Framework的程序没有办法使用C++编译的静态库,所以产生封装Native DLL提供给.NET Compact Framework程序调用的需求。
简介
本文讲述在Windows Mobile和Wince(Windows Embedded CE)下如何封装Native DLL提供给.NET Compact Framework进行调用。
可选方案
在.NET framework下可以把C++编译成托管的DLL,这样.NET的程序可以直接调用这个DLL了,但是在.NET Compact Framework下,不支持managed c++,所以只能封装成Native的DLL,然后.NET Compact Framework P/Invoke该Native DLL。 封装Native的DLL有两个可选的方案。
方案一: .DEF文件
使用.DEF文件,.DEF文件可以定义输出接口
LIBRARY NativeLib
EXPORTS
Insert @1
Delete @2
Member @3
Min @4
上面的.def文件定义了四个输出接口。如果使用.def文件,需要在编译选项进行配置,如下图:
使用.def文件麻烦的地方是每次更新接口都要手工更新.def文件,但是使用.def文件有一个好处是当dll发生更新以后,如果定义的ordinal没有发生改变的话,调用方不需要重新链接程序就可以使用新的DLL,Windows Mobile的今日组件就是使用.def文件进行定义的,需要实现ordinal为240和241两个接口:
InitializeCustomItem @240 NONAME
CustomItemOptionsDlgProc @241 NONAME
这样shell32进程就可以加载实现了这两个接口的DLL,不需要重新连接来支持新的组件。
方案二: __declspec(dllexport)
我个人偏向于使用__declspec(dllexport)的方案,因为P/Invoke不存在调用方重新链接的问题,所以我偏向于使用__declspec(dllexport)的方案。
使用__declspec(dllexport)的方案,在需要输出的函数加入__declspec(dllexport)。这样该函数就会生成输出表。
#define EXPORT_API __declspec(dllexport)
EXPORT_API BOOL OpenIndicator()
{
return TRUE;
}
例如上述定义生成以下输出。使用Dumpbin /exports进行查看。
Microsoft (R) COFF/PE Dumper Version 9.00.30729.01
Copyright (C) Microsoft Corporation. All rights reserved.
Dump of file NativeLib.dll
File Type: DLL
Section contains the following exports for NativeLib.dll
00000000 characteristics
4AF757EA time date stamp Sun Nov 08 10:44:42 2009
0.00 version
1 ordinal base
1 number of functions
1 number of names
ordinal hint RVA name
1 0 0000BA30 ?OpenIndicator@@YAHXZ = ?OpenIndicator@@YAHXZ (int __c
decl OpenIndicator(void))
Summary
1000 .data
2000 .pdata
2000 .rdata
2000 .reloc
19000 .text
有没有发现输出是?OpenIndicator@@YAHXZ 而不是 OpenIndicator。
如果.NET Compact Framework的调用方如下:
[System.Runtime.InteropServices.DllImport("DeviceCommsLib.dll", SetLastError = true]
private static extern bool OpenIndicator();
程序执行的时候会抛出找不到入口点的异常,如下图:
原因是生成输出的时候使用C++的命名输出了?OpenIndicator@@YAHXZ 而不是 OpenIndicator。如果要解决这个问题,有两个办法:方法一,在调用方指定入口点:
[System.Runtime.InteropServices.DllImport("DeviceCommsLib.dll", SetLastError = true, EntryPoint = "?OpenIndicator@@YAHXZ")]这个办法不好,因为调用方需要使用Dumpbin /exports查看输出入口点。
private static extern bool OpenIndicator();
方法二,增加extern "C",如下:
#define EXPORT_API __declspec(dllexport)
extern "C" {
EXPORT_API bool OpenIndicator()
{
return true;
}
}
这样使用了C的命名,使用Dumpbin /exports查看输出如下:
Microsoft (R) COFF/PE Dumper Version 9.00.30729.01
Copyright (C) Microsoft Corporation. All rights reserved.
Dump of file NativeLib.dll
File Type: DLL
Section contains the following exports for NativeLib.dll
00000000 characteristics
4AF75999 time date stamp Sun Nov 08 10:51:53 2009
0.00 version
1 ordinal base
1 number of functions
1 number of names
ordinal hint RVA name
1 0 0000BA30 OpenIndicator = OpenIndicator
Summary
1000 .data
2000 .pdata
2000 .rdata
2000 .reloc
19000 .text
输出接口变成OpenIndicator 了。
总结
总结一下,如果有Native C++的共用静态库需要提供给.NET Compact Framework调用,首先新建一个win32的DLL,把改静态库链接到这个DLL里面,然后定义DLL通过extern "C" __declspec(dllexport) 来输出出接口,最后在.NET Compact Framework程序就可以通过P/Invoke来调用原先在静态库中的功能模块来。
关于P/Invoke,我原先写过些文章可以参考。
.NET Compact Framework 下Win32 API P/Invoke 的使用
本文转自Jake Lin博客园博客,原文链接:http://www.cnblogs.com/procoder/archive/2009/11/09/Windows_Mobile_Native_DLL.html,如需转载请自行联系原作者