使用平台 Invoke (C#) 在非托管代码中分配和释放内存

发布于 2024-12-02 16:41:50 字数 774 浏览 1 评论 0原文

我想在非托管代码 (C++) 中分配和释放内存,我们将它们称为托管代码 (C#) 中的函数。 我不确定下面的代码是否没有内存泄漏?

C# 代码:

[DllImport("SampleDLL.dll")]
public extern void getString([MarshalAs(UnmanagedType.LPStr)] out String strbuilder);

[DllImport("SampleDLL.dll")]
public extern void freeMemory([MarshalAs(UnmanagedType.LPStr)] out String strBuilder);

....

//call to unmanaged code
getString(out str);
Console.WriteLine(str);
freeMemory(out str);

C++ 代码:

extern void _cdecl getString(char **str)
{
    *str = new char[20];
    std::string temp = "Hello world";
    strncpy(*str,temp.c_str(),temp.length()+1);
}

extern void _cdecl freeMemory(char **str)
{
    if(*str)
        delete []*str;
    *str=NULL;
}

I want to allocate and deallocate memory in unmanaged code (C++) and we call them functions from managed code (C#).
Iam not sure whether the following code is fine without memory leaks or not?

C# code:

[DllImport("SampleDLL.dll")]
public extern void getString([MarshalAs(UnmanagedType.LPStr)] out String strbuilder);

[DllImport("SampleDLL.dll")]
public extern void freeMemory([MarshalAs(UnmanagedType.LPStr)] out String strBuilder);

....

//call to unmanaged code
getString(out str);
Console.WriteLine(str);
freeMemory(out str);

C++ code:

extern void _cdecl getString(char **str)
{
    *str = new char[20];
    std::string temp = "Hello world";
    strncpy(*str,temp.c_str(),temp.length()+1);
}

extern void _cdecl freeMemory(char **str)
{
    if(*str)
        delete []*str;
    *str=NULL;
}

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

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

发布评论

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

评论(2

陌路黄昏 2024-12-09 16:41:50

不,这行不通。 pinvoke 编组器将尝试使用 CoTaskMemFree() 释放字符串的内存。否则它不知道你有一个释放函数。这不会很好地工作,您没有使用 CoTaskMemAlloc 分配字符串。在 XP 中这将是无声内存泄漏,在 Vista 及更高版本中将是崩溃。

您必须阻止编组器尝试执行正确的工作:

[DllImport("SampleDLL.dll")]
public extern void getString(out IntPtr strptr);

[DllImport("SampleDLL.dll")]
public extern void freeMemory(IntPtr strptr);

这需要 C# 代码中的 Marshal.PtrToStringAnsi() 自行从返回的指针编组字符串。

No, this cannot work. The pinvoke marshaller is going to try to release the memory for the string with CoTaskMemFree(). It doesn't otherwise know that you have a release function. That's not going to work well, you didn't allocate the string with CoTaskMemAlloc. This is going to be a silent memory leak in XP, a crash in Vista and up.

You have to stop the marshaller from trying to do the right job:

[DllImport("SampleDLL.dll")]
public extern void getString(out IntPtr strptr);

[DllImport("SampleDLL.dll")]
public extern void freeMemory(IntPtr strptr);

Which then requires Marshal.PtrToStringAnsi() in your C# code to marshal the string yourself from the returned pointer.

你又不是我 2024-12-09 16:41:50

我个人认为使用 BSTR 可以最轻松地完成此操作,从而避免导出释放器。

C++

BSTR ANSItoBSTR(const char* input)
{
    BSTR result = NULL;
    int lenA = lstrlenA(input);
    int lenW = ::MultiByteToWideChar(CP_ACP, 0, input, lenA, NULL, 0);
    if (lenW > 0)
    {
        result = ::SysAllocStringLen(0, lenW);
        ::MultiByteToWideChar(CP_ACP, 0, input, lenA, result, lenW);
    } 
    return result;
}

BSTR __stdcall getString()
{
    return ANSItoBSTR("Hello world");
}

当然,如果您使用 Unicode 字符串,那就更容易了。

BSTR __stdcall getString()
{
    return ::SysAllocString(L"Hello world");
}

C#

[DllImport(@"test.dll")]
[return: MarshalAs(UnmanagedType.BStr)]
private static extern string getString();

在 C# 方面就是这样。您只需调用 getString(),它就会返回一个 .net string,并且您不需要编组任何内容或调用释放器。

Personally I think this is most easily done using a BSTR and thus avoiding the need to export a deallocator.

C++

BSTR ANSItoBSTR(const char* input)
{
    BSTR result = NULL;
    int lenA = lstrlenA(input);
    int lenW = ::MultiByteToWideChar(CP_ACP, 0, input, lenA, NULL, 0);
    if (lenW > 0)
    {
        result = ::SysAllocStringLen(0, lenW);
        ::MultiByteToWideChar(CP_ACP, 0, input, lenA, result, lenW);
    } 
    return result;
}

BSTR __stdcall getString()
{
    return ANSItoBSTR("Hello world");
}

Of course, if you are working with Unicode strings its even easier.

BSTR __stdcall getString()
{
    return ::SysAllocString(L"Hello world");
}

C#

[DllImport(@"test.dll")]
[return: MarshalAs(UnmanagedType.BStr)]
private static extern string getString();

And on the C# side that's it. You just call getString() and it returns a .net string and you don't need to marshall anything or call a deallocator.

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