Click here to Skip to main content
Click here to Skip to main content

MessageBox with a timeout for .NET

By , 12 Aug 2004
 

Introduction

Have you ever wanted a message box that times out? Well here you go.

Using the code

  1. Add MessageBoxEx.cs to your project or assembly your project references.
  2. Change MessageBox.Show(...); calls to MessageBoxEx.Show(..., timeout);.
  3. That's it!

Notes

  1. MessageBoxEx has all 12 of the MessageBox.Show overloads.
  2. The timeout is expressed in milliseconds.
  3. MessageBoxEx does not support reentrancy. Good thing too. Why would you want to have more than one message box at the same time?
  4. If the user doesn't press a button before the timeout elapses the function will return the DialogResult of the messagebox dialogs default button.

How does it work?

Just before calling MessageBox.Show(...), SetWindowsHookEx(WH_CALLWNDPROCRET, ...) is called. The hook proc looks for a WM_INITDIALOG on a window with text equal to the message box caption. A windows timer is started on that window with the appropriate timeout. When the timeout fires EndDialog is called with the result set to the dialog default button ID. You can get that ID by sending a dialog box a DM_GETDEFID message. Pretty simple.

References

KB318804: HOW TO: Set a Windows Hook in Visual C#.NET

Acknowledgements

GipsySoft: http://www.gipsysoft.com/messagebox. You'll find a C++ implementation with many more features.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

RodgerB
Software Developer (Senior) Bloomberg
United States United States
Member
No Biography provided

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
QuestionGracias CApo!memberovamendocino23 Feb '13 - 5:37 
Gracias, es muy buena la utilidad! Big Grin | :-D
QuestionThanksmembervasanthkumarmk17 Aug '12 - 2:40 
Really very useful this code. Thanks a lot
Questionit does not work when we use servicenotification message box (please suggest something)memberhsubhashis4 Jun '12 - 23:20 
when servicenotification is set as default option it does not time out
AnswerRe: it does not work when we use servicenotification message box (please suggest something)memberRodgerB4 Jun '12 - 23:34 
It can't find the message box window. Try using the real API. MessageBoxTimeout API[^]
GeneralWowmemberWabiSabi16 Oct '11 - 4:52 
Thanks, you are awesome!
Thumbs Up | :thumbsup:
GeneralConverted to C++/CLImemberSharjith1 Sep '11 - 9:07 
Hi,
 
First let me thank you for this quick and smart implementation. My 5 for the article. Secondly, as a part of using the code in my project which is in C++/CLI, I converted the entire code into C++/CLI including some changes required to suit the C++ language rules and to handle deprecations. I would like to give the code back to the community. Can you please upload it as an addendum to this article? Please let me know how to send you the code too. Thanks!
RegardsSmile | :)
N. Sharjith

GeneralRe: Converted to C++/CLImemberRodgerB1 Sep '11 - 9:28 
That's very generous of you. You could always make a new article with your code and reference mine. But if you want to send it to me I guess I can always add it.
GeneralThank you!memberChrisKane15 May '11 - 22:19 
Just wanted to say thanks for this useful piece of code.
GeneralMy vote of 5membermatrixology4 May '11 - 5:06 
Great code.
GeneralIts not working with DefaultDesktopOnly/ ServiceNotification optionmemberkirangiri4 Jan '11 - 7:26 
Hi,
 
I am trying to use this with option DefaultDesktopOnly/ ServiceNotification. Its not taking the time out and behaves as a normal MessageBox. I am not sure how to debug this.
 
Any inputs will be helpful.
 
-Kiran
Kiran

GeneralIts not working with DefaultDesktopOnly/ ServiceNotification optionmemberkirangiri4 Jan '11 - 7:26 
Hi,
 
I am trying to use this with option DefaultDesktopOnly/ ServiceNotification. Its not taking the time out and behaves as a normal MessageBox. I am not sure how to debug this.
 
Any inputs will be helpful.
 
-Kiran
GeneralMy vote of 5memberFrancesco Pasquesi1 Dec '10 - 3:10 
Realy usefull
GeneralSome enhancementsmemberDanielLey25 Sep '10 - 6:59 
Thanks Rodger for the great posting; it helped me a lot!
 
Attached the vb.net code with a few enhancements:
 
1. The remaining time will be shown either in text or in caption if the placeholder {0} can be found.
2. The timeout can be set to 0 to have no timing.
3. Possibility to show a checkbox below the buttons - e.g.: Dont ask me again. The checkstate will be
returned as CheckAndExDialogResult
4. New DialogResults (ExDialogResult): Save, Discard, TryAgain and Continue to use this MessageBox also as
"Unsaved changes" dialog with save, discard and cancel results; therefore is does not longer return the
System.Windows.Forms.DialogResult.
5. Adding of language support: The button text will be replaced by GUI language - if there are native
speakers of below languages pls. feel free to correct the google translations. Thanks for that!!
Additional languages can be easy included: Just a the related enumeration and change the small part
within the sub: _setLanguage.
The languages are: English, German, French, Spain, Portugese, Polish, Czech and Dutch.
 
Here the code:
#Region " Imports "
Imports System.Text
Imports System.Runtime.InteropServices
Imports System.Security.Permissions
Imports System.Reflection
#End Region
 
'--------------------------------------------------------------------------
'Purpose:       Standard MessageBox with TimeOut
'Original code: http://www.codeproject.com/KB/miscctrl/CsMsgBoxTimeOut.aspx
'               Author: RodgerB / 13.08.2004
'               Licence: CPOL > http://www.codeproject.com/info/cpol10.aspx
'--------------------------------------------------------------------------
'Changed by:    Daniel Leykauf (23.09.2010)
'Changes:       - TimeOut can be set to 0 to have no automated closing
'               - If a placeholder ({0}) is used in Text or Caption, the 
'                 remaining time will be shown
'               - Possibility to show a CheckBox and to retrieve its value
'                 (e.g. "Don't remind me again.")
'               - Added of language support: Buttons will be shown in GUI 
'                 language instead of system language
'--------------------------------------------------------------------------

<Assembly: SecurityPermission(SecurityAction.RequestMinimum, UnmanagedCode:=True)> 
Namespace ExDialogs
 
    Public Enum ExDialogResult
        Abort = 3
        Cancel = 2
        Ignore = 5
        No = 7
        OK = 1
        Retry = 4
        Yes = 6
        TryAgain = 10
        [Continue] = 11
        Save = 12
        Discard = 13
    End Enum
 
    Public Enum ExMessageBoxButtons
        OK = 0
        OKCancel = 1
        AbortRetryIgnore = 2
        YesNoCancel = 3
        YesNo = 4
        RetryCancel = 5
        CancelTryContinue = 6
        SaveDiscardCancel = 7
    End Enum
 
    Public Class ExMessageBox
 
