对于我的自定义 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# 的 boolUnmanagedType.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) 有啥问题?的主要内容,如果未能解决你的问题,请参考以下文章

为啥我的 C# 和 C++ dll 表现出不同的行为?

如何将 C++ dll 连接到 C# 项目?

在 C#(WPF) 中使用 C++ dll

创建 C++ Dll,并从 C# 调用它

C#项目中如何调用C#写的dll中的资源文件,如 xml文件

在 C# 中使用带有字符串的 C++ 类 (Dll)