Получение char * из c ++ в c # и его повторная передача - PullRequest
5 голосов
/ 15 марта 2011

У меня проблемы с утечкой памяти у стороннего c ++ dll. Для определенных вызовов dll выделяет память для строки, передает ее в виде символа * и затем ожидает получить этот указатель обратно, чтобы он мог отменить выделение памяти.

Вот некоторые комментарии из заголовочного файла, пара примеров, где возвращается символ *, и подпись метода «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.

Любая помощь, полученная с благодарностью.

Бретт.

Ответы [ 2 ]

2 голосов
/ 15 марта 2011

Мое лучшее предположение - то, что IntPtr не эквивалентен символу * вашей строки. Поэтому, когда вы вызываете SW_ReleaseString, вы не предоставляете тот же указатель.

Что вы можете сделать, так это собрать немного посредника в C ++ CLI. В C ++ CLI вы получите прямой доступ к char*, а также сможете использовать Marshal :: PtrToString и управляемые строковые указатели с String^.

Вот как я думаю, что выглядело бы так:

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();
0 голосов
/ 16 марта 2011

Я парень на C #, а не на C ++, но в неуправляемых DLL-библиотеках C ++, с которыми я работаю и использую параметры char *, я называю их StringBuilders.Я где-то читал, что для const char * лучший выбор - System.String, но StringBuilder для char *.Однако, если вам нужно сохранить указатель, чтобы вы могли отправить его обратно, чтобы освободить память, возможно, StringBuilder будет работать лучше, поскольку System.String является неизменным?

...