在 C# 中为 p/invoke 创建一个基本的 C++ .dll

Posted

技术标签:

【中文标题】在 C# 中为 p/invoke 创建一个基本的 C++ .dll【英文标题】:Creating a basic C++ .dll for p/invoke in C# 【发布时间】:2011-01-13 12:13:00 【问题描述】:

我是一名 C# 程序员,不幸的是,由于年龄和经验的原因,我没有机会在我的学习中经历 C++ 编程时代 - 其中很多对我来说是神秘而新奇的.并不是真的在这里讨论学习与否的重要性,但我需要一些帮助来解决应该是一件微不足道的事情。

问题

我在将 C++ 代码打包成 .dll 时需要帮助。我没有使用 C++ 的经验,并且在制作可以从(Visual Studio 2010)p/invoke 的工作 .dll 时遇到很大困难。请继续阅读以了解更多详细信息以及我尝试打包的代码。

详情

我有一些代码需要在非托管环境中运行。一般情况下,一个简单的 p/invoke 就适合这个任务。连接一些[ImportDll] 就可以了。最坏的情况我有时可以使用编组。然而,由于我尚未真正发现的原因,这在我目前的任务中似乎是不可能的。

我正在尝试从在旧的非托管 C++ 应用程序中创建的 owner drawn list box 中读取一些数据,并从中获取文本。我已经看到了几个如何做到这一点的例子——但它们是在较旧的 VB6 代码中。

我发现了 C++ 等价物,但这也不适合我。即使使用 p/invoke,内存的移动以及它需要如何操作似乎也有很多麻烦。这个过程非常简单。

我需要创建一个简单的 C++ .dll,它在非托管代码中运行我需要的方法,并用结果填充变量。

这听起来很愚蠢,我不知道该怎么做。我试过在 Visual Studio 中运行简单的 C++ 向导(使用 Visual Studio 2010),但我运气不好。我希望有人能帮我解决这个问题,或许可以解释发生了什么,以便我从中学习。

这是我需要放入.dll 的代码。

目的是让hListWnd 在C# 中作为IntPtr 传递,index 将是一个简单的Int32,而outputResult 将尽一切可能使这项工作发挥作用。在其他 p/invoke 情况下,我已经看到使用 System.Text.StringBuilder 完成此操作,但我愿意与 anything 合作以使这项工作正常进行。

void GetListItemData( HWND hListWnd, long index, char *outputResult )

    int result;
    DWORD processID;
    HANDLE hProcess;
    char *itemData;
    char sDataRead[5];
    DWORD bytes;
    DWORD lListItemHold, lListItemDataHold;
    *outputResult=0;

    if( hListWnd )
    
        GetWindowThreadProcessId( hListWnd, &processID );

        hProcess=OpenProcess( 0x10|0xf0000|PROCESS_VM_READ, 0, processID );

        if( hProcess )
        
            lListItemHold=(DWORD)SendMessage( hListWnd, LB_GETITEMDATA, index-1, 0 );
            lListItemHold=lListItemHold+24;

            result=ReadProcessMemory( hProcess, (void *)lListItemHold, &sDataRead, 4, &bytes );
            if( !result )
            
                RaiseWinErr();
            

            memcpy( &lListItemDataHold, &sDataRead, 4 );
            lListItemDataHold=lListItemDataHold+6;

            ReadProcessMemory( hProcess, (void *)lListItemDataHold, outputResult, 16, &bytes );

            CloseHandle( hProcess );
        
    

如果有人可以帮助我,请,我在这里乞求。这对我来说已经成为一个非常令人沮丧的问题。我曾尝试使用RtlMoveMemoryReadProcessMemory p/invoke 在C# 中重现这一点,但无济于事。我一直在寻找几天和几天,并继续想出死结果。我知道有些代码没有多大意义(有人问我为什么需要+6 以及+24 是什么等),绝对事实是我不需要t 完全知道。我所知道的是所有者绘制的列表框在其上使用了一些图形,而这些偏移量的目的是检查绘制图形区域之外的数据,因为@的实际结构987654335@ 未知。

我尝试过的另一种方法是使用Accessibility.dll 库将我的项目检索为IAccessible。这也被证明是徒劳的。我也尝试过 pinvoke.net 的ManagedWinApi,但那里的库并没有成功。

我知道这段代码有效。它只是在 C# 中不起作用。我的理论是使用[DllImport] 在 C# 中调用它会给我想要的结果。

再次感谢任何人提供的任何帮助。

【问题讨论】:

【参考方案1】:

第三个参数需要是字节[],编组为 char*,大小为 16。

如果您查看 ReadProcessMemory 的文档,它需要一个缓冲区和一个大小,并且此函数将大小作为 16 个字节传递。这意味着您将需要一个 16 字节的数组。运行时应该非常乐意编组大小为 16 的 byte[]。注意不要将 C++ char 与 C# char 混合使用——C# char 实际上是 Windows 上的 C++ wchar_t。 C++ char 是 C# 的字节。

并不是说我对这些函数的工作原理有任何实际的想法——我只是跟踪了第三个参数的用法,它实际上只被传递给了 ReadProcessMemory,其参数已经非常清楚地记录在案。

要打包到 .dll 中(假设您没有其他内容可进入该 .dll),您需要在开头添加一些内容,要求 C++ 编译器不要破坏名称并为您导出它, 将其作为第一行

extern "C" __declspec(dllexport) void GetListItemData( HWND hListWnd, long index, char *outputResult )

您只需要一个 C++ 源文件,没有头文件,并编译为 DLL(它在项目设置中)。除了您需要的函数和任何包含之外,您不需要任何代码。

在 C# 中

[System.Runtime.InteropServices.DllImport(
    DLLPath,
    CallingConvention = CallingConvention.Cdecl
)]
private static extern void GetListItemData(
    System.IntPtr hWnd,
    System.Int32 index,
    [MarshalAs(UnmanagedType.LPArray)]byte[] buffer
);

很遗憾,您需要确保缓冲区足够大。

【讨论】:

谢谢,但这不是我要问的。我正在寻求帮助将其打包成 .dll - 尽管调试它也非常感谢。如果你知道如何用 C# 编写它,我会非常渴望看到它——但我所有的翻译尝试都失败了。 抱歉,您的问题对于当前的实际问题是什么不是很具体。 所以我只想通过 Visual Studio 向导,不让它创建任何东西,只添加一个 .cpp 文件? 我为这么多问题道歉,C++ 对我来说太陌生了。您陈述了代码本身的一些问题,我可以麻烦您在您的文本中发布一个附录并告诉我您将解决什么问题吗?我正在努力跟进,但我有点困惑。 不,不。我并不是说它不好或坏了,因为我不太了解它。 WinAPI 对我来说太具体了,无法分析好坏 - 我不太使用本机 WinAPI。是的 - 您只需要一个 .cpp 文件。包括 Windows 标头(看不到任何其他依赖项)。在设置中设置项目以编译为 .DLL。编译。

以上是关于在 C# 中为 p/invoke 创建一个基本的 C++ .dll的主要内容,如果未能解决你的问题,请参考以下文章

C#高阶与初心:P/Invoke平台调用

在 C# 中使用 P/Invoke 注册 _set_purecall_handler 函数

如何使用 p/invoke 终止从 C# 调用的 C++ 函数

使用 C# P/Invoke 方法在 Struct 内编组数组

在通过 P/Invoke 获得的 C++ 结构上设置 C# 回调

P/Invoke之C#调用动态链接库DLL