c#编程指南(十) 平台调用P-INVOKE完全掌握, 字符串和指针

可以说新手使用P-INVOKE最开始的头疼就是C#和C++的字符串传递,因为这里涉及到两个问题。

第一:C#的string和C++的字符串首指针如何对应。

第二:字符串还有ANSI和UNICODE(宽字符串)之分。

本文分三部分阐述:

第一:字符串指针当输入参数,

第二:字符串指针作为返回值,

第三:字符串指针作为输入输出参数。

C++部分的测试代码很简单这里就全部贴出来了:

1 #include "stdafx.h"
2 #include "TestDll.h"
3 #include <stdio.h>
4 #include <string.h>
5 #include <tchar.h>
6
7
8  staticchar* _hello ="Hello,World!!";
9  static TCHAR * _helloW = TEXT("Hello,World!!");
10
11  void __stdcall PrintString(char* hello)
12 {
13 printf("%s\n",hello);
14 }
15
16  void __stdcall PrintStringW(TCHAR * hello)
17 {
18 _tprintf(TEXT("%s\n"),hello);
19 }
20
21
22  char* __stdcall GetStringReturn()
23 {
24 return _hello;
25 }
26
27 TCHAR * __stdcall GetStringReturnW()
28 {
29 return _helloW;
30 }
31
32
33  void __stdcall GetStringParam(char* outHello,int len)
34 { //output "aaaaaaaa"
35  for(int i=0; i< len -1 ;i++) outHello[i] =‘a‘;
36 outHello[len -1] =‘\0‘;
37 }
38
39  void __stdcall GetStringParamW(TCHAR * outHello,int len)
40 { //output "aaaaaaaa" unicode version.
41  for(int i=0; i< len -1 ;i++) outHello[i] = TEXT(‘a‘);
42 outHello[len -1] = TEXT(‘\0‘);
43 }

下面看C#如何调用。

第一:字符串指针作为输入参数,可以使用byte[] 和MarshalAs来解决。(注意四个P-INVOKE,两个ANSI版本,和两个UNICODE版本),推荐使用MarshalAs方法简单明了。

1 [DllImport("TestDll", EntryPoint ="PrintString")]
2 publicstaticexternvoid PrintStringByBytes(byte[] hello);
3
4 [DllImport("TestDll", EntryPoint ="PrintString")]
5 publicstaticexternvoid PrintStringByMarshal([MarshalAs(UnmanagedType.LPStr)]string hello);
6
7 [DllImport("TestDll", EntryPoint ="PrintStringW")]
8 publicstaticexternvoid PrintStringByBytesW(byte[] hello);
9
10 [DllImport("TestDll", EntryPoint ="PrintStringW")]
11 publicstaticexternvoid PrintStringByMarshalW([MarshalAs(UnmanagedType.LPWStr)]string hello);
12
13
14 publicvoid Run()
15 {
16 PrintStringByBytes(Encoding.ASCII.GetBytes("use byte[]"));
17 PrintStringByMarshal("use MarshalAs");
18 PrintStringByBytesW(Encoding.Unicode.GetBytes("use byte[]"));
19 PrintStringByMarshalW("use MarshalAs");
20 }

第二:字符串指针作为返回值,和上面一样也有两种声明方法,同样也包含两个版本。注意:Marshal.PtrToStringAnsi()函数的使用,把字符串指针转变为C#的string.推荐使用MarshalAs方法简单明了。

1 [DllImport("TestDll", EntryPoint ="GetStringReturn")]
2 publicstaticextern IntPtr GetStringReturnByBytes();
3
4 [DllImport("TestDll", EntryPoint ="GetStringReturn")]
5 [return:MarshalAs(UnmanagedType.LPStr)]
6 publicstaticexternstring GetStringReturnByMarshal();
7
8 [DllImport("TestDll", EntryPoint ="GetStringReturnW")]
9 publicstaticextern IntPtr GetStringReturnByBytesW();
10
11 [DllImport("TestDll", EntryPoint ="GetStringReturnW")]
12 [return: MarshalAs(UnmanagedType.LPWStr)]
13 publicstaticexternstring GetStringReturnByMarshalW();
14
15
16 publicvoid Run()
17 { //Marshal.PtrToStringAuto(GetStringReturnByBytes()); 自动判断类型不错。
18   Console.WriteLine(Marshal.PtrToStringAnsi(GetStringReturnByBytes()));
19 Console.WriteLine(GetStringReturnByMarshal());
20 Console.WriteLine(Marshal.PtrToStringUni(GetStringReturnByBytesW()));
21 Console.WriteLine(GetStringReturnByMarshalW());
22 }

