简体   繁体   English

难以从C#调用非托管dll

[英]Difficulty calling unmanaged dll from C#

I'm going bleary-eyed trying to figure out why I can't call an external method in an old C++ .dll from my C# application. 我正试着弄清楚为什么我不能在C#应用程序中使用旧的C ++ .dll调用外部方法。

Here's the function header: 这是函数头:

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);

Here's my C# declaration: 这是我的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);

When I call this method I'm getting one of the two following errors (declared in the header): 当我调用此方法时,我得到以下两个错误之一(在标头中声明):

#define SIMPLEPGPENCRYPTFILE_RECIPIENTLISTDOESNOTENDWITHNEWLINE    408
#define SIMPLEPGPENCRYPTFILE_RECIPIENTLISTDOESNOTSTARTWITHGOODCODECHAR   409

This is also defined as a constant in the header: 这也被定义为标题中的常量:

#define INCLUDE_ONLYUSERIDS 1

This is C++ code that is known to work calling this function: 这是已知可以调用此函数的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

Passing this for the 'RecipientList' parameter gives me the '409' error: 将此传递给'RecipientList'参数会给出'409'错误:

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

Passing this for the 'RecipientList' parameter gives me the '408' error: 将此传递给'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();

Can anyone spot an obvious oversight on my part? 我可以发现一个明显的疏忽吗? I feel like I've got everything I need to solve this, but nothing is working as expected. 我觉得我已经拥有了解决这个问题所需要的一切,但没有任何工作能够按预期进行。

Side note: I'm successfully calling a different function in the same .dll. 旁注:我在同一个.dll中成功调用了一个不同的函数。 Also, I've experimented using StringBuilder to construct the RecipientList parameter. 此外,我已经尝试使用StringBuilder来构造RecipientList参数。

Thanks for any suggestions! 谢谢你的任何建议!

TLDR: TLDR:

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

Did some digging based on Jeffora's comment... He's probably right, my first suggestion probably won't buy you anything. 根据Jeffora的评论进行了一些挖掘...他可能是对的,我的第一个建议可能不会给你买任何东西。 I wrote some test code, here's what I found: 我写了一些测试代码,这是我发现的:

When you use this code: 当您使用此代码时:

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

The bytes you end up with on the inside of the function after marshaling as an LPStr are: 在封送为LPStr之后,您在函数内部结束的字节是:

49-67-65-10-0

Which looks like what you want. 这看起来像你想要的。

When you use this code: 当您使用此代码时:

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();

The bytes you get on the inside of the function after marshaling as an LPStr are: 在封送为LPStr之后,在函数内部获得的字节是:

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

Which is ASCII for "System.Char[]". 哪个是“System.Char []”的ASCII。 So you'll definitely need to use an encoder to turn that char[] into the string you want. 因此,您肯定需要使用编码器将char []转换为您想要的字符串。

As for why the first string isn't working... I suspect your DLL is really looking for the value '1', and not the ascii character for '1' (49). 至于为什么第一个字符串不起作用...我怀疑你的DLL真的在寻找值'1',而不是'1'(49)的ascii字符。 In the code that works, you say: 在有效的代码中,您说:

recipients[0] = INCLUDE_ONLYUSERIDS;

where INCLUDE_ONLYUSERIDS = 1 其中INCLUDE_ONLYUSERIDS = 1

Try this: 尝试这个:

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


Original answer: 原始答案:
I believe the issue is that Char.ToString() treats the char as a unicode character, but you're marshalling as LPStr, not LPWStr. 我认为问题在于Char.ToString()将char视为unicode字符,但是你将编组为LPStr,而不是LPWStr。 LPStr is looking for a string of ANSII characters. LPStr正在寻找一串ANSII字符。

To fix this, try using a byte array. 要解决此问题,请尝试使用字节数组。

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

Did You try to change the calling conventions? 您是否尝试更改调用约定? In C++ you declare the function with pascal calling convention and in C# as stdcall. 在C ++中,您使用pascal调用约定声明函数,并在C#中声明为stdcall。 The calling convention must match. 调用约定必须匹配。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM