可以说新手使用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  static char * _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 public static extern void PrintStringByBytes(byte[] hello);
3
4 [DllImport("TestDll", EntryPoint = "PrintString")]
5 public static extern void PrintStringByMarshal([MarshalAs(UnmanagedType.LPStr)]string hello);
6
7 [DllImport("TestDll", EntryPoint = "PrintStringW")]
8 public static extern void PrintStringByBytesW(byte[] hello);
9
10 [DllImport("TestDll", EntryPoint = "PrintStringW")]
11 public static extern void PrintStringByMarshalW([MarshalAs(UnmanagedType.LPWStr)]string hello);
12
13
14 public void 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 public static extern IntPtr GetStringReturnByBytes();
3
4 [DllImport("TestDll", EntryPoint = "GetStringReturn")]
5 [return:MarshalAs(UnmanagedType.LPStr)]
6 public static extern string GetStringReturnByMarshal();
7
8 [DllImport("TestDll", EntryPoint = "GetStringReturnW")]
9 public static extern IntPtr GetStringReturnByBytesW();
10
11 [DllImport("TestDll", EntryPoint = "GetStringReturnW")]
12 [return: MarshalAs(UnmanagedType.LPWStr)]
13 public static extern string GetStringReturnByMarshalW();
14
15
16 public void 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 public static extern void GetStringParamByBytes(byte[] outHello, int len);
3
4 [DllImport("TestDll", EntryPoint = "GetStringParam")]
5 public static extern void GetStringParamByMarshal([Out, MarshalAs(UnmanagedType.LPStr)]StringBuilder outHello, int len);
6
7 [DllImport("TestDll", EntryPoint = "GetStringParamW")]
8 public static extern void GetStringParamByBytesW(byte[] outHello, int len);
9
10 [DllImport("TestDll", EntryPoint = "GetStringParamW")]
11 public static extern void GetStringParamByMarshalW([Out, MarshalAs(UnmanagedType.LPWStr)]StringBuilder outHello, int len);
12
13
14 public byte[] _outHello = new byte[10];
15 public byte[] _outHelloW = new byte[20];
16 public StringBuilder _builder = new StringBuilder(10); //很重要设定string的容量。
17  
18 public void 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完全掌握, 字符串和指针的更多相关文章

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

    Visual Studio 2013 其他版本 Lambda 表达式是一种可用于创建委托或表达式目录树类型的匿名函数. 通过使用 lambda 表达式,可以写入可作为参数传递或作为函数调用值返回的本地 ...

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

    https://msdn.microsoft.com/zh-cn/library/bb397687.aspx Lambda 表达式是一种可用于创建委托或表达式目录树类型的匿名函数.通过使用 lambd ...

  3. 高质量C++/C编程指南(林锐)

    推荐-高质量C++/C编程指南(林锐) 版本/状态 作者 参与者 起止日期 备注 V 0.9 草稿文件 林锐   2001-7-1至 2001-7-18 林锐起草 V 1.0 正式文件 林锐   20 ...

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

    原文:Swift语言指南(十)--字符串与字符 字符串是一段字符的有序集合,如"hellow,world"或"信天翁".Swift 中的字符串由 String ...

  5. 《JavaScript面向对象编程指南(第2版)》读书笔记(一)

    目录 一.对象 1.1 获取属性值的方式 1.2 获取动态生成的属性的值 二.数组 2.1 检测是否为数组 2.2 增加数组长度导致未赋值的位置为undefined 2.3 用闭包实现简易迭代器 三. ...

  6. 高质量C++/C编程指南

    http://man.chinaunix.net/develop/c&c++/c/c.htm#_Toc520634042 高质量C++/C编程指南 文件状态 [  ] 草稿文件 [√] 正式文 ...

  7. <译>Flink编程指南

    Flink 的流数据 API 编程指南 Flink 的流数据处理程序是常规的程序 ,通过再流数据上,实现了各种转换 (比如 过滤, 更新中间状态, 定义窗口, 聚合).流数据可以来之多种数据源 (比如 ...

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

    http://timyang.net/category/erlang/ 在云时代,我们需要有更好的能利用多核功能及分布式能力的编程语言,Erlang在这方面具有天生的优势,因此我们始终对它保持强烈关注 ...

  9. C++编程指南续(10-11)

    十.类的继承与组合 对象(Object)是类(Class)的一个实例(Instance).如果将对象比作房子,那么类就是房子的设计图纸.所以面向对象设计的重点是类的设计,而不是对象的设计. 对于C++ ...

随机推荐

  1. iOS 根据字符串内容动态计算行高

    + (CGFloat)textHeightFromTextString:(NSString *)text width:(CGFloat)textWidth fontSize:(CGFloat)size ...

  2. javascript全局变量和局部变量

    局部变量和全局变量可以同名.不过在函数体内部,局部变量的优先级高于全局变量.需要格外注意:专用于函数体内部的变量一定要用var关键字声明,否则该变量会变成全局变量.因为js是弱类型语言,所以它可以存放 ...

  3. python之socket 网络编程

    提到网络通信不得不复习下osi七层模型: 七层模型,亦称OSI(Open System Interconnection)参考模型,是参考模型是国际标准化组织(ISO)制定的一个用于计算机或通信系统间互 ...

  4. 使用PS过程

    1. 设置图层背景色和前景色 设置背景色和前景色方法类似,下面以设置背景色为例说明.方法一:使用色板设置图片背景色第一步:单击"文件"菜单,选择"新建文档",这 ...

  5. oc 单例

    单例模式: //static id _instace; // //+ (id)allocWithZone:(struct _NSZone *)zone //{ // static dispatch_o ...

  6. Gradle笔记系列(一)

    1.Gradle概述 Gradle是一个基于Apache Ant和Apache Maven概念的项目自动化构建工具.它使用一种基于Groovy的特定领域语言(DSL)来声明项目设置,抛弃了基于XML的 ...

  7. hdu 5534 (完全背包) Partial Tree

    题目:这里 题意: 感觉并不能表达清楚题意,所以 Problem Description In mathematics, and more specifically in graph theory, ...

  8. h5整理--详解css的相对定位和绝对定位

    浏览器默认状态下position的属性默认值是static也就是没有定位,元素出现在正常的文档流中,这个时候给元素设置的left,right.bottom.top这些偏移属性是没有效果的,不会生效: ...

  9. 《Linux 多线程服务端编程:使用 muduo C++ 网络库》电子版上市

    <Linux 多线程服务端编程:使用 muduo C++ 网络库> 电子版已在京东和亚马逊上市销售. 京东购买地址:http://e.jd.com/30149978.html 亚马逊Kin ...

  10. verilog循环结构

    1. always(posedge CLOCK) . case(i) . . : . ) ’d0; i <= i + ’b1; end . ’b1; C1 <= C1 + ’b1; end ...