P / вызов функции, переданной StringBuilder - PullRequest
3 голосов
/ 21 мая 2010

в файле C # у меня есть

class Archiver {
    [DllImport("Archiver.dll")]
    public static extern void archive(string data, StringBuilder response);
}

строковые данные являются входными данными, а в ответе StringBuilder функция записывает что-то

прототип функции архива (написанный на C) выглядит так:

void archive(char * dataChr, char * outChr);

и он получает строку в dataChr, а затем делает

strcpy(outChr,"some big text");

из C # я называю это примерно так:

string message = "some text here";
StringBuilder response = new StringBuilder(10000);
Archiver.archive(message,response);

это работает, но проблема, как вы можете заметить, заключается в том, что я даю значение размеру StringBuilder, но функция архива может вернуть (в разы) больший текст, чем размер, который я дал моему StringBuilder. Любой способ исправить это?

Ответы [ 5 ]

6 голосов
/ 21 мая 2010

Вам нужно написать функцию, которая скажет вам, насколько большим должен быть StringBuilder, а затем вызвать эту функцию для инициализации StringBuilder.

2 голосов
/ 22 мая 2010

Я хотел бы, чтобы неуправляемый код выделил память, а затем позволил бы управляемой стороне скопировать ее в управляемый массив через IntPtr и освободить распределение.

Сначала необходимо, чтобы ваша неуправляемая функция возвращала размер выходного массива:

void archive(char * dataChr, char * outChr, int length);

Тогда управляемая сторона должна получить его как IntPtr:

class Archiver {
    public static byte[] Archive(string data) {
        IntPtr responsePtr = IntPtr.Zero;
        int length = 0;

        // Call the unmanaged function with our output params
        archive(data, responsePtr, length);

        byte[] responseArray;
        try {
            // Create an array for the response
            responseArray = new byte[length];
            // Copy from unmanaged into managed
            Marshal.Copy(responsePtr, responseArray, 0, length);
        } finally {
            // Free the unmanaged memory once copied
            Marshal.FreeHGlobal(responsePtr);
        }

        return responseArray;
    }

    [DllImport("Archiver.dll")]
    private static extern void archive(string data, [Out]IntPtr encoded, [Out]int length);
}

Вы не указали, были ли ваши данные на самом деле строкой или вы использовали строковый буфер для хранения непрозрачных двоичных данных. Если данные ответа являются строкой с нулевым символом в конце, вы можете легко использовать PtrToStringUni или PtrToStringAnsi, чтобы получить string вместо простого массива:

На неуправляемой стороне:

void archive(char * dataChr, char * outChr);

На управляемой стороне:

class Archiver {
    public static string Archive(string data) {
        IntPtr responsePtr = IntPtr.Zero;

        // Call the unmanaged function with our output params
        archive(data, responsePtr);

        string responseString;
        try {
            // Marshal the string from unmanaged SZ String
            responseString = Marshal.PtrToStringAnsi(responsePtr);
        } finally {
            // Free the unmanaged memory once copied
            Marshal.FreeHGlobal(responsePtr);
        }

        return responseString;
    }

    [DllImport("Archiver.dll")]
    private static extern void archive(string data, [Out]IntPtr encoded);
}

NB. Я не тестировал ни один из этих кодов, поэтому могут быть небольшие упущения или ошибки ...

2 голосов
/ 21 мая 2010

Контролируете ли вы реализацию функции archive?Если вы это сделаете, у вас есть несколько вариантов.

1- Функция archive выделяет буфер и возвращает его вызывающей стороне.Недостатком является то, что вызывающей стороне потребуется использовать правильную функцию для освобождения буфера, в противном случае существует риск утечки памяти и даже повреждения кучи.

2 - Передайте размер имеющегося буфера в archiveФункция так, чтобы она не превышала длину буфера при заполнении буфера.

3 - Если у вас может быть функция, которая может возвращать требуемый размер буфера, вы можете использовать это.Это распространенный подход в Windows API: передача null в качестве буфера и указатель на DWORD, который получает требуемый размер буфера, который затем можно выделить и сделать второй вызов, передав выделенный буфер.

0 голосов
/ 21 мая 2010

Однажды у меня была очень похожая проблема, но у меня был исходный код DLL, поэтому я добавил функцию, которая будет выгружать файл, новый вызов выглядел так:прочитать содержимое файла

File.ReadAllText(filename)
0 голосов
/ 21 мая 2010

Вам не нужно указывать размер ответа.

...