内容 介绍GetHashCode在VB6 如何在。net中做一个类似c的导出 让我们做一些更复杂的事情:钩子 .NET来了,让我们回到VB6项目 结论引用历史 介绍 本文的目的是在不涉及COM的情况下解释。net和VB6之间的互操作。为了实现这样的结果,我们将从VB中导出一些类似c语言的函数。NET DLL(等一下,您将看到如何操作)。本教程的第一部分将介绍一种技术使用一个非常简单的示例,然后它将解释了如何设置一个全局钩子使用VB(6和. net),正常情况下不可能没有涉及C或c++,但出口从。net C-way我们会这样做。 GetHashCode方法在VB6 每个优秀的。net程序员都知道String类的GetHashCode方法:它“返回这个字符串的哈希代码”。现在假设,出于某种原因,你需要使用算法[String]。GetHashCode方法在VB6。让我们考虑一种聪明的方法来做这件事。VB6代码(VB6Hasher项目)非常简单。您只有一个带有两个文本框和一个按钮的表单:当您单击按钮时,第一个文本框的内容将被散列,结果将显示在第二个文本框中。 隐藏,复制Code
Private Declare Function JustHash Lib "HashExporter.Net.dll" _ (ByVal str As String) As Long Private Sub btnHash_Click() txtHash.Text = JustHash(txtInput.Text) End Sub
如果你对VB6语法有点熟悉,你已经认识声明语句:它指的是以下简单的函数在一个外部DLL在VB制作。净(HashExporter。Net项目): 隐藏,复制Code
Public Function JustHash(ByVal str As String) As Integer Return str.GetHashCode() End Function
显然,这种语法不足以实现类似于c的导出(也就是说,通过Declare语句从VB6中访问),实际上,您不能在VB中实现这样的导出。NET,既不是c#,也不是我知道的任何。NET语言,除了…除了ILAsm (IL汇编语言)。不要问我为什么使用c#或VB。NET没有实现这个特性… 如何在。net中做一个类似c的导出 构建完hash导出器后。Net项目,打开Visual Studio命令提示符(开始菜单,程序,Microsoft .NET Framework SDK, SDK命令提示符),到达包含HashExporter.Net.dll的文件夹,类型: 隐藏,复制Code
ildasm /out:HashExporter.Net.il HashExporter.Net.dll
这个命令从编译后的DLL中提取ILAsm代码:通过一个目录,您将能够看到HashExporter.Net。和HashExporter.Net。res已经生成。现在,通过一个简单的文本编辑器,让我们对hashexporters . net .il做一些更改。首先要做的是找到以“”开头的那一行。corflags"并将其(通常指定值0x00000001)替换为"。corflags 0x00000002”:这意味着可执行文件将只在Win32上工作。然后,在这一行后面加上以下内容: 隐藏,复制Code
.vtfixup [1] int32 fromunmanaged at VT_01 .data VT_01 = int32(0)
这在我们的例子中是可行的,因为我们只需要导出一个函数,但显然你可以导出任意多的函数: 隐藏,复制Code
.vtfixup [3] int32 fromunmanaged at VT_01 .data VT_01 = int32[3]
然后你必须找到类的声明包含要导出方法(类似私人汽车ansi密封HashExporter.Net.mdlHashExporter . class),内部类块寻找.method公共静态int32 JustHash (string str) cil管理和右花括号后,添加以下代码: 隐藏,复制Code
.vtentry 1:1
.export [1] as JustHash
或更多: 隐藏,复制Code
.vtentry VTable:VTEntryIndex .export [ExportOrdinal] as ExportedFunctionName
VTable表明(VT_XX)数量的VTable(数组存储出口,可以有多个,例如VT_01, VT_02,但通常一个就足够了),VTEntryIndex VTable的元素的索引,ExportOrdinal序数的出口和ExportedFunctionName代表函数的名字将被导出。此时,我们可以在命令提示符下重新编写代码: 隐藏,复制Code
ilasm "HashExporter.Net.il" /DLL /OUT:"HashExporter.Net.dll" /RESOURCE:"HashExporter.Net.res"
如果您以正确的方式完成了所有的通道,那么使用依赖Walker (VS工具)打开新的hashexporters . net .dll,您将在右侧窗格中看到导出函数的名称!这是实现类似c的导出的漫长道路:自动化这个过程是可能的。我开发了一个工具,可以像在C/ c++中那样管理这些导出:使用.def文件。在我的例子中,一个后构建工具读取了一个.defnet文件并用于编辑ILAsm。无论如何,这是针对。net 1.1的,要在2.0中工作,需要进行一些编辑。也许在将来,我会出版它。与此同时,你可以使用这个[^],但我认为用ILAsm手工工作是有趣的。更多细节,请参见非托管代码可以包装托管方法[^],在Microsoft . net汇编程序[^]和标准ECMA-335 -公共语言基础设施(CLI)[^],分区III: CIL指令集。现在你可以尝试运行vb6haser .exe看看会发生什么。 让我们做一些更复杂的事情:钩子 Windows钩子。钩子是一种子类化,只是它不与单个窗口关联,而是与线程甚至整个系统关联。它是一种Windows消息过滤器,允许您在par出现时覆盖或添加功能收到提示信息。MSDN[^]钩子定义: 钩子是系统消息处理机制中的一个点,应用程序可以在其中安装子例程来监视系统中的消息流量,并在某些类型的消息到达目标窗口过程之前处理它们。 要设置一个钩子,你需要从user32.dll调用SetWindowsHookEx。下面是VB6的声明: 隐藏,复制Code
Private Declare Function SetWindowsHookEx Lib "user32" _ Alias "SetWindowsHookExA" (ByVal idHook As Long, ByVal _ lpfn As Long, ByVal hmod As Long, ByVal dwThreadId As Long) As Long
如果你曾经尝试过在VB6中使用SetWindowsHookEx,很可能你知道这种语言有什么限制,事实上MSDN文档说: 如果dwThreadId参数为0或者指定了一个由不同进程创建的线程的标识符,那么lpfn参数必须指向动态链接库(DLL)中的一个钩子子程。 SetWindowsHookEx参考[^] 这是因为您想要监视的线程(或者如果钩子是全局的,dwThreadId = 0的线程)需要将指定的DLL加载到自己进程的地址空间中。当您创建一个钩子时,您必须传递给SetWindowsHookEx一个指向一个函数的指针(lpfn参数)。你可能会说:嗯,有什么问题吗?VB6有运营商的地址!是的,确实是这样,而且使用AddressOf也可以工作,但前提是您要尝试挂钩由调用进程创建的线程!正如MSDN所说,接收指针的参数必须指向一个外部DLL的函数,但是VB6(通常)不能以类似c的方式公开函数!因此,不可能创建全局钩子或与其他进程关联的钩子。 net来了 HookCExport。Net解决方案包含一个称为HookWndProc的函数,它的定义完全类似于CallWndProc[^]。让我们重复本文第一部分中完成的过程,以c方式公开这个钩子过程。 构建HookCExport。Net解决方案使用ildasm在HookCExport.Net. dll编辑HookCExport.Net。如果在HookCExport.Net.DLL中HookWndProc是可见的,检查依赖Walker 重要提示:要避免系统崩溃,还有一件基本的事情要做。一个全局钩子每秒会调用回调函数很多次,因此,这个函数必须非常快,否则你会拖慢整个系统,直到崩溃。如你所知,. net程序在第一次执行时由JIT编译器编译,所以我们的DLL在第一次调用HookWndProc时得到。这是非常危险的,因为在编译库时,同一个函数被多次调用,这总是导致系统阻塞。解决方案从SDK命令提示符手动编译DLL: 隐藏,复制Code
ngen install "HashExporter.Net.dll"
让我们回到VB6项目 这是VB6HookListener项目的核心,你可以在mdlHooking中找到它: 隐藏,复制Code
'Creates the Hook Public Sub Hook(hThread As Long) Dim hProc As Long 'Load the external DLL hModule = Chk(LoadLibrary("HookCExport.Net.DLL")) 'Gets the address of the callback function in the external DLL hProc = Chk(GetProcAddress(hModule, "HookWndProc")) 'Sets the Hook for the message sent using SendMessage hHook = Chk(SetWindowsHookEx(WH_CALLWNDPROC, hProc, hModule, hThread)) [...] End Sub
注意:Chk是一个函数,它检查API调用是否返回0,如果是,则抛出错误,否则返回接收到的值。我们说传递给SetWindowsHookEx的函数指针必须在外部DLL中,所以我们使用了LoadLibrary和GetProcAddress,它们分别在内存中加载指定的DLL,并获取指定函数(HookWndProc)的函数指针(地址)。你可能会问为什么我们没有像在VB6Hasher例子中那样使用声明语句;这有两个原因:首先,不能在通过Declare语句声明的函数上使用地址,其次,不能从SetWindowsHookEx获得所需的模块句柄。最后,钩子被设置好了。解释关于钩子的其余代码超出了本文的范围。 结论 在这篇文章中,我们看到了如何利用VB6中的。net功能来实现一些通常不可能实现的功能,而无需使用笨重的COM包装器,而只需从VB中暴露出来。NET一些类似c的方法。当您需要使用。net的底层特性时,这项技术非常有用(就像设置一个全局钩子一样)。你再也不需要找爸爸帮忙了! 参考文献 MSDN,对于各种Win32 API函数[^]非托管代码可以封装托管方法[^]- Emilio Reale如何在Visual Basic .NET中设置一个钩子[^]- Microsoft知识库(读“.NET Framework中不支持全局钩子”一节,它是假的!我刚刚在本文中演示了它!)内部Microsoft .NET汇编程序[^]- Serge Lidin, Microsoft Press Standard ECMA-335 - Common Language Infrastructure (CLI)[^],分区III: CIL指令集- ECMA-international.org Spy: Monitoring Messages with Spy [^] - vc++ 6.0 SDK示例 历史 2007年6月26日:初任 本文转载于:http://www.diyabc.com/frontweb/news2388.html