[转]动态改变MessageBox的按钮文本的语言

本文转自: http://whatsthematrix.spaces.live.com/blog/cns!52A6BFE11C08D828!158.entry

 

通过设置CultureUIInfo无法改变MessageBox的按钮的文本语言,因为这个文本是根据你所安装的操作系统的语言决定的:你装的英文操作系统,
那么就是"Yes""No";你装的中文操作系统,那么就是“是”“否”。
所以对于可以改变语言版本的应用程序,就会遇到英文版应用程序下的MessageBox的按钮显示“是”“否”(中文操作系统);中文版应用程序下的
MessageBox的按钮显示"Yes""No"。傻呼呼。
想达到目的,还是可以的,只是比较麻烦,要不择手段。要使用win-API函数,以及Hook技术
下面是我参照着C++代码写的C#代码
一、首先声明Hook的类型
   public enum HookType
        {
            Keyboard = 2,//键盘操作
            CBT = 5,//窗口操作
            Mouse = 7, //鼠标操作
        };
二、声明API函数
        [DllImport("kernel32.dll")]
        static extern int GetCurrentThreadId();//得到当前的线程ID
        [DllImport("user32.dll")]
        static extern int GetDlgItem(IntPtr hDlg, int nIDDlgItem);//得到Dialog窗口的子项
        [DllImport("user32", EntryPoint = "SetDlgItemText")]
        static extern int SetDlgItemTextA(IntPtr hDlg, int nIDDlgItem, string lpString);//设置Dialog窗口子项的文本
        [DllImport("user32.dll")]
        static extern void UnhookWindowsHookEx(IntPtr handle);//解掉挂钩
        [DllImport("user32.dll")]
        static extern IntPtr SetWindowsHookEx(int idHook, [MarshalAs(UnmanagedType.FunctionPtr)] HookProc lpfn, IntPtr
hInstance, int threadID);//设置挂钩
        [DllImport("user32.dll")]
        static extern IntPtr CallNextHookEx(IntPtr handle, int code, IntPtr wparam, IntPtr lparam);//进行下一个挂钩,如果有的话
三、定义局部变量
        static IntPtr _nextHookPtr;
        static HookProc myProc = new HookProc(MyHookProc);//must be global, or it will be Collected by GC, then no callback
func can be used for the Hook
 delegate IntPtr HookProc(int code, IntPtr wparam, IntPtr lparam);
四、自定义HookProck逻辑,这是最关键的部分,如何劫持人质,如何勒索人质,如何释放人质
   static IntPtr MyHookProc(int code, IntPtr wparam, IntPtr lparam)
          {
            IntPtr hChildWnd;// msgbox is "child"
            // notification that a window is about to be activated
            // window handle is wParam
            if (code == 5)//HCBT_ACTIVATE = 5
            {
                // set window handles of messagebox
                hChildWnd = wparam;
                //to get the text of yes button
                int result;
                if (GetDlgItem(hChildWnd, 6) != 0)//IDYES = 6
                {
                    result = SetDlgItemTextA(hChildWnd, 6, Properties.Resources.YES);//在Project.Resources里自定义文本
                }
                if (GetDlgItem(hChildWnd, 7) != 0)//IDNO = 7
                {
                    result = SetDlgItemTextA(hChildWnd, 7, Properties.Resources.NO);
                }
            }
            else
            {
                CallNextHookEx(_nextHookPtr, code, wparam, lparam);// otherwise, continue with any possible chained hooks
            }
            //return (IntPtr)1; //直接返回了,该消息就处理结束了
            return IntPtr.Zero;//返回,让后面的程序处理该消息
        }
五、提供给外部调用的Hook方法,如在Form_Load时SetHook,Form_Closing时UnHook.
  public static void SetHook()
        {
            if (_nextHookPtr != IntPtr.Zero)//Hooked already
            {
                return;
            }
            _nextHookPtr = SetWindowsHookEx((int)HookType.CBT, myProc, IntPtr.Zero, GetCurrentThreadId());

        }
        public static void UnHook()
        {
            if (_nextHookPtr != IntPtr.Zero)
            {
                UnhookWindowsHookEx(_nextHookPtr);
                _nextHookPtr = IntPtr.Zero;
            }
        }
上面的代码可以放在一个独立文件如APIHelper.cs,供User使用。
目的达到了,看代码就知道,这样不仅可以让button的文本可以根据语言改变,文本的内容也可以自定义。
事实证明抢劫的收获是诱人的,但是风险成本也是很高的,
1.对于挂钩Hook技术,我们还不是很熟悉,比如什么死后SetHook(),什么时候UnHook()就是值得考虑的一个问题,你也可以在程序启动就
SetHook,也可以在进行某项操作后就SetHook,但是一定不要忘了UnHook,否则你的Hook会造成你意想不到的范围的影响。
2.回调函数要判断消息号,所有的Form在Activate的时候都是code = 5,但是我们只有在弹出MessageBox的时候设置Button的文本。所以如果我
们在启动程序的时候就SetHook(),必然会让每次消息处理多走了几段代码,虽然肉眼无法识别这些差别,但是的确不必要。
如果SetHook和UnHook的成本不高,我们可以考虑在MessageBox.Show()的地方进行挂钩和解钩。假如这样,那么如果你的程序里面散布着
MessageBox.show(),你就哭吧;TMS真是英明,把MessageBox.Show()的功能都抽取到了一个全局静态类里面,我只要改这个全局方法就可以了。
可见抽取共用逻辑还是很重要的!
结论:就让我们的程序傻傻的,傻傻要比傻B强得多。
      至少我们知道了,这个问题有解决方案。

 

posted on 2010-11-24 10:05  freeliver54  阅读(5158)  评论(2编辑  收藏  举报

导航