Почему замена char [] на IntPtr или StringBuilder в возвращаемом значении DllImport приводит к тому, что моя программа больше не находит правильную точку входа? - PullRequest
1 голос
/ 18 ноября 2009

РЕДАКТИРОВАТЬ: я только что понял, что это определяется как MACRO, а не функция. Как, черт возьми, я бы импортировал макрос из DLL в C #? (возможно, это новый вопрос).

Это связано с вопросом, который я только что задал:

Как правильно вернуть Char из неуправляемой Dll в C #?

В некоторых ответах предлагается изменить сигнатуру функции на IntPtr или StringBuilder. Я также видел эти решения на нескольких сайтах в моем поиске, в частности, здесь . (Были и другие, но у меня нет времени выслеживать ссылки).

Подпись функции:

[DllImport("api.dll")]       
internal static extern char[] errMessage(int err);

Если я изменяю тип возврата, мой вызов вызывает следующее исключение:

"Невозможно найти точку входа с именем 'errMessage' в DLL"

Я не могу представить, что люди предложили бы это, если бы это не сработало. Я что-то здесь не так делаю? Чего-то не хватает? Как ни печально, но мои навыки C / C ++ ужасны, поэтому я мог упустить что-то действительно простое. Спасибо за любую помощь.

РЕДАКТИРОВАТЬ: подпись функции из документации:

char * errMessage(int err);

Ответы [ 4 ]

2 голосов
/ 18 ноября 2009

Напишите оболочку C ++ / CLI. Это удивительно простой и мощный способ преодолеть разрыв между C # и неуправляемым (старым) C / C ++.

1 голос
/ 18 ноября 2009

РЕДАКТИРОВАТЬ : обновлено для отражения импорта макроса:

Вы не можете.

Теперь вы можете посмотреть, что делает макрос, и реализовать это с помощью P / Invoke. Приведенный ниже совет относится и к этому.


При работе с Platform Invoke (P / Invoke) лучше всего быть максимально явным:

internal static class NativeMethods
{
    private static string DllName = @"api.dll";

    // This uses 'string' assuming you do not have to free the memory.
    [DllImport(DllName, EntryPoint = "errMessage",
         CharSet = YourCharacterSet,              // CharSet.Ansi? .Unicode?
         CallingConvention = DllCallingConvention // .StdCall? .Cdecl?
    )]
    public static string errMessage(int errorCode);
}

Кроме того, лучше всего обеспечить управляемую точку входа, которая делает метод более ".Net". Здесь вы должны убедиться, что выделенная память вызывающей стороны удерживается в течение соответствующего времени (вам, возможно, придется реализовать SafeHandle ) или что обрабатывается другое ручное помахивание.

Предположим, errMessage возвращает строку, которую мы не несем ответственности за освобождение:

public static class ManagedMethods
{
    public static string ErrorMessage(ErrorCode errorCode)
    {
        return NativeMethods.errMessage((int)errorCode);
    }
}
0 голосов
/ 18 ноября 2009

Похоже, что соглашение о вызовах не является стандартным вызовом (это стандартное соглашение, используемое DllImport). C / C ++ использует соглашение о вызовах CDECL, если не объявлено иначе. Таким образом, вы должны добавить это соглашение о вызовах к вызову DllImport.

0 голосов
/ 18 ноября 2009

Вам может понадобиться изменить ваш метод C / C ++, чтобы он возвращал указатель, AFAIK Я не видел DllImport, возвращающий массив символов из функции. Я бы посоветовал вам изменить код API внутри функции errMessage, возможно, вместо возврата назад char [], вы могли бы вернуть обратно указатель на char в вашей библиотеке api.dll ... т.е.

void errMessage (int err, char * ptr);

Во втором примечании ... вы используете это для возврата стандартного кода ошибки Win API, есть класс Marshal.Win32Error, который будет выполнять эту работу за вас? Как вы думаете? Том.

...