#Region " Show with checkbox "
        Public Shared Function Show(ByVal Text As String, ByVal CheckMsgText As String, ByVal Checked As Boolean, ByVal uTimeout As UInteger) As CheckAndExDialogResult
            _setup([Assembly].GetExecutingAssembly.GetName.Name, uTimeout)
            mChecked = Checked
            mButtons = ExMessageBoxButtons.OK
            mCheckMsgText = CheckMsgText
            Return New CheckAndExDialogResult(_getResult(MessageBox.Show(Text, [Assembly].GetExecutingAssembly.GetName.Name)), Math.Abs(CInt(mChecked)))
        End Function
 
        Public Shared Function Show(ByVal text As String, ByVal caption As String, ByVal CheckMsgText As String, ByVal Checked As Boolean, ByVal uTimeout As UInteger) As CheckAndExDialogResult
            _setup(caption, uTimeout)
            mButtons = ExMessageBoxButtons.OK
            mChecked = Checked
            mCheckMsgText = CheckMsgText
            Return New CheckAndExDialogResult(_getResult(MessageBox.Show(text, caption)), Math.Abs(CInt(mChecked)))
        End Function
 
        Public Shared Function Show(ByVal text As String, ByVal caption As String, ByVal CheckMsgText As String, ByVal Checked As Boolean, ByVal buttons As ExMessageBoxButtons, ByVal uTimeout As UInteger) As CheckAndExDialogResult
            _setup(caption, uTimeout)
            mButtons = buttons
            mChecked = Checked
            mCheckMsgText = CheckMsgText
            Return New CheckAndExDialogResult(_getResult(MessageBox.Show(text, caption, _getButtons)), Math.Abs(CInt(mChecked)))
        End Function
 
        Public Shared Function Show(ByVal text As String, ByVal caption As String, ByVal CheckMsgText As String, ByVal Checked As Boolean, ByVal buttons As ExMessageBoxButtons, ByVal icon As MessageBoxIcon, ByVal uTimeout As UInteger) As CheckAndExDialogResult
            _setup(caption, uTimeout)
            mButtons = buttons
            mChecked = Checked
            mCheckMsgText = CheckMsgText
            Return New CheckAndExDialogResult(_getResult(MessageBox.Show(text, caption, _getButtons, icon)), Math.Abs(CInt(mChecked)))
        End Function
 
        Public Shared Function Show(ByVal text As String, ByVal caption As String, ByVal CheckMsgText As String, ByVal Checked As Boolean, ByVal buttons As ExMessageBoxButtons, ByVal icon As MessageBoxIcon, ByVal defButton As MessageBoxDefaultButton, ByVal uTimeout As UInteger) As CheckAndExDialogResult
            _setup(caption, uTimeout)
            mButtons = buttons
            mChecked = Checked
            mCheckMsgText = CheckMsgText
            Return New CheckAndExDialogResult(_getResult(MessageBox.Show(text, caption, _getButtons, icon, defButton)), Math.Abs(CInt(mChecked)))
        End Function
 
        Public Shared Function Show(ByVal text As String, ByVal caption As String, ByVal CheckMsgText As String, ByVal Checked As Boolean, ByVal buttons As ExMessageBoxButtons, ByVal icon As MessageBoxIcon, ByVal defButton As MessageBoxDefaultButton, ByVal uTimeout As UInteger, ByVal displayHelpButton As Boolean) As CheckAndExDialogResult
            _setup(caption, uTimeout)
            mButtons = buttons
            mChecked = Checked
            mCheckMsgText = CheckMsgText
            Return New CheckAndExDialogResult(_getResult(MessageBox.Show(text, caption, _getButtons, icon, defButton, 0, displayHelpButton)), Math.Abs(CInt(mChecked)))
        End Function
 
        Public Shared Function Show(ByVal text As String, ByVal caption As String, ByVal CheckMsgText As String, ByVal Checked As Boolean, ByVal buttons As ExMessageBoxButtons, ByVal icon As MessageBoxIcon, ByVal defButton As MessageBoxDefaultButton, ByVal options As MessageBoxOptions, _
          ByVal uTimeout As UInteger) As CheckAndExDialogResult
            _setup(caption, uTimeout)
            mButtons = buttons
            mChecked = Checked
            mCheckMsgText = CheckMsgText
            Return New CheckAndExDialogResult(_getResult(MessageBox.Show(text, caption, _getButtons, icon, defButton, options)), Math.Abs(CInt(mChecked)))
        End Function
 
        Public Shared Function Show(ByVal text As String, ByVal caption As String, ByVal CheckMsgText As String, ByVal Checked As Boolean, ByVal buttons As ExMessageBoxButtons, ByVal icon As MessageBoxIcon, ByVal defButton As MessageBoxDefaultButton, ByVal options As MessageBoxOptions, _
           ByVal uTimeout As UInteger, ByVal displayHelpButton As Boolean) As CheckAndExDialogResult
            _setup(caption, uTimeout)
            mButtons = buttons
            mChecked = Checked
            mCheckMsgText = CheckMsgText
            Return New CheckAndExDialogResult(_getResult(MessageBox.Show(text, caption, _getButtons, icon, defButton, options, displayHelpButton)), Math.Abs(CInt(mChecked)))
        End Function
 
        Public Shared Function Show(ByVal text As String, ByVal caption As String, ByVal CheckMsgText As String, ByVal Checked As Boolean, ByVal buttons As ExMessageBoxButtons, ByVal icon As MessageBoxIcon, ByVal defButton As MessageBoxDefaultButton, ByVal options As MessageBoxOptions, _
           ByVal uTimeout As UInteger, ByVal HelpFile As String) As CheckAndExDialogResult
            _setup(caption, uTimeout)
            mButtons = buttons
            mChecked = Checked
            mCheckMsgText = CheckMsgText
            Return New CheckAndExDialogResult(_getResult(MessageBox.Show(text, caption, _getButtons, icon, defButton, options, HelpFile)), Math.Abs(CInt(mChecked)))
        End Function
 
#End Region
 
#Region " Show default // "
        Public Shared Function Show(ByVal text As String, ByVal uTimeout As UInteger) As ExDialogResult
            _setup([Assembly].GetExecutingAssembly.GetName.Name, uTimeout)
            mButtons = ExMessageBoxButtons.OK
            Return _getResult(MessageBox.Show(text, [Assembly].GetExecutingAssembly.GetName.Name))
        End Function
 
        Public Shared Function Show(ByVal text As String, ByVal caption As String, ByVal uTimeout As UInteger) As ExDialogResult
            _setup(caption, uTimeout)
            mButtons = ExMessageBoxButtons.OK
            Return _getResult(MessageBox.Show(text, caption))
        End Function
 
        Public Shared Function Show(ByVal text As String, ByVal caption As String, ByVal buttons As ExMessageBoxButtons, ByVal uTimeout As UInteger) As ExDialogResult
            _setup(caption, uTimeout)
            mButtons = buttons
            Return _getResult(MessageBox.Show(text, caption, _getButtons))
        End Function
 
        Public Shared Function Show(ByVal text As String, ByVal caption As String, ByVal buttons As ExMessageBoxButtons, ByVal icon As MessageBoxIcon, ByVal uTimeout As UInteger) As ExDialogResult
            _setup(caption, uTimeout)
            mButtons = buttons
            Return _getResult(MessageBox.Show(text, caption, _getButtons, icon))
        End Function
 
        Public Shared Function Show(ByVal text As String, ByVal caption As String, ByVal buttons As ExMessageBoxButtons, ByVal icon As MessageBoxIcon, ByVal defButton As MessageBoxDefaultButton, ByVal uTimeout As UInteger) As ExDialogResult
            _setup(caption, uTimeout)
            mButtons = buttons
            Return _getResult(MessageBox.Show(text, caption, _getButtons, icon, defButton))
        End Function
 
        Public Shared Function Show(ByVal text As String, ByVal caption As String, ByVal buttons As ExMessageBoxButtons, ByVal icon As MessageBoxIcon, ByVal defButton As MessageBoxDefaultButton, ByVal uTimeout As UInteger, ByVal displayHelpButton As Boolean) As ExDialogResult
            _setup(caption, uTimeout)
            mButtons = buttons
            Return _getResult(MessageBox.Show(text, caption, _getButtons, icon, defButton, 0, displayHelpButton))
        End Function
 
        Public Shared Function Show(ByVal text As String, ByVal caption As String, ByVal buttons As ExMessageBoxButtons, ByVal icon As MessageBoxIcon, ByVal defButton As MessageBoxDefaultButton, ByVal options As MessageBoxOptions, _
          ByVal uTimeout As UInteger) As ExDialogResult
            _setup(caption, uTimeout)
            mButtons = buttons
            Return _getResult(MessageBox.Show(text, caption, _getButtons, icon, defButton, options))
        End Function
 
        Public Shared Function Show(ByVal text As String, ByVal caption As String, ByVal buttons As ExMessageBoxButtons, ByVal icon As MessageBoxIcon, ByVal defButton As MessageBoxDefaultButton, ByVal options As MessageBoxOptions, _
           ByVal uTimeout As UInteger, ByVal displayHelpButton As Boolean) As ExDialogResult
            _setup(caption, uTimeout)
            mButtons = buttons
            Return _getResult(MessageBox.Show(text, caption, _getButtons, icon, defButton, options, displayHelpButton))
        End Function
 
        Public Shared Function Show(ByVal text As String, ByVal caption As String, ByVal buttons As ExMessageBoxButtons, ByVal icon As MessageBoxIcon, ByVal defButton As MessageBoxDefaultButton, ByVal options As MessageBoxOptions, _
           ByVal uTimeout As UInteger, ByVal HelpFile As String) As ExDialogResult
            _setup(caption, uTimeout)
            mButtons = buttons
            Return _getResult(MessageBox.Show(text, caption, _getButtons, icon, defButton, options, HelpFile))
        End Function
 
