空闲内存不清除内存块

发布于 2024-08-06 10:12:38 字数 1393 浏览 6 评论 0原文

我正在使用 DllImport 从我自己的 .net 类调用 c 包装库中的方法。 c dll中的该方法创建一个字符串变量并返回该字符串的指针。

像这样的东西;

_declspec(dllexport) int ReturnString()
{
 char* retval = (char *) malloc(125);
 strcat(retval, "SOMETEXT");
 strcat(retval, "SOMETEXT MORE");
 return (int)retval;
}

然后我使用 Marshall.PtrToStringAnsi(ptr) 读取字符串。在获得字符串的副本后,我只需调用另一个 c 方法 HeapDestroy,该方法位于调用 free(ptr) 的 c 包装库中。

这是问题; 最近,当它像魅力一样工作时,我开始收到“尝试读取或写入受保护的内存区域”异常。经过更深入的分析,我发现,我相信,虽然我为这个指针调用了 free 方法,但指针的值没有被清除,这会在无人值守的情况下填充堆,并使我的 iis 工作进程抛出此异常。顺便说一下,这是一个在c库中调用此方法的网站项目。

您能帮我解决这个问题吗?

当然,这是 C# 代码;

    [DllImport("MyCWrapper.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
    private extern static int ReturnString();

    [DllImport("MyCWrapper.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
    private extern static void HeapDestroy(int ptr);

    public static string GetString()
    {
        try
        {

            int i = ReturnString();
            string result = String.Empty;
            if (i > 0)
            {
                IntPtr ptr = new IntPtr(i);
                result = Marshal.PtrToStringAnsi(ptr);
                HeapDestroy(i);
            }

            return result;
        }
        catch (Exception e)
        {
            return String.Empty;
        }
    }

I am using DllImport to call method in c wrapper library from my own .net class. This method in c dll creates a string variable and returns the pointer of the string.

Something like this;

_declspec(dllexport) int ReturnString()
{
 char* retval = (char *) malloc(125);
 strcat(retval, "SOMETEXT");
 strcat(retval, "SOMETEXT MORE");
 return (int)retval;
}

Then i read the string using Marshall.PtrToStringAnsi(ptr). After i get a copy of the string, i simply call another c method HeapDestroy which is in c wrapper library that calls free(ptr).

Here is the question;
Recently while it is working like a charm, I started to get "Attempted to read or write protected memory area" exception. After a deeper analysis, i figured out, i beleive, although i call free method for this pointer, value of the pointer is not cleared, and this fills the heap unattended and makes my iis worker process to throw this exception. By the way, it is an web site project that calls this method in c library.

Would you kindly help me out on this issue?

Sure, here is C# code;

    [DllImport("MyCWrapper.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
    private extern static int ReturnString();

    [DllImport("MyCWrapper.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
    private extern static void HeapDestroy(int ptr);

    public static string GetString()
    {
        try
        {

            int i = ReturnString();
            string result = String.Empty;
            if (i > 0)
            {
                IntPtr ptr = new IntPtr(i);
                result = Marshal.PtrToStringAnsi(ptr);
                HeapDestroy(i);
            }

            return result;
        }
        catch (Exception e)
        {
            return String.Empty;
        }
    }

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

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

发布评论

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

评论(3

拿命拼未来 2024-08-13 10:12:39

问题可能出在底层 C 代码上。您没有向 strcat 所依赖的字符串添加 NULL 终止符(或检查 malloc 是否返回 NULL)。在这种情况下很容易损坏内存。您可以通过执行以下操作来修复该问题。

retval[0] = '\0';
strcat(retval, "SOMETEXT");

问题的一部分还在于您在系统上耍花招。最好正确地编写它并让系统处理正确运行的代码。第一步是修复本机代码以正确返回字符串。您需要考虑的一件事是,只有某些类型的内存可以由 CLR 本机释放(HGlobal 和 CoTask 分配)。因此,让我们更改函数签名以返回 char* 并使用不同的分配器。

_declspec(dllexport) char* ReturnString()
{
 char* retval = (char *) CoTaskMemAlloc(125);
 retval[0] = '\0';
 strcat(retval, "SOMETEXT");
 strcat(retval, "SOMETEXT MORE");
 return retval;
}

然后,您可以使用以下 C# 签名并使用 Marshal.FreeCoTaskMem 释放 IntPtr。

[DllImport("SomeDll.dll")]
public static extern IntPtr ReturnString();

甚至更好。编组时,如果 CLR 认为需要释放内存,它将使用 FreeCoTaskMem 来执行此操作。这通常与字符串返回相关。由于您使用 CoTaskMemAlloc 分配了内存,因此您可以节省编组+释放步骤并执行以下操作

[DllImport("SomeDll.dll", CharSet=Ansi)]
public static extern String ReturnString();

What may be the problem is the underlying C code. You are not adding a NULL terminator to the string which strcat relies on (or checking for a NULL return from malloc). It's easy to get corrupted memory in that scenario. You can fix that by doing the following.

retval[0] = '\0';
strcat(retval, "SOMETEXT");

Also part of the problem is that you are playing tricks on the system. It's much better to write it correctly and let the system work on correctly functioning code. The first step is fixing up the native code to properly return the string. One thing you need to consider is that only certain types of memory can be natively freed by the CLR (HGlobal and CoTask allocations). So lets change the function signature to return a char* and use a different allocator.

_declspec(dllexport) char* ReturnString()
{
 char* retval = (char *) CoTaskMemAlloc(125);
 retval[0] = '\0';
 strcat(retval, "SOMETEXT");
 strcat(retval, "SOMETEXT MORE");
 return retval;
}

Then you can use the following C# signature and free the IntPtr with Marshal.FreeCoTaskMem.

[DllImport("SomeDll.dll")]
public static extern IntPtr ReturnString();

Even better though. When marshalling, if the CLR ever thinks it needs to free memory it will use FreeCoTaskMem to do so. This is typically relevant for string returns. Since you allocated the memory with CoTaskMemAlloc you can save yourself the marshalling + freeing steps and do the following

[DllImport("SomeDll.dll", CharSet=Ansi)]
public static extern String ReturnString();
天荒地未老 2024-08-13 10:12:39

释放内存并不会清除它,它只是释放它以便可以重新使用它。一些调试版本会为您覆盖内存,以便更容易地发现 0xBAADFOOD 等值的问题。

调用者应该分配内存,而不是传回分配的内存:

_declspec(dllexport) int ReturnString(char*buffer, int bufferSize)
{
    if (bufferSize < 125) {
        return 125;
    } else {
        strcat(buffer, "SOMETEXT");
        strcat(buffer, "SOMETEXT MORE");
        return 0;
    }
}

Freeing memory doesn't clear it, it just frees it up so it can be re-used. Some debug builds will write over the memory for you to make it easier to find problems with values such as 0xBAADFOOD

Callers should allocate memory, never pass back allocated memory:

_declspec(dllexport) int ReturnString(char*buffer, int bufferSize)
{
    if (bufferSize < 125) {
        return 125;
    } else {
        strcat(buffer, "SOMETEXT");
        strcat(buffer, "SOMETEXT MORE");
        return 0;
    }
}
淡紫姑娘! 2024-08-13 10:12:39

虽然内存是由 DLL 分配在与应用程序相同的堆中,但它可能使用不同的内存管理器,具体取决于它所链接的库。您需要确保使用完全相同的库,或者在 DLL 代码本身中添加代码以释放 DLL 分配的内存。

Although memory is allocated by the DLL in the same heap as your application, it MAY be using a different memory manager, depending on the library it was linked with. You need to either make sure you're using the same exact library, or add code to release the memory that the DLL allocates, in the DLL code itself.

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