对于我的自定义 C++ DLL,我的 C# 项目中的 DLLImport(s) 有啥问题?
Posted
技术标签:
【中文标题】对于我的自定义 C++ DLL,我的 C# 项目中的 DLLImport(s) 有啥问题?【英文标题】:What is wrong with my DLLImport(s) in my C# project for my custom C++ DLL?对于我的自定义 C++ DLL,我的 C# 项目中的 DLLImport(s) 有什么问题? 【发布时间】:2019-06-03 20:03:01 【问题描述】:我目前正在学习 C++(最终为 Hooks 创建 DLL)。我在 C# 方面有优势,想通过 pInvoke/DllImports 访问 DLL。
我正在使用 VS 2019(和 .NET Framework 4.7.2)。作为用于学习目的的示例,我复制了 DLL 以及 C++(控制台应用程序),如下所述:https://docs.microsoft.com/en-us/cpp/build/walkthrough-creating-and-using-a-dynamic-link-library-cpp?view=vs-2019。这工作得很好(正如预期的那样)。
现在,当我开始尝试将 DLL 导入 C# 项目时,我遇到了问题(我认为这与 DllImport 相关)
在示例 (c++) 控制台应用程序(正在运行)中:
int main()
// Initialize a Fibonacci relation sequence.
fibonacci_init(1, 1);
// Write out the sequence values until overflow.
do
std::cout << fibonacci_index() << ": " << fibonacci_current() << std::endl;
while (fibonacci_next());
// Report count of values written before overflow.
std::cout << fibonacci_index() + 1 << " Fibonacci sequence values fit in an " << "unsigned 64-bit integer." << std::endl;
在 C# 测试控制台应用程序中(无法正常工作):
class Program
[DllImport("MathLibrary.dll", CallingConvention = CallingConvention.Cdecl)]
internal static extern void fibonacci_init(ulong a, ulong b);
[DllImport("MathLibrary.dll", CallingConvention = CallingConvention.Cdecl))]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool fibonacci_next();
[DllImport("MathLibrary.dll", CallingConvention = CallingConvention.Cdecl)]
internal static extern ulong fibonacci_current();
[DllImport("MathLibrary.dll", CallingConvention = CallingConvention.Cdecl)]
internal static extern uint fibonacci_index();
static void Main(string[] args)
// Initialize a Fibonacci relation sequence.
fibonacci_init(1, 1);
// Write out the sequence values until overflow.
do
Console.WriteLine($"fibonacci_index(): fibonacci_current()");
while (fibonacci_next());
// Report count of values written before overflow.
Console.WriteLine($"fibonacci_index() + 1 Fibonacci sequence values fit in an unsigned 64-bit integer.");
在工作 (C++) 应用程序中,循环达到 92 并打印
92: 12200160415121876738
然后退出循环并打印93 Fibonacci sequence values fit in an unsigned 64-bit integer.
但是,在失败的 (C#) 应用程序中,它一直工作到 92: 12200160415121876738
,但 fabonacci_next()
并没有退出循环,而是继续返回 true
并继续循环 92: 12200160415121876738
行(不再递增,并且永不退出)。
对于 C# 中的 DllImport,我都尝试过:
[DllImport("MathLibrary.dll", CallingConvention = CallingConvention.Cdecl))]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool fibonacci_next();
和:
[DllImport("MathLibrary.dll", CallingConvention = CallingConvention.Cdecl))]
public static extern bool fibonacci_next();
无论哪种方式,结果都相同。如果有人可以向我解释我在使用 DllImport 时做错了什么(导致它永远不会返回 true,当同一个 DLL 在链接到 C++ 控制台应用程序时正常工作时)。
--- 更新 ---
因此,上述示例的解决方案将[return: MarshalAs(UnmanagedType.Bool)]
更改为[return: MarshalAs(UnmanagedType.I1)]
确实有效(在下面的cmets 中GSerg 提供的链接中提供了更多重要的相关信息)。
--- 更新 2 ---
(@David)我故意(并且错误地)省略了导出,因为它们位于我提供给原始 DLL“演练”的链接中(我很抱歉)。为了问题的完整性,出口是:
#ifdef MATHLIBRARY_EXPORTS
#define MATHLIBRARY_API __declspec(dllexport)
#else
#define MATHLIBRARY_API __declspec(dllimport)
#endif
extern "C" MATHLIBRARY_API void fibonacci_init(unsigned long long a, unsigned long long b);
extern "C" MATHLIBRARY_API bool fibonacci_next();
extern "C" MATHLIBRARY_API unsigned long long fibonacci_current();
extern "C" MATHLIBRARY_API unsigned fibonacci_index();
【问题讨论】:
请解释一下投反对票,也许我可以改写一下? C++ 的bool
不是 C# 的 bool
。 UnmanagedType.Bool
是 C++ 的 BOOL
,而不是 bool
。见***.com/q/32110152/11683。
没有这也是一个问题(参见***.com/q/53898839/11683)。
@SimonMourier 他们改变了事情,我想我必须等待 24 小时才能回答我自己的问题。 (另外我希望其他具有更好“术语”技能的人会首先回答:)
@DavidHeffernan 同意,我理解,因此我的更新和道歉指出我在您之前的评论之后错误地排除了它。
【参考方案1】:
tldr;
解决方案是简单地将 fibonacci_next() 导入的 DllImport 更改为:
[DllImport("MathLibrary.dll", CallingConvention = CallingConvention.Cdecl))]
[return: MarshalAs(UnmanagedType.I1)]
public static extern bool fibonacci_next();
一点额外的:
从技术上讲,这就是回答这个问题所需要的全部内容。对于那些在类似术语下发现这个问题的人,我在下面添加了更多信息,因为问题的真正重点更多是关于理解和学习,而不是复制和粘贴其他人的代码并试图让它工作。
从 GSerg 的评论开始:
C++ 的 bool 不是 C# 的 bool。 UnmanagedType.Bool 是 C++ 的 BOOL,而不是 bool。
我在转换数据类型时使用了这个列表:
下表取自Converting C++ Data Types to C#(我相信周围还有更多官方转换图表)
C++ Type C# Type Size -------- ------- ---- BOOL bool 1 byte BYTE byte 1 byte CHAR byte 1 byte DECIMAL Decimal 16 bytes DOUBLE double 8 bytes DWORD uint, UInt32 4 bytes FLOAT float, single 4 bytes INT, signed int int, Int32 4 bytes INT16, signed short int short, Int16 2 bytes INT32, signed int int, Int32 4 bytes INT64 long, Int64 8 bytes LONG int, Int32 4 bytes LONG32, signed int int, Int32 4 bytes LONG64 long, Int64 8 bytes LONGLONG long, Int64 8 bytes SHORT, signed short int short, Int16 2 bytes UCHAR, unsigned char byte 1 byte UINT, unsigned int uint, UInt32 4 bytes UINT16, WORD ushort, UInt16 2 bytes UINT32, unsigned int uint, UInt32 4 bytes UINT64 ulong, UInt64 8 bytes ULONG, unsigned long uint, UInt32 4 bytes ULONG32 uint, UInt32 4 bytes ULONG64 ulong, UInt64 8 bytes ULONGLONG ulong, UInt64 8 bytes WORD ushort 2 bytes void*, pointers IntPtr x86=4 bytes, x64=8 bytes
由于我刚刚开始学习一点 C++,我假设上面列表中的 (c++) BOOL 等同于 (c++) bool,当没有 [return: MarshalAs(UnmanagedType.Bool)]
失败时,我尝试添加,这也失败了。
有关 UnmanagedTypes 的更多信息的快速注释请参阅Microsoft: UnmanagedType Enum:
UnmanagedType.I1 :一个 1 字节有符号整数。您可以使用此成员将布尔值转换为 1 字节的 C 样式布尔值(true = 1,false = 0)。
我喜欢的另一点是https://***.com/a/32115697/716296 的公认解决方案,它与这个问题并不完全相关,因为我并没有真正尝试改变 c++ 方面,而是将其用作基本的 sn-p代码来了解事物如何跨语言组合在一起。
也可以使用 ref 参数代替返回值。 这实际上是非托管互操作中的常见模式:
我已经看到这种“常见模式”经常与包装非托管库的 3rd 方工具一起使用。当然,如果我正在编写自己的非托管 dll / 库,我将编写它们以最适合它们的目的和用途,这与最初的问题不同,它只是开始学习如何操作的工具。
我希望这个答案对一些人有所帮助(我的术语很糟糕),但这个问题非常基础,我不仅得到了答案,而且现在还找到了一些很好的扩展参考。
【讨论】:
以上是关于对于我的自定义 C++ DLL,我的 C# 项目中的 DLLImport(s) 有啥问题?的主要内容,如果未能解决你的问题,请参考以下文章