Eigene MSGBox - Buttonrückgabe aber wie

  • VB.NET
  • .NET (FX) 4.5–4.8

Es gibt 31 Antworten in diesem Thema. Der letzte Beitrag () ist von volti.

    Eigene MSGBox - Buttonrückgabe aber wie

    Hallo zusammen,

    ich möchte mir eine eigene MSGBox erstellen, in der ich dann x beliebig viele Buttons einfügen kann.

    Basis ist: VB.Net 2022, Projektvorlage ist Windows Forms-Steuerelementbibliothek (.NET Framework), Keine Veränderungen in den Eigenschaften, VB.Net als Admin gestartet

    Jetzt ist aber direkt schon das erste Problem aufgetreten: Wie bekomme ich in der Hauptanwendung mit, welcher Button gedrückt wurde?

    Mein bisheriger Controlcode sieht so aus:

    Spoiler anzeigen

    VB.NET-Quellcode

    1. Public Class NeueMSGBox
    2. Private selectedButton As String = ""
    3. ' Diese Methode zeigt die MsgBox an und gibt das Ergebnis zurück
    4. Public Function ShowDialog() As String
    5. Me.ShowDialog()
    6. Return selectedButton
    7. End Function
    8. ' Event-Handler für Button 1
    9. Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    10. selectedButton = "Option 1"
    11. Me.Close()
    12. End Sub
    13. ' Event-Handler für Button 2
    14. Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
    15. selectedButton = "Option 2"
    16. Me.Close()
    17. End Sub
    18. ' Event-Handler für Button 3
    19. Private Sub Button3_Click(sender As Object, e As EventArgs) Handles Button3.Click
    20. selectedButton = "Option 3"
    21. Me.Close()
    22. End Sub
    23. End Class


    Soweit ist mir der Code eigentlich klar, aber bei me.close() kommt die Fehlermeldung "BC30256 Close ist kein Member von NeueMSGBox". Die Korrekturmöglichkeit wäre

    Private Sub Close()
    Throw New NotImplementedException()
    End Sub

    In der Hauptform gibt es einen Verweis auf die DLL-Datei und der Code sieht so aus

    Spoiler anzeigen

    VB.NET-Quellcode

    1. Imports NeuMsgBox
    2. Public Class Form1
    3. Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    4. Dim msgBox As New NeueMSGBox()
    5. Dim result As String = msgBox.ShowDialog()
    6. Select Case result
    7. Case "Option 1"
    8. MessageBox.Show("Du hast Option 1 gewählt.")
    9. Case "Option 2"
    10. MessageBox.Show("Du hast Option 2 gewählt.")
    11. Case "Option 3"
    12. MessageBox.Show("Du hast Option 3 gewählt.")
    13. Case Else
    14. MessageBox.Show("Keine Option gewählt.")
    15. End Select
    16. End Sub
    17. End Class


    Nachdem ich auf den Testbutton geklickt habe, kommt folgender Fehler

    System.StackOverflowException
    HResult=0x800703E9
    Nachricht = Eine Ausnahme vom Typ "System.StackOverflowException" wurde ausgelöst.

    Könnt Ihr mir evtl. helfen und mir die richtige Vorgehensweise nennen?

    Vielen Dank schon einmal im Voraus.

    Volker
    Deine NeueMSGBox Klasse hat halt keine Close Methode.
    Vielleicht sollte sie von Form erben. Denn ein Form hat eine Close Methode.

    Aber das ganze gleich als DLL? Warum fügst du deinem Projekt nicht einfach erstmal ein zweites Form hinzu, damit du weißt was du tust?

    Wenn nur drei Buttons brauchst, die alle auch noch das Form schließen, dann geht das mit der DialogResult Eigenschaft.
    Vielleicht brauchst nichmal selbst was basteln. Die MessageBox Klasse bietet sich selbst auch in einer drei Button Variante an: YesNoCancel
    Wenn die neue MessageBox von nix erbt, kennt sie auch keine Close-Methode. Dazu müsste sie also schon von Form erben. Das sehen wir hier aber nicht.

    Haudruferzappeltnoch schrieb:

    Vielleicht brauchst nichmal selbst was basteln. Die MessageBox Klasse bietet sich selbst auch in einer drei Button Variante an: YesNoCancel
    Ich hab mir auch ne eigene MessageBox gebastelt, die ich seit langem schon sehr zufriedenstellend nutze. Manchmal reicht eine MessageBox einfach nicht. Fiktive Beispiele:
    Welcher passende Vorgang soll aufgerufen werden? [1. vom 12.12.23] [2. vom 16.12.23] [3. vom 18.12.23] [(keiner)] - klassische Alternative: InputBox <X
    Das aktuelle Konto ist nicht gedeckt. Was soll vom Referenzkonto abgezogen werden? [Gesamtbetrag] [fehlender Betrag] [Vorgang abbrechen] - klassische Alternative: Frage umstellen: »Soll der Gesamtbetrag vom Referenzkonto abgezogen werden? Bei [Nein] wird nur der Differenzbetrag abgezogen.« - Macht es meines Erachtens komplizierter.

    ##########

    Bezüglich der Buttonrückgabe: Das ist ganz einfach.
    Eine Möglichkeit: Erstelle z.B. eine Property Result, mit Public Getter und Private Setter. Sobald ein Button gedrückt wird, weist Du dieser Property einen entsprechenden Wert zu. Das aufrufende Programm kann das Result auslesen (aber selber nicht verändern).
    Eine andere Variante: Deine Methoden, die Deine MessageBox aufrufen, geben ein entsprechendes Ergebnis zurück. So habe ich entweder klassisch DialogResult. Oder (abhängig vom Aufruf) einen Integer als Rückgabewert. Wenn also z.B. 5 Buttons erstellt werden sollen, bekomme ich abhängig davon, welcher Button gedrückt wurde, einen Zahl zwischen 0 und 4 zurück. Oder -1, wenn ich oben rechts auf das [X] klicke. Man kann das [X] natürlich auch wegmachen, wenn man den Auswahlabbruch lieber über einen eigenen, regulären Button machen will.
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „VaporiZed“ ()

    Ich habe so was ähnliches in meine Anwendung (bopv.uno) integriert). Ist zwar eine Inputbox, aber kann man mit einer MsgBox ebenfalls so machen:


    Eine kleine Routine, die die Daten in eine Form schreibt und die Form dann ausliest.
    Die Form macht dann die Logik und schließt sich wieder.
    Das Ergebnis der Textbox aus der Form ist dann die Rückgabe der Inputbox.

    VB.NET-Quellcode

    1. My.Settings.License = InputBoxOwn("Enter activation code for " + HardwareID + " or plug in your hardware dongle:", "License", HardwareID)


    VB.NET-Quellcode

    1. Public Function InputBoxOwn(Haupttext As String, TitelText As String, Sollwert As String) As String
    2. FRM_Inputbox.Label_Title.Text = TitelText
    3. FRM_Inputbox.Label_Message.Text = Haupttext
    4. FRM_Inputbox.TextBoxInput.Text = Sollwert
    5. FRM_Inputbox.ShowDialog()
    6. Return FRM_Inputbox.TextBoxInput.Text
    7. End Function



    VB.NET-Quellcode

    1. Public Class FRM_Inputbox
    2. Private Sub ButtonContinue_Click(sender As Object, e As EventArgs) Handles ButtonContinue.Click
    3. TouchBeep()
    4. Me.Close()
    5. End Sub
    6. Private Sub ButtonOK_Click(sender As Object, e As EventArgs) Handles ButtonOK.Click
    7. TouchBeep()
    8. Me.Close()
    9. End Sub
    10. Private Sub FRM_Inputbox_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    11. Hauptform.RoundButton(ButtonOK)
    12. Hauptform.RoundButton(ButtonContinue)
    13. Hauptform.RoundTextBoxen(TextBoxInput)
    14. End Sub
    15. Private Sub ButtonContinue_Paint(sender As Object, e As PaintEventArgs) Handles ButtonContinue.Paint, ButtonOK.Paint
    16. Dim b = DirectCast(sender, Button)
    17. Hauptform.FarbVerlauf(b, e) 'Farbverlauf, Antialising und Haupttexterstellung ausgelagert
    18. End Sub
    19. End Class
    Bilder
    • 26112024103444.jpg

      137,94 kB, 969×596, 151 mal angesehen
    • 26112024103628.jpg

      151,76 kB, 906×563, 147 mal angesehen
    Liebe Grüße
    Roland Berghöfer

    Meine aktuellen und kostenlos verwendbaren Tools (mit VB.NET erstellt): freeremarkabletools.com | priconman.com | SimpleCalendar | AudibleTouch | BOComponent.com | bonit.at

    Volker Bunge schrieb:

    ich möchte mir eine eigene MSGBox erstellen, in der ich dann x beliebig viele Buttons einfügen kann.
    Wie viele Buttons sollen es denn maximal werden? Es gibt mehrer Möglichkeiten eine MessageBox zu erzeugen. Da wäre eine Form nur eine Möglichkeit. Dann gibt es in Windows vorhandene Messagebox-APIs die man nutzen könnte. z.B. mit der API SoftModalMessageBox kannst Du bis zu 11 Buttons auf eine Standard-Messagebox anzeigen lassen wobei die Buttonbeschriftung angepasst werden kann, inkl falls man es benötigt, ein Timeout so das sich die MessageBox von allein schließt wenn nach ablauf des Timeout kein Button gedrückt wurde. Einen sehr guten Einblick in diese API findest Du hier: github.com/imgdrive/SoftModalMessageBox

    VB.NET selbst nutzt für eine Standard Messagebox ebenfalls die in Windows vorhandenen APIs (MessageBox oder MessageBoxIndirect).
    Mfg -Franky-
    Ergänzend zu Messageboxes der API:

    Es gibt mehrere Funktionen Messagebox in der Windows-API. Die bekannteste ist die MessgeboxA.
    Mit weiteren API-Funktionen lässt sich diese manipulieren.
    Diese Messagebox handelt intern 5 Button-IDs, wovon aber mit den MB-Funktionen nur drei gleichzeitig sichtbar geschaltet werden können.
    Inklusive des Help-Buttons kann man hier also vier Buttons anzeigen lassen. Der Help-Button muss allerdings als Sonderfall anders als die anderen gemanagt werden.

    Wer mehr will, muss sich eine Form/Userform basteln oder gleich ein neues Fenster mit der Dlg-Klasse bauen. Das erfordert allerdings intensive API-Programmierkennnisse und ist m.E. zu aufwendig für diese einfache Nutzung.
    Das Einfügen weiterer Buttons in die Standard-Messagebox ist möglich, erfordert dann aber auch wieder das Extraverarbeiten.

    In den nachfolgenden Links habe ich mal ein paar Beispiele aufgezeigt. Hier nutze ich allerdings oft die Excelinterne Version, wobei allerdings nur das Einhooken in die MsgBox abweichend von der API-Version ist.
    Auf der Homepage sind noch viele Beispiele, wie man z.B. Radiobutton und Checkboxen zu einer Inputbox hinzufügt, Buttonbeschriftungen, Icons, Schriftart einer MsgBox ändert und so weiter.
    Es ist allerdings in VBA geschrieben. Die API-Programmierung ist allerdings fast überall ähnlich.

    clever-excel-forum.de/Thread-M…r-Button-und-eigenem-Icon
    clever-excel-forum.de/Thread-Projekt-Coole-MessageBox
    Gruß Karl-Heinz
    Oh, sorry Coldfire,

    das war mir jetzt gar nicht bewusst, dass zum Runterladen der Dateien eine Anmeldung erfolgt sein muss. :(

    Das Format xlsb ist ein gängiges Excelformat, das die Daten in binärer Form, so wie früher mal das xls-Format war, speichert. Ist platzsparender als die jetzt üblichen xlsx bzw. xlsm-Formate.
    Also nix gefährliches.

    Nun, die Beispiele enthalten sehr viel Code, die kann ich jetzt leider nicht hier alle zeigen. Und ich habe zig verschiedene Beispiele....

    Für das eine Beispiel mit den vier Buttons über die MessageBoxIndirect hier mal der Code unverbindlich zur Ansicht. Die Icons sind hier aber in der Tabelle deponiert und die fehlt ja jetzt.
    Der Code ist mittlerer Größe. Für drei Button wäre er mit MessageBoxA deutlich kleiner und für weitere Dinge eben mehr.

    Und, man kann alles immer auch anders machen :)

    Visual Basic-Quellcode

    1. ​Option Explicit
    2. #If Win64 Then
    3. Private Declare PtrSafe Function GetWindowLong Lib "user32" Alias "GetWindowLongPtrA" ( _
    4. ByVal hwnd As LongPtr, ByVal nIndex As Long) As LongPtr
    5. Private Declare PtrSafe Function SetWindowLong Lib "user32" Alias "SetWindowLongPtrA" ( _
    6. ByVal hwnd As LongPtr, ByVal nIndex As Long, _
    7. ByVal dwNewLong As LongPtr) As LongPtr
    8. #Else
    9. Private Declare PtrSafe Function GetWindowLong Lib "user32" Alias "GetWindowLongA" ( _
    10. ByVal hwnd As LongPtr, ByVal nIndex As Long) As LongPtr
    11. Private Declare PtrSafe Function SetWindowLong Lib "user32" Alias "SetWindowLongA" ( _
    12. ByVal hwnd As LongPtr, ByVal nIndex As Long, _
    13. ByVal dwNewLong As LongPtr) As LongPtr
    14. #End If
    15. Private Declare PtrSafe Function GetCursorPos Lib "user32" (lpPoint As POINTAPI) As Long
    16. Private Declare PtrSafe Function SetWindowPos Lib "user32" (ByVal hwnd As LongPtr, _
    17. ByVal hWndInsertAfter As LongPtr, ByVal x As Long, ByVal y As Long, _
    18. ByVal cx As Long, ByVal cy As Long, ByVal wFlags As Long) As Long
    19. Private Declare PtrSafe Function MessageBoxIndirectA Lib "user32" ( _
    20. lpMsgBoxParams As MSGBOXPARAMS) As Long
    21. Private Declare PtrSafe Function FindWindowA Lib "user32" ( _
    22. ByVal lpClassName As String, ByVal lpWindowName As String) As LongPtr
    23. Private Declare PtrSafe Function SetDlgItemTextA Lib "user32" ( _
    24. ByVal hDlg As LongPtr, ByVal nIDDlgItem As Long, ByVal lpString As String) As Long
    25. Private Declare PtrSafe Function SendDlgItemMessageA Lib "user32" ( _
    26. ByVal hDlg As LongPtr, ByVal nIDDlgItem As Long, ByVal wMsg As Long, _
    27. ByVal wParam As LongPtr, ByVal lParam As LongPtr) As LongPtr
    28. Private Declare PtrSafe Function PostMessageA Lib "user32" ( _
    29. ByVal hwnd As LongPtr, ByVal wMsg As Long, _
    30. ByVal wParam As LongPtr, ByVal lParam As LongPtr) As Long
    31. Private Declare PtrSafe Function GetSystemMetrics Lib "user32" ( _
    32. ByVal nIndex As Long) As Long
    33. Private Declare PtrSafe Function GetWindowRect Lib "user32" ( _
    34. ByVal hwnd As LongPtr, lpRect As RECT) As Long
    35. Private Declare PtrSafe Function SetTimer Lib "user32" ( _
    36. ByVal hwnd As LongPtr, ByVal nIDEvent As LongPtr, _
    37. ByVal uElapse As Long, ByVal lpTimerFunc As LongPtr) As LongPtr
    38. Private Declare PtrSafe Function KillTimer Lib "user32" ( _
    39. ByVal hwnd As LongPtr, ByVal nIDEvent As LongPtr) As Long
    40. Private Type POINTAPI
    41. x As Long
    42. y As Long
    43. End Type
    44. Dim PtKreuz As POINTAPI
    45. Private Type RECT
    46. Left As Long
    47. Top As Long
    48. Right As Long
    49. Bottom As Long
    50. End Type
    51. Private Type MSGBOXPARAMS
    52. cbSize As Long
    53. hwndOwner As LongPtr
    54. hInstance As LongPtr
    55. sText As String
    56. sCaption As String
    57. dwStyle As Long
    58. hIcon As LongPtr ' lpszIcon
    59. hDlg As LongPtr ' dwContextHelpId
    60. lpMsgBoxCallback As LongPtr
    61. iBtn As Long ' dwLanguageId
    62. hTimer As LongPtr ' Neu
    63. tPt As POINTAPI ' Neu
    64. bNoSysMenu As Boolean ' Neu
    65. End Type
    66. Public Const vbNoSysMenu As Long = 2097152
    67. Private Const WS_SYSMENU As Long = &H80000
    68. Private Const SM_CXSCREEN As Long = 0
    69. Private Const SM_CYSCREEN As Long = 1
    70. Dim MB As MSGBOXPARAMS, msBtn() As String
    71. Const sIconSheet As String = "Tabelle1" ' <<<<Blatt ggf. anpassen>>>>
    72. Private Function MsgboxEx(sText As String, _
    73. Optional ByVal iStyle As Long = 64, _
    74. Optional sCaption As String = "Microsoft Excel", _
    75. Optional sBtnText As String = "OK", _
    76. Optional sIconname As String, _
    77. Optional x As Long, Optional y As Long) As String
    78. ' MsgBox mit Anpassungen für bis zu 4 Buttons, Icon, Position
    79. Dim iAnzBtn As Integer, Wsh As Worksheet
    80. Set Wsh = ThisWorkbook.Sheets("Tabelle1") ' Blatt mit den Icons <<<anpassen>>>
    81. msBtn = Split(",,," & sBtnText & ",,,,,,", ",") ' Buttontexte aufsplitten
    82. iAnzBtn = UBound(msBtn) - 8 ' Anzahl der Buttons incl. Pseudo
    83. msBtn(9) = msBtn(6): msBtn(7) = msBtn(4) ' Umsetzung einiger Btn-Texte
    84. msBtn(1) = msBtn(3): msBtn(6) = msBtn(3) ' 3, 4 und 5 schon beim Splitten gesetzt
    85. msBtn(2) = IIf(iAnzBtn = 4, msBtn(5), msBtn(4))
    86. With MB
    87. .cbSize = LenB(MB)
    88. .hwndOwner = Application.hwnd ' An Excel gebunden
    89. .hInstance = Application.HinstancePtr ' Instanz übernehmen
    90. .sText = Replace(sText, "¶", vbLf) ' MsgBox-Text ggf. mit Umbruch
    91. .sCaption = sCaption ' MsgBox Caption
    92. .tPt.x = x: .tPt.y = y ' MsgBox-Position
    93. .bNoSysMenu = iStyle And vbNoSysMenu ' Kein Schließenkreuz
    94. .dwStyle = (iStyle And &HFBFF8) Or (iAnzBtn - 1) ' Style-Parameter ohne Buttons
    95. If iAnzBtn > 3 Then .dwStyle = .dwStyle Or vbMsgBoxHelpButton
    96. On Error Resume Next
    97. .hIcon = 0 ' Handle des gefundenen Icons
    98. .hIcon = Wsh.OLEObjects(sIconname).Object.Picture.handle
    99. .lpMsgBoxCallback = GetAddressOf(AddressOf MsgBoxCallbackProc)
    100. .hTimer = SetTimer(0&, 0&, 25, AddressOf MsgBoxCallbackProc)
    101. MsgboxEx = Replace(msBtn(MessageBoxIndirectA(MB)), "&", "")
    102. ' Systemkreuz geklickt?
    103. GetCursorPos .tPt ' Mausposition holen
    104. If .tPt.y < PtKreuz.y Then MsgboxEx = "SystemAbbruch"
    105. End With
    106. End Function
    107. Private Function GetAddressOf(ByVal lpProcAddress As LongPtr) As LongPtr
    108. GetAddressOf = lpProcAddress
    109. End Function
    110. Private Sub MsgBoxCallbackProc()
    111. ' TYPE HELP_INFO abgeschaltet, wird nicht benötigt
    112. ' Setzt die Button-Texte und das Icon individuell
    113. Dim R As RECT, lPos As Long
    114. With MB
    115. If .hTimer <> 0 Then
    116. KillTimer 0&, .hTimer: .hTimer = 0 ' Timer löschen
    117. .hDlg = FindWindowA("#32770", .sCaption) ' MsgBox-Handle ermitteln
    118. ' Icon setzen &H170=STM_SETICON
    119. If .hIcon <> 0 Then SendDlgItemMessageA .hDlg, 20, &H170, .hIcon, 0
    120. For .iBtn = 1 To 9 ' Buttontexte setzen
    121. SetDlgItemTextA .hDlg, .iBtn, msBtn(.iBtn)
    122. Next .iBtn
    123. If .tPt.x < 0 Or .tPt.y < 0 Then ' MsgBox-Pos. auf Maus-Pos.
    124. GetCursorPos .tPt ' Mausposition holen
    125. End If
    126. If .tPt.x > 0 And .tPt.y > 0 Then ' MsgBox-Position setzen
    127. GetWindowRect .hDlg, R ' Koordinaten der MsgBox holen
    128. lPos = GetSystemMetrics(SM_CXSCREEN) - (R.Right - R.Left)
    129. If .tPt.x > lPos Then .tPt.x = lPos
    130. lPos = GetSystemMetrics(SM_CYSCREEN) - (R.Bottom - R.Top) - 50
    131. If .tPt.y > lPos Then .tPt.y = lPos
    132. SetWindowPos .hDlg, 0, .tPt.x, .tPt.y, 0, 0, &H1 ' &H1=SWP_NOSIZE
    133. End If
    134. GetWindowRect .hDlg, R ' Koordinaten der MsgBox holen
    135. PtKreuz.y = R.Top + 40 ' Position des
    136. PtKreuz.x = R.Right - 40 ' roten Systemkreuzes
    137. ' GWL_STYLE = (-16) ' System/Schließenkreuz auschalten
    138. If .bNoSysMenu Then _
    139. SetWindowLong .hDlg, -16, GetWindowLong(.hDlg, -16) And Not WS_SYSMENU
    140. Else ' Vierter Button (Help) wurde geklickt
    141. msBtn(2) = msBtn(9) ' Rückgabewert HelpBtn setzen
    142. PostMessageA .hDlg, &H10, 0&, 0& ' MsgBox schließen &H10=WM_CLOSE
    143. End If
    144. End With
    145. End Sub
    146. ' ########## Beispiele ##########
    147. ' Diese Sub's sind zum Testen
    148. ' Es können 1,2,3 oder 4 Buttons angezeigt werden => kommagetrennt angeben
    149. ' & vor einem Buchstaben stellt die Shortcut-Taste dar z.B. Alt-S usw.
    150. ' ¶ stellt einen Zeilenumbruch dar (vbLf)
    151. ' Über x und y kann die Position der MsgBox festgelegt oder an der Mausposition gestartet werden
    152. ' Zurückgegeben wird keine Nummer, sondern der Buttontext des geklickten Buttons
    153. Private Sub MeinMsgBoxTest1()
    154. Call MsgboxEx("ok")
    155. End Sub
    156. Private Sub Aufruftest1()
    157. MsgBox (MsgboxEx("Bitte wähle die Schlumpfaktion aus!", vbInformation, "Schlumpftest", _
    158. "Schlumpfe &aus,Schlumpfe &ein,Schlumpfe &um,My&Help", _
    159. "Image3", -1, -1))
    160. End Sub
    161. Private Sub MeinMsgBoxTest2()
    162. Select Case MsgboxEx("Bitte wähle eine Option aus!", vbExclamation, "Auswahl", _
    163. "Option &1,Option &2,Option &3,Option &4")
    164. Case "Option 1": MsgBox "Habe die Option 1 gewählt"
    165. Case "Option 2": MsgBox "Option 2 wurde gewählt"
    166. Case "Option 3": MsgBox "Option 3 ist gewählt worden"
    167. Case "Option 4": MsgBox "Du hast Option &4 gewählt"
    168. Case Else: MsgBox "Du hast abgebrochen"
    169. End Select
    170. End Sub


    Gruß
    Karl-Heinz
    Weil ich gerade etwas lange weile hatte, hier mal ein einfaches (unvollständiges) Beispiel mit der API SoftModalMessageBox (max. 11 Buttons). Ihr kennt mich ja, keine zusätzlichen Verweise oder Nuget-Pakete. ;)

    Eine Klasse MessageBoxEx
    Spoiler anzeigen

    VB.NET-Quellcode

    1. Imports System.Runtime.InteropServices
    2. Public Class MessageBoxEx
    3. #Region "APIs"
    4. <DllImport("User32.dll", EntryPoint:="MB_GetString")>
    5. Private Shared Function MB_GetString(<[In]> dwBtnId As MessageBoxCommandID) As IntPtr
    6. End Function
    7. <DllImport("User32.dll", EntryPoint:="SoftModalMessageBox")>
    8. Private Shared Function SoftModalMessageBox(<[In]> ByRef lpMbd As MSGBOXDATA) As MessageBoxCommandID
    9. End Function
    10. #End Region
    11. #Region "Enums"
    12. Public Enum MessageBoxCommandID As Integer
    13. ID_OK = 1
    14. ID_CANCEL = 2
    15. ID_ABORT = 3
    16. ID_RETRY = 4
    17. ID_IGNORE = 5
    18. ID_YES = 6
    19. ID_NO = 7
    20. ID_CLOSE = 8
    21. ID_HELP = 9
    22. ID_TRYAGAIN = 10
    23. ID_CONTINUE = 11
    24. ID_TIMEOUT = 32000
    25. End Enum
    26. Public Enum MessageBoxDefaultButton As Integer
    27. MB_DEFBUTTON1 = 0
    28. MB_DEFBUTTON2 = 1
    29. MB_DEFBUTTON3 = 2
    30. MB_DEFBUTTON4 = 3
    31. MB_DEFBUTTON5 = 4
    32. MB_DEFBUTTON6 = 5
    33. MB_DEFBUTTON7 = 6
    34. MB_DEFBUTTON8 = 7
    35. MB_DEFBUTTON9 = 8
    36. MB_DEFBUTTON10 = 9
    37. MB_DEFBUTTON11 = 10
    38. End Enum
    39. Public Enum MessageBoxButtons As Integer
    40. MB_OK = 0
    41. MB_OKCANCEL = 1
    42. MB_ABORTRETRYIGNORE = 2
    43. MB_YESNOCANCEL = 3
    44. MB_YESNO = 4
    45. MB_RETRYCANCEL = 5
    46. MB_CANCELTRYCONTINUE = 6
    47. MB_MyMessageBox = 7
    48. End Enum
    49. Private Enum MessageBoxFlags As Integer
    50. MB_APPLMODAL = &H0
    51. MB_SYSTEMMODAL = &H1000
    52. MB_TASKMODAL = &H2000
    53. MB_NOFOCUS = &H8000
    54. MB_SETFOREGROUND = &H10000
    55. MB_DEFAULT_DESKTOP_ONLY = &H20000
    56. MB_TOPMOST = &H40000
    57. MB_RIGHT = &H80000
    58. MB_RTLREADING = &H100000
    59. MB_SERVICE_NOTIFICATION = &H200000
    60. MB_SERVICE_NOTIFICATION_NT3X = &H40000
    61. MB_TYPEMASK = &HF
    62. MB_ICONMASK = &HF0
    63. MB_DEFMASK = &HF00
    64. MB_MODEMASK = &H3000
    65. MB_MISCMASK = &HC000
    66. End Enum
    67. Public Enum MessageBoxIcon As Integer
    68. MB_ICONNONE = &H0
    69. MB_ICONHAND = &H10
    70. MB_ICONSTOP = &H10
    71. MB_ICONERROR = &H10
    72. MB_ICONQUESTION = &H20
    73. MB_ICONWARNING = &H30
    74. MB_ICONEXCLAMATION = &H30
    75. MB_ICONASTERISK = &H40
    76. MB_ICONINFORMATION = &H40
    77. MB_USERICON = &H80
    78. End Enum
    79. #End Region
    80. #Region "Class"
    81. Public Sub New()
    82. For MBCommandID As MessageBoxCommandID = MessageBoxCommandID.ID_OK To MessageBoxCommandID.ID_CONTINUE
    83. m_DefaultMessageBoxButtonText.Add(MB_GetString(CType(MBCommandID - MessageBoxCommandID.ID_OK, MessageBoxCommandID)))
    84. Next
    85. End Sub
    86. #End Region
    87. #Region "Structures"
    88. ' https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-msgboxparamsw
    89. Private Structure MSGBOXPARAMS
    90. Dim cbSize As Integer
    91. Dim hWndOwner As IntPtr
    92. Dim hInstance As IntPtr
    93. <MarshalAs(UnmanagedType.LPWStr)> Dim lpszText As String
    94. <MarshalAs(UnmanagedType.LPWStr)> Dim lpszCaption As String
    95. Dim dwStyle As MessageBoxFlags
    96. <MarshalAs(UnmanagedType.LPWStr)> Dim lpszIcon As String
    97. Dim dwContextHelpId As Integer
    98. Dim lpfnMsgBoxCallback As IntPtr '-> MSGBOXCALLBACK -> struct HELPINFO
    99. Dim dwLanguageId As Integer
    100. End Structure
    101. Private Structure MSGBOXDATA
    102. Dim MBParams As MSGBOXPARAMS
    103. Dim pwndOwner As IntPtr
    104. Dim dwPadding As Integer
    105. Dim wLanguageId As Short
    106. Dim pidButton As IntPtr
    107. Dim ppszButtonText As IntPtr
    108. Dim cButtons As Integer
    109. Dim defButton As MessageBoxDefaultButton
    110. Dim cancelId As Integer
    111. Dim dwTimeout As Integer
    112. Dim phwndList As IntPtr
    113. <MarshalAs(UnmanagedType.ByValArray, SizeConst:=20)> Dim dwReserved As Integer()
    114. End Structure
    115. #End Region
    116. #Region "Variables"
    117. Private ReadOnly m_DefaultMessageBoxButtonText As New List(Of IntPtr)
    118. #End Region
    119. #Region "Functions"
    120. Public Function ShowDialog(Owner As IWin32Window,
    121. Caption As String,
    122. Text As String,
    123. Buttons As MessageBoxButtons,
    124. Icon As MessageBoxIcon,
    125. Optional DefaultButton As MessageBoxDefaultButton = MessageBoxDefaultButton.MB_DEFBUTTON1,
    126. Optional ShowHelpButton As Boolean = False,
    127. Optional TimeOut As Integer = -1) As MessageBoxCommandID
    128. Dim Ret As MessageBoxCommandID
    129. Dim IsCancelButton As Boolean
    130. Dim MBButtons As Integer() = Nothing
    131. Dim MBButtonsText As IntPtr() = Nothing
    132. Select Case Buttons
    133. Case MessageBoxButtons.MB_OK
    134. MBButtons = New Integer() {MessageBoxCommandID.ID_OK}
    135. MBButtonsText = New IntPtr() {m_DefaultMessageBoxButtonText(MBButtons(0) - MessageBoxCommandID.ID_OK)}
    136. Case MessageBoxButtons.MB_OKCANCEL
    137. IsCancelButton = True
    138. MBButtons = New Integer() {MessageBoxCommandID.ID_OK,
    139. MessageBoxCommandID.ID_CANCEL}
    140. MBButtonsText = New IntPtr() {m_DefaultMessageBoxButtonText(MBButtons(0) - MessageBoxCommandID.ID_OK),
    141. m_DefaultMessageBoxButtonText(MBButtons(1) - MessageBoxCommandID.ID_OK)}
    142. Case MessageBoxButtons.MB_ABORTRETRYIGNORE
    143. MBButtons = New Integer() {MessageBoxCommandID.ID_ABORT,
    144. MessageBoxCommandID.ID_RETRY,
    145. MessageBoxCommandID.ID_IGNORE}
    146. MBButtonsText = New IntPtr() {m_DefaultMessageBoxButtonText(MBButtons(0) - MessageBoxCommandID.ID_OK),
    147. m_DefaultMessageBoxButtonText(MBButtons(1) - MessageBoxCommandID.ID_OK),
    148. m_DefaultMessageBoxButtonText(MBButtons(2) - MessageBoxCommandID.ID_OK)}
    149. Case MessageBoxButtons.MB_YESNOCANCEL
    150. IsCancelButton = True
    151. MBButtons = New Integer() {MessageBoxCommandID.ID_YES,
    152. MessageBoxCommandID.ID_NO,
    153. MessageBoxCommandID.ID_CANCEL}
    154. MBButtonsText = New IntPtr() {m_DefaultMessageBoxButtonText(MBButtons(0) - MessageBoxCommandID.ID_OK),
    155. m_DefaultMessageBoxButtonText(MBButtons(1) - MessageBoxCommandID.ID_OK),
    156. m_DefaultMessageBoxButtonText(MBButtons(2) - MessageBoxCommandID.ID_OK)}
    157. Case MessageBoxButtons.MB_YESNO
    158. MBButtons = New Integer() {MessageBoxCommandID.ID_YES,
    159. MessageBoxCommandID.ID_NO}
    160. MBButtonsText = New IntPtr() {m_DefaultMessageBoxButtonText(MBButtons(0) - MessageBoxCommandID.ID_OK),
    161. m_DefaultMessageBoxButtonText(MBButtons(1) - MessageBoxCommandID.ID_OK)}
    162. Case MessageBoxButtons.MB_RETRYCANCEL
    163. IsCancelButton = True
    164. MBButtons = New Integer() {MessageBoxCommandID.ID_RETRY,
    165. MessageBoxCommandID.ID_CANCEL}
    166. MBButtonsText = New IntPtr() {m_DefaultMessageBoxButtonText(MBButtons(0) - MessageBoxCommandID.ID_OK),
    167. m_DefaultMessageBoxButtonText(MBButtons(1) - MessageBoxCommandID.ID_OK)}
    168. Case MessageBoxButtons.MB_CANCELTRYCONTINUE
    169. IsCancelButton = True
    170. MBButtons = New Integer() {MessageBoxCommandID.ID_CANCEL,
    171. MessageBoxCommandID.ID_TRYAGAIN,
    172. MessageBoxCommandID.ID_CONTINUE}
    173. MBButtonsText = New IntPtr() {m_DefaultMessageBoxButtonText(MBButtons(0) - MessageBoxCommandID.ID_OK),
    174. m_DefaultMessageBoxButtonText(MBButtons(1) - MessageBoxCommandID.ID_OK),
    175. m_DefaultMessageBoxButtonText(MBButtons(2) - MessageBoxCommandID.ID_OK)}
    176. Case MessageBoxButtons.MB_MyMessageBox
    177. IsCancelButton = True
    178. MBButtons = New Integer() {MessageBoxCommandID.ID_OK,
    179. MessageBoxCommandID.ID_CANCEL,
    180. MessageBoxCommandID.ID_ABORT,
    181. MessageBoxCommandID.ID_RETRY,
    182. MessageBoxCommandID.ID_IGNORE,
    183. MessageBoxCommandID.ID_YES,
    184. MessageBoxCommandID.ID_NO,
    185. MessageBoxCommandID.ID_CLOSE,
    186. MessageBoxCommandID.ID_TRYAGAIN,
    187. MessageBoxCommandID.ID_CONTINUE}
    188. MBButtonsText = New IntPtr() {Marshal.StringToHGlobalUni("1"),
    189. Marshal.StringToHGlobalUni("2"),
    190. Marshal.StringToHGlobalUni("3"),
    191. Marshal.StringToHGlobalUni("4"),
    192. Marshal.StringToHGlobalUni("5"),
    193. Marshal.StringToHGlobalUni("6"),
    194. Marshal.StringToHGlobalUni("7"),
    195. Marshal.StringToHGlobalUni("8"),
    196. Marshal.StringToHGlobalUni("9"),
    197. Marshal.StringToHGlobalUni("10")}
    198. End Select
    199. '-> MSGBOXCALLBACK -> struct HELPINFO
    200. If ShowHelpButton Then
    201. Array.Resize(MBButtons, MBButtons.Length + 1)
    202. Array.Resize(MBButtonsText, MBButtonsText.Length + 1)
    203. MBButtons(MBButtons.Length - 1) = MessageBoxCommandID.ID_HELP
    204. MBButtonsText(MBButtonsText.Length - 1) = m_DefaultMessageBoxButtonText(MBButtons(MBButtons.Length - 1) - MessageBoxCommandID.ID_OK)
    205. End If
    206. Dim hMBButtons As GCHandle = GCHandle.Alloc(MBButtons, GCHandleType.Pinned)
    207. Dim hMBButtonsText As GCHandle = GCHandle.Alloc(MBButtonsText, GCHandleType.Pinned)
    208. Dim MBData As New MSGBOXDATA
    209. MBData.MBParams.cbSize = Marshal.SizeOf(Of MSGBOXDATA)
    210. MBData.MBParams.hWndOwner = Owner.Handle
    211. MBData.MBParams.hInstance = Marshal.GetHINSTANCE(GetType(MessageBoxEx).Module)
    212. MBData.MBParams.lpszText = Text
    213. MBData.MBParams.lpszCaption = Caption
    214. MBData.MBParams.dwStyle = CType(MessageBoxFlags.MB_TYPEMASK Or Icon, MessageBoxFlags)
    215. MBData.MBParams.lpszIcon = Nothing
    216. MBData.MBParams.dwContextHelpId = 0
    217. MBData.MBParams.lpfnMsgBoxCallback = IntPtr.Zero '-> MSGBOXCALLBACK -> struct HELPINFO
    218. MBData.MBParams.dwLanguageId = 0
    219. MBData.pwndOwner = IntPtr.Zero
    220. MBData.dwPadding = 0
    221. MBData.wLanguageId = 0
    222. MBData.pidButton = hMBButtons.AddrOfPinnedObject
    223. MBData.ppszButtonText = hMBButtonsText.AddrOfPinnedObject
    224. MBData.cButtons = MBButtons.Length
    225. MBData.defButton = If(DefaultButton <= MBButtons.Length, DefaultButton, CType(MBButtons.Length, MessageBoxDefaultButton))
    226. MBData.cancelId = If(IsCancelButton, MessageBoxCommandID.ID_CANCEL, 0) 'geht nur wenn ein Cancel-Button im Dialog ist
    227. MBData.dwTimeout = If(TimeOut < -1, -1, TimeOut) 'in Millisekunden
    228. MBData.phwndList = IntPtr.Zero
    229. Ret = SoftModalMessageBox(MBData)
    230. hMBButtonsText.Free()
    231. hMBButtons.Free()
    232. If Buttons = MessageBoxButtons.MB_MyMessageBox Then
    233. For Index = 0 To If(ShowHelpButton, MBButtonsText.Length - 2, MBButtonsText.Length - 1)
    234. Marshal.FreeHGlobal(MBButtonsText(Index))
    235. Next
    236. End If
    237. Return Ret
    238. End Function
    239. #End Region
    240. End Class


    und zum testen in der Form

    VB.NET-Quellcode

    1. Public Class Form1
    2. Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    3. Dim MBEx As New MessageBoxEx
    4. Debug.Print(MBEx.ShowDialog(Me, "Caption", "Text",
    5. MessageBoxEx.MessageBoxButtons.MB_MyMessageBox,
    6. MessageBoxEx.MessageBoxIcon.MB_ICONWARNING,
    7. MessageBoxEx.MessageBoxDefaultButton.MB_DEFBUTTON1,
    8. True, -1).ToString)
    9. End Sub
    10. End Class
    Mfg -Franky-
    Hallo Franky,

    vielen Dank für Deinen Input zur Funktion SoftModalMessageBox. Finde ich sehr interessant. Sie fehlte noch in meinem API-Viewer, wo ich sie jetzt aufnehmen werde.

    Da ich zur Zeit mit VBA unterwegs bin, habe ich mich daran gemacht, das Ganze unter VBA zum Laufen zu bringen. Hierzu gibt es im Netz leider keine bzw. nur weinige widersprüchliche Beiträge.
    Und Dein Code bringt mich auch nicht weiter.

    Meine eigenen Interpretationen (s. Code) lassen leider Excel abstürzen.
    Vielleicht hast Du ja (auch wenn es nicht VB.Net ist) eine Idee, was da noch fehlerhaft sein könnte.

    M.E. könnte es an der Übergabe der Arrays liegen.

    Visual Basic-Quellcode

    1. ​Private Type MSGBOXPARAMS
    2. cbSize As Long
    3. hwndOwner As LongPtr
    4. hInstance As LongPtr
    5. lpszText As String
    6. lpszCaption As String
    7. dwStyle As Long
    8. lpszIcon As String
    9. dwContextHelpId As LongPtr
    10. lpfnMsgBoxCallback As LongPtr
    11. dwLanguageId As Long
    12. End Type
    13. Private Type MSGBOXDATA
    14. params As MSGBOXPARAMS
    15. pwndOwner As LongPtr ' Nur intern
    16. Padding As Long
    17. wLanguageId As Long
    18. pidButton As LongPtr ' Array (Button-IDs)
    19. ppszButtonText As LongPtr ' Array (Buttontext)
    20. cButtons As Long ' Anzahl der Buttons
    21. DefButton As Long ' Button-ID Default
    22. CancelId As Long ' Button-ID Abbruch
    23. Timeout As Long ' Timeout
    24. phwndList As LongPtr ' Nur intern
    25. Reserved As Long ' Reserviert
    26. End Type
    27. Private Declare PtrSafe Function SoftModalMessageBox Lib "user32" (pMsgBoxParams As MSGBOXDATA) As Long
    28. Function MsgboxEx()
    29. Dim md As MSGBOXDATA
    30. Dim lArrBtn() As Variant, sArrTxt() As String
    31. lArrBtn = Array(1, 2, 3, 4)
    32. sArrTxt = Split("Option 1,Option 2,Option 3,Option 4", ",")
    33. With md
    34. With .params
    35. .cbSize = LenB(md.params)
    36. .lpszText = "Mein Messagetext"
    37. .lpszCaption = "Mein MsgBox-Kopf"
    38. .dwStyle = vbQuestion Or vbYesNoCancel
    39. End With
    40. .DefButton = 0
    41. .CancelId = 2
    42. .cButtons = 4
    43. .pidButton = VarPtr(lArrBtn(0))
    44. .ppszButtonText = VarPtr(sArrTxt(0))
    45. .Timeout = -1
    46. End With
    47. MsgboxEx = sArrTxt(SoftModalMessageBox(md))
    48. End Function


    Gruß
    Karl-Heinz
    @volti VBA ist nicht meine favorisierte Programmiersprache. VB6 schon eher und VBA ist da ja sehr ähnlich. Den VB.Net Code habe ich von meinem VB6 Code übersetzt. Die lpszXXXX Member in MSGBOXPARAMS sind in VB6 ein Long und werden mit lpszXXXX = StrPtr("xxx") gefüttert. In VBA müssten dann wohl die lpszXXXX ein LongPtr sein. Ich könnte am Montag bei Bedarf auch noch meinen VB6 Code hier posten. Das lässt sich sicher leichter nach VBA übersetzen.

    Edit: wLanguageId müsste in VBA ein Integer sein. Ist in meinem VB6 Bsp. glaub auch so.
    Mfg -Franky-

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „-Franky-“ ()

    Super Franky,

    da bin ich mal gespannt.

    PS: Ich habe hier MS-Office 64 Bit.
    Die lpszXXXX = StrPtr("xxx") setze ich in anderen Beispielen bei mir auch als STRING ein, das funktioniert dann ohne StrPtr. Ich hatte es aber auch hier schon mit LongPtr und StrPtr("xxx") probiert.

    Gruß KH
    @volti Moin. Wie versprochen hier mein VB6 Code dazu.

    Alles in einer Klasse clsMessageBox
    Spoiler anzeigen

    Visual Basic-Quellcode

    1. Option Explicit
    2. ' ----==== Enums ====----
    3. Public Enum MessageBoxCommandID
    4. ID_OK = 1
    5. ID_CANCEL = 2
    6. ID_ABORT = 3
    7. ID_RETRY = 4
    8. ID_IGNORE = 5
    9. ID_YES = 6
    10. ID_NO = 7
    11. ID_CLOSE = 8
    12. ID_HELP = 9
    13. ID_TRYAGAIN = 10
    14. ID_CONTINUE = 11
    15. ID_TIMEOUT = 32000
    16. End Enum
    17. Public Enum MessageBoxButtons
    18. MB_OK = &H0&
    19. MB_OKCANCEL = &H1&
    20. MB_ABORTRETRYIGNORE = &H2&
    21. MB_YESNOCANCEL = &H3&
    22. MB_YESNO = &H4&
    23. MB_RETRYCANCEL = &H5&
    24. MB_CANCELTRYCONTINUE = &H6&
    25. End Enum
    26. Public Enum MessageBoxIcon
    27. MB_ICONNONE = &H0&
    28. MB_ICONHAND = &H10&
    29. MB_ICONSTOP = &H10&
    30. MB_ICONERROR = &H10&
    31. MB_ICONQUESTION = &H20&
    32. MB_ICONWARNING = &H30&
    33. MB_ICONEXCLAMATION = &H30&
    34. MB_ICONASTERISK = &H40&
    35. MB_ICONINFORMATION = &H40&
    36. MB_USERICON = &H80&
    37. End Enum
    38. Public Enum MessageBoxDefaultButton
    39. MB_DEFBUTTON1 = 0
    40. MB_DEFBUTTON2 = 1
    41. MB_DEFBUTTON3 = 2
    42. MB_DEFBUTTON4 = 3
    43. MB_DEFBUTTON5 = 4
    44. MB_DEFBUTTON6 = 5
    45. MB_DEFBUTTON7 = 6
    46. MB_DEFBUTTON8 = 7
    47. MB_DEFBUTTON9 = 8
    48. MB_DEFBUTTON10 = 9
    49. MB_DEFBUTTON11 = 10
    50. End Enum
    51. Private Enum MessageBoxFlags
    52. MB_APPLMODAL = &H0
    53. MB_SYSTEMMODAL = &H1000
    54. MB_TASKMODAL = &H2000
    55. MB_NOFOCUS = &H8000
    56. MB_SETFOREGROUND = &H10000
    57. MB_DEFAULT_DESKTOP_ONLY = &H20000
    58. MB_TOPMOST = &H40000
    59. MB_RIGHT = &H80000
    60. MB_RTLREADING = &H100000
    61. MB_SERVICE_NOTIFICATION = &H200000
    62. MB_SERVICE_NOTIFICATION_NT3X = &H40000
    63. MB_TYPEMASK = &HF
    64. MB_ICONMASK = &HF0
    65. MB_DEFMASK = &HF00
    66. MB_MODEMASK = &H3000
    67. MB_MISCMASK = &HC000
    68. End Enum
    69. ' ----==== Types ====----
    70. Private Type MSGBOXPARAMS
    71. cbSize As Long
    72. hWndOwner As Long
    73. hInstance As Long
    74. lpszText As Long
    75. lpszCaption As Long
    76. dwStyle As Long
    77. lpszIcon As Long
    78. dwContextHelpId As Long
    79. lpfnMsgBoxCallback As Long
    80. dwLanguageId As Long
    81. End Type
    82. Private Type MSGBOXDATA
    83. PARAMS As MSGBOXPARAMS
    84. pwndOwner As Long
    85. dwPadding As Long
    86. wLanguageId As Integer
    87. pidButton As Long
    88. ppszButtonText As Long
    89. cButtons As Long
    90. defButton As Long
    91. cancelId As Long
    92. dwTimeout As Long
    93. phwndList As Long
    94. dwReserved(19) As Long
    95. End Type
    96. ' ----==== User32.dll Declarations ====----
    97. Private Declare Function MB_GetString Lib "User32.dll" ( _
    98. ByVal wBtn As MessageBoxCommandID) As Long
    99. Private Declare Function SoftModalMessageBox Lib "User32.dll" ( _
    100. ByRef lpmb As MSGBOXDATA) As Long
    101. ' ----==== Kernel32 Declarations ====----
    102. Private Declare Function LoadLibraryW Lib "Kernel32.dll" ( _
    103. ByVal lpLibName As Long) As Long
    104. Private Declare Function FreeLibrary Lib "Kernel32.dll" ( _
    105. ByVal hModule As Long) As Long
    106. Private Declare Function GetProcAddress Lib "Kernel32.dll" ( _
    107. ByVal hModule As Long, _
    108. ByVal lpProcName As String) As Long
    109. ' ----==== Variables ====----
    110. Private m_DefaultDialogBoxCommandText(1 To 11) As Long
    111. ' ----==== Class ====----
    112. Private Sub Class_Initialize()
    113. Dim CommandID As MessageBoxCommandID
    114. For CommandID = ID_OK To ID_CONTINUE
    115. m_DefaultDialogBoxCommandText(CommandID) = MB_GetString(CommandID - ID_OK)
    116. Next
    117. End Sub
    118. Private Sub Class_Terminate()
    119. '
    120. End Sub
    121. ' ----==== Functions ====----
    122. Public Function MessageBox(ByVal hWndOwner As Long, _
    123. ByVal Caption As String, _
    124. ByVal Text As String, _
    125. ByVal Buttons As MessageBoxButtons, _
    126. ByVal Icon As MessageBoxIcon, _
    127. Optional ByVal ShowHelpButton As Boolean = False, _
    128. Optional ByVal DefaultButton As MessageBoxDefaultButton = MB_DEFBUTTON1, _
    129. Optional ByVal TimeOut As Long = -1&) As MessageBoxCommandID
    130. Dim MBButtons() As MessageBoxCommandID
    131. Dim MBButtonsText() As Long
    132. Dim IsCancelButton As Boolean
    133. Dim Ret As MessageBoxCommandID
    134. If IsSoftModalMessageBoxExists Then
    135. Select Case Buttons
    136. Case MessageBoxButtons.MB_OK
    137. ReDim MBButtons(0)
    138. ReDim MBButtonsText(0)
    139. MBButtons(0) = ID_OK
    140. MBButtonsText(0) = m_DefaultDialogBoxCommandText(MBButtons(0))
    141. Case MessageBoxButtons.MB_OKCANCEL
    142. IsCancelButton = True
    143. ReDim MBButtons(1)
    144. ReDim MBButtonsText(1)
    145. MBButtons(0) = ID_OK
    146. MBButtons(1) = ID_CANCEL
    147. MBButtonsText(0) = m_DefaultDialogBoxCommandText(MBButtons(0))
    148. MBButtonsText(1) = m_DefaultDialogBoxCommandText(MBButtons(1))
    149. Case MessageBoxButtons.MB_ABORTRETRYIGNORE
    150. ReDim MBButtons(2)
    151. ReDim MBButtonsText(2)
    152. MBButtons(0) = ID_ABORT
    153. MBButtons(1) = ID_RETRY
    154. MBButtons(2) = ID_IGNORE
    155. MBButtonsText(0) = m_DefaultDialogBoxCommandText(MBButtons(0))
    156. MBButtonsText(1) = m_DefaultDialogBoxCommandText(MBButtons(1))
    157. MBButtonsText(2) = m_DefaultDialogBoxCommandText(MBButtons(2))
    158. Case MessageBoxButtons.MB_YESNOCANCEL
    159. IsCancelButton = True
    160. ReDim MBButtons(2)
    161. ReDim MBButtonsText(2)
    162. MBButtons(0) = ID_YES
    163. MBButtons(1) = ID_NO
    164. MBButtons(2) = ID_CANCEL
    165. MBButtonsText(0) = m_DefaultDialogBoxCommandText(MBButtons(0))
    166. MBButtonsText(1) = m_DefaultDialogBoxCommandText(MBButtons(1))
    167. MBButtonsText(2) = m_DefaultDialogBoxCommandText(MBButtons(2))
    168. Case MessageBoxButtons.MB_YESNO
    169. ReDim MBButtons(1)
    170. ReDim MBButtonsText(1)
    171. MBButtons(0) = ID_YES
    172. MBButtons(1) = ID_NO
    173. MBButtonsText(0) = m_DefaultDialogBoxCommandText(MBButtons(0))
    174. MBButtonsText(1) = m_DefaultDialogBoxCommandText(MBButtons(1))
    175. Case MessageBoxButtons.MB_RETRYCANCEL
    176. IsCancelButton = True
    177. ReDim MBButtons(1)
    178. ReDim MBButtonsText(1)
    179. MBButtons(0) = ID_RETRY
    180. MBButtons(1) = ID_CANCEL
    181. MBButtonsText(0) = m_DefaultDialogBoxCommandText(MBButtons(0))
    182. MBButtonsText(1) = m_DefaultDialogBoxCommandText(MBButtons(1))
    183. Case MessageBoxButtons.MB_CANCELTRYCONTINUE
    184. IsCancelButton = True
    185. ReDim MBButtons(2)
    186. ReDim MBButtonsText(2)
    187. MBButtons(0) = ID_CANCEL
    188. MBButtons(1) = ID_TRYAGAIN
    189. MBButtons(2) = ID_CONTINUE
    190. MBButtonsText(0) = m_DefaultDialogBoxCommandText(MBButtons(0))
    191. MBButtonsText(1) = m_DefaultDialogBoxCommandText(MBButtons(1))
    192. MBButtonsText(2) = m_DefaultDialogBoxCommandText(MBButtons(2))
    193. End Select
    194. If ShowHelpButton Then
    195. ReDim Preserve MBButtons(UBound(MBButtons) + 1)
    196. ReDim Preserve MBButtonsText(UBound(MBButtons) + 1)
    197. MBButtons(UBound(MBButtons)) = MessageBoxCommandID.ID_HELP
    198. MBButtonsText(UBound(MBButtons)) = m_DefaultDialogBoxCommandText(MBButtons(UBound(MBButtons)))
    199. End If
    200. Dim MBDATA As MSGBOXDATA
    201. With MBDATA
    202. .PARAMS.cbSize = Len(MBDATA)
    203. .PARAMS.hWndOwner = hWndOwner
    204. .PARAMS.hInstance = App.hInstance
    205. .PARAMS.lpszText = StrPtr(Text)
    206. .PARAMS.lpszCaption = StrPtr(Caption)
    207. .PARAMS.dwStyle = MessageBoxFlags.MB_TYPEMASK Or Icon
    208. .PARAMS.lpszIcon = 0&
    209. .PARAMS.dwContextHelpId = 0&
    210. .PARAMS.lpfnMsgBoxCallback = 0&
    211. .PARAMS.dwLanguageId = 0&
    212. .pwndOwner = 0&
    213. .dwPadding = 0&
    214. .wLanguageId = 0&
    215. .pidButton = VarPtr(MBButtons(0))
    216. .ppszButtonText = VarPtr(MBButtonsText(0))
    217. .cButtons = UBound(MBButtons) + 1
    218. .defButton = DefaultButton
    219. .cancelId = IIf(IsCancelButton, MessageBoxCommandID.ID_CANCEL, 0&)
    220. .dwTimeout = TimeOut
    221. .phwndList = 0&
    222. End With
    223. Ret = SoftModalMessageBox(MBDATA)
    224. End If
    225. MessageBox = Ret
    226. End Function
    227. Private Function IsSoftModalMessageBoxExists() As Boolean
    228. Dim Ret As Boolean
    229. Dim hModule As Long
    230. hModule = LoadLibraryW(StrPtr("User32.dll"))
    231. If hModule <> 0& Then
    232. Ret = CBool(GetProcAddress(hModule, "SoftModalMessageBox"))
    233. Call FreeLibrary(hModule)
    234. End If
    235. IsSoftModalMessageBoxExists = Ret
    236. End Function


    und aus der Form heraus

    Visual Basic-Quellcode

    1. Option Explicit
    2. Private Sub Command1_Click()
    3. Dim cMessageBox As New clsMessageBox
    4. Debug.Print cMessageBox.MessageBox(Me.hWnd, "Caption", "Text", _
    5. MB_OKCANCEL, MB_ICONEXCLAMATION, _
    6. True, MB_DEFBUTTON1, -1)
    7. End Sub

    Du musst hier mit StrPtr arbeiten da zb. lpszText ein LPCWSTR ist. Also LongPointerConstantWideString. MB_GetString gibt Dir ebenfalls einen Pointer auf einen WideString zurück (LPCWSTR).
    Mfg -Franky-
    Hallo,

    ich habe es jetzt zum Laufen gebracht. Danke noch mal Franky, das hat mir sehr geholfen.

    Auch wenn meine Minimalversion deutlich von Deiner abweicht, u.a. auch weil ich keine Nummer sondern den Buttontext zurückgeben will usw. funktioniert jetzt diese Minimalversion unter VBA zu meiner vollste Zufriedenheit.
    Als nächstes werde ich mich mal an alternative Icons, die Schriftfarben-, größen usw. machen. Mal sehen, ob's klappt.
    Spoiler anzeigen

    Visual Basic-Quellcode

    1. Option Explicit
    2. Private Declare PtrSafe Function GetProcAddress Lib "kernel32" ( _
    3. ByVal hModule As LongPtr, ByVal lpProcName As String) As LongPtr
    4. Private Declare PtrSafe Function LoadLibraryA Lib "kernel32" ( _
    5. ByVal lpLibFileName As String) As LongPtr
    6. Private Declare PtrSafe Function FreeLibrary Lib "kernel32" ( _
    7. ByVal hLibModule As LongPtr) As Long
    8. Private Type MSGBOXPARAMS
    9. cbSize As Long
    10. hWndOwner As LongPtr
    11. hInstance As LongPtr
    12. lpszText As LongPtr
    13. lpszCaption As LongPtr
    14. dwStyle As Long
    15. lpszIcon As LongPtr
    16. dwContextHelpId As Long
    17. lpfnMsgBoxCallback As LongPtr
    18. dwLanguageId As Long
    19. End Type
    20. Private Type MSGBOXDATA
    21. PARAMS As MSGBOXPARAMS
    22. pwndOwner As LongPtr ' Nur intern
    23. dwPadding As Long
    24. wLanguageId As Long
    25. pidButton As LongPtr ' Array (Button-IDs)
    26. ppszButtonText As LongPtr ' Array (Buttontext)
    27. cButtons As Long ' Anzahl der Buttons
    28. defButton As Long ' Button-ID Default
    29. cancelId As Long ' Button-ID Abbruch
    30. Timeout As Long ' Timeout
    31. phwndList As LongPtr ' Nur intern
    32. dwReserved(19) As Long ' Reserviert
    33. End Type
    34. Private Declare PtrSafe Function SoftModalMessageBox Lib "user32" (pMsgBoxParams As MSGBOXDATA) As Long
    35. Function MsgboxEx(ByVal sText As String, _
    36. Optional ByVal sBtns As String = "OK", _
    37. Optional ByVal sCaption As String = "Microsoft Excel", _
    38. Optional ByVal iIcon As Long, _
    39. Optional ByVal iTimeOut As Long, _
    40. Optional ByVal iDefBtn As Long) As String
    41. Dim md As MSGBOXDATA
    42. Dim lArrBtn() As Long, sArrTxt() As String, i As Long
    43. Dim hModul As LongPtr, hRet As LongPtr
    44. hModul = LoadLibraryA("User32.dll") ' Bibliothek laden
    45. If hModul <> 0& Then
    46. hRet = GetProcAddress(hModul, "SoftModalMessageBox") ' Funktion vorhanden?
    47. FreeLibrary hModul ' Bibliothek schließen
    48. End If
    49. If hRet = 0 Then Exit Function ' Anzeige nicht möglich
    50. sArrTxt = Split(sBtns, ",") ' Buttontexte in Array
    51. ReDim lArrBtn(UBound(sArrTxt)) ' ID-Array dimensionieren
    52. For i = 0 To UBound(lArrBtn): lArrBtn(i) = i + 1: Next i ' IDs in Array setzen
    53. With md
    54. With .PARAMS
    55. .cbSize = LenB(md.PARAMS)
    56. .hWndOwner = Application.hwnd ' Excel-Handle
    57. .hInstance = Application.HinstancePtr ' Excel-Instance
    58. .lpszText = StrPtr(sText) ' Messagetext
    59. .lpszCaption = StrPtr(sCaption) ' Titel
    60. .dwStyle = iIcon ' Icon setzen
    61. End With
    62. .cancelId = 0
    63. .cButtons = UBound(lArrBtn) + 1 ' Anzahl der Buttons
    64. If iDefBtn = 0 Or iDefBtn > .cButtons Then iDefBtn = 1
    65. .defButton = (iDefBtn - 1) ' DefaultButtonID
    66. .pidButton = VarPtr(lArrBtn(0)) ' IDs übergeben
    67. .ppszButtonText = VarPtr(sArrTxt(0)) ' Buttontexte übergeben
    68. .Timeout = (iTimeOut - 1) ' Timeout setzen, 0=abgeschaltet
    69. End With
    70. i = SoftModalMessageBox(md) ' MsgBox anzeigen
    71. If i = 32000 Then
    72. MsgboxEx = "Timeout"
    73. Else
    74. MsgboxEx = sArrTxt(i - 1) ' Ergebnistext zurückgeben
    75. End If
    76. End Function
    77. Sub Test()
    78. MsgBox MsgboxEx("Bitte wähle eine Option aus!", "Option 1,Option 2,Option 3,Option 4", "Meine Auswahl", vbExclamation, 0, 1)
    79. End Sub


    Wie sicher ist diese nicht dokumentiert Funktion? Kann es sein, dass die mal weg ist oder nicht bei jedem User vorhanden ist?
    Ich habe auch eine Vorhandenseinprüfung über die User32-DLL gemacht. Aber brauche ich die überhaupt?

    Gruß
    Karl-Heinz

    volti schrieb:

    Wie sicher ist diese nicht dokumentiert Funktion? Kann es sein, dass die mal weg ist oder nicht bei jedem User vorhanden ist?
    Ich habe auch eine Vorhandenseinprüfung über die User32-DLL gemacht. Aber brauche ich die überhaupt?

    Die Funktion gibt es schon lange, der VB6 Code entstand unter Win7, und ja, die ist bei allen, die auf einem noch supporteten Windows unterwegs sind, vorhanden. Die Prüfung, ob eine bestimmte Funktion aus einer DLL exportiert wird, kannst Du auch weglassen wenn Du nicht noch für kleiner Win7 entwickelst. Es war schon immer so das viele Funktionen in Windows zuerst nicht dokumentiert sind, nicht mal über einen Namen, sonder nur über deren OrdinalNummer, aus der DLL angesprochen werden konnten. Irgendwann waren die Funktionen offiziell dokumentiert und konnten, wie jede andere DLL-Funktion angesprochen werden. Wenn ich mir das Diagram auf github.com/imgdrive/SoftModalMessageBox anschau, wird diese API sowieso von allen anderen MessageBox-APIs angesprochen. Da kann man auch direkt die API verwenden und hat nicht die Einschränkungen die die anderen MessageBox-APIs so mitbringen. Wenn MS diese API nur über die OrdinalNummer verfügbar gemacht hätte, würde ich auch sagen, Finger davon weg. Windows ist dafür bekannt abwärts kompatibel zu sein. Bedeutet viele Funktionen die schon lange als deprecated eingestuft wurden, gibt es heute noch und können immer noch verwendet werden. Intern wird dann meist auf die neueren Funktionen umgeleitet bzw die veraltete Funktion gibt halt einen Fehlercode zurück. Ansonsten kannst Du auch mal in den SourceCode von WinXP schauen der 2020, versehentlich (hust), geleakt wurde ob es da schon entsprechende Funktion gab. ;)
    Mfg -Franky-
    Danke für Deine umfangreichen Informationen.

    Es hat funktioniert. Aber jetzt liefert die Funktion plötzlich nur noch 1 als Rückgabe, egal welchen Button ich klicke und auch bei Klick auf das Systemkreuz kommt statt 32000 nur noch ein Rückgabewert 1.
    Ich habe, soweit wie ich weiß, nichts gegenüber der funktionierenden Version gemacht und krieg es nicht mehr hin.

    Ist Dir das auch schon untergekommen?

    Gruß KH

    volti schrieb:

    Ist Dir das auch schon untergekommen?
    Nope, funktioniert bei mir wie es soll. ID_TIMEOUT (32000) kommt wenn ein TimeOut > -1 ist. Ansonsten der ID_xxx von dem Button den ich anklicke. ID_HELP muss entweder über eine MSGBOXCALLBACK behandelt werden wenn lpfnMsgBoxCallback <> IntPtr.Zero (Pointer auf die CallBack-Funktion) ist. Bei lpfnMsgBoxCallback = IntPtr.Zero über den MessageLoop vom im hwndOwner angegebenen Fenster -> Message WM_HELP. cancelId sollte nur dann auf ID_CANCEL gesetzt werden, wenn es auch einen Button mit ID_CANCEL gibt, ansonsten = 0. Nur dann lässt sich der Dialog auch über das X scließen und dann gibt die API entsprechend auch ID_CANCEL zurück.
    Mfg -Franky-

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „-Franky-“ ()

    volti schrieb:

    plötzlich nur noch 1 als Rückgabe,
    Poste mal Deinen Code / Dein Projekt.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!