Сложность вызова неуправляемой DLL из C # - PullRequest
3 голосов
/ 09 декабря 2010

Я иду тупо, пытаясь выяснить, почему я не могу вызвать внешний метод в старом C ++ .dll из моего приложения на C #.

Вот заголовок функции:

int __export FAR PASCAL SimplePGPEncryptFile(
                                      HWND hWnd1,
                                      LPSTR InputFileName,
                                      LPSTR OutputFileName,
                                      BOOL SignIt,
                                      BOOL Wipe,
                                      BOOL Armor,
                                      BOOL TextMode,
                                      BOOL IDEAOnly,
                                      BOOL UseUntrustedKeys,
                                      LPSTR RecipientList,
                                      LPSTR SignerKeyID,
                                      int SignerBufferLen,
                                      LPSTR SignerPassphrase,
                                      int SignerPwdBufferLen,
                                      LPSTR IDEAPassphrase,
                                      int IDEAPwdBufferLen,
                                      LPSTR PublicKeyRingName,
                                      LPSTR PrivateKeyRingName);

Вот мое объявление C #:

        [DllImport("smplpgp_32.dll", CallingConvention = CallingConvention.StdCall)]
    public static extern int SimplePGPEncryptFile(
        IntPtr hWnd1,
        [MarshalAs(UnmanagedType.LPStr)] string InputFileName,
        [MarshalAs(UnmanagedType.LPStr)] string OutputFileName,
        bool SignIt,
        bool Wipe,
        bool Armor,
        bool TextMode,
        bool IDEAOnly,
        bool UseUntrustedKeys,
        [MarshalAs(UnmanagedType.LPStr)] string RecipientList,
        [MarshalAs(UnmanagedType.LPStr)] string SignerKeyID,
        int SignerBufferLen,
        [MarshalAs(UnmanagedType.LPStr)] string SignerPassphrase,
        int SignerPwdBufferLen,
        [MarshalAs(UnmanagedType.LPStr)] string IDEAPassphrase,
        int IDEAPwdBufferLen,
        [MarshalAs(UnmanagedType.LPStr)] string PublicKeyRingName,
        [MarshalAs(UnmanagedType.LPStr)] string PrivateKeyRingName);

Когда я вызываю этот метод, я получаю одну из двух следующих ошибок (объявленных в заголовке):

#define SIMPLEPGPENCRYPTFILE_RECIPIENTLISTDOESNOTENDWITHNEWLINE    408
#define SIMPLEPGPENCRYPTFILE_RECIPIENTLISTDOESNOTSTARTWITHGOODCODECHAR   409

Thisтакже определяется как константа в заголовке:

#define INCLUDE_ONLYUSERIDS 1

Это код C ++, который, как известно, работает при вызове этой функции:

    char recipients[512];
recipients[0] = INCLUDE_ONLYUSERIDS;
strcat(strcpy(&recipients[1], rID), "\n"); \\ rID is equal to "CA"
return 0 == SimplePGPEncryptFile(INI.m_hWnd,
    (char*)plain, (char*)cipher,
    0,
    0,
    1,
    0,
    0,
    1, // UseUntrustedKeys
    recipients,
    0, 0,
    0, 0,
    0, 0,
    PGPKM.pub, 0); //PGPKM.pub is declared earlier

Передача этого параметра в RecipientList даетмне ошибка '409':

string recipientList = "1CA\n\0";

Передача этого значения для параметра 'RecipientList' дает мне ошибку '408':

char[] recipients = new char[512];
recipients[0] = '1';
recipients[1] = 'C';
recipients[2] = 'A';
recipients[3] = '\n'; // also tried '\r', then '\n'
recipients[4] = Char.MinValue;
string paramValue = recipients.ToString();

Может кто-нибудь заметить очевидный упущение с моей стороны?Я чувствую, что у меня есть все, что нужно для решения этой проблемы, но ничего не работает, как ожидалось.

Примечание: я успешно вызываю другую функцию в той же .dll.Кроме того, я экспериментировал с использованием StringBuilder для создания параметра RecipientList.

Спасибо за любые предложения!

Ответы [ 2 ]

2 голосов
/ 09 декабря 2010

TLDR:

string recipientList = (char) 1 + "CA\n\0";

Проделал какие-то раскопки, основываясь на комментариях Джеффоры ... Вероятно, он прав, мое первое предложение, вероятно, ничего вам не купит.Я написал некоторый тестовый код, вот что я нашел:

Когда вы используете этот код:

string recipientList = "1CA\n\0";

Байты, которые вы заканчиваете внутри функции после маршалинга как LPStr,:

49-67-65-10-0

Что похоже на то, что вы хотите.

Когда вы используете этот код:

char[] recipients = new char[512];
recipients[0] = '1';
recipients[1] = 'C';
recipients[2] = 'A';
recipients[3] = '\n'; // also tried '\r', then '\n'
recipients[4] = Char.MinValue;
string paramValue = recipients.ToString();

Байты, которые вы получаете внутри функции после маршалингав качестве LPStr используются:

83-121-115-116-101-109-46-67-104-97-114-91-93

Что означает ASCII для "System.Char []".Так что вам определенно нужно использовать кодировщик, чтобы превратить этот символ [] в нужную вам строку.

Что касается того, почему первая строка не работает ... Я подозреваю, что ваша DLL действительно ищетзначение «1», а не символ ascii для «1» (49).В коде, который работает, вы говорите:

recipients[0] = INCLUDE_ONLYUSERIDS;

, где INCLUDE_ONLYUSERIDS = 1

Попробуйте это:

string recipientList = (char) 1 + "CA\n\0";

Оригинальный ответ: Я считаю, что проблема в том, что Char.ToString () обрабатывает символ как символ Юникода, но вы маршалируете как LPStr, а не LPWStr.LPStr ищет строку символов ANSII.

Чтобы исправить это, попробуйте использовать байтовый массив.

byte[] recipients= new byte[512];
 ...
//  Pass this string to SimplePGPEncryptFile
string recipientsToPass = System.Text.ASCIIEncoding.ASCII.GetString(recipients);

0 голосов
/ 09 декабря 2010

Вы пытались изменить правила вызова? В C ++ вы объявляете функцию с соглашением вызовов Паскаля, а в C # - как stdcall. Соглашение о вызовах должно совпадать.

...