问题描述

今天在做项目的时候碰到一个问题,就是用C++编写CLR类库dll的时候,C++的函数参数列表中包含一个char*的输出型参数,然而在C#调用该dll时候,会自动将函数的中的char*参数“翻译”为sbyte*, 使用了各种方法都不能调用函数,主要是不能合适的转换为sbyte*。

简单示例

举个简单的例子,比如我有一个CLR的类库为MyDll.dll, 其中头文件为Mydll.h,简单的只有一个函数,其中我想要为char*的作为输出参数, 代码如下

 // MyDll.h

 #pragma once
#include <cstdio>
#include <cstring>
using namespace System; namespace MyDll { public ref class MyClass
{
// TODO: 在此处添加此类的方法。
public:
MyClass()
{}
public:
static bool TestFunc(char* outPutStr, int *p)
{
*p = ;
char* s = "Hello world";
strcpy_s(outPutStr, , s);
return true;
}
};
}

源文件内容MyDll.cpp为空。【生成解决方案】后,得到MyDll.dll库。

 // 这是主 DLL 文件, MyDll.cpp

 #include "stdafx.h"

 #include "MyDll.h"

然后新建一个C# WPF项目,右键 【引用】->【添加引用...】->左侧Tab【浏览】->右下角【浏览...】选择刚才生成的 MyDll.dll 文件后,点击【确定】。这样我们就可以在WPF项目中调用 TestFunc 函数了。

我们在 WPF 的 MainWidnow 中随便加入一个 Button,然后在 Button 的 Click 事件中尝试调用 TestFunc 函数,Mainwindow 的 cs 文件如下:

 using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Runtime.InteropServices; namespace MyWPF
{
/// <summary>
/// MainWindow.xaml 的交互逻辑
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
} private void Button_Click(object sender, RoutedEventArgs e)
{
unsafe
{
string str = new string('A', );
IntPtr intPtrStr = (IntPtr)Marshal.StringToHGlobalAnsi(str);
sbyte* sbyteStr = (sbyte*)intPtrStr; int p = ;
MyDll.MyClass.TestFunc(sbyteStr, &p); String tmp = new String(sbyteStr);
MessageBox.Show(tmp);
MessageBox.Show(p.ToString());
}
}
}
}

注意引入第 15 行的命名空间, 同时【项目】->【属性】->【生成】->勾选 【允许不安全代码】。 主要转换的部分出现在 33-35 行,首先建立一个普通的 string 变量,然后新建一个指针,指向该 string的内容,再讲该指针强制类型转化为sbyte*, 从而实现从 char* 中获取内容。

问题讨论

问题1: 为什么 C# 会将 C++ 中的 char* 转换为 sbyte* 呢?

这是因为 C++ 中的 char 占用一个字节,而 C#中 char 占用两个字节,所以不能讲 char* 直接转换为 C# 中的 char*,  而 sbyte 是有符号单字节,取值区间 [-128, 127],所以 C# 将 char* 转换成 sbyte* 是合理的。

问题2:CLR 的 dll 和普通的 dll (Native dll) 有什么区别呢?

CLR 有点类似于 java 的 “虚拟机” , 提供运行环境,负责检查 内存管理、安全、异常等比较容易出错的问题,提高程序的安全性和健壮性,是 .NET Framework 的主要引擎。在 CLR 监视下的代码称为 “托管代码 (managed)”,  而其他的不在其监管下的代码称为 “非托管代码 (unmanaged)”。在 CLR 环境下生成的代码是中间代码,有点类似于 java 的字节码。所以通过 CLR Dll 生成的 dll 类库是中间代码,普通的 dll 则是直接是本机化的二进制文件, 通过 CLR DLL 生成的 dll 是程序集 (Assembly), 可以在直接在C#工程中【添加引用】就可以直接调用,从前面的例子也可以看出来,不需要任何的 DllImport 之类的包含与结构体重定义,函数指针定义等等复杂的操作,使得调用更加简单。

源码下载

http://files.cnblogs.com/files/python27/MyWPF.zip

http://files.cnblogs.com/files/python27/MyDll.zip

C# 调用C++ CLR dll类库时,实现从 string 到 sbyte* 的转换的更多相关文章

  1. PB调用C#编写的Dll类库

    在c# 中编写com组件,供PB调用实例 前言:c#中写的dll直接是不能被pb调用的,只有写成com组件才可以调用,所以用c#写dll时要注意. c#中新建类库 类库类型为通用类库,项目名为AddC ...

  2. 利用C#的反射机制动态调用DLL类库

    最近由于业务要求,需要动态调用DLL类库,所以研究了一下,感觉还好也不太难,今天就把自己理解的写了一个小例子(已经通过VS2005跑通),供大家一起研究和探讨,有理解不当的地方还请高手们多多指正,谢谢 ...

  3. CLR调试时的sos.dll/clr.dll/mscorwks.dll/mscordacwks.dll等动态库的版本对应

    大家都知道,在调试托管代码时,一定会加载到sos/clr/mscorwks/mscordacwks这些动态库,才能够很好的完成我们的调试工作,那么他们的版本对应关系是怎样的呢,特别是clr.dll/m ...

  4. .Net DLL类库引用时没有注释信息

    自己编写的类库提供给别人引用时,别人获取不到DLL内部的方法.变量的注释信息,无法了解内部情况和使用方法. 原因:没有随DLL类库一同输出注释文档 解决方案: 在VS界面中选中提供给别人的类库项目 在 ...

  5. C#创建dll类库

    类库让我们的代码可复用,我们只需要在类库中声明变量一次,就能在接下来的过程中无数次地使用,而无需在每次使用前都要声明它.这样一来,就节省了我们的内存空间.而想要在类库添加什么类,还需取决于类库要实现哪 ...

  6. 浅谈C++调用C#的DLL程序方法

    把C#编译成DLL或者Axtive控件,再由C调用!比如使用C++调用C#的DLL. SwfDotNet是.net下输出flash的类库.SwfDotNet是C#编写的,作者的C#水平,真是令我佩服. ...

  7. 在C#调用C++的DLL方法(二)生成托管的DLL

    写操作之前,还是扼要的说一下托管与非托管C++的区别好了,其实我也并没有深入了解过托管C++的特点所在,其最大的特征就是可以由系统来调试回收相关的代码资源,跟C#的特性一样,只是编程风格跟C++类似而 ...

  8. java调用C#的dll

    链接地址:http://www.cnblogs.com/yinhaiming/articles/1712463.html .net产生的比java晚,其类库的封装在某些方面也比java更优秀,更全面. ...

  9. Python调用C# Com dll组件实战

    之前公司有套C# AES加解密方案,但是方案加密用的是Rijndael类,而非AES的四种模式(ECB.CBC.CFB.OFB,这四种用的是RijndaelManaged类),Python下Crypt ...

随机推荐

  1. linux一些基本常识(三)

    acl:对本身权限的扩展    打包:zip 111.zip a.txt b.txt.....    zip -r /etc/sysconfig/* (样才能第归所有内容0)    解宝:uzip 1 ...

  2. Bean Validation技术实现对Javabean的校验

    概述:在java开发时,由于分层的原因(表现层-控制层-业务层-数据持久层),有时候需要对传入的Javabean进行校验,如果过多的校验会导致比较繁琐,做重复的工作,下面将介绍Bean Validat ...

  3. ThreadPoolExecutor的execute源码分析

    上一篇文章指出,ThreadPoolExecutor执行的步骤如下: 向线程池中添加任务,当任务数量少于corePoolSize时,会自动创建thead来处理这些任务: 当添加任务数大于corePoo ...

  4. socket接口详解

    1. socket概述 socket是在应用层和传输层之间的一个抽象层,它把TCP/IP层复杂的操作抽象为几个简单的接口供应用层调用已实现进程在网络中通信. socket起源于UNIX,在Unix一切 ...

  5. awk:好用的数据处理工具

    awk 也是一个非常棒的数据处理工具!sed 常常用于一整个行的处理, awk 则比较倾向于一行当中分成数个『栏位』(或者称为一个域,也就是一列)来处理.因此,awk 相当的适合处理小型的数据数据处理 ...

  6. docker搭建nginx

    在Docker下部署Nginx,包括: 部署一个最简单的Nginx,可以通过端口访问默认的网站 设置记录访问和错误日志的路径 以交互模式创建centos容器,如果本地没有镜像,会从仓库获取, 等待即可 ...

  7. php中session入memcached

    1.使用memcache扩展,提供的session处理器(session.save_handler) memcahe即可. 通过存储位置配置项(session.save_path),设置使用的memc ...

  8. ip地址后边加个/8(16,24,32)是什么意思

    是掩码的位数,A类IP地址的默认子网掩码为255.0.0.0(由于255相当于二进制的8位1,所以也缩写成“/8”,表示网络号占了8位);B类的为255.255.0.0(/16);C类的为255.25 ...

  9. CentOS7下部署NTP Server

    系统环境:CentOS Linux release 7.4.1708 (Core) 3.10.0-693.el7.x86_64 软件版本:chrony-3.1-2.el7.centos.x86_64 ...

  10. 在Excel中根据某一个单元格的出生日期自动精确计算年龄

    =IF(MONTH(NOW())<MONTH(G4),INT(YEAR(NOW())-YEAR(G4))-1,IF(MONTH(NOW())>MONTH(G4),YEAR(NOW())-Y ...