第三:字符串指针作为输入输出参数时,因为要求有固定的容量,所以这里使用的是StringBuilder,大家仔细看了,当然也有byte[]版本。这个看大家喜欢那个版本就是用那个.

1 [DllImport("TestDll", EntryPoint ="GetStringParam")]
2 publicstaticexternvoid GetStringParamByBytes(byte[] outHello, int len);
3
4 [DllImport("TestDll", EntryPoint ="GetStringParam")]
5 publicstaticexternvoid GetStringParamByMarshal([Out, MarshalAs(UnmanagedType.LPStr)]StringBuilder outHello, int len);
6
7 [DllImport("TestDll", EntryPoint ="GetStringParamW")]
8 publicstaticexternvoid GetStringParamByBytesW(byte[] outHello, int len);
9
10 [DllImport("TestDll", EntryPoint ="GetStringParamW")]
11 publicstaticexternvoid GetStringParamByMarshalW([Out, MarshalAs(UnmanagedType.LPWStr)]StringBuilder outHello, int len);
12
13
14 publicbyte[] _outHello =newbyte[10];
15 publicbyte[] _outHelloW =newbyte[20];
16 public StringBuilder _builder =new StringBuilder(10); //很重要设定string的容量。
17  
18 publicvoid Run()
19 {
20 //
21   GetStringParamByBytes(_outHello, _outHello.Length);
22 GetStringParamByMarshal(_builder, _builder.Capacity);
23 GetStringParamByBytesW(_outHelloW, _outHelloW.Length /2);
24 GetStringParamByMarshalW(_builder, _builder.Capacity);
25
26 //
27   Console.WriteLine(Encoding.ASCII.GetString(_outHello));
28 Console.WriteLine(_builder.ToString());
29 Console.WriteLine(Encoding.Unicode.GetString(_outHelloW));
30 Console.WriteLine(_builder.ToString());
31 }
32

下载:代码

c#编程指南(十) 平台调用P-INVOKE完全掌握, 字符串和指针

时间: 2024-11-04 18:36:31

c#编程指南(十) 平台调用P-INVOKE完全掌握, 字符串和指针的相关文章

Metal 着色语言编程指南 十八

矩阵的操作符(Operators on Matrix) 加,减操作符(+, -) 适用于矩阵运算.  但是要求参与运算的矩阵行数和列数必须相等. 其运算过程就是矩阵的每个相同位置的分量执行加或者减操作,  其结果为相同大小的矩阵.   乘操作符(*) 可以适用于: 标量与矩阵 矩阵与标量 矢量与矩阵 矩阵与矢量 矩阵与矩阵 标量乘矩阵运算的过程是,  此标量与矩阵的每个成员相乘, 其结果为大小同参与运算矩阵一样的矩阵. 举例如下: float3x3 m; float a = 3.0f; floa

《高级Bash脚本编程指南》十年来首次修订

新年伊始,开源慕课的知识库上线了.初期提供了<Linux命令大全/Bash 参考><高级Bash脚本编程指南>两个系列.其他资料正将陆续上线. 其中,<高级Bash脚本编程指南>对于Shell学习者来说,是一本神一样的书籍. 它的原作者是Mendel Cooper,英文名叫<Advanced Bash-Scripting Guide>发表在http://tldp.org/ 上,是一本免费的书籍. 有人说,学习Unix/Linux,有座山叫shell编程,你总

MEF 编程指南(十):重组

