Windows Mobile和Wince(Windows Embedded CE)下如何封装Native DLL提供给.NET Compact Framework进行调用

背景

在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文件,需要在编译选项进行配置,如下图:

native_dll2

使用.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();

程序执行的时候会抛出找不到入口点的异常,如下图:

native_dll1

原因是生成输出的时候使用C++的命名输出了?OpenIndicator@@YAHXZ 而不是 OpenIndicator。如果要解决这个问题,有两个办法:方法一,在调用方指定入口点:

[System.Runtime.InteropServices.DllImport("DeviceCommsLib.dll", SetLastError = true, EntryPoint = "?OpenIndicator@@YAHXZ")]
private static extern bool OpenIndicator();
这个办法不好,因为调用方需要使用Dumpbin /exports查看输出入口点。

 

 

 

方法二,增加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 的使用

开发P/Invoke的工具与Website

posted @ 2009-11-09 14:23  Jake Lin  阅读(4045)  评论(14编辑  收藏  举报