创建一个基本的 C++ C# 中 p/invoke 的 .dll
我是一名 C# 程序员,不幸的是,由于年龄和经验的限制,我在学习中没有机会经历 C++ 编程时代 - 其中很多内容对我来说是神秘而新鲜的。并不是在这里争论学习这些知识的重要性,而是我需要一些帮助来解决这本应该是一件微不足道的事情。
问题
我需要帮助将我的 C++ 代码打包到 .dll 中。我没有 C++ 经验,并且在制作可以从中 p/调用的工作 .dll 时遇到很大困难(Visual Studio 2010)。请继续阅读更多详细信息以及我尝试打包的代码。
详细信息
我有一些代码需要在非托管环境中运行。通常情况下,简单的 p/invoke 适合此任务。连接一些[ImportDll]
,就可以开始了。最坏的情况我有时可以使用编组。然而,由于我尚未真正发现的原因,这对于我目前的任务来说似乎是不可能的。
我正在尝试从旧的非托管 C++ 应用程序中创建的所有者绘制的列表框
读取一些数据,并从中获取文本。我已经看过几个如何做到这一点的示例 - 但它们是在较旧的 VB6 代码中。
我已经发现了 C++ 的等效项,但这也不适合我。即使使用p/invoke,内存的移动以及它需要如何操作似乎也有很多麻烦。这个过程非常简单。
我需要创建一个简单的 C++ .dll,它在非托管代码中运行我需要的方法,并用结果填充变量。
尽管这听起来很愚蠢,但我不知道该怎么做。我尝试在 Visual Studio 中运行简单的 C++ 向导(使用 Visual Studio 2010),但运气不佳。我希望有人能帮助我解决这个问题,也许还能解释一下发生了什么,以便我可以从中学习。
这是我需要放入 .dll
中的代码。
目的是让 hListWnd
在 C# 中作为 IntPtr
传递,index
将是一个简单的 Int32
,并且outputResult
将执行此操作所需的任何操作。在其他 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 );
}
}
}
如果有人可以帮助我,请,我在这里乞求。这对我来说已经成为一个非常令人沮丧的问题。我尝试使用 RtlMoveMemory 和 ReadProcessMemory p/invoke 在 C# 中重现这一点,但无济于事。我已经寻找了好几天了,并且不断地得到无效的结果。我知道有些代码没有多大意义(有人问我为什么需要 +6
以及 +24
是什么,等),而绝对的事实是我并不完全知道。我所知道的是所有者绘制的列表框在其上使用了一些图形,这些偏移的目的是检查绘制图形区域之外的数据,因为<的实际结构代码>列表项未知。
我尝试过的另一种方法是使用 Accessibility.dll
库以 IAccessible
形式检索我的项目。这也被证明是徒劳的。我还尝试过来自 pinvoke.net 的 ManagedWinApi
,但那里的库没有成功。
我知道这段代码有效。它只是在 C# 中不起作用。我的理论是,使用 [DllImport]
在 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. 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. 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). 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. Wire up some [ImportDll]
and you're good to go. 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. I have seen several examples of how to do this - but they are in older VB6 code.
I have discovered the C++ equivalent, but this too is unsuitable for me. Even with p/invoke, there seems to be a lot of trouble with the moving of memory and how it needs to operate. 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.
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. 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
.
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. 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.
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. 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. 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.
Another method I have tried is retrieving my item as an IAccessible
using the Accessibility.dll
library. This too proved fruitless. I have also tried ManagedWinApi
from pinvoke.net and the libraries there did not prove any success.
I know this code works. It just doesn't work in C#. My theory is that using a [DllImport]
to invoke it in C# will give me the desired result.
Thanks again for any help anyone provides.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
第三个参数需要是一个 byte[],编组为 char*,大小为 16。
如果您查看 ReadProcessMemory 的文档,它需要一个缓冲区和一个大小,并且该函数将大小传递为 16 个字节。这意味着您将需要一个 16 字节的数组。运行时应该非常乐意封送大小为 16 的 byte[]。请注意不要将 C++ char 与 C# char 混合 - C# char 实际上是 Windows 上的 C++ wchar_t。 C++ 的 char 是 C# 的字节。
并不是说我对这个函数的工作原理有任何实际的了解 - 我只是跟踪了第三个参数的用法,它实际上只传递给 ReadProcessMemory,其参数有非常清楚的记录。
要打包到 .dll 中(假设您没有其他东西可以进入该 .dll),您需要在开头添加一些内容,要求 C++ 编译器不要破坏名称并为您导出它,生成以下内容作为第一行,
您只需要一个 C++ 源文件,没有标头,并编译为 DLL(位于项目设置中)。除了您需要的函数和任何包含内容之外,您不需要任何代码。
在 C# 中
,不幸的是,您需要确保缓冲区有足够的大小。
The third parameter needs to be a byte[], marshalled as a char*, of size 16.
If you check the documentation for ReadProcessMemory, it takes a buffer and a size, and this function passes the size as 16 bytes. This means that you will need a 16-byte array. 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. The C++ char is C#'s byte.
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.
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
You'll only need one C++ source file, no headers, and compile as DLL (it's in the project settings). You won't need any code except the function and any includes that you need.
In C#
Unfortunately, it's up to you to make sure that buffer is of sufficient size.