非托管C++通过C++/CLI包装调用C# DLL
项目中要给其它客户程序提供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) + );
}
else
{
error = nullptr;
} return sum;
}
catch(exception e)
{
memcpy(error,e.what(),strlen(e.what()) + );
return -;
}
}
项目输出和使用
- 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[]= {NULL};
int sum = DoTesting(, , "Hello", error);
//show testing results
char strSum[];
_itoa_s(sum,strSum,);
::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();
int sum = DoTesting(, , "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(, 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)+;
wchar_t* wc = new wchar_t[cSize];
MultiByteToWideChar(CP_ACP,,(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);
转载请注明出处:http://www.cnblogs.com/keitsi/p/5554110.html
非托管C++通过C++/CLI包装调用C# DLL的更多相关文章
- [.net 面向对象程序设计进阶] (8) 托管与非托管
本节导读:虽然在.NET编程过程中,绝大多数内存垃圾回收由CLR(公共语言运行时)自动回收,但也有很多需要我们编码回收.掌握托管与非托管的基本知识,可以有效避免某些情况下导致的程序异常. 1.什么是托 ...
- .net 资源释放(托管资源和非托管资源)
1.托管资源 像int.float.DateTime等都是托管资源:net中80%的资源都是托管资源: 托管资源的回收通过GC(垃圾回收器)自动释放分配给该对象的内存,但无法预测进行垃圾回收的时间,我 ...
- CLR回收非托管资源
一.非托管资源 在<垃圾回收算法之引用计数算法>.<垃圾回收算法之引用跟踪算法>和<垃圾回收算法之引用跟踪算法>这3篇文章中,我们介绍了垃圾回收的一些基本概念和原理 ...
- C#中的托管与非托管
在.net 编程环境中,系统的资源分为托管资源和非托管资源. 字面理解托管,就是托付个别人管理,要的是结果,具体怎么完成的我并不关心,就像某些'牛逼'的老板“我只要结果”那样. 在.NET FRAME ...
- .net非托管资源的回收
释放未托管的资源有两种方法 1.析构函数 2.实现System.IDisposable接口 一.析构函数 构造函数可以指定必须在创建类的实例时进行的某些操作,在垃圾收集器删除对象时,也可以调用析构函数 ...
- 编写高质量代码改善C#程序的157个建议——建议50:在Dispose模式中应区别对待托管资源和非托管资源
建议50:在Dispose模式中应区别对待托管资源和非托管资源 真正资源释放代码的那个虚方法是带一个bool参数的,带这个参数,是因为我们在资源释放时要区别对待托管资源和非托管资源. 提供给调用者调用 ...
- C#内存管理之托管堆与非托管堆( reprint )
在 .NET Framework 中,内存中的资源(即所有二进制信息的集合)分为“托管资源”和“非托管资源”.托管资源必须接受 .NET Framework 的 CLR (通用语言运行时)的管理(诸如 ...
- 在 C# 中通过 P/Invoke 调用Win32 DLL
在 C# 中通过 P/Invoke 调用Win32 DLL 发布日期 : 1/13/2005 | 更新日期 : 1/13/2005 Jason Clark 下载本文的代码: NET0307.exe ( ...
- C#调用C++ DLL动态库的两种方式
第一种方式:C++导出函数, c#dllimport 的方式 在很多地方都看到过,如[dllimport "user32.dll"]这种代码,调用windows API,就是通过这 ...
随机推荐
- 区间DP+next求循环节 uva 6876
// 区间DP+next求循环节 uva 6876 // 题意:化简字符串 并表示出来 // 思路:dp[i][j]表示 i到j的最小长度 // 分成两部分 再求一个循环节 #include < ...
- 最短路+线段交 POJ 1556 好题
// 最短路+线段交 POJ 1556 好题 // 题意:从(0,5)到(10,5)的最短距离,中间有n堵墙,每堵上有两扇门可以通过 // 思路:先存图.直接n^2来暴力,不好写.分成三部分,起点 终 ...
- MFC工程目录
如果已经以Debug方式编译链接过程序,则会在解决方案文件夹下和工程子文件夹下各有一个名为“Debug”的文件夹,而如果是Release方式编译则会有名为“Release”的文件夹.这两种编译方式将产 ...
- storm,hbase和storm-kafka-0.8-plus兼容性问题
1 org.slf4j.impl.StaticLoggerBinder.SINGLETON错误 方案: 确保slf4j-api-1.5.6.jar 和slf4j-log4j12-1.5.6.jar ...
- [HIve - LanguageManual] Hive Operators and User-Defined Functions (UDFs)
Hive Operators and User-Defined Functions (UDFs) Hive Operators and User-Defined Functions (UDFs) Bu ...
- 《Genesis-3D开源游戏引擎完整实例教程-2D射击游戏篇07:全屏炸弹》
7.全屏炸弹 全屏炸弹概述: 为了增设游戏的趣味性,我们制作一个游戏的基本框架以外.还会增设一些其他的额外的功能.比如5秒无敌状态.冰冻效果等.下面咱们以消灭屏幕中所有炸弹为例,看除了碰撞可以触发事件 ...
- ubuntu使用问题与解决记录[持续更新]
1. 添加到计划任务 为脚本增加可执行权限 sudo chmod +x yeelink.sh 将脚本加入cronjob(计划任务) sudo crontab -e 在cornjob文件中添加下面一行, ...
- Bmob第三方登录详解
Bmob第三方登录详解 Bmob 第三方登录 简介 本文主要介绍新浪微博,QQ,微信的登录接入以及如何配合BmobSDK中的第三方登录功能实现第三方登录. 在使用之前请先按照快速入门创建好可以调用Bm ...
- delphi 单例模式实现
unit Unit2; interface uses System.SysUtils; type { TSingle } TSingle = class(TObject) private FStr: ...
- MAC机常用快捷键整理表格
MAC机常用快捷键整理表格 范围 快捷键 说明 图形 (Command 键)在某些 Apple 键盘上,此键也可能为标志() Control (Control 键) Alt Opt ...