我的自定义 C++ DLL 的 C# 项目中的 DLL 端口有什么问题?
What is wrong with my DLLImport(s) in my C# project for my custom C++ DLL?
我目前正在学习C++(最终为Hooks创建DLL)。 我在C#方面有优势,并希望通过pInvoke/DllImports访问DLL。
我正在使用VS 2019(和.NET Framework 4.7.2)。 作为学习目的的示例,我复制了 DLL 以及C++(控制台应用),如下所述:https://learn.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 上做错了什么(当相同的 DLL 在链接到C++控制台应用程序时正常工作时,导致它永远不会返回 true)。
---更新---
因此,上面示例的解决方案将[return: MarshalAs(UnmanagedType.Bool)]
更改为[return: MarshalAs(UnmanagedType.I1)]
确实有效(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();
TLDR;
解决方案是简单地将 fibonacci_next() 导入的 DllImport 更改为:
[DllImport("MathLibrary.dll", CallingConvention = CallingConvention.Cdecl))]
[return: MarshalAs(UnmanagedType.I1)]
public static extern bool fibonacci_next();
一点额外的:
从技术上讲,这就是回答这个问题所需要的一切。 对于那些在类似术语下发现这个问题的人,我在下面添加了更多信息,因为问题的真正意义在于理解和学习,而不是复制和粘贴其他人的代码并试图让它工作。
从GSerg的评论开始:
C++的布尔值不是 C# 的布尔值。UnmanagedType.Bool是C++的BOOL,而不是bool。
我在转换数据类型时使用此列表:
下表取自将C++数据类型转换为 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)]
失败时,我尝试添加它,这也是失败的。
有关非托管类型的详细信息,请参阅Microsoft:非托管类型枚举:
UnmanagedType.I1:一个 1 字节的有符号整数。可以使用此成员将布尔值转换为 1 字节的 C 样式布尔值(true = 1,false = 0)。
我喜欢的另一点是公认的 https://stackoverflow.com/a/32115697/716296 解决方案,这与这个问题并不完全相关,因为我并没有真正尝试改变 c++ 方面的东西,而是将其用作基本代码片段来学习事物如何跨语言组合在一起。
也可以使用 ref 参数代替返回值。 这实际上是非托管互操作中的常见模式:
我已经看到这种"通用模式"经常与包装非托管库的第三方工具一起使用。 当然,如果我正在编写自己的非托管dll/库,我将编写它们以最适合其目的和用法,这与原始问题不同,它只是一个开始学习如何的工具。
我希望这个答案对一些人有所帮助(我不擅长术语),但这个问题非常基本,我不仅得到了答案,而且还找到了一些很好的参考资料,现在也进行了扩展。
- DLL共享数据的推荐方式是什么
- Dll中缺少什么(致命错误LNK1107:无效或损坏的文件:)
- 是否可以将要"ShellExecute"的文件包含在 dll 中?如果是这样,"ShellExecute"中的文件位置应该是什么?
- 如果在 DLL 和调用应用程序中使用 GPGPU API,会发生什么情况
- 我的自定义 C++ DLL 的 C# 项目中的 DLL 端口有什么问题?
- DLL 的"good"调用约定是什么?
- 以下的 c# dll 导入声明是什么?
- 如果在调用 DLL 中的函数时没有传递足够的参数,会发生什么情况?
- 注册和调用 DLL 回调的正确方法是什么
- 测试DLL函数的最佳方法是什么
- 在执行任何程序代码之前,通过静态 .lib 链接到 dll 的程序会发生什么情况
- 在不同于依赖DLL的文件夹中输出已编译目标的正确方法是什么
- 什么时候将整个C 类框架放入DLL中
- STLPORT.5.2.dll 和视觉C++之间有什么关系?我是否需要 stlport 才能运行我的程序
- C++DLL头中的DLL_EXPORT是什么意思
- 在任何平台上执行任何Qt5应用程序的重要dll文件是什么(运行时文件dll)
- 我对此C++函数的 C# DLL 导入进行封送处理有什么问题
- 如果实际导出"ordinal N not found in DLL"错误,可能导致什么原因?
- 是什么原因阻止我将VS2010中的静态库正确链接到我的.dll
- stlportd.5.2.dll 和 stlportstld.5.2.dll 有什么区别?