#End Region
 
#Region " Show with owner "
        Public Shared Function Show(ByVal owner As IWin32Window, ByVal text As String, ByVal uTimeout As UInteger) As ExDialogResult
            _setup([Assembly].GetExecutingAssembly.GetName.Name, uTimeout)
            mButtons = ExMessageBoxButtons.OK
            Return MessageBox.Show(owner, text, [Assembly].GetExecutingAssembly.GetName.Name)
        End Function
 
        Public Shared Function Show(ByVal owner As IWin32Window, ByVal text As String, ByVal caption As String, ByVal uTimeout As UInteger) As ExDialogResult
            _setup(caption, uTimeout)
            mButtons = ExMessageBoxButtons.OK
            Return MessageBox.Show(owner, text, caption)
        End Function
 
        Public Shared Function Show(ByVal owner As IWin32Window, ByVal text As String, ByVal caption As String, ByVal buttons As ExMessageBoxButtons, ByVal uTimeout As UInteger) As ExDialogResult
            _setup(caption, uTimeout)
            mButtons = buttons
            Return MessageBox.Show(owner, text, caption, _getButtons)
        End Function
 
        Public Shared Function Show(ByVal owner As IWin32Window, ByVal text As String, ByVal caption As String, ByVal buttons As ExMessageBoxButtons, ByVal icon As MessageBoxIcon, ByVal uTimeout As UInteger) As ExDialogResult
            _setup(caption, uTimeout)
            mButtons = buttons
            Return MessageBox.Show(owner, text, caption, _getButtons, icon)
        End Function
 
        Public Shared Function Show(ByVal owner As IWin32Window, ByVal text As String, ByVal caption As String, ByVal buttons As ExMessageBoxButtons, ByVal icon As MessageBoxIcon, ByVal defButton As MessageBoxDefaultButton, _
         Optional ByVal uTimeout As UInteger = 0) As ExDialogResult
            _setup(caption, uTimeout)
            mButtons = buttons
            Return MessageBox.Show(owner, text, caption, _getButtons, icon, defButton)
        End Function
 
        Public Shared Function Show(ByVal owner As IWin32Window, ByVal text As String, ByVal caption As String, ByVal buttons As MessageBoxButtons, ByVal icon As MessageBoxIcon, ByVal defButton As MessageBoxDefaultButton, _
         ByVal options As MessageBoxOptions, ByVal uTimeout As UInteger) As ExDialogResult
            _setup(caption, uTimeout)
            mButtons = buttons
            Return MessageBox.Show(owner, text, caption, _getButtons, icon, defButton, options)
        End Function
#End Region
 
#Region "Save Dialog"
        Public Shared Function Save(ByVal text As String) As ExDialogResult
            _setup([Assembly].GetExecutingAssembly.GetName.Name, 0)
            mButtons = ExMessageBoxButtons.SaveDiscardCancel
            Return _getResult(MessageBox.Show(text, [Assembly].GetExecutingAssembly.GetName.Name, _getButtons, MessageBoxIcon.Exclamation))
        End Function
 
        Public Shared Function Save(ByVal text As String, ByVal defaultButton As MessageBoxDefaultButton) As ExDialogResult
            _setup([Assembly].GetExecutingAssembly.GetName.Name, 0)
            mButtons = ExMessageBoxButtons.SaveDiscardCancel
            Return _getResult(MessageBox.Show(text, [Assembly].GetExecutingAssembly.GetName.Name, _getButtons, MessageBoxIcon.Exclamation, defaultButton))
        End Function
 
        Public Shared Function Save(ByVal text As String, ByVal caption As String) As DialogResult
            _setup(caption, 0)
            mButtons = ExMessageBoxButtons.SaveDiscardCancel
            Return _getResult(MessageBox.Show(text, caption, _getButtons, MessageBoxIcon.Exclamation))
        End Function
 
        Public Shared Function Save(ByVal text As String, ByVal caption As String, ByVal defaultButton As MessageBoxDefaultButton) As ExDialogResult
            _setup(caption, 0)
            mButtons = ExMessageBoxButtons.SaveDiscardCancel
            Return _getResult(MessageBox.Show(text, caption, _getButtons, MessageBoxIcon.Exclamation, defaultButton))
        End Function
 