有些应用程序被设计成在运行时动态地改变.例如,一个新的扩展可能被下载,或者其他原因变得不可用.MEF 依靠我们称之为重组(Composition)的技术处理,在初始化组合以后改变导入值的场景. 导入可以通过 [System.ComponentModel.Composition.ImportAttribute] 使用 Allowrecompostion 属性通知 MEF 支持重组.参考下面的代码: [Export] public class HttpServerHealthMonitor { [I

兰姆达表达式Lambda 表达式(C# 编程指南)

转https://msdn.microsoft.com/zh-cn/library/bb397687.aspx Lambda 表达式是一种可用于创建委托或表达式目录树类型的匿名函数.通过使用 lambda 表达式,可以写入可作为参数传递或作为函数调用值返回的本地函数.Lambda 表达式对于编写 LINQ 查询表达式特别有用. 若要创建 Lambda 表达式,需要在 Lambda 运算符 => 左侧指定输入参数(如果有),然后在另一侧输入表达式或语句块.例如,lambda 表达式 x => x

Lambda 表达式(C# 编程指南) 微软microsoft官方说明

Visual Studio 2013 其他版本 Lambda 表达式是一种可用于创建委托或表达式目录树类型的匿名函数. 通过使用 lambda 表达式,可以写入可作为参数传递或作为函数调用值返回的本地函数. Lambda 表达式对于编写 LINQ 查询表达式特别有用. 若要创建 Lambda 表达式,需要在 Lambda 运算符 => 左侧指定输入参数(如果有),然后在另一侧输入表达式或语句块. 例如,lambda 表达式 x => x * x 指定名为 x 的参数并返回 x 的平方值. 如下

(zz)Lambda 表达式(C# 编程指南)

https://msdn.microsoft.com/zh-cn/library/bb397687.aspx Lambda 表达式是一种可用于创建委托或表达式目录树类型的匿名函数.通过使用 lambda 表达式,可以写入可作为参数传递或作为函数调用值返回的本地函数.Lambda 表达式对于编写 LINQ 查询表达式特别有用. 若要创建 Lambda 表达式,需要在 Lambda 运算符 => 左侧指定输入参数(如果有),然后在另一侧输入表达式或语句块.例如,lambda 表达式 x => x

Swift语言指南(十)--字符串与字符

原文:Swift语言指南(十)--字符串与字符 字符串是一段字符的有序集合,如"hellow,world"或"信天翁".Swift 中的字符串由 String 类型表示,对应着 Character 类型值的集合. Swift 中的 String 类型为你的编程提供了一个高速的,兼容 Unicode规范 的文本处理方式.Swift 创建和处理字符串的语法轻量可读,与 C 语言的字符串语法颇为相似.字符串的拼接非常简单,只需将两个字符串用 + 运算符相加.字符串的值是否

&lt;译&gt;Flink编程指南

Flink 的流数据 API 编程指南 Flink 的流数据处理程序是常规的程序 ,通过再流数据上,实现了各种转换 (比如 过滤, 更新中间状态, 定义窗口, 聚合).流数据可以来之多种数据源 (比如, 消息队列, socket 流, 文件). 通过sink组件落地流计算的最终结果,比如可以把数据落地文件系统,标准输出流比如命令行界面, Flink 的程序可以运行在多种上下文环境 ,可以单独只是Flink api,也可以嵌入其他程序. execution可以运行在本地的 JVM里, 也可以 运行

Archive for the ‘Erlang’ Category 《Erlang编程指南》读后感

http://timyang.net/category/erlang/ 在云时代,我们需要有更好的能利用多核功能及分布式能力的编程语言,Erlang在这方面具有天生的优势,因此我们始终对它保持强烈关注. 按:此为客座文章,投稿人为新浪微博基础研发工程师赵鹏城(http://weibo.com/iamzpc),以下为原文.在对一个分布式KV存储系统的研究过程中,我有幸遇到了Erlang语言.因此,我研究工作的第一目标就是快速入门Erlang语言并在实际研究过程中进一步深入理解Erlang的精髓.在