难以从C#调用非托管dll

时间:2010-12-09 04:43:59

标签: c# c++ dll pinvoke unmanaged

我正试着弄清楚为什么我不能在C#应用程序的旧C ++ .dll中调用外部方法。

这是函数头:

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

这也被定义为标题中的常量:

#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 个答案:

答案 0 :(得分:2)

TLDR:

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

基于Jeffora的评论进行了一些挖掘......他可能是对的,我的第一个建议可能不会给你买任何东西。我写了一些测试代码,这是我发现的:

使用此代码时:

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

“System.Char []”的ASCII是。因此,您肯定需要使用编码器将char []转换为您想要的字符串。

至于为什么第一个字符串不起作用...我怀疑你的DLL真的在寻找值'1',而不是'1'(49)的ascii字符。在有效的代码中,您说:

recipients[0] = INCLUDE_ONLYUSERIDS;

其中INCLUDE_ONLYUSERIDS = 1

试试这个:

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


原答案:
我认为问题在于Char.ToString()将char视为unicode字符,但是您将编组为LPStr,而不是LPWStr。 LPStr正在寻找一串ANSII字符。

要解决此问题,请尝试使用字节数组。

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

答案 1 :(得分:0)

您是否尝试更改通话约定? 在C ++中,您使用pascal调用约定声明函数,并在C#中声明为stdcall。 调用约定必须匹配。