#End Region
 

 
#Region " Delegates "
        Public Delegate Function HookProc(ByVal nCode As Integer, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As IntPtr
        Public Delegate Sub TimerProc(ByVal hWnd As IntPtr, ByVal uMsg As UInteger, ByVal nIDEvent As UIntPtr, ByVal dwTime As UInteger)
#End Region
 
#Region " Enums "
        <Flags()> _
    Private Enum SetWindowPosFlags As UInteger
            ''' <summary>If the calling thread and the thread that owns the window are attached to different input queues, 
            ''' the system posts the request to the thread that owns the window. This prevents the calling thread from 
            ''' blocking its execution while other threads process the request.</summary>
            ''' <remarks>SWP_ASYNCWINDOWPOS</remarks>
            SynchronousWindowPosition = &H4000
            ''' <summary>Prevents generation of the WM_SYNCPAINT message.</summary>
            ''' <remarks>SWP_DEFERERASE</remarks>
            DeferErase = &H2000
            ''' <summary>Draws a frame (defined in the window's class description) around the window.</summary>
            ''' <remarks>SWP_DRAWFRAME</remarks>
            DrawFrame = &H20
            ''' <summary>Applies new frame styles set using the SetWindowLong function. Sends a WM_NCCALCSIZE message to 
            ''' the window, even if the window's size is not being changed. If this flag is not specified, WM_NCCALCSIZE 
            ''' is sent only when the window's size is being changed.</summary>
            ''' <remarks>SWP_FRAMECHANGED</remarks>
            FrameChanged = &H20
            ''' <summary>Hides the window.</summary>
            ''' <remarks>SWP_HIDEWINDOW</remarks>
            HideWindow = &H80
            ''' <summary>Does not activate the window. If this flag is not set, the window is activated and moved to the 
            ''' top of either the topmost or non-topmost group (depending on the setting of the hWndInsertAfter 
            ''' parameter).</summary>
            ''' <remarks>SWP_NOACTIVATE</remarks>
            DoNotActivate = &H10
            ''' <summary>Discards the entire contents of the client area. If this flag is not specified, the valid 
            ''' contents of the client area are saved and copied back into the client area after the window is sized or 
            ''' repositioned.</summary>
            ''' <remarks>SWP_NOCOPYBITS</remarks>
            DoNotCopyBits = &H100
            ''' <summary>Retains the current position (ignores X and Y parameters).</summary>
            ''' <remarks>SWP_NOMOVE</remarks>
            IgnoreMove = &H2
            ''' <summary>Does not change the owner window's position in the Z order.</summary>
            ''' <remarks>SWP_NOOWNERZORDER</remarks>
            DoNotChangeOwnerZOrder = &H200
            ''' <summary>Does not redraw changes. If this flag is set, no repainting of any kind occurs. This applies to 
            ''' the client area, the nonclient area (including the title bar and scroll bars), and any part of the parent 
            ''' window uncovered as a result of the window being moved. When this flag is set, the application must 
            ''' explicitly invalidate or redraw any parts of the window and parent window that need redrawing.</summary>
            ''' <remarks>SWP_NOREDRAW</remarks>
            DoNotRedraw = &H8
            ''' <summary>Same as the SWP_NOOWNERZORDER flag.</summary>
            ''' <remarks>SWP_NOREPOSITION</remarks>
            DoNotReposition = &H200
            ''' <summary>Prevents the window from receiving the WM_WINDOWPOSCHANGING message.</summary>
            ''' <remarks>SWP_NOSENDCHANGING</remarks>
            DoNotSendChangingEvent = &H400
            ''' <summary>Retains the current size (ignores the cx and cy parameters).</summary>
            ''' <remarks>SWP_NOSIZE</remarks>
            IgnoreResize = &H1
            ''' <summary>Retains the current Z order (ignores the hWndInsertAfter parameter).</summary>
            ''' <remarks>SWP_NOZORDER</remarks>
            IgnoreZOrder = &H4
            ''' <summary>Displays the window.</summary>
            ''' <remarks>SWP_SHOWWINDOW</remarks>
            ShowWindow = &H40
        End Enum
 
        Private Enum DialogStyles
            [Default]
            Save
        End Enum
 

#End Region
 
#Region " User32.dll "
        <DllImport("User32.dll")> _
        Public Shared Function SetTimer(ByVal hWnd As IntPtr, ByVal nIDEvent As UIntPtr, ByVal uElapse As UInteger, ByVal lpTimerFunc As TimerProc) As UIntPtr
        End Function
 
        <DllImport("User32.dll")> _
        Public Shared Function SendMessage(ByVal hWnd As IntPtr, ByVal Msg As Integer, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As IntPtr
        End Function
 
        <DllImport("user32.dll")> _
        Public Shared Function SetWindowsHookEx(ByVal idHook As Integer, ByVal lpfn As HookProc, ByVal hInstance As IntPtr, ByVal threadId As Integer) As IntPtr
        End Function
 
        <DllImport("user32.dll")> _
        Public Shared Function UnhookWindowsHookEx(ByVal idHook As IntPtr) As Integer
        End Function
 
        <DllImport("user32.dll")> _
        Public Shared Function CallNextHookEx(ByVal idHook As IntPtr, ByVal nCode As Integer, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As IntPtr
        End Function
 
        <DllImport("user32.dll")> _
        Public Shared Function GetWindowTextLength(ByVal hWnd As IntPtr) As Integer
        End Function
 
        <DllImport("user32.dll")> _
        Public Shared Function GetWindowText(ByVal hWnd As IntPtr, ByVal text As StringBuilder, ByVal maxLength As Integer) As Integer
        End Function
 
        <DllImport("user32.dll")> _
        Public Shared Function EndDialog(ByVal hDlg As IntPtr, ByVal nResult As IntPtr) As Integer
        End Function
 
        <DllImport("kernel32.dll")> _
        Private Shared Function GetCurrentThreadId() As Integer
        End Function
 
        <DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)> _
        Private Shared Function SetWindowText(ByVal hwnd As IntPtr, ByVal lpString As String) As Boolean
        End Function
 
        <DllImport("user32.dll")> _
         Private Shared Function SetDlgItemText(ByVal hWnd As IntPtr, ByVal nIDDlgItem As Integer, ByVal lpString As String) As Boolean
        End Function
 
        <DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)> _
        Private Shared Function GetDlgItemText(ByVal hDlg As IntPtr, ByVal nIDDlgItem As Integer, <Out()> ByVal lpString As StringBuilder, ByVal nMaxCount As Integer) As UInteger
        End Function
 
        <DllImport("user32.dll", SetLastError:=True)> _
        Private Shared Function SetWindowPos(ByVal hWnd As IntPtr, ByVal hWndInsertAfter As IntPtr, ByVal X As Integer, ByVal Y As Integer, ByVal cx As Integer, ByVal cy As Integer, ByVal uFlags As SetWindowPosFlags) As Boolean
        End Function
 
        <DllImport("user32.dll")> _
        Private Shared Function GetWindowRect(ByVal hWnd As IntPtr, ByRef lpRect As RECT) As Boolean
        End Function
 
        <DllImport("user32.dll", CharSet:=CharSet.Auto)> _
        Private Shared Function CreateWindowEx(ByVal dwExStyle As Integer, ByVal lpClassName As String, ByVal lpWindowName As String, ByVal dwStyle As UInteger, ByVal x As Integer, ByVal y As Integer, ByVal nWidth As Integer, ByVal nHeight As Integer, ByVal hWndParent As Integer, ByVal hMenu As Integer, ByVal hInstance As Integer, ByVal lpParam As Integer) As Integer
        End Function
 
        <DllImport("user32.dll")> _
        Private Shared Function DestroyWindow(ByVal hwnd As Integer) As Boolean
        End Function
 
#End Region
 
#Region " Structures "
        <StructLayout(LayoutKind.Sequential)> _
        Public Structure CWPRETSTRUCT
            Public lResult As IntPtr
            Public lParam As IntPtr
            Public wParam As IntPtr
            Public message As UInteger
            Public hwnd As IntPtr
        End Structure
 
        <StructLayout(LayoutKind.Sequential)> _
    Public Structure RECT
            Private _Left As Integer, _Top As Integer, _Right As Integer, _Bottom As Integer
 
            Public Sub New(ByVal Rectangle As Rectangle)
                Me.New(Rectangle.Left, Rectangle.Top, Rectangle.Right, Rectangle.Bottom)
            End Sub
            Public Sub New(ByVal Left As Integer, ByVal Top As Integer, ByVal Right As Integer, ByVal Bottom As Integer)
                _Left = Left
                _Top = Top
                _Right = Right
                _Bottom = Bottom
            End Sub
 
            Public Property X() As Integer
                Get
                    Return _Left
                End Get
                Set(ByVal value As Integer)
                    _Left = value
                End Set
            End Property
            Public Property Y() As Integer
                Get
                    Return _Top
                End Get
                Set(ByVal value As Integer)
                    _Top = value
                End Set
            End Property
            Public Property Left() As Integer
                Get
                    Return _Left
                End Get
                Set(ByVal value As Integer)
                    _Left = value
                End Set
            End Property
            Public Property Top() As Integer
                Get
                    Return _Top
                End Get
                Set(ByVal value As Integer)
                    _Top = value
                End Set
            End Property
            Public Property Right() As Integer
                Get
                    Return _Right
                End Get
                Set(ByVal value As Integer)
                    _Right = value
                End Set
            End Property
            Public Property Bottom() As Integer
                Get
                    Return _Bottom
                End Get
                Set(ByVal value As Integer)
                    _Bottom = value
                End Set
            End Property
            Public Property Height() As Integer
                Get
                    Return _Bottom - _Top
                End Get
                Set(ByVal value As Integer)
                    _Bottom = value - _Top
                End Set
            End Property
            Public Property Width() As Integer
                Get
                    Return _Right - _Left
                End Get
                Set(ByVal value As Integer)
                    _Right = value + _Left
                End Set
            End Property
            Public Property Location() As Point
                Get
                    Return New Point(Left, Top)
                End Get
                Set(ByVal value As Point)
                    _Left = value.X
                    _Top = value.Y
                End Set
            End Property
            Public Property Size() As Size
                Get
                    Return New Size(Width, Height)
                End Get
                Set(ByVal value As Size)
                    _Right = value.Width + _Left
                    _Bottom = value.Height + _Top
                End Set
            End Property
 
            Public Shared Widening Operator CType(ByVal Rectangle As RECT) As Rectangle
                Return New Rectangle(Rectangle.Left, Rectangle.Top, Rectangle.Width, Rectangle.Height)
            End Operator
            Public Shared Widening Operator CType(ByVal Rectangle As Rectangle) As RECT
                Return New RECT(Rectangle.Left, Rectangle.Top, Rectangle.Right, Rectangle.Bottom)
            End Operator
            Public Shared Operator =(ByVal Rectangle1 As RECT, ByVal Rectangle2 As RECT) As Boolean
                Return Rectangle1.Equals(Rectangle2)
            End Operator
            Public Shared Operator <>(ByVal Rectangle1 As RECT, ByVal Rectangle2 As RECT) As Boolean
                Return Not Rectangle1.Equals(Rectangle2)
            End Operator
 
            Public Overrides Function ToString() As String
                Return "{Left: " & _Left & "; " & "Top: " & _Top & "; Right: " & _Right & "; Bottom: " & _Bottom & "}"
            End Function
 
            Public Overloads Function Equals(ByVal Rectangle As RECT) As Boolean
                Return Rectangle.Left = _Left AndAlso Rectangle.Top = _Top AndAlso Rectangle.Right = _Right AndAlso Rectangle.Bottom = _Bottom
            End Function
            Public Overloads Overrides Function Equals(ByVal [Object] As Object) As Boolean
                If TypeOf [Object] Is RECT Then
                    Return Equals(DirectCast([Object], RECT))
                ElseIf TypeOf [Object] Is Rectangle Then
                    Return Equals(New RECT(DirectCast([Object], Rectangle)))
                End If
 
                Return False
            End Function
        End Structure
#End Region
 
#Region " Consts "
        Private Const TimerID As Integer = 42
        Private Const WH_CALLWNDPROCRET As Integer = 12
        Private Const WM_DESTROY As Integer = &H2
        Private Const WM_INITDIALOG As Integer = &H110
        Private Const WM_TIMER As Integer = &H113
        Private Const WM_USER As Integer = &H400
        Private Const DM_GETDEFID As Integer = WM_USER + 0
        Private Const WM_COMMAND As UInteger = &H111
        Private Const WS_VISIBLE As UInteger = &H10000000
        Private Const WS_CHILD As UInteger = &H40000000
        Private Const WS_TABSTOP As UInteger = &H10000
        Private Const WM_SETFONT As Integer = &H30
        Private Const WM_GETFONT As Integer = &H31
        Private Const BS_AUTOCHECKBOX As UInteger = &H3
        Private Const WM_NOTIFY As Integer = &H4E
        Private Const CB_SETCURSEL As Integer = &H14E
        Private Const CB_GETCURSEL As Integer = &H147
        Private Const BM_GETCHECK As Integer = &HF0
        Private Const BM_SETCHECK As Integer = &HF1
        Private Const BST_CHECKED As Integer = &H1
        Private Const BST_INDETERMINATE As Integer = &H2
        Private Const BST_UNCHECKED As Integer = &H0
#End Region
 
#Region " Variables "
        Private Shared mHookProc As HookProc
        Private Shared mHookTimer As TimerProc
        Private Shared mHookTimeout As Integer
        Private Shared mHookCaption As String
        Private Shared mHook As IntPtr
        Private Shared mHandle As Long
        Private Shared WithEvents mTimer As New Timer
        Private Shared mCaption As String = String.Empty
        Private Shared mdteStart As Date
        Private Shared mCheckBoxHandle As IntPtr
        Private Shared mCheckMsgText As String = String.Empty
        Private Shared mButtons As ExMessageBoxButtons
        Private Shared ReadOnly HWND_BOTTOM As New IntPtr(1)
        Private Shared ReadOnly HWND_NOTOPMOST As New IntPtr(-2)
        Private Shared ReadOnly HWND_TOP As New IntPtr(0)
        Private Shared ReadOnly HWND_TOPMOST As New IntPtr(-1)
        Private Shared mChecked As Boolean
        Private Shared mText As String = ""
#End Region
 
#Region " Public methods "
        Shared Sub New()
            mHookProc = New HookProc(AddressOf _messageBoxHookProc)
            mHookTimer = New TimerProc(AddressOf _messageBoxTimerProc)
            mHookTimeout = 0
            mHookCaption = Nothing
            mHook = IntPtr.Zero
        End Sub
#End Region
 
#Region " Private methods "
        Private Shared Function _getResult(ByVal r As DialogResult) As ExDialogResult
            Select Case mButtons
                Case ExMessageBoxButtons.SaveDiscardCancel
                    Select Case r
                        Case DialogResult.Yes
                            Return ExDialogResult.Save
                        Case DialogResult.No
                            Return ExDialogResult.Discard
                        Case Else
                            Return ExDialogResult.Cancel
                    End Select
                Case ExMessageBoxButtons.CancelTryContinue
                    Select Case r
                        Case DialogResult.Yes
                            Return ExDialogResult.Cancel
                        Case DialogResult.No
                            Return ExDialogResult.TryAgain
                        Case Else
                            Return ExDialogResult.Continue
                    End Select
                Case Else
                    Return CType(CInt(r), ExDialogResult)
            End Select
        End Function
 
        Private Shared Function _getButtons() As MessageBoxButtons
            Select Case mButtons
                Case ExMessageBoxButtons.AbortRetryIgnore
                    Return MessageBoxButtons.AbortRetryIgnore
                Case ExMessageBoxButtons.CancelTryContinue
                    Return MessageBoxButtons.YesNoCancel
                Case ExMessageBoxButtons.OK
                    Return MessageBoxButtons.OK
                Case ExMessageBoxButtons.OKCancel
                    Return MessageBoxButtons.OKCancel
                Case ExMessageBoxButtons.RetryCancel
                    Return MessageBoxButtons.RetryCancel
                Case ExMessageBoxButtons.SaveDiscardCancel
                    Return MessageBoxButtons.YesNoCancel
                Case ExMessageBoxButtons.YesNo
                    Return MessageBoxButtons.YesNo
                Case ExMessageBoxButtons.YesNoCancel
                    Return MessageBoxButtons.YesNoCancel
            End Select
        End Function
 
        Private Shared Sub _setup(ByVal caption As String, ByVal uTimeout As UInteger)
            If mHook <> IntPtr.Zero Then
                Throw New NotSupportedException("multiple calls are not supported")
            End If
 
            mCaption = caption
            If uTimeout > 0 Then mHookTimeout = uTimeout
            mHookCaption = If(caption IsNot Nothing, caption, "")
            mdteStart = Now
 
            mHook = SetWindowsHookEx(WH_CALLWNDPROCRET, mHookProc, IntPtr.Zero, GetCurrentThreadId())
            If mHookTimeout > 0 Then
                mTimer.Interval = 10
                mTimer.Enabled = True
            End If
        End Sub
 
        Private Shared Function _messageBoxHookProc(ByVal nCode As Integer, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As IntPtr
            If nCode < 0 Then
                Return CallNextHookEx(mHook, nCode, wParam, lParam)
            End If
 
            Dim msg As CWPRETSTRUCT = CType(Marshal.PtrToStructure(lParam, GetType(CWPRETSTRUCT)), CWPRETSTRUCT)
            Dim hook As IntPtr = mHook
 
            mHandle = msg.hwnd
 

            If mHookCaption IsNot Nothing AndAlso msg.message = WM_INITDIALOG Then
                Dim nLength As Integer = GetWindowTextLength(msg.hwnd)
                Dim text As New StringBuilder(nLength + 1)
 
                GetWindowText(msg.hwnd, text, text.Capacity)
 
                If mHookCaption = text.ToString() AndAlso mHookTimeout > 0 Then
                    mHookCaption = Nothing
                    SetTimer(msg.hwnd, CType(TimerID, UIntPtr), mHookTimeout, mHookTimer)
                    UnhookWindowsHookEx(mHook)
                    mHook = IntPtr.Zero
                End If
 
                'set button language
                _setLanguage(msg.hwnd)
 

 
                If mCheckMsgText.Length > 0 Then
                    Dim r As RECT
                    GetWindowRect(msg.hwnd, r)
                    SetWindowPos(msg.hwnd, HWND_TOP, r.X, r.Y, r.Width, r.Height + 30, SetWindowPosFlags.FrameChanged)
                    Dim intWidth As Integer = TextRenderer.MeasureText(mCheckMsgText, SystemFonts.DialogFont).Width + 10
 
                    'create checkbox
                    Dim chkHandle As Integer = CreateWindowEx(0, "BUTTON", "myCheckBox", BS_AUTOCHECKBOX Or WS_VISIBLE Or WS_CHILD Or WS_TABSTOP, 10, r.Height - 30, _
                      intWidth, 20, msg.hwnd, 0, 0, 0)
                    SetWindowText(chkHandle, mCheckMsgText)
                    Dim fontHandle As Integer = SendMessage(msg.hwnd, WM_GETFONT, 0, 0)
                    SendMessage(chkHandle, WM_SETFONT, fontHandle, 0)
                    mCheckBoxHandle = chkHandle
 
                    'set checkstate
                    If mChecked Then
                        SendMessage(mCheckBoxHandle, BM_SETCHECK, BST_CHECKED, 0&)
                    Else
                        SendMessage(mCheckBoxHandle, BM_SETCHECK, BST_UNCHECKED, 0&)
                    End If
                End If
 
            ElseIf msg.message = WM_DESTROY Then
                If mCheckBoxHandle <> 0 Then DestroyWindow(mCheckBoxHandle)
            ElseIf msg.message = WM_COMMAND Then
                'get checkstate
                mChecked = SendMessage(mCheckBoxHandle, BM_GETCHECK, 0&, 0&)
            End If
 
            Return CallNextHookEx(hook, nCode, wParam, lParam)
        End Function
 
        Private Shared Sub _messageBoxTimerProc(ByVal hWnd As IntPtr, ByVal uMsg As UInteger, ByVal nIDEvent As UIntPtr, ByVal dwTime As UInteger)
            If nIDEvent = CType(TimerID, UIntPtr) Then
                mTimer.Enabled = False
                mTimer.Dispose()
                'get checkstate
                mChecked = SendMessage(mCheckBoxHandle, BM_GETCHECK, 0&, 0&)
                EndDialog(hWnd, CType(_getLowWord(SendMessage(hWnd, DM_GETDEFID, IntPtr.Zero, IntPtr.Zero)), IntPtr))
            End If
        End Sub
 
        Private Shared mTextTemp As String = String.Empty
        Private Shared Sub _timer_Tick(ByVal sender As Object, ByVal e As System.EventArgs) Handles mTimer.Tick
            If mTimer.Interval = 10 Then mTimer.Interval = 250
 
            Dim intSec As Integer = Math.Round(mHookTimeout / 1000 - CType(Now - mdteStart, TimeSpan).TotalSeconds, 0)
            Dim str As String = String.Format(mCaption, intSec)
            If mCaption.Contains("{0}") Then SetWindowText(mHandle, str)
 
            Static intStart As Integer = 0
 
            If mHandle <> IntPtr.Zero Then
                If intStart = 0 Then
                    'get original text 
                    Dim sb As New StringBuilder(256)
                    GetDlgItemText(mHandle, &HFFFF, sb, sb.MaxCapacity)
                    If sb.ToString.Length > 0 Then mText = sb.ToString
                    intStart += 1
                End If
 
                If mText.Contains("{0}") Then
                    If Not mTextTemp = String.Format(mText, intSec) Then
                        SetDlgItemText(mHandle, &HFFFF, String.Format(mText, intSec))
                    End If
                    mTextTemp = String.Format(mText, intSec)
                End If
            End If
 
            mChecked = SendMessage(mCheckBoxHandle, BM_GETCHECK, 0&, 0&)
 
        End Sub
 
        Private Shared Function _getLowWord(ByRef pintValue As Int32) As Int32
            Return pintValue And &HFFFF
        End Function
 
        Private Shared Function _getLowWord(ByRef pudtValue As IntPtr) As Int32
            Return _getLowWord(pudtValue.ToInt32)
        End Function
 
        Private Shared Function _getHighWord(ByRef pintValue As Int32) As Int32
            If (pintValue And &H80000000) = &H80000000 Then
                Return ((pintValue And &H7FFF0000) \ &H10000) Or &H8000&
            Else
                Return (pintValue And &HFFFF0000) \ &H10000
            End If
        End Function
 
        Private Shared Sub _setLanguage(ByVal hwd As IntPtr)
            Dim ty As Type = GetType(en)
            Select Case System.Threading.Thread.CurrentThread.CurrentUICulture.TwoLetterISOLanguageName.ToLower
                Case "de"
                    ty = GetType(de)
                Case "fr"
                    ty = GetType(fr)
                Case "pl"
                    ty = GetType(pl)
                Case "cs"
                    ty = GetType(cs)
                Case "es"
                    ty = GetType(es)
                Case "pt"
                    ty = GetType(pt)
                Case "nl"
                    ty = GetType(nl)
            End Select
 
            Select Case mButtons
                Case ExMessageBoxButtons.SaveDiscardCancel
                    SetDlgItemText(hwd, 6, "&" & Replace(Split([Enum].GetName(ty, 12), "__")(1), "_", " "))
                    SetDlgItemText(hwd, 7, "&" & Replace(Split([Enum].GetName(ty, 13), "__")(1), "_", " "))
                    SetDlgItemText(hwd, 2, "&" & Replace(Split([Enum].GetName(ty, 2), "__")(1), "_", " "))
                Case ExMessageBoxButtons.CancelTryContinue
                    SetDlgItemText(hwd, 6, "&" & Replace(Split([Enum].GetName(ty, 2), "__")(1), "_", " "))
                    SetDlgItemText(hwd, 7, "&" & Replace(Split([Enum].GetName(ty, 10), "__")(1), "_", " "))
                    SetDlgItemText(hwd, 2, "&" & Replace(Split([Enum].GetName(ty, 11), "__")(1), "_", " "))
                Case Else
                    For Each i As Integer In [Enum].GetValues(ty)
                        If Not mButtons = ExMessageBoxButtons.OK Then
                            SetDlgItemText(hwd, i, "&" & Replace(Split([Enum].GetName(ty, i), "__")(1), "_", " "))
                        Else
                            'if there is only the ok button then is dlgid 2 instead 1
                            SetDlgItemText(hwd, 2, "&" & Replace(Split([Enum].GetName(ty, 1), "__")(1), "_", " "))
                        End If
                    Next
            End Select
 

        End Sub
#End Region
 
#Region " Languages "
        Enum de
            Abort__Abbrechen = 3
            Cancel__Abbrechen = 2
            Ignore__Ignorieren = 5
            No__Nein = 7
            OK__OK = 1
            Retry_Wiederholen = 4
            Yes__Ja = 6
            Help__Hilfe = 9
            TryAgain__Wiederholen = 10
            Continue__Fortsetzen = 11
            Save__Speichern = 12
            Discard__Verwerfen = 13
        End Enum
 
        Enum en
            Abort__Abort = 3
            Cancel__Cancel = 2
            Ignore__Ignore = 5
            No__No = 7
            OK__OK = 1
            Retry__Retry = 4
            Yes__Yes = 6
            Help__Help = 9
            TryAgain__Try_Again = 10
            Continue__Continue = 11
            Save__Save = 12
            Discard__Discard = 13
        End Enum
 
        Enum fr
            Abort__Arrêt = 3
            Cancel__Annuler = 2
            Ignore__Ignorer = 5
            No__Non = 7
            OK__OK = 1
            Retry__Réessayer = 4
            Yes__Oui = 6
            Help__Aide = 9
            TryAgain__Réessayer = 10
            Continue__Continuer = 11
            Save__Sauver = 12
            Discard__Jeter = 13
        End Enum
 
        Enum cs
            Abort__Potratit = 3
            Cancel__Zrušit = 2
            Ignore__Ignorovat = 5
            No__Ne = 7
            OK__OK = 1
            Retry__Opakovat = 4
            Yes__Ano = 6
            Help__Pomoc = 9
            TryAgain__Zkusit = 10
            Continue__Pokračovat = 11
            Save__Uložit = 12
            Discard__Odhodit = 13
        End Enum
 
        Enum pl
            Abort__Przerwij = 3
            Cancel__Anulować = 2
            Ignore__Zignorować = 5
            No__Nie = 7
            OK__OK = 1
            Retry__Powtarzanie = 4
            Yes__Tak = 6
            Help__Pomoc = 9
            TryAgain__Próbować = 10
            Continue__Kontynuować = 11
            Save__Zaoszczędzić = 12
            Discard__Odrzuć = 13
        End Enum
 
        Enum es
            Abort__Abortar = 3
            Cancel__Cancelar = 2
            Ignore__Pasar = 5
            No__No = 7
            OK__Aceptar = 1
            Retry_Intentar = 4
            Yes__Sí = 6
            Help__Ayuda = 9
            TryAgain__Intentar = 10
            Continue__Continuar = 11
            Save__Guardar = 12
            Discard__Descartar = 13
        End Enum
 
        Enum pt
            Abort__Abortar = 3
            Cancel__Cancelar = 2
            Ignore__Ignorar = 5
            No__Não = 7
            OK__OK = 1
            Retry__Tentar = 4
            Yes__Sim = 6
            Help__Ajuda = 9
            TryAgain__Intentar = 10
            Continue__Continuar = 11
            Save__Guardar = 12
            Discard__Descartar = 13
        End Enum
 
        Enum nl
            Abort__Afbreken = 3
            Cancel__Annuleren = 2
            Ignore__Negeren = 5
            No__Nee = 7
            OK__OK = 1
            Retry__Herhaal = 4
            Yes__Ja = 6
            Help__Help = 9
            TryAgain__Proberen = 10
            Continue__Voortzetten = 11
            Save__Besparen = 12
            Discard__Ontdoen = 13
        End Enum
#End Region
 
#Region " CheckAndExDialogResult "
        Public Class CheckAndExDialogResult
            Private mChecked As CheckState
            Private mResult As ExDialogResult
 
            ReadOnly Property CheckState() As CheckState
                Get
                    Return mChecked
                End Get
            End Property
 
            ReadOnly Property ExDialogResult() As ExDialogResult
                Get
                    Return mResult
                End Get
            End Property
 
            Sub New(ByVal dResult As ExDialogResult, ByVal cResult As CheckState)
                mResult = dResult
                mChecked = cResult
            End Sub
        End Class
#End Region
    End Class
End Namespace

GeneralMy vote of 5memberTomChantler22 Sep '10 - 4:59 
Nice and simple and it works (with the modification mentioned by Mr C P). Smile | :)
GeneralThis isn't working in win7, Visual Studio 2008memberMr C P9 Mar '10 - 23:10 
Hullo
 
At the moment I can't seem to get this working in Windows 7, Visual Studio 2008. The messagebox appears but never times out.
 
It may be a security issue, it may also be the line I changed to correct the deprecated thread ID error:
 
hHook = SetWindowsHookEx(WH_CALLWNDPROCRET, hookProc, IntPtr.Zero, System.Threading.Thread.CurrentThread.ManagedThreadId);
 
..perhaps it's not getting the right ID so the hook can't call EndDialog on the right window.
 
I'll report back if I get a fix, or if somebody else spots it first please do.
 
Carlos
GeneralRe: This isn't working in win7, Visual Studio 2008memberMr C P9 Mar '10 - 23:15 
Right.... fixed.
 
If you use some of the methods suggested in these comments to replace AppDomain.GetCurrentThreadId() with a method from System.Threading.Thread.... to fix the deprecation warning, this will NOT work in windows 7. So, instead, can I suggest the method used by Dana Hall in teh comments below.
 
In other words, within the class defition, add:
 
[DllImport("kernel32.dll")]
static extern int GetCurrentThreadId();
 
Then modify this line:
hHook = SetWindowsHookEx(WH_CALLWNDPROCRET, hookProc, IntPtr.Zero, AppDomain.GetCurrentThreadId());
 
To this line:
hHook = SetWindowsHookEx(WH_CALLWNDPROCRET, hookProc, IntPtr.Zero, GetCurrentThreadId());
 

Carl
GeneralRe: This isn't working in win7, Visual Studio 2008memberTomChantler22 Sep '10 - 5:00 
This is a good article. I found the same issue as you Mr C P. Smile | :)
Generalgood articlememberDonsw17 Jan '09 - 17:51 
good article, you might want to show some of the code in the article and a screen shot of the messagebox to make it a better article. Smile | :)
GeneralVery very very good code, but...memberseb.4924 Apr '08 - 5:20 
I have change your code to make a countdown messagebox. I know change the text to show the countdown : SetWindowText(hWnd, nCountDown); but it change the caption of the messagebox not the real text.
 
Is someone know how to achive this issue ? Thanks
QuestionDoes this support multi threading callsmemberKanna22 Mar '08 - 5:23 
If two threads calls this MessageBoxEx, would this be thread safe?
I am a bit skeptical with the timer event ID being set to a constant 42.
 
~Kanna

GeneralRe: Does this support multi threading callsmemberRodgerB31 Mar '08 - 13:00 
Timers belong to an HWND. An HWND belongs to the thread it was created on. The WM_TIMER will be received on the thread the HWND belongs to. So what does it matter what the timer ID is? As long as no one else is using it for a given HWND you're good to go. And since this is a MessageBox its pretty safe no one else is using ID 42.
GeneralPocketPCmemberrichardjjs14 Apr '07 - 21:56 
This looks brilliant but I am not sure it will work in PocketPC(2003)
 
I am getting 9 error messages the first of which is
The type or namesapce name MessageBoxoptions could not be found (are you missin etc...
The type or namespace nane IWin32Windows etc (eight messages based on this!!
 
Richard
GeneralRe: PocketPC [modified]memberRodgerB15 Apr '07 - 4:50 
This will not work on the pocket or compact framework. The implementation uses windows hooks which aren't available on the pocket.
 
Sorry!
 
modified on Tuesday, January 19, 2010 6:01 PM

GeneralAmazingmemberazam's13 Feb '07 - 18:21 
hi
 
Great work
and thanks a lot for sharing too..Smile | :)

 
azam's
GeneralRe: AmazingmemberAndreas Hollmann2 May '07 - 0:24 
I want also say thanks for this grCool | :cool: eat article. Great work!!! Cool | :cool:
QuestionHow to implement in CFmemberAjatashatru13 Sep '06 - 3:19 
As compact framework does not have this User32.dll then how can we implement this functionality.
List of functions impoted not in coredll.dll
1.SendMessage
2.SetWindowsHookEx
3.GetWindowTextLength
4.GetWindowText
 
Can somebody help me out,
my mail id is yuvrajaa@integramicro.com/yuvraj_1981@walla.com

 
Yuvraj

AnswerRe: How to implement in CFmemberRodgerB13 Sep '06 - 3:46 
Most functions should come from coredll.dll. However, some bad news. Windows CE doesn't support windows hooks. So you're SOL, sorry.
GeneralServiceNotificationmemberRyan McFarren5 Jun '06 - 10:23 
Anyone had any luck getting it to work with MessageBoxOptions.ServiceNotification? We have a service that does some checks, makes system changes if necessary, and then tells the user they need to reboot. Hoping to put up a message box with a countdown to an auto-reboot. Works great, until I put the MessageBoxOptions.ServiceNotification option on. Nuts.
QuestionMessageBoxTimeOut API function?membereshipman13 Dec '04 - 3:42 
[quote]http://www.gipsysoft.com/messagebox. You'll find a C++ implementation[/quote]
 
Why not use the MessageBoxTimeOut API function instead of having a whole freaking class?

AnswerRe: MessageBoxTimeOut API function?memberRodgerB13 Dec '04 - 5:08 
Very interesting.
 
1) I had no idea there was such a function.
2) It's undocumented, it's generally not a good idea to use undocumented functions. Still, I would think you could try to use the undocumented version and if it's not there you could use this code.
3) Looking at another article on this site it appears that MessageBoxTimeOut is only available on WinXP. People do still write code for Win9X, WinNT, etc...
4) This technique for making a MessageBox with a timeout allows you do change the text of the buttons or move them around or do whatever else you need to do with MessageBox. The C++ code at gipsysoft does all of these things and more. I only needed the timeout so that's all I ported to C#.
GeneralAppDomain.GetCurrentThreadId() is deprecated with Visual Studio 2005 Beta 1sussAnonymous13 Oct '04 - 12:51 
Hello,
I like your MessageBoxEx with timer solution. I recently upgraded to Visual Studio 2005 beta 1 and get the following warning:
Warning 2 'System.AppDomain.GetCurrentThreadId()' is obsolete: 'AppDomain.GetCurrentThreadId has been deprecated because it does not provide a stable Id when managed threads are running on fibers (aka lightweight threads). To get a stable identifier for a managed thread, use the Thread object returned from Thread.CurrentThread.'
 
I cannot find a working substitute. Any ideas would be appreciated.
 
Thanks,
Dana Hall


GeneralRe: AppDomain.GetCurrentThreadId() is deprecated with Visual Studio 2005 Beta 1memberRodgerB13 Oct '04 - 16:17 
That is interesting but I wouldn't worry about fibers much. They were intended to make the porting of Unix applications easier. I don't imagine they are used much in Windows apps.
 
SetWindowsHookEx is a windows API after all. Perhaps in the new version of the .NET framework there is an managed alternative to SetWindowsHookEx. If you want to get rid of the warning use P/Invoke to get the current thread id.
 
Regards,
Rodger
GeneralRe: AppDomain.GetCurrentThreadId() is deprecated with Visual Studio 2005 Beta 1sussAnonymous13 Oct '04 - 17:22 
Rodger,
Thanks for the help. This is what I did and it worked. I added this P/Invoke signature.
 
[DllImport("kernel32.dll")]
static extern int GetCurrentThreadId();
 
Then modified this line:
hHook = SetWindowsHookEx(WH_CALLWNDPROCRET, hookProc, IntPtr.Zero, AppDomain.GetCurrentThreadId());
 
To this line:
hHook = SetWindowsHookEx(WH_CALLWNDPROCRET, hookProc, IntPtr.Zero, GetCurrentThreadId());
 
I am new to .NET, so this was a fun learning experiment.
 
Thanks again,
Dana Hall

GeneralRe: AppDomain.GetCurrentThreadId() is deprecated with Visual Studio 2005 Beta 1memberTim McCurdy7 Dec '04 - 1:56 
This is off the top of my head, and I don't know if it will work in 2005, but try this instead...
 
System.Threading.Thread.CurrentThread.GetDomainID()
Or
System.Threading.Thread.CurrentThread.CurrentContext.ContextID()
GeneralRe: AppDomain.GetCurrentThreadId() is deprecated with Visual Studio 2005 Beta 1memberDan Ganiere30 Aug '05 - 5:13 
Threading.Thread.CurrentThread.GetHashCode
 
GetHashCode on the thread object returns the thread ID. From MS Docs
GeneralRe: AppDomain.GetCurrentThreadId() is deprecated with Visual Studio 2005 Beta 1memberJ Whattam4 May '06 - 19:36 
I haven't tried this suggestion yet, but I have spent two days researching this problem on the NET to no avail until I stumbled across this message. Thanks very much. Maybe Microsoft should seriously think about a free brain transplant for some of its employees - who in their right minds would go looking for the thread ID under GetHashCode?Confused | :confused:
 
John Whattam
GeneralRe: AppDomain.GetCurrentThreadId() is deprecated with Visual Studio 2005 Beta 1memberJanis Ozo10 Feb '07 - 23:21 
(int)AppDomain.GetCurrentThreadId()
 
change to:
 
System.Threading.Thread.CurrentThread.ManagedThreadId
GeneralRe: AppDomain.GetCurrentThreadId() is deprecated with Visual Studio 2005 Beta 1membersgissinger24 Mar '11 - 2:54 
I use VS 2010 with .NET 4 and i replaced
 
Int32 mainThreadId = AppDomain.GetCurrentThreadId();
with
Int32 mainThreadId = Process.GetCurrentProcess().Threads[0].Id;
 
It's not ideal but it works for my personal use and is not deprecated (for the moment).
GeneralThanks a lotmemberaemmons6 Oct '04 - 9:54 
This class is exactly what I was looking for. Very helpful and so easy - just one extra parameter.
 
Thanks again, I'll get a lot of use out of this.
 
Anne
GeneralpicturememberTaha Zayed24 Sep '04 - 15:55 
please put a picture to your article
GeneralRe: picturememberRodgerB24 Sep '04 - 23:51 
What would you like to see a picture of?
GeneralNamespace Errors when trying to buildmemberLordRhys3 Sep '04 - 6:49 
I started a test project and included your MessageBoxEx.cs file, when I try to build I get the following errors:
 
E:\VBNet Projects\TestMessageboxEx\MessageBoxEx\MessageBoxEx.cs(12): The type or namespace name 'DialogResult' could not be found (are you missing a using directive or an assembly reference?)
 
E:\VBNet Projects\TestMessageboxEx\MessageBoxEx\MessageBoxEx.cs(24): The type or namespace name 'MessageBoxButtons' could not be found (are you missing a using directive or an assembly reference?)
 
E:\VBNet Projects\TestMessageboxEx\MessageBoxEx\MessageBoxEx.cs(48): The type or namespace name 'IWin32Window' could not be found (are you missing a using directive or an assembly reference?)
 
I would like to compile this to a .dll if possible then use it in some of my VB apps.
 
These errors are in a C# project.
 
Thanks for any help you can provide.
Frank
 

GeneralRe: Namespace Errors when trying to buildmemberRodgerB3 Sep '04 - 7:00 
Sorry,
 
In my instructions, I assumed the assembly you were developing already had a reference to System.Windows.Forms and were using MessageBox.Show. Add that assembly reference and you should be good to go. MessageBoxEx is in the System.Windows.Forms namespace just like MessageBox.
 
Regards,
Rodger
GeneralRe: Namespace Errors when trying to buildmemberLordRhys3 Sep '04 - 7:38 
ok, that worked was able to build it to a dll, but I still cannot use it as a referenced dll in a VB app, it will break the Form class by giving an error to everything in the System.Windows.Forms Class. thanks anyway I will see if i can implement it another way.
 
Frank

GeneralYou beat me to it.memberAshaman13 Aug '04 - 2:54 
I was gonna try to write this yesterday, but I got distracted by real work and didn't get to it.
 
Instead, I'll take your nice class and convert it to VB.
 
Thanks! I gave this a 5 for code cleanliness, readability and utility.
 
-Kevin Buchan
GeneralRe: You beat me to it.sussManolo Herrera13 Aug '04 - 5:32 
I try to convert in VB but I have problem convertin this line:
 
executingPage.Unload += new EventHandler( ExecutingPage_Unload );
 
To VB.
 

Could you help me?
 

Thanks in advance!
 

 


 
Manolo Herrera
GeneralRe: You beat me to it.memberDaniel Steuernol14 Aug '04 - 6:25 
There's a couple of ways you can add a handler in vb.net, you can use the AddHandler function.
 
AddHandler executingPage.Unload AddressOf ExecutingPage_Unload
 
or on the declaration line of ExecutingPage_Unload make it like this.
 
Public Sub ExecutingPage_Unload(...) Handles executingPage.Unload
 
the handles part specifies which events the method is associated with.
 
Hope this helps
GeneralRe: You beat me to it.sussManolo Herrera16 Aug '04 - 4:26 
Thanks, Man It works very well. I was confusing with that line. I did not know what c# was telling me but with you help, I get it!. Now I undertand what we need to do. Thanks a lot!. and I was doing for asp.net messagbox but Now I see that this messagebox is for Windows application. However you answer my question with a not exactly related question...Realiy thanks to you! and thanks for all writers in codeproject and with this cool site!.
 

Manolo Herrera.
 

 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web02 | 2.6.130523.1 | Last Updated 13 Aug 2004
Article Copyright 2004 by RodgerB
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid