简体   繁体   English

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

[英]Creating a basic C++ .dll for p/invoke in C#

I am a C# programmer, and unfortunately due to both age and experience, I did not have the luxury of getting the chance to go through the C++ era of programming in my learning - so much of it is mysterious and new to me. 我是一名C#程序员,不幸的是由于年龄和经验,我没有机会在我的学习中有机会完成编程的C ++时代 - 这对我来说是多么神秘和新鲜。 Not really here to debate the importance of learning such or not, but I am in need of some help with what should be a trivial matter. 这里并没有真正讨论学习这种与否的重要性,但我需要一些应该是一件小事的帮助。

PROBLEM 问题

I need assistance in packaging my C++ code into a .dll. 我需要帮助将我的C ++代码打包成.dll。 I do not have experience with C++ and am having a great deal of difficulty making a working .dll that I can p/invoke from ( Visual Studio 2010 ). 我没有使用C ++的经验,并且我很难创建一个可以从( Visual Studio 2010 )调用的.dll。 Please keep reading for more details and for the code I am trying to package. 请继续阅读以获取更多详细信息以及我要打包的代码。

DETAILS 细节

I have some code that needs to run in an unmanaged environment. 我有一些代码需要在非托管环境中运行。 Under normal circumstances, a simple p/invoke is suitable for this task. 在正常情况下,简单的p / invoke适用于此任务。 Wire up some [ImportDll] and you're good to go. 连接一些[ImportDll] ,你很高兴。 Worst case scenario I can sometimes use Marshalling. 最糟糕的情况我有时可以使用编组。 However for reasons that I have yet to really discover, this does not seem possible with my present task. 然而,由于我还没有真正发现的原因,我目前的任务似乎不可能。

I am trying to read some data from an owner drawn list box that was made in an old unmanaged C++ application, and get the text from it. 我试图从旧的非托管C ++应用程序owner drawn list boxowner drawn list box读取一些数据,并从中获取文本。 I have seen several examples of how to do this - but they are in older VB6 code. 我已经看到了几个如何执行此操作的示例 - 但它们是旧的VB6代码。

I have discovered the C++ equivalent, but this too is unsuitable for me. 我发现了C ++等价物,但这对我来说也不合适。 Even with p/invoke, there seems to be a lot of trouble with the moving of memory and how it needs to operate. 即使使用p / invoke,内存的移动以及它需要如何操作似乎也会遇到很多麻烦。 The process is pretty simplistic. 这个过程非常简单。

I need to create a simple C++ .dll that runs the method I need in unmanaged code, and populates a variable with the result. 我需要创建一个简单的C ++ .dll来运行我在非托管代码中需要的方法,并使用结果填充变量。

As dumb as this sounds, I do not know how to do this . 虽然这听起来很蠢,但我不知道该怎么做 I have tried just running through the simple C++ Wizards in Visual Studio (using Visual Studio 2010) and I'm just not having luck. 我试过在Visual Studio中运行简单的C ++向导(使用Visual Studio 2010),我只是没有运气。 I was hoping someone could help me walk through this, and perhaps explain what is going on so I can learn from it. 我希望有人可以帮我解决这个问题,也许可以解释一下发生了什么,这样我就可以从中学习。

Here is the code I need to place into a .dll . 这是我需要放入.dll的代码。

The intention is for hListWnd to be passed as an IntPtr in C#, index will be a simple Int32 , and outputResult will go to whatever is neccessary to make this work. 目的是让hListWnd在C#中作为IntPtr传递, index将是一个简单的Int32 ,并且outputResult将转到任何必要的东西以使其工作。 In other p/invoke situations, I have seen this done with System.Text.StringBuilder , but I am willing to work with anything to make this work right. 在其他p / invoke情况下,我已经看到使用System.Text.StringBuilder完成此操作,但我愿意使用任何东西来使这项工作正常。

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 );
        }
    }
}

