项目中要给其它客户程序提供DLL做为接口,该项目是在.Net4.0平台下开发。终所周知.Net的各个版本之间存在着兼容性的问题,但是为了使用高版本运行平台的新特性,又不得不兼顾其它低版本平台客户程序的调用。为了解决这个问题尝试通过一个C++/CLI DLL对高版本的.Net DLL的接口加了一层包装,对外暴露C风格的接口给客户程序调用。
可支持的客户语言平台:
- VB 6.0
- VC++
- .Net 1.0/.Net 1.1
- .Net 2.0
- .Net 3.5
创建C# .Net4.0的类库
-
创建一个C#项目:Csharp
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Csharp { public class CsharpClass { public int DoTesting(int x, int y, string testing, out string error) { error = testing + " -> testing is ok."; return x + y; } } }
创建C++/CLI包装类库
-
创建项目C++/CLI项目:CsharpWrap
-
添加对Csharp的引用
-
CsharpWrap.h
// CsharpWrap.h #pragma once #include <windows.h> #include <string> using namespace System; using namespace Csharp; using namespace std; using namespace Runtime::InteropServices;
-
CsharpWrap.cpp
// This is the main DLL file. #include "stdafx.h" #include "CsharpWrap.h" extern "C" _declspec(dllexport) int DoTesting(int x, int y, char* testing, char* error) { try { CsharpClass ^generator = gcnew CsharpClass(); String^ strTesting = gcnew String(testing); String^ strError; int sum = generator->DoTesting(x, y,strTesting, strError); if(strError != nullptr) { char* cError = (char*)(Marshal::StringToHGlobalAnsi(strError)).ToPointer(); memcpy(error,cError,strlen(cError) + 1); } else { error = nullptr; } return sum; } catch(exception e) { memcpy(error,e.what(),strlen(e.what()) + 1); return -1; } }
-
项目输出和使用
- Csharp.dll
- CsharpWrap.dll
如果要调用CsharpWrap.dll必须保证Csharp.dll也被调用程序可见(即应该放在进程EXE文件同一目录下)
调用Demo代码
-
C++
HINSTANCE hInst= LoadLibrary(_T("CsharpWrap.dll")); if(hInst) { pfunc DoTesting = (pfunc)GetProcAddress(hInst,"DoTesting"); if(DoTesting) { char error[100]= {NULL}; int sum = DoTesting(1, 2, "Hello", error); //show testing results char strSum[8]; _itoa_s(sum,strSum,16); ::MessageBoxA(NULL, error, strSum, MB_OK); } else { ::MessageBoxA(NULL, "Get function fail.", "Fail", MB_OK); } //free library FreeLibrary(hInst); hInst = nullptr; } else { ::MessageBoxA(NULL, "Load dll fail.", "Fail", MB_OK); }
-
C#低版本.Net
导出函数
[DllImport("CsharpWrap.dll")]static extern int DoTesting(int x, int y, string testing, StringBuilder error);
调用
StringBuilder sb = new StringBuilder(200); int sum = DoTesting(1, 2, "Hellow", sb); MessageBox.Show(sb.ToString(), sum.ToString());
-
VB6.0
导出函数
Public Declare Function DoTesting Lib "CsharpWrap.dll" (ByVal x As Integer, ByVal y As Integer, ByVal testing As String, ByVal error As String) As Integer
调用
Dim error As String Dim testing As String Dim x As Integer Dim y As Integer Dim sum As Integer testing = "Hello" error = String(60000, vbNullChar) sum = DoTesting(x, y, testing, error)
常见问题及注意事项
- 平台工具集的问题
如果使用的是VS2010以上的版本编译出来的C++/CLI DLL可能会遇到缺少依赖等运行错误。
如果要支持XP系统工具集尽量用_xp结尾的
平台工具集的依赖DLL,把下面的或其它相应版本的依赖DLL放到目标机器的C:\WINDOWS\SYSTEM32下
-
- msvcp100.dll
- msvcr100.dll
- msvcp110.dll
- msvcr110.dll
- 字符集的问题,因为.Net默认用的字符集是Unicode,但是客户程序有可能是其它字符集,这样也可能会造成字符串在程序间的兼容问题。
可选择Unicode/Multi-Byte,根据不项目需求选择相应的字符集
代码内对不同字符集进行转换
从char* to 宽字符
wchar_t *GetWC(const char *c) { const size_t cSize = strlen(c)+1; wchar_t* wc = new wchar_t[cSize]; MultiByteToWideChar(CP_ACP,0,(const char *)c,int(cSize),wc,int(cSize)); return wc; }
String^ to Char*
char* cError = (char*)(Marshal::StringToHGlobalAnsi(strError)).ToPointer();
- 调用的C++/CLI DLL的时候传入参数的问题
C#调用的时候String参数对应的类型应该是StringBuilder,要注意StringBuilder的容量,默认是256个字符,如果返回的比较多的东西要注意初始化相应大小的容量。
- DLL多层嵌套的问题
如果用LoadLibrary加载DLL失败,可以尝试用LoadLibraryEx,同时保证所依赖的C#DLL放到进程EXE同级目录。
LoadLibraryEx("DLL绝对路径", NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
时间: 2024-10-10 17:42:57