介绍 人所做的任何Windows窗体的编程在。net框架熟悉对话框类。然而,管理对话框不见了几个功能在Win32 API可用,包括可以添加一个帮助按钮,指定一种语言中使用的对话框按钮,并添加一个自定义图标。在本文中,我提出一个管理MessageBoxIndirect Win32 API函数的包装器,它提供了这些功能。 背景 Win32 API包括三个功能向用户显示消息框。这些都是对话框,MessageBoxEx, MessageBoxIndirect。三,MessageBoxIndirect是最强大的,允许您添加一个帮助按钮,自定义图标,按钮的文本和具体的语言。也至少程序员友好,这常常导致人们把一个友好的包装类。这样做的一个例子在c++中可以在代码项目的这篇文章中找到。 获得MessageBoxIndirect函数从托管的。net框架意味着编写一些交互操作的代码使用平台调用段(PInvoke)。段自PInvoke代码可以读写有些麻烦,你通常要做一次,让它工作,忘记它。这意味着你应该包装在一个可重用的类。进入MessageBoxIndirect类,c#包装类暴露MessageBoxIndirect API的完整功能。 设计说明中 MessageBoxIndirect类暴露潜在的四个关键功能MessageBoxIndirect API:选择不同的警报模式,指定一种语言标识符用于按钮,添加一个工作帮助按钮,并添加一个自定义图标。下面我们将讨论每一个细节,但首先我将提到一些设计决策。 MessageBoxIndirect支持标准对话框的行为,比如选择一组标准的按钮和图标。因此,尽可能简化的编程模型,MessageBoxIndirect类使用现有对话框中定义的枚举类,如MessageBoxButtons MessageBoxDefaultButton MessageBoxOptions, DialogResult。 在我看来,对话框类在。net框架是平庸的面向对象设计的一个很好的例子。这是一个静态类。也就是说,它只是提供了一个名称空间几个静态显示方法。十二个静态显示方法,确切地说。为什么这个可怜的设计?想象将会发生什么如果微软想要添加,说,可以指定一个自定义图标的消息框。多少更显过载会迫使他们要补充的吗?所有这些选择,支持显示方法为每个场景的组合非常不利于维护。在这种情况下你应该做的是允许用户创建一个实例,即。”对话框mb = new对话框()”,并设置属性。为加快速度,只添加几个构造函数重载最常见的选择,或许文本、标题和按钮。 我采用了面向对象的模型建议MessageBoxIndirect类上面。让您将您的代码更容易使用这个类,然而,我添加签名匹配的构造函数重载的静态展示方法内置对话框。后,您已经创建了您的MessageBoxIndirect实例并设置所需的所有选项,你只需要调用实例方法显示(返回DialogResult)向用户显示消息框。 演示项目 包括解决方案,MessageBoxIndirect。sln,包含一个Windows应用程序DemoForm表单演示了许多不同的方式使用MessageBoxIndirect类。现在我们将详细讨论这些。在演示项目,SetResult方法显示的输出显示方法(即用户选择)状态栏的窗口。 我包括一个Visual c++的解决方案叫做Win32Resources构建资源DLL包含自定义图标。如果你想尝试演示的一部分,从一个单独的DLL加载自定义图标,构建该解决方案和DLL的输出复制到相同的位置作为演示项目可执行( MessageBoxIndirect bin 调试或 MessageBoxIndirect bin 发布)。 模式 有三种基本的行为,一个消息框可以对用户可以做什么在消息框。这些都是application-modal, task-modal, system-modal。Application-modal消息框接受一个所有者窗口作为参数,不允许任何与给定的互动窗口,直到消息框解雇。Task-modal消息框以同样的方式工作,除了生成的消息框窗口是最高的。这是为了表明一个相对严重的情况。最后,system-modal消息框不允许任何与所有顶级窗口调用应用程序而不需要通过一个所有者窗口。 这是一个例子指定使用MessageBoxIndirect类形态: 隐藏,复制Code
MessageBoxIndirect mb = new MessageBoxIndirect( this, "App Modal", "Test" ); mb.Modality = MessageBoxIndirect.MessageBoxExModality.AppModal; DialogResult dr = mb.Show();
通过LangID MessageBoxIndirect类允许您指定一个语言标识符(LangID)表明语言中使用默认消息框按钮。在下面的例子中,我通过在同一地区标识符(LCID)而不是LangID,但比特0-15的LCID,事实上,LangID,所以我可以离开。langid和lcid进行更深层的讨论超出了本文的范围,但是如果你有兴趣,看看这个MSDN主题在Windows国家语言支持。请注意,如果您选择通过langid不同,你需要有适当的语言(s)安装在您的系统上看到你的努力的成果。 隐藏,复制Code
MessageBoxIndirect mb = new MessageBoxIndirect( "Pass a LangID: " + Thread.CurrentThread.CurrentUICulture.LCID.ToString(), "Test" ); mb.LanguageID = (uint) Thread.CurrentThread.CurrentUICulture.LCID; DialogResult dr = mb.Show();
添加一个帮助按钮 您可以添加一个帮助按钮来你的消息框将ShowHelp属性设置为true。有两种不同的方法,你可以处理的帮助按钮。首先,你可以提供一个委托类型的MsgBoxCallback点击帮助按钮时调用,就像下面的例子: 隐藏,复制Code
MessageBoxIndirect mb = new MessageBoxIndirect( "Help Button", "Test", MessageBoxButtons.YesNoCancel ); mb.ShowHelp = true; mb.ContextHelpID = 555; mb.Callback = new MessageBoxIndirect.MsgBoxCallback( this.ShowHelp ); DialogResult result = mb.Show();
ShowHelp函数返回无效并接受HELPINFO实例。最重要的是,dwContextId HELPINFO成员实例包含上下文ID设置为MessageBoxIndirect类调用之前显示在上面的例子中(555)。 第二种方法来处理,请求帮助WM_HELP消息被发送到父窗口。下面的代码演示了这一点。注意,我们给MessageBoxIndirect类一个所有者窗口(“这”,想必父窗体)在构造函数中作为目标WM_HELP信息: 隐藏,复制Code
MessageBoxIndirect mb = new MessageBoxIndirect( this, "Help Button", "Test", MessageBoxButtons.YesNoCancel ); mb.ShowHelp = true; mb.ContextHelpID = 444; DialogResult result = mb.Show();
处理表单中的WM_HELP消息覆盖的指向,注意信息。LParam指向HELPINFO实例。您可以使用HELPINFO之前,你必须将它分解它。我添加了一个静态的助手方法HELPINFO类称为UnmarshalFrom在这个过程中帮助你。LParam传给它并返回适当的调用帮助HELPINFO供您使用。 添加一个自定义图标 这无疑是最困难的,但我认为最有趣的我努力包装MessageBoxIndirect API的一部分。如果你设置MB_USERICON国旗,MessageBoxIndirect尝试加载资源的ID给lpszIcon MSGBOXPARAMS成员从模块的实例处理实例句柄中提供成员。问题是,这一定是一个传统的Win32资源。net使用一个完全不同的技术来存储和管理资源,和Visual Studio。网2003年版本似乎无法将Win32资源添加到编译后的程序集(更不用说建筑.rc在c#或VB脚本到r文件。网项目),保存应用程序的图标。Win32和。net资源之间的差异的讨论超出了本文的范围,但我想说的是,MessageBoxIndirect显示一个自定义的图标,你必须跳过一些步骤。我看到三个不同的选项,让你的图标进入游戏: 1. 您可以使用一个resource-editing工具构建装配后摔在Win32图标资源。虽然。net资源不使用相同的格式,它创建标准的Win32二进制文件,没有什么阻止你添加资源。我个人没有一个工具可以推荐,所以我不会进一步讨论这个选项。 2. 您可以构建使用csc.exe或vbc.exe命令行编译器,支持/ win32res标志允许您指定一个资源文件编译(r)在Win32资源链接。当然,这个选择意味着你必须放弃Visual Studio构建环境,这可以是一个问题尤其是在大型项目。/ win32res国旗也不符合/ win32icon标记用于指定一个应用程序图标,如果你选择这条路,你必须确保你的所需的应用程序图标资源在Win32 r文件序号最小的图标。 在示例代码中,有一个build.bat脚本演示这种技术使用r文件命名Win32Resources。res供应。在演示的形式,点击“自定义图标(这Exe)”按钮试试。注意,这个按钮将不会给你一个定制图标,除非你使用命令行编译和/ win32res国旗。 3.你可以动态加载资源DLL,通过结果MessageBoxIndirect实例句柄。这是我的首选。在样例项目中,我只提供一个DLL组成的图标资源称为Win32Resources.dll。在实践中,您需要创建资源DLL使用Visual c++之类的工具。段演示形式加载这个模块使用PInvoke LoadLibraryEx和指示MessageBoxIndirect使用它作为源的自定义图标,如以下代码所示: 隐藏,复制Code
if( hWin32Resources == IntPtr.Zero ) { hWin32Resources = LoadLibraryEx( Application.StartupPath + "\Win32Resources.dll", IntPtr.Zero, 0 ); Debug.Assert( hWin32Resources != IntPtr.Zero ); } // Win32 Resource ID of the icon we want to put in the message box. const int Smiley = 102; MessageBoxIndirect mb = new MessageBoxIndirect( "Custom Icon", "Test" ); // Load the icon from the resource DLL that we loaded. mb.Instance = hWin32Resources; mb.UserIcon = new IntPtr(Smiley); DialogResult result = mb.Show();
如果这对你不起作用,检查年代保证Win32Resources DLL是在正确的位置(下一个应用程序可执行文件)。 实现 详细讨论。net互操作的代码以使MessageBoxIndirect类工作本身会填补的一篇文章。幸运的是,有许多伟大的教程段互操作和PInvoke可以在MSDN网站,这里我只会关注突出特定调用MessageBoxIndirect API。 MessageBoxIndirect API函数接受一个参数,这是一个结构包含所有需要的选项生成的消息框对话框。宣言是: 隐藏,复制Code
[DllImport("user32", EntryPoint="MessageBoxIndirect")] private static extern int _MessageBoxIndirect( ref MSGBOXPARAMS msgboxParams );
注意“ref”装饰表明底层API接受一个指向结构的指针。 结构被传递给这个函数有以下管理声明: 隐藏,复制Code
[StructLayout(LayoutKind.Sequential)] public struct MSGBOXPARAMS { public uint cbSize; public IntPtr hwndOwner; public IntPtr hInstance; public String lpszText; public String lpszCaption; public uint dwStyle; public IntPtr lpszIcon; public IntPtr dwContextHelpId; public MsgBoxCallback lpfnMsgBoxCallback; public uint dwLanguageId; };
该结构(以及随后的其他声明)起源于winuser.h平台SDK头文件。注意,我们使用每个处理IntPtr推荐的做法。同时,lpszIcon(稍后我们将讨论更多)被定义为一个IntPtr即使它是LPCTSTR API。这是因为典型的值传递给lpszIcon MAKEINTRESOURCE宏调用的结果,这只是做一些类型转换使它看起来像一个地址传递。 lpfnMsgBoxCallback这个结构包含一个成员,是一个用户按下时调用的回调可选的消息框上的帮助按钮对话框。在。net中,回调函数是自然地实现为代表,因此我们将这个回调在以下兼容委托声明: 隐藏,复制Code
public delegate void MsgBoxCallback( HELPINFO lpHelpInfo );
HELPINFO结构提供了一些有用的信息的特定请求帮助。其管理的声明: 隐藏,复制Code
[StructLayout(LayoutKind.Sequential)] public struct HELPINFO { public uint cbSize; public int iContextType; public int iCtrlId; public IntPtr hItemHandle; public IntPtr dwContextId; public POINT MousePos; };
正如上面所讨论的,一个方法处理消息框上的帮助按钮来处理WM_HELP消息。检索HELPINFO处理WM_HELP时,你需要做一些解封,如以下HELPINFO类中定义的helper方法: 隐藏,复制Code
public static HELPINFO UnmarshalFrom( IntPtr lParam ) { return (HELPINFO) Marshal.PtrToStructure( lParam, typeof( HELPINFO ) ); }
最后,我们定义一个数量winuser.h message-box-related常量的使用在我们的实现中,连同下面的枚举一个消息框来表示各种模态行为可以采取: 隐藏,复制Code
public enum MessageBoxExModality : uint { AppModal = MB_APPLMODAL, SystemModal = MB_SYSTEMMODAL, TaskModal = MB_TASKMODAL }
自定义系统图标有点棘手的工作。从理论上讲,所有要做的就是把一个WM_SETICON消息对话框窗口。这个问题实际上是获取发送消息的窗口句柄。把这事办成,我安装一个挂钩使用SetWindowsHookEx WH_CBT执行线程上的消息。 隐藏,复制Code
DialogResult retval = DialogResult.Cancel; try { // Only hook if we have a reason to, namely, to set the custom icon. if( _sysSmallIcon != IntPtr.Zero ) { HookProc CbtHookProcedure = new HookProc(CbtHookProc); hHook = SetWindowsHookEx(WH_CBT, CbtHookProcedure, (IntPtr) 0, AppDomain.GetCurrentThreadId()); } retval = (DialogResult) _MessageBoxIndirect( ref parms ); } finally { if( hHook > 0 ) { UnhookWindowsHookEx(hHook); hHook = 0; } }
WH_CBT消息可以方便,尤其是在自动化方面的系统。在这种情况下,我对HCBT_CREATEWND消息感兴趣,在窗口创建但WM_CREATE播出前。当我看到它,我确认它是对话框对话框中,加载图标,和发送WM_SETICON消息: 隐藏,收缩,复制Code
private int CbtHookProc(int nCode, IntPtr wParam, IntPtr lParam) { if( nCode == HCBT_CREATEWND ) { // Make sure this is really a dialog. StringBuilder sb = new StringBuilder(); sb.Capacity = 100; GetClassName( wParam, sb, sb.Capacity ); string className = sb.ToString(); if( className == "#32770" ) { // Found it, look to set the icon if necessary. if( _sysSmallIcon != IntPtr.Zero ) { EnsureInstance(); IntPtr hSmallSysIcon = LoadIcon( Instance, _sysSmallIcon ); if( hSmallSysIcon != IntPtr.Zero ) { SendMessage( wParam, WM_SETICON, new IntPtr(ICON_SMALL), hSmallSysIcon ); } } } } return CallNextHookEx(hHook, nCode, wParam, lParam); }
连接时,最后调用CallNextHookEx至关重要。显示对话框并收集结果后,我和UnhookWindowsHookEx清理钩。 其余的大部分MessageBoxIndirect类中的代码来支持所有不同的构造函数,并建立dwStyle价值的高级属性类。 历史 最初版本:10/01/2004更新自定义系统图标:03/03/2007 本文转载于:http://www.diyabc.com/frontweb/news6685.html