在DllImport中使用Unicode字符串和用Rust编写的DLL [英] Using Unicode strings in DllImport with a DLL written in Rust

查看:156
本文介绍了在DllImport中使用Unicode字符串和用Rust编写的DLL的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试从C#程序调用用Rust编写的DLL. DLL具有两个简单的函数,它们以不同的方式显示字符串并打印到控制台.

I am trying to call a DLL written in Rust from a C# program. The DLL has two simple functions that take stings (in different manner) and prints to the console.

#![crate_type = "lib"]
extern crate libc;

use libc::{c_char};
use std::ffi::CStr;

#[no_mangle]
pub extern fn printc(s: *const c_char){
    let c_str : &CStr = unsafe {
        assert!(!s.is_null());

        CStr::from_ptr(s)
    };

    println!("{:?}", c_str.to_bytes().len()); //prints "1" if unicode

    let r_str = std::str::from_utf8(c_str.to_bytes()).unwrap();
    println!("{:?}", r_str);
}

#[no_mangle]
pub extern fn print2(string: String) {
    println!("{:?}", string)
}

C#控制台程序代码

[DllImport("lib.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
static extern void print2(ref string str);

[DllImport("lib.dll", CallingConvention = CallingConvention.Cdecl)]
static extern void printc(string str);

static void Main(string[] args)
{
  try
  {
    var graw = "yeyeye";
    printc(graw);
    print2(ref graw);
  }
  catch (Exception ex)
  {
    Console.WriteLine("calamity!, {0}", ex.Message);
  }
  Console.ReadLine();
}

对于print2功能,它将一直在屏幕上打印垃圾,直到导致AccessViolationException

For the print2 function it keep printing garbage on screen until it causes AccessViolationException

第二个printc函数确实会打印字符串,但前提是未设置CharSet.Unicode.如果已设置,它将仅打印第一个字符,因此println!("{:?}", c_str.to_bytes().len());将打印1.

The 2nd printc function does print the string, but only if CharSet.Unicode is not set. If it is set, it will only print the first char, hence the println!("{:?}", c_str.to_bytes().len()); will print 1.

我相信Cstr::from_ptr函数不支持Unicode,因此它仅返回字符串的第一个字符.

I believe that Cstr::from_ptr function does not support Unicode, that is why it returns only the first char of the string.

有什么想法如何将Unicode字符串作为参数传递给Rust DLL?是否可以像print2函数中那样使事情更简单?

Any idea how to pass Unicode string as parameters to Rust DLLs? Is it possible to make things simpler like in print2 function?

推荐答案

如果选中

If you check the documentation on CharSet, you'll see that CharSet.Unicode tells .NET to marshal strings as UTF-16 (i.e. two bytes per code point). Thus, .NET is trying to pass printc what should be a *const u16, not a *const libc::c_char. When CStr goes to compute the length of the string, what it sees is the following:

b"y\0e\0y\0e\0y\0e\0"

也就是说,它看到一个一个代码单元,然后是一个空字节,因此它停止了;因此为什么说长度为"1".

That is, it sees one code unit, then a null byte, so it stops; hence why it says the length is "1".

Rust不支持UTF-16字符串的标准支持,但是如果您在Windows上工作,则有一些转换方法:在文档中搜索OsStrExtOsStringExt.请注意,您必须使用随编译器一起安装的文档.那些在线的将不包含它.

Rust has no standard support for UTF-16 strings, but if you're working on Windows, there are some conversion methods: search the docs for OsStrExt and OsStringExt. Note that you must use the docs that installed with the compiler; the ones online won't include it.

可悲的是,没有什么可以直接处理以null终止的UTF-16字符串.您需要编写一些不安全的代码才能将*const u16转换为可以传递给OsStringExt::from_wide&[u16].

Sadly, there's nothing for dealing directly with null-terminated UTF-16 strings. You'll need to write some unsafe code to turn a *const u16 into a &[u16] that you can pass to OsStringExt::from_wide.

现在,Rust 确实使用Unicode,但它使用的是UTF-8.可悲的是,没有直接方法让.NET将字符串编组为UTF-8.使用任何其他编码似乎都会丢失信息,因此您要么必须在Rust方面显式处理UTF-16,要么在C#方面显式处理UTF-8.

Now, Rust does use Unicode, but it uses UTF-8. Sadly, there is no direct way to get .NET to marshal a string as UTF-8. Using any other encoding would appear to lose information, so you either have to explicitly deal with UTF-16 on the Rust side, or explicitly deal with UTF-8 on the C# side.

在C#中将字符串重新编码为UTF-8更为简单.您可以利用以下事实:.NET将封送一个数组作为指向第一个元素的原始指针(就像C一样),并传递以null终止的UTF-8字符串.

It's much simpler to re-encode the string as UTF-8 in C#. You can exploit the fact that .NET will marshal an array as a raw pointer to the first element (just like C) and pass a null-terminated UTF-8 string.

首先,这是一个静态方法,用于获取.NET字符串并生成存储在字节数组中的UTF-8字符串:

First, a static method for taking a .NET string and producing a UTF-8 string stored in a byte array:

byte[] NullTerminatedUTF8bytes(string str)
{
    return Encoding.GetBytes(str + "\0");
}

然后像下面这样声明Rust函数的签名:

Then declare the signature of the Rust function like this:

[DllImport(dllname, CallingConvention = CallingConvention.Cdecl)]
static extern void printc([In] byte[] str);

最后,这样称呼它:

printc(NullTerminatedUTF8bytes(str));

要获得加分,您可以重新制作printc,以获取*const u8 一个u32,并传递重新编码的字符串及其长度.那么您就不需要空终止符,并且可以使用std::slice::from_raw_parts函数来重建字符串(但这已经超出了原始问题).

For bonus points, you can rework printc to instead take a *const u8 and a u32, passing the re-encoded string plus it's length; then you don't need the null terminator and can reconstruct the string using the std::slice::from_raw_parts function (but that's starting to go beyond the original question).

至于print2,那是行不通的. .NET对Rust的String类型一无所知,而且与.NET字符串也不兼容.不仅如此,String甚至没有一个有保证的布局,因此安全地绑定到它几乎是不可能的.

As for print2, that one is just unworkable. .NET knows nothing about Rust's String type, and it is in no way compatible with .NET strings. More than that, String doesn't even have a guaranteed layout, so binding to it safely is more or less not possible.

这是一个很冗长的说法:不要在跨语言函数中使用String或任何其他非FFI安全类型 .如果您的目的是在Rust中传递一个拥有的"字符串...我不知道是否可能与.NET一起使用.

All that is a very long-winded way of saying: don't use String, or any other non-FFI-safe type, in cross-language functions, ever. If your intention here was to pass an "owned" string into Rust... I don't know if it's even possible to do in concert with .NET.

Aside :Rust中的"FFI安全"本质上可以归结为:是内置的固定大小类型( ie not usize/isize),或者是用户定义的类型,并附加了#[repr(C)].可悲的是,文档中没有包含这种类型的"FFI安全"性.

Aside: "FFI-safe" in Rust essentially boils down to: is either a built-in fixed-size type (i.e. not usize/isize), or is a user-defined type with #[repr(C)] attached to it. Sadly, the "FFI-safe"-ness of a type isn't included in the documentation.

这篇关于在DllImport中使用Unicode字符串和用Rust编写的DLL的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