从 C# 调用 C++ dll 函数时出现问题
Posted
技术标签:
【中文标题】从 C# 调用 C++ dll 函数时出现问题【英文标题】:Problem in calling a C++ dll function from C# 【发布时间】:2009-03-29 21:29:48 【问题描述】:这是我关于 C# 中的河豚问题的第三个线程。尽管事实上我无法在我的应用程序中实现河豚,但我决定将其用作外部 C++ dll。 请注意我已经尝试过 Blowfish.NET 和其他任何方法,问题是我正在将代码从 C++ 转换为 C#,并且 C# 代码必须与 C++ 代码完全相同。
到目前为止:
--->C++ DLL source<---
注意导出的函数在代码末尾
C#代码(定义)
[DllImport("TestDLL.dll", EntryPoint = "Initkey" ,ExactSpelling = true , CallingConvention = CallingConvention.Cdecl)]
public static unsafe extern void Initkey(byte[] key);
[DllImport("TestDLL.dll", EntryPoint = "encode", ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)]
public static unsafe extern void encode(UInt32 *stream);
C#代码(函数调用)
-初始化河豚键
UInt32[] keyarray = new UInt32[2];
//some code
Extern.Initkey(Misc.ConvertFromUInt32Array(keyarray));
//
//
//Helper function used to convert a UInt32 array into Byte array.
public static byte[] ConvertFromUInt32Array(UInt32[] array)
List<byte> results = new List<byte>();
foreach (UInt32 value in array)
byte[] converted = BitConverter.GetBytes(value);
results.AddRange(converted);
return results.ToArray();
-对数据进行编码。
UInt32[] keyarray2 = new UInt32[2];
//some code
unsafe
fixed (UInt32* LPBYTE = keyarray2)
Extern.encode(LPBYTE);
在 keyarray2 被 Encode 函数覆盖后,我通过解密检查 C++ 代码中的值以确保一切正常。
好吧,这不太好。那是我的问题,这就是我向你寻求帮助的原因。
解密时值不同,但如果我在C++源代码中加密和解密,它们是相等的。C++代码是完全一样的,除了没有DLL,因为代码是C++。
可能是因为 Initialize 函数。几个月前我读到 C++ 中的数组是作为指针传递的。我不相信,但即便如此 - 这可能是问题吗?
我找不到线索。我用 C# 中的河豚浪费了我的生命。至少该解决方案应该有效,但它没有 - 为什么?
【问题讨论】:
你浪费了你的妻子? IMO有点过度反应.. 【参考方案1】:要补充 jachymko 所说的内容,还请查看 BitConverter 的文档 - 您需要确保以您想要的字节顺序传递密钥和数据。 注意 - 在您之前的帖子中,我使用修改后的 Blowfish.NET 加密器成功加密了数据,并使其与您的 C++ 代码的结果相匹配。
【讨论】:
@Dave,感谢您的帮助!你能把修改后的 Blowfish.NET 的源上传到某个地方吗? 是的,我会在某处上传源代码。这是一个 C# 2008 项目和一个 C++ 2008 项目——它们都为我产生了相同的输出。 @Dave,谢谢!请在此处粘贴链接,而不是在我的旧线程中。您使用的是我的 C++ 河豚加密,而不是其他的吗? 有效!上帝保佑你戴夫!我误解了你最后的说法,你说我们最后的“修改”造成了一些影响,我认为我必须恢复原来的,所以只有 EncryptBlock 被改变了。我已经成功了,谢谢! 你好戴夫!我遇到了河豚的另一个问题。我无法加密小于 8 字节的字节数组。我做了一些研究,发现为什么它在 C++ Blowfish 中有效,但在 C# 中无效。请查看我的其他线程。 ***.com/questions/705596/…谢谢!【参考方案2】:您是否知道您正在初始化一个临时实例,该实例在 Initkey 函数返回时被销毁?您应该将整个内容组合在一个函数中,或者在堆上创建 cBlowfish 并返回指向 C# 代码的指针(它可以将其视为不透明的 IntPtr)。
此外,您不需要在这里使用 unsafe 。直接传递 UInt32[] 即可。
【讨论】:
【参考方案3】:C 和 C++ 中的数组作为指针传递。 C# 中数组的语义包括一个额外的间接层。您的 C# 程序(可能)正在传递保存数据地址的变量的地址。
我最近在将一个字节数组从 C# 传递给 C 函数时遇到了困难,并且不得不在两个方向上编组数据。对我来说,您的代码正在这样做并不明显。
比解释更好,我将提供一个具体的例子:
//extern "C" LIBEXIFWRAPPER_API void findEXIFtagValue( void * load, unsigned int tagID, char* buf, int bufLen );
[DllImport("libexif-wrapper.dll")]
unsafe public static extern IntPtr findEXIFtagValue([MarshalAs(UnmanagedType.SysUInt)]IntPtr load, int tagID, [MarshalAs(UnmanagedType.SysUInt)]IntPtr buf, int bufLen);
此函数接受一个指向不透明数据的指针、一个 int 和一个字节数组,其中存储了一个文本字符串。
以下 sn-p 显示了如何声明字节数组、传递给 C 函数并提取结果:
const int tagBuffer = 1024;
IntPtr buf = Marshal.AllocHGlobal(tagBuffer);
findEXIFtagValue(loader, (int)ExifTag.EXIF_TAG_DATE_TIME, buf, tagBuffer);
string s = Marshal.PtrToStringAnsi(buf);
请注意,参数在原型中被封送,并再次被封送以检索结果。
这些对我来说都不是特别明显。似乎 IntPtr 是 void* 的 C# 等价物,除非我必须使用 SysUInt 来管理 (C) void*。指向字节数组的指针被封送为 SysUInt。
(来自 C# 菜鸟)
HTH
【讨论】:
【参考方案4】:您可能应该尝试将ref
用于指针,将out
用于通过参数返回数据的函数。让我知道这是否有效。谢谢。
【讨论】:
以上是关于从 C# 调用 C++ dll 函数时出现问题的主要内容,如果未能解决你的问题,请参考以下文章
使用 C# 访问非托管 C++ DLL 时出现 AccessViolationException
使用 MatLab dll 时出现 C++ 错误:找到一个或多个多重定义符号