If anyone can help me, please, I am begging here. 如果有人可以帮助我,拜托,我在这里乞讨。 This has become a very frustrating problem for me. 这对我来说已成为一个非常令人沮丧的问题。 I have tried to reproduce this in C# using RtlMoveMemory and ReadProcessMemory p/invoke, but to no avail. 我试图使用RtlMoveMemoryReadProcessMemory p / invoke在C#中重现这一点,但无济于事。 I have been hunting for days and days, and continue to come up with dead results. 我一直在寻找日子和日子,并继续拿出死的结果。 I know that some of the code does not make a lot of sense (I've had people ask me why it is requiring the +6 and what the +24 is, etc), and the absolute truth is that I don't entirely know. 我知道有些代码没有多大意义(我有人问我为什么需要+6+24是什么等等),绝对的事实是我并不完全知道。 What I do know is that the owner drawn list box employs some graphics on it, and the purpose of these offsets is to examine the data beyond where the graphic area is drawn, because the actual structure of the List Item is unknown. 我所知道的是所有者绘制列表框上有一些图形,这些偏移的目的是检查绘制图形区域之外的数据,因为List Item的实际结构是未知的。

Another method I have tried is retrieving my item as an IAccessible using the Accessibility.dll library. 我尝试过的另一种方法是使用Accessibility.dll库将我的项目检索为IAccessible This too proved fruitless. 这也证明没有结果。 I have also tried ManagedWinApi from pinvoke.net and the libraries there did not prove any success. 我也尝试过ManagedWinApi的ManagedWinApi,那里的图书馆没有任何成功。

I know this code works. 我知道这段代码有效。 It just doesn't work in C#. 它只是在C#中不起作用。 My theory is that using a [DllImport] to invoke it in C# will give me the desired result. 我的理论是使用[DllImport]在C#中调用它会给我想要的结果。

Thanks again for any help anyone provides. 再次感谢任何人提供的帮助。

The third parameter needs to be a byte[], marshalled as a char*, of size 16. 第三个参数需要是一个byte [],编组为char *,大小为16。

If you check the documentation for ReadProcessMemory, it takes a buffer and a size, and this function passes the size as 16 bytes. 如果检查ReadProcessMemory的文档,它需要一个缓冲区和一个大小,并且此函数将大小传递为16个字节。 This means that you will need a 16-byte array. 这意味着您将需要一个16字节的数组。 The runtime should be perfectly happy to marshal a byte[] of size 16. Be careful not to mix C++ char with C# char- the C# char is actually a C++ wchar_t on Windows. 运行时应该非常乐意编组一个大小为16的byte []。注意不要将C ++ char与C#char混合使用 - C#char实际上是Windows上的C ++ wchar_t。 The C++ char is C#'s byte. C ++ char是C#的字节。

Not that I have any actual idea about how that functions works- I just traced the usage of the third parameter and it only actually gets passed to ReadProcessMemory, the parameters for which are quite clearly documented. 并不是说我对这些函数的工作方式有任何实际的想法 - 我只是跟踪了第三个参数的用法,它实际上只传递给了ReadProcessMemory,其参数清楚地记录在案。

To package into a .dll (assuming you have nothing else to go into that .dll), you want to add a couple things on to the beginning to ask the C++ compiler not to mangle the name and to export it for you, producing this as the first line 要打包成.dll(假设你没有其他内容可以进入.dll),你想在开头添加一些东西,要求C ++编译器不要破坏名称并为你导出它,产生这个作为第一线

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

You'll only need one C++ source file, no headers, and compile as DLL (it's in the project settings). 您只需要一个C ++源文件,没有标题,并编译为DLL(它在项目设置中)。 You won't need any code except the function and any includes that you need. 除了函数和任何您需要的包含之外,您不需要任何代码。

In C# 在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
);

Unfortunately, it's up to you to make sure that buffer is of sufficient size. 不幸的是,您可以确保缓冲区的大小足够。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM