• 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  static char * _hello = "Hello,World!!";
    9  static TCHAR * _helloW = TEXT("Hello,World!!");
    10
    11  void __stdcall PrintString(char * hello)
    12 {
    13 printf("%s ",hello);
    14 }
    15
    16  void __stdcall PrintStringW(TCHAR * hello)
    17 {
    18 _tprintf(TEXT("%s "),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] = '';
    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('');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++基础】类class
    【C++基础】结构struct
    【C++基础】C-串知识整理
    GeoServer war包在tomcat7中配置遇到的一个问题
    pgrouting 2.0 的环境配置
    阿里2014年9月笔试中的一个算法设计题--擦黑板剩余数字
    VisualSVN Server的启动关闭脚本
    二叉树遍历(前序、中序、后序)的递归及非递归实现(小结)
  • 原文地址:https://www.cnblogs.com/gxh973121/p/4078248.html
Copyright © 2020-2023  润新知