我被客户要求解决以下讨厌的问题。他们有一个定制软件,有一种“左右”显示消息框的倾向,没有任何明显的原因。例如,软件本身是一个会计程序,当他们接受客户付款时,消息框可以连续显示约3或4次。每个消息框都播放Windows默认声音。不幸的是,这个软件的编程方式,它播放的声音类型是完全错误的。例如,当消息本身只是一个信息时,它可能会显示一个警告消息框并播放警告系统声音。对于使用该软件的员工来说,这一切都非常烦人。
我试图联系分发该软件的供应商,但我跟他们打了一个电子邮件。所以现在我正在寻找缓解这个问题的方法。
我最简单的解决方案是建议将扬声器静音,但不幸的是,它们需要声音才能听到收到的电子邮件,最重要的是,能够在以后播放语音邮件。因此,我的解决方案是以某种方式将消息框声音静音,仅用于单个进程。
根据我的经验,我知道有两个API可能会产生这些声音:MessageBeep和较早的Beep。
我还发现this article解释了如何使用AppInit_DLL来挂钩系统API。它工作得很好,除了我需要挂钩的两个API来自User32.dll而不是像作者所建议的那样来自kernel32.dll。
问题部分还有this post,它提供了从User32.dll连接到API的大致步骤,但是当我尝试实现它们时,没有足够的信息(据我所知)。
所以我的问题是,有没有人知道如何挂钩User32.dll模块中的API?
编辑:PS。忘了提。此软件安装在Windows 7 Professional上,禁用UAC - 因为它与UAC不兼容:)
答案 0 :(得分:2)
作为替代方案,您可以修补应用程序。查找MessageBeep
的来电并用nop
覆盖它们。
答案 1 :(得分:1)
这是很难做到的:如果您的应用应该在Vista之前的Windows上以管理员身份运行,您可以通过::GetProcAddress()
获取API的地址,给自己写入的权限它的内存页面,并用“jmp
”汇编指令覆盖API代码的开头,跳转到覆盖函数的地址。确保您的覆盖函数采用相同的参数,并声明为__cdecl
。
扩展答案如下。
API挂钩的“标准”技术包括以下步骤:
这通常首先在目标进程中为包含DLL名称/路径的字符串(例如“MyHook.dll”)分配内存,然后在目标进程中创建一个远程线程,其入口点为{ {1}}将DLL的名称作为参数传递。 This page具有此技术的实现。您必须使用权限进行一些争论,但它保证在Windows XP和早期操作系统上100%工作。我不确定Vista和后Vista,地址空间布局随机化可能会让这个变得棘手。
将DLL加载到目标进程后,其kernel32::LoadLibraryA()
将自动执行,使您有机会在目标进程中运行任何所需内容。在DllMain()
中,使用DllMain
获取包含您要挂接的API的库的::LoadLibraryA()
(例如“user32.dll”)并将其传递给HMODULE
与您要挂钩的API的名称(例如“MessageBeep”)一起获取API本身的地址。 Eventaully给自己写入该地址页面的权限,并用::GetProcAddress()
指令覆盖你的绕道(即进入你的API的“版本”)来覆盖API的开头。请注意,您的绕行需要具有与要挂钩的API相同的签名和调用约定(通常为jmp
),否则将唤醒怪物。
正如这里所描述的,这种技术有点破坏性:你不能从绕道回调原始的API,因为原始的API已被修改为跳入你的,你最终会得到一个非常紧凑和漂亮无限循环。有许多不同的技术可以让您保留和/或回调原始API,其中一种技术是挂钩_cdecl
版本的API然后调用...A()
版本(如果有的话)并非所有...W()
Windows API都将ASCII字符串转换为UNICODE字符串并最终调用其...A()
对应字符串。)
答案 2 :(得分:0)
无需花时间在自定义程序上执行此操作。
您可以在特定应用程序运行时将其静音,并在下次打开应用程序时记住该设置。请参阅https://superuser.com/questions/37281/how-to-disable-sound-of-certain-applications。
还有Windows Sound Sentry可以关闭大多数系统声音,虽然我不知道Sound Sentry的每个应用程序设置。
答案 3 :(得分:0)
您可以使用Deviare API hook并在几个C#行中解决问题。或者您可以使用更困难且更不稳定的EasyHook。