和.NET Framework
一样,.NET Compact Framework
也提供了平台调用P/Invoke
功能以支持托管代码调用驻留于 DLL
中的非托管函数。关于.NET Compact Framework下p/Invoke的使用
详细讨论,请见
http://blog.csdn.net/YanChaoChao/archive/2007/07/17/1696325.aspx
通过P/Invoke
我们可以充分利用已有的非托管资源,使用非托管的系统API
函数,以弥补.NET Compact Framework
的不完备性。因此我们可以使用C++
来编写非托管的DLL
函数,然后在借助通过P/Invoke
在C#
中进行调用。vs2005
提供了一个很好的集成环境,我们可以使用一个解决方案同时管理托管的C#
项目和非托管的C++
项目。下面介绍使用vs2005
创建C++
和C#
混合项目的方法。
1、
首先创建一个C#
智能设备项目。如智能设备-Pockent PC 2003
-设备应用程序,假定解决方案名为“MixedSolution
”,项目名为“DeviceApplication1
”。
2、
添加C++
智能设备项目。在解决方案“MixedSolution
”中添加一个新的项目,使用C++
创建相同平台(
如Pockent PC 200)
的智能设备Win32
或MFC
项目,假定项目名为“CppProject
”。注意CppProject
必须是Win32
或MFC
的DLL
项目,因为我们需要使用P/Invoke
功能实现由C#
调用C++
项目的DLL
。在C++
项目中编写需要的函数处理后,对要导出的函数需要进行导出定义,该函数将被C#
的P/Invoke
调用,只有被正确导出的函数才能被P/Invoke
识别。这里值得注意的只有使用修饰符extern “C” _declspec(dllexport)
修饰的函数才能被P/Invoke
调用。在该修饰符中_declspec(DLLexport)
表示输出,即导出函数的定义;extern “C”
表示该函数使用C
编译方式,可以被C
调用,P/Invoke
只能调用使用这种方式编译的函数。
我们可以定义如下符号:
#define
DLLAPI extern "C" __declspec(dllexport)
定义了该符号后,可以使用它来修饰要导出的函数,如声明函数MyFunction
:
DLLAPI
int MyFunction(int, int);
声明了该函数后,在函数的定义部分使用或不使用DLLAPI
修饰都是被允许的。
int
MyFunction(int, int) //
声明了函数后,在定义部分可以不加修饰符
{
int ret = 0;
//...
处理
return ret;
}
也可以只给函数定义,而省略声明部分,这时当然就得加上修饰:
DLLAPI
int MyFunction(int, int) //
只给出函数定义,必须加上修饰
{
int ret = 0;
//...
处理
return ret;
}
在
extern
"C" __declspec(dllexport)
修饰符中
我们知道,DLL
本身不仅可以导出函数,还可以导出变量和类,但由于P/Invoke
只能导入DLL
中函数的定义,因此这里只关注
函数的导出。
3、
使用DllImport
导入函数定义。在C#
项目
“DeviceApplication1
”添加一个包装类,使用DllImport
导入“CppProject
”项目的导出函数。
internal
class Wrapper
{
[DllImport("CppProject.dll")]
internal static extern int MyFunction(int k1, int k2);
}
4、
修改项目配置实现混合编译。前面的过程只是在一个解决方案下建立了C#
项目和C++
项目,这两个项目物理上没有进行关联,因此我们必须先编译C++
项目,生成"CppProject.dll"
,然后拷贝该文件到设备上,再运行C#
项目时才能通过P/Invoke
调用该文件中的导出函数。如果对C++
项目进行了修改,必须重复以上过程,非常地麻烦。利用vs2005
的集成管理特点,对项目配置进行一下修改,我们就可以在两个项目建立关联。
首先修改C++
项目的输出路径,在项目属性的[
配置属性]
-[
常规]
-[
输出目录]
项下,将输出目录改为“$(SolutionDir)/ DeviceApplication1
”,即输出到C#
项目所在目录。然后生成一下C++
项目,这时在C#
项目所在目录下会生成“CppProject.dll
”文件,将该文件添加到C#
项目中,并在属性中修改[
复制到输出目录]
为“如果较新则复制”。最后在解决方案的项目依赖项中,设置项目“DeviceApplication1
”依赖于项目“CppProject
”。
通过这样的配置,在启动项目的调试(F5)
时,会先生成C++
项目,即输出“CppProject.dll
”文件到C#
项目所在目录,然后再生成C#
项目。在生成C#
项目的过程中,会检查CppProject.dll
是否被更新,如果被更新,则部署到设备上。这样修改了C++
项目后同样可以执行启动调试来进行整体调试,不需要再手动去单独编译C++
项目,以及复制DLL
文件了。
5、 C++DLL
项目的调试。vs2005
提供了多种调试模式,可以使用本机EXE
程序来对DLL
进行调试,也可以使用托管 EXE
中对DLL
进行调试。这里的DLL
项目最终是要被托管C#
调用的,因此我们使用由托管C#
项目创建的托管 EXE
来对C++
项目DLL
进行调试。
首先设置C++DLL
项目为启动项目,并将其项目属性的[
配置属性]
-[
调试]
-[
远程可执行文件]
项改为C#
项目输出的EXE
程序名,如%CSIDL_PROGRAM_FILES%/MixedSolution/DeviceApplication1.exe
,注意该EXE
文件是在设备上的路径而不是在本机的路径。该EXE
程序名是由C#
项目的[
输出文件夹] + [
程序集名称]
确定。
按照以上创建智能设备的C++
和C#
混合项目的方法,我创建了一个混合项目应用,为C#
应用程序增加等待光标(等待动画)的功能,以向用户表明程序正在处理,如下图:
设置等待光标可以使用Windows CE
的API
函数SetCursor(LoadCursor(NULL, IDC_WAIT));
但实际上IDC_WAIT
是一个宏,在展开后等于(LPWSTR)((DWORD)((WORD)(32514)))
,因此IDC_WAIT
是无法直接在C#
下使用的,因而要在C#
下直接使用这个API
函数是非常困难的任务。而通过一个DLL
项目间接的使用这条API
则非常方便。DLL
项目向外导出函数SetWaitCursor
,这个函数不使用任何参数,可以方便被导入到C#
中。这个函数的定义如下:
void
SetWaitCursor(void)
{
hCursor = SetCursor(LoadCursor(NULL, IDC_WAIT));
}
相应的C#
的导入定义如下:
internal
class Wrapper
{
[DllImport("W32DLL.dll")]
internal static extern void SetWaitCursor();
}
当然,我们还需要一个恢复光标状态的函数,也使用这种方式进行定义。这里给出全部源码,有兴趣的朋友可以下载。代码在vs2005+ppc2003
模拟器下调试通过。
源码:http://dl2.csdn.net/down4/20070718/18001510326.rar
源码:http://dl2.csdn.net/down4/20070718/18001510326.rar