在 C# 中编组 char**

发布于 2024-10-09 22:12:07 字数 1012 浏览 4 评论 0原文

我正在与采用 char** (即指向字符串的指针)的代码进行交互:

int DoSomething(Whatever* handle, char** error);

基本上,它需要一个状态句柄,如果出现问题,它会返回一个错误代码以及可选的错误消息(内存是在外部分配的,并通过第二个函数释放。我已经弄清楚了这部分:))。

但是,我不确定如何在 C# 中处理。我目前拥有的:

[DllImport("mydll.dll", CallingConvention = CallingConvention.Cdecl)]
private static unsafe extern int DoSomething(IntPtr handle, byte** error);

public static unsafe int DoSomething(IntPtr handle, out string error) {
    byte* buff;

    int ret = DoSomething(handle, &buff);

    if(buff != 0) {
        // ???
    } else {
        error = "";
    }

    return ret;
}

我已经研究过,但我不知道如何将其转换为 byte[],适合提供给 UTF8Encoding.UTF8.GetString()< /code>

我走在正确的道路上吗?

编辑:为了更明确,库函数分配内存,必须通过调用另一个库函数来释放内存。如果解决方案没有给我留下可以释放的指针,则该解决方案是不可接受的。

额外问题:如上所述,该库使用 UTF-8 作为其字符串。我是否需要在 P/Invoke 中执行任何特殊操作,或者仅使用 string 作为普通 const char* 参数?

I am interfacing with code that takes a char** (that is, a pointer to a string):

int DoSomething(Whatever* handle, char** error);

Basically, it takes a handle to its state, and if something goes wrong, it returns an error code and optionally an error message (the memory is allocated externally and freed with a second function. That part I've figued out :) ).

I, however, am unsure how to handle in in C#. What I have currently:

[DllImport("mydll.dll", CallingConvention = CallingConvention.Cdecl)]
private static unsafe extern int DoSomething(IntPtr handle, byte** error);

public static unsafe int DoSomething(IntPtr handle, out string error) {
    byte* buff;

    int ret = DoSomething(handle, &buff);

    if(buff != 0) {
        // ???
    } else {
        error = "";
    }

    return ret;
}

I've poked around, but I can't figure out how to turn that into a byte[], suitable for feeding to UTF8Encoding.UTF8.GetString()

Am I on the right track?

EDIT: To make more explicit, the library function allocates memory, which must be freed by calling another library function. If a solution does not leave me with a pointer I can free, the solution is unacceptable.

Bonus question: As implied above, this library uses UTF-8 for its strings. Do I need to do anything special in my P/Invokes, or just use string for normal const char* parameters?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

我也只是我 2024-10-16 22:12:07

您应该能够使用引用字符串并让运行时默认编组器为您处理此转换。您可以使用 [MarshalAs(UnmanagedType.LPStr)] 提示参数上的字符宽度,以确保您使用的是 8 位字符。

因为您有一个特殊的释放方法可以调用,您需要保留指针,就像您在问题示例中所示的那样。

我是这样写的:

[DllImport("mydll.dll", CallingConvention = CallingConvention.Cdecl)] 
private static unsafe extern int DoSomething(
    MySafeHandle handle, void** error); // byte** should work, too, I'm just lazy

然后你可以获得一个字符串:

var errorMsg = Marshal.PtrToStringAnsi(new IntPtr(*error));

并清理:

[DllImport("mydll.dll", CallingConvention = CallingConvention.Cdecl)] 
private static extern int FreeMyMemory(IntPtr h);

// ...

FreeMyMemory(new IntPtr(error));

现在我们有了编组错误,所以只需返回它即可。

return errorMsg;

另请注意 MySafeHandle 类型,它将继承自 System.Runtime.InteropServices.SafeHandle。虽然不是严格需要的(您可以使用 IntPtr),但它在与本机代码互操作时为您提供更好的句柄管理。请在此处阅读:http://msdn.microsoft。 com/en-us/library/system.runtime.interopservices.safehandle.aspx

You should just be able to use a ref string and have the runtime default marshaller take care of this conversion for you. You can hint the char width on the parameter with [MarshalAs(UnmanagedType.LPStr)] to make sure that you are using 8-bit characters.

Since you have a special deallocation method to call, you'll need to keep the pointer, like you've already shown in your question's example.

Here's how I'd write it:

[DllImport("mydll.dll", CallingConvention = CallingConvention.Cdecl)] 
private static unsafe extern int DoSomething(
    MySafeHandle handle, void** error); // byte** should work, too, I'm just lazy

Then you can get a string:

var errorMsg = Marshal.PtrToStringAnsi(new IntPtr(*error));

And cleanup:

[DllImport("mydll.dll", CallingConvention = CallingConvention.Cdecl)] 
private static extern int FreeMyMemory(IntPtr h);

// ...

FreeMyMemory(new IntPtr(error));

And now we have the marshalled error, so just return it.

return errorMsg;

Also note the MySafeHandle type, which would inherit from System.Runtime.InteropServices.SafeHandle. While not strictly needed (you can use IntPtr), it gives you a better handle management when interoping with native code. Read about it here: http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.safehandle.aspx.

盛夏已如深秋| 2024-10-16 22:12:07

作为参考,这里是编译的代码(但是,尚未测试,正在处理下一个测试,100%工作),它可以满足我的需要。如果有人能做得更好,那就是我所追求的:D

public static unsafe int DoSomething(IntPtr handle, out string error) {
    byte* buff;

    int ret = DoSomething(handle, &buff);

    if(buff != null) {
        int i = 0;

        //count the number of bytes in the error message
        while (buff[++i] != 0) ;

        //allocate a managed array to store the data
        byte[] tmp = new byte[i];

        //(Marshal only works with IntPtrs)
        IntPtr errPtr = new IntPtr(buff);

        //copy the unmanaged array over
        Marshal.Copy(buff, tmp, 0, i);

        //get the string from the managed array
        error = UTF8Encoding.UTF8.GetString(buff);

        //free the unmanaged array
        //omitted, since it's not important

        //take a shot of whiskey
    } else {
        error = "";
    }

    return ret;
}

编辑:修复了 while 循环中的逻辑,它有一个错误。

For reference, here is code that compiles (but, not tested yet, working on that next tested, works 100%) that does what I need. If anyone can do better, that's what I'm after :D

public static unsafe int DoSomething(IntPtr handle, out string error) {
    byte* buff;

    int ret = DoSomething(handle, &buff);

    if(buff != null) {
        int i = 0;

        //count the number of bytes in the error message
        while (buff[++i] != 0) ;

        //allocate a managed array to store the data
        byte[] tmp = new byte[i];

        //(Marshal only works with IntPtrs)
        IntPtr errPtr = new IntPtr(buff);

        //copy the unmanaged array over
        Marshal.Copy(buff, tmp, 0, i);

        //get the string from the managed array
        error = UTF8Encoding.UTF8.GetString(buff);

        //free the unmanaged array
        //omitted, since it's not important

        //take a shot of whiskey
    } else {
        error = "";
    }

    return ret;
}

Edit: fixed the logic in the while loop, it had an off by one error.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文