当前位置: 首页 > news >正文

C# 调用C++ CLR dll类库时,实现从 string 到 sbyte* 的转换

问题描述

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

简单示例

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

 1 // MyDll.h
 2 
 3 #pragma once
 4 #include <cstdio>
 5 #include <cstring>
 6 using namespace System;
 7 
 8 namespace MyDll {
 9 
10     public ref class MyClass
11     {
12         // TODO:  在此处添加此类的方法。
13     public:
14         MyClass()
15         {}
16     public:
17         static bool TestFunc(char* outPutStr, int *p)
18         {
19             *p = 10;
20             char* s = "Hello world";
21             strcpy_s(outPutStr, 32, s);
22             return true;
23         }
24     };
25 }

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

1 // 这是主 DLL 文件, MyDll.cpp
2 
3 #include "stdafx.h"
4 
5 #include "MyDll.h"

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

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

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6 using System.Windows;
 7 using System.Windows.Controls;
 8 using System.Windows.Data;
 9 using System.Windows.Documents;
10 using System.Windows.Input;
11 using System.Windows.Media;
12 using System.Windows.Media.Imaging;
13 using System.Windows.Navigation;
14 using System.Windows.Shapes;
15 using System.Runtime.InteropServices;
16 
17 namespace MyWPF
18 {
19     /// <summary>
20     /// MainWindow.xaml 的交互逻辑
21     /// </summary>
22     public partial class MainWindow : Window
23     {
24         public MainWindow()
25         {
26             InitializeComponent();
27         }
28 
29         private void Button_Click(object sender, RoutedEventArgs e)
30         {
31             unsafe
32             {
33                 string str = new string('A', 32);
34                 IntPtr intPtrStr = (IntPtr)Marshal.StringToHGlobalAnsi(str);
35                 sbyte* sbyteStr = (sbyte*)intPtrStr;
36 
37                 int p = 0;
38                 MyDll.MyClass.TestFunc(sbyteStr, &p);
39 
40                 String tmp = new String(sbyteStr);
41                 MessageBox.Show(tmp);
42                 MessageBox.Show(p.ToString());
43             }
44         }
45     }
46 }

注意引入第 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

转载于:https://www.cnblogs.com/python27/p/4370162.html

相关文章:

  • BaseTDI.sys 瑞星卡巴冲突,导致机器蓝屏
  • Freemodbus介绍及测试
  • struts tech
  • html代码中显示系统时间
  • 提高工作效率的一些方法
  • ASP.NET通过Global.asax和Timer定时器 定时调用WebService 运行后台代码(转)
  • POJ3216 最小路径覆盖
  • 一个字让你从新手成为合格的程序员
  • 学习ASP.NET缓存机制
  • 理解分析问题的常用方法
  • BZOJ 1010: [HNOI2008]玩具装箱toy(dp+斜率优化)
  • 解决问题的方法
  • html5开发之viewport使用
  • 迅雷下载百度云文件
  • Linux 命令的一些记录(五):在安装ubuntu的一些操作
  • 《深入 React 技术栈》
  • 【Leetcode】104. 二叉树的最大深度
  • angular组件开发
  • css系列之关于字体的事
  • Docker 笔记(1):介绍、镜像、容器及其基本操作
  • IIS 10 PHP CGI 设置 PHP_INI_SCAN_DIR
  • JavaScript设计模式与开发实践系列之策略模式
  • JS学习笔记——闭包
  • leetcode386. Lexicographical Numbers
  • Linux下的乱码问题
  • Logstash 参考指南(目录)
  • Netty源码解析1-Buffer
  • Redis学习笔记 - pipline(流水线、管道)
  • TypeScript迭代器
  • webpack入门学习手记(二)
  • 极限编程 (Extreme Programming) - 发布计划 (Release Planning)
  • 计算机常识 - 收藏集 - 掘金
  • 理解IaaS, PaaS, SaaS等云模型 (Cloud Models)
  • 码农张的Bug人生 - 见面之礼
  • ​​​​​​​Installing ROS on the Raspberry Pi
  • ​Java并发新构件之Exchanger
  • ​MPV,汽车产品里一个特殊品类的进化过程
  • ​直流电和交流电有什么区别为什么这个时候又要变成直流电呢?交流转换到直流(整流器)直流变交流(逆变器)​
  • #include
  • (007)XHTML文档之标题——h1~h6
  • (一)C语言之入门:使用Visual Studio Community 2022运行hello world
  • (转载)CentOS查看系统信息|CentOS查看命令
  • **Java有哪些悲观锁的实现_乐观锁、悲观锁、Redis分布式锁和Zookeeper分布式锁的实现以及流程原理...
  • .\OBJ\test1.axf: Error: L6230W: Ignoring --entry command. Cannot find argumen 'Reset_Handler'
  • .Net 4.0并行库实用性演练
  • .net core 连接数据库,通过数据库生成Modell
  • .NET开源项目介绍及资源推荐:数据持久层
  • .NET面试题解析(11)-SQL语言基础及数据库基本原理
  • .NET设计模式(2):单件模式(Singleton Pattern)
  • .pings勒索病毒的威胁:如何应对.pings勒索病毒的突袭?
  • @ConditionalOnProperty注解使用说明
  • @在php中起什么作用?
  • [ vulhub漏洞复现篇 ] Celery <4.0 Redis未授权访问+Pickle反序列化利用
  • [AX]AX2012开发新特性-禁止表或者表字段
  • [BZOJ 4034][HAOI2015]T2 [树链剖分]