从 c++ 接收 char*进入c#,然后再次将其传回

发布于 2024-10-22 11:21:37 字数 2825 浏览 6 评论 0原文

我遇到了第三方 c++ dll 的内存泄漏问题。对于某些调用,dll 为字符串分配内存,将其作为 char* 传递出去,然后期望接收回该指针,以便可以取消分配内存。

以下是头文件中的一些注释、返回 char* 的几个示例以及“Release”方法的签名。

(该 dll 称为 SW_API,它来自贸易清算所 - 如果有人可能已经封装了这个,我很乐意与他们交谈!)。

   /* Strings returned by the API are similarly normal nul-terminated C strings.
   * The user should not attempt to change any of the bytes or read past the
   * terminating nul of any returned string. All returned strings must be
   * released using SW_ReleaseString() once the user is finished with the
   * result. Failure to do this will result in memory leaks.
   */

    /**
     * @typedef const char* SW_XML
     * @brief A string containing an XML documents text.
     * @note As with all output strings, returned XML must be freed
     * by the user. See @ref resource.
     * @sa ErrorCodes
     */
    typedef const char* SW_XML;

    const char* STDAPICALLTYPE SW_GetLastErrorSpecifics();

    SW_ErrCode STDAPICALLTYPE SW_DealGetSWML(SW_LoginID           lh,
                                     const char*          swmlVersion,
                                     SW_DealVersionHandle dealVersionHandle,
                                     SW_XML*              resultXML_out);


    void STDAPICALLTYPE SW_ReleaseString(const char* buffer);

尝试从各种来源进行阅读,我尝试了以下操作:

    // Extern declarations
    [DllImport(sw_api_dll, EntryPoint = "_SW_GetLastErrorSpecifics@0", CharSet = CharSet.Ansi)]
    public static extern IntPtr SW_GetLastErrorSpecifics();

    [DllImport(sw_api_dll, EntryPoint = "_SW_DealGetSWML@16", CharSet = CharSet.Ansi)]
    public static extern int SW_DealGetSWML(int lh, string swmlVersion, string dealVersionHandle, [Out] out IntPtr outputSWML);

    [DllImport(sw_api_dll, EntryPoint = "_SW_ReleaseString@4", CharSet=CharSet.Ansi)]
    public static extern void SW_ReleaseString(IntPtr buffer);


    // Using the externs.
    private static string GetIntPtrStringAndRelease(IntPtr ptr)
    {
        string result = Marshal.PtrToStringAnsi(ptr);
        API.SW_ReleaseString(ptr);
        return result;
    }

    public static int SW_DealGetSWML(int lh, string swmlVersion, string dealVersionHandle, ref string outputSWML)
    {
        IntPtr outputSWML_out = new IntPtr();
        int result = API.SW_DealGetSWML(lh, swmlVersion, dealVersionHandle, out outputSWML_out);

        outputSWML = GetIntPtrStringAndRelease(outputSWML_out);

        return result;
    }

    public static string SW_GetLastErrorSpecifics()
    {
        IntPtr ptr = API.SW_GetLastErrorSpecifics();
        return GetIntPtrStringAndRelease(ptr);
    }

看来我无法获取 API 来释放字符串。

现在,这可能只是 API 中的一个错误,但我对此表示怀疑。 更有可能的是我做了一些根本性错误的事情。

我所知道的是我的工作集一直在增长。

该公司提供了 Java 包装器,但不会扩展到 .Net 包装器。

非常感激收到的任何帮助。

布雷特。

I'm having memory leak issues with a third party c++ dll. For certain calls, the dll allocates memory for the string, passes it out as a char* and then expects to receive that pointer back so that it can de-allocate the memory.

Here are some comments from the header file, a couple of examples of where the char* get returned, and the signature of the "Release" method.

(The dll is called SW_API, it's from a trade clearing house - if anyone has perhaps wrapped this already I'd love to talk to them!).

   /* Strings returned by the API are similarly normal nul-terminated C strings.
   * The user should not attempt to change any of the bytes or read past the
   * terminating nul of any returned string. All returned strings must be
   * released using SW_ReleaseString() once the user is finished with the
   * result. Failure to do this will result in memory leaks.
   */

    /**
     * @typedef const char* SW_XML
     * @brief A string containing an XML documents text.
     * @note As with all output strings, returned XML must be freed
     * by the user. See @ref resource.
     * @sa ErrorCodes
     */
    typedef const char* SW_XML;

    const char* STDAPICALLTYPE SW_GetLastErrorSpecifics();

    SW_ErrCode STDAPICALLTYPE SW_DealGetSWML(SW_LoginID           lh,
                                     const char*          swmlVersion,
                                     SW_DealVersionHandle dealVersionHandle,
                                     SW_XML*              resultXML_out);


    void STDAPICALLTYPE SW_ReleaseString(const char* buffer);

Attempting to read up from various sources, I have tried the following:

    // Extern declarations
    [DllImport(sw_api_dll, EntryPoint = "_SW_GetLastErrorSpecifics@0", CharSet = CharSet.Ansi)]
    public static extern IntPtr SW_GetLastErrorSpecifics();

    [DllImport(sw_api_dll, EntryPoint = "_SW_DealGetSWML@16", CharSet = CharSet.Ansi)]
    public static extern int SW_DealGetSWML(int lh, string swmlVersion, string dealVersionHandle, [Out] out IntPtr outputSWML);

    [DllImport(sw_api_dll, EntryPoint = "_SW_ReleaseString@4", CharSet=CharSet.Ansi)]
    public static extern void SW_ReleaseString(IntPtr buffer);


    // Using the externs.
    private static string GetIntPtrStringAndRelease(IntPtr ptr)
    {
        string result = Marshal.PtrToStringAnsi(ptr);
        API.SW_ReleaseString(ptr);
        return result;
    }

    public static int SW_DealGetSWML(int lh, string swmlVersion, string dealVersionHandle, ref string outputSWML)
    {
        IntPtr outputSWML_out = new IntPtr();
        int result = API.SW_DealGetSWML(lh, swmlVersion, dealVersionHandle, out outputSWML_out);

        outputSWML = GetIntPtrStringAndRelease(outputSWML_out);

        return result;
    }

    public static string SW_GetLastErrorSpecifics()
    {
        IntPtr ptr = API.SW_GetLastErrorSpecifics();
        return GetIntPtrStringAndRelease(ptr);
    }

It seems I just can't get the API to release the strings.

Now, it's possible that this is just a bug in the API, but I doubt it.
More likely is I'm doing something funamentally wrong.

All I know is that my working set just keeps on growing.

The company in question provide a Java wrapper but won't stretch to a .Net wrapper.

Any help most gratefully received.

Brett.

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

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

发布评论

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

评论(2

尴尬癌患者 2024-10-29 11:21:37

我最好的猜测是 IntPtr 不等于字符串的 char* 。因此,当您调用 SW_ReleaseString 时,您没有提供相同的指针。

你能做的就是整合一些 C++CLI 中介。在 C++CLI 中,您将可以直接访问 char*,并且能够通过 String^ 使用 Marshal::PtrToString 和托管字符串指针。

我认为这应该是这样的:

C++/CLI:

String^ GetStringAndRelease(char* ptr)
{
    string result = Marshal::PtrToStringAnsi(ptr);
    SW_ReleaseString(ptr);
    return result;
}

int SW_DealGetSWML(int lh, const char* swmlVersion, const char* dealVersionHandle, String% outputSWML)
{
    char* outputSWML_out;
    int result = SW_DealGetSWML(lh, swmlVersion, dealVersionHandle, outputSWML_out);

    outputSWML = GetStringAndRelease(outputSWML_out);

    return result;
}

String^ SW_GetLastErrorSpecifics()
{
    char* ptr = SW_GetLastErrorSpecifics();
    return GetStringAndRelease(ptr);
}

然后是 C#:

[DllImport(your_wrapper_dll, EntryPoint = "_SW_DealGetSWML@16", CharSet = CharSet.Ansi)]
public static extern int SW_DealGetSWML(int lh, string swmlVersion, string dealVersionHandle, [Out] out string outputSWML);


[DllImport(your_wrapper_dll, EntryPoint = "_SW_GetLastErrorSpecifics@0", CharSet = CharSet.Ansi)]
public static extern string SW_GetLastErrorSpecifics();

My best guess is that the IntPtr is not equivalent to the char* of your string. So when you call SW_ReleaseString, you're not providing the same pointer.

What you can do, is throw together a little C++CLI intermediary. In C++CLI, and you will have access to the char* directly, as well as being able to use Marshal::PtrToString and managed string pointers, with String^.

Here's what I think that would look like:

C++/CLI:

String^ GetStringAndRelease(char* ptr)
{
    string result = Marshal::PtrToStringAnsi(ptr);
    SW_ReleaseString(ptr);
    return result;
}

int SW_DealGetSWML(int lh, const char* swmlVersion, const char* dealVersionHandle, String% outputSWML)
{
    char* outputSWML_out;
    int result = SW_DealGetSWML(lh, swmlVersion, dealVersionHandle, outputSWML_out);

    outputSWML = GetStringAndRelease(outputSWML_out);

    return result;
}

String^ SW_GetLastErrorSpecifics()
{
    char* ptr = SW_GetLastErrorSpecifics();
    return GetStringAndRelease(ptr);
}

and then in C#:

[DllImport(your_wrapper_dll, EntryPoint = "_SW_DealGetSWML@16", CharSet = CharSet.Ansi)]
public static extern int SW_DealGetSWML(int lh, string swmlVersion, string dealVersionHandle, [Out] out string outputSWML);


[DllImport(your_wrapper_dll, EntryPoint = "_SW_GetLastErrorSpecifics@0", CharSet = CharSet.Ansi)]
public static extern string SW_GetLastErrorSpecifics();
随遇而安 2024-10-29 11:21:37

我是 C# 人员,而不是 C++ 人员,但在我使用的非托管 C++ DLL 中使用 char* 参数,我将它们编组为 StringBuilder。我确实在某处读到,对于 const char* 来说,最好的选择是 System.String,但对于 char * 来说,最好的选择是 StringBuilder。但是,如果您需要保留指针,以便可以将其发送回去以释放内存,也许 StringBuilder 会工作得更好,因为 System.String 是不可变的?

I'm a C# guy, not a C++ guy, but in the unmanaged C++ DLLs that I work with that use char* parameters I marshall them as StringBuilders. I did read somewhere that for const char* the best choice is System.String but StringBuilder for char *. However, if you need to keep the pointer so you can send it back to release the memory maybe StringBuilder would work better since System.String is immutable?

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