从 C# 调用非托管 dll 很困难

发布于 2024-10-06 22:25:38 字数 3391 浏览 2 评论 0原文

我睡眼惺忪地试图弄清楚为什么我无法从我的 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 参数。

感谢您的任何建议!

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.

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:

        [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:

    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:

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

Passing this for the 'RecipientList' parameter gives me the '408' error:

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. Also, I've experimented using StringBuilder to construct the RecipientList parameter.

Thanks for any suggestions!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

咽泪装欢 2024-10-13 22:25:38

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”的ascii字符(49)。在有效的代码中,您可以这样说:

recipients[0] = INCLUDE_ONLYUSERIDS;

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

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. 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:

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:

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

Which is ASCII for "System.Char[]". So you'll definitely need to use an encoder to turn that char[] into the string you want.

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). In the code that works, you say:

recipients[0] = INCLUDE_ONLYUSERIDS;

where 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. LPStr is looking for a string of ANSII characters.

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

噩梦成真你也成魔 2024-10-13 22:25:38

您是否尝试更改调用约定?
在 C++ 中,您可以使用 pascal 调用约定来声明该函数,而在 C# 中,您可以将其声明为 stdcall。
调用约定必须匹配。

Did You try to change the calling conventions?
In C++ you declare the function with pascal calling convention and in C# as stdcall.
The calling convention must match.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文