Eingabe von Passwörtern in Inputbox
Mit einer schnöden User-Form und darauf einer Textbox lässt sich das recht einfach lösen. Es geht aber auch etwas schöner, leider auch weitaus komplizierter mit einer echten Inputbox.
Ürsprünglich wurde die erweiterte Inputbox im Jahr 2002 noch unter Excel 97 entwickelt. Nun bleibt die Zeit aber nicht stehen und Excel 97 weilt nur noch selten unter uns. Man könnte nun einfach die AddressOf-Nachbildung rausschmeißen, was bei neueren Versionen dann keine Fehlermeldung mehr bringt.
Ich habe aber nun die neue Version komplett überarbeitet, so dass man nun auch die Texte der Buttons ändern kann. Außerdem kann eine Timeoutzeit übergeben werden, nach der der Dialog mit dem Betätigen eines vorher angegebenen Buttons beendet wird. Die alte Version findet man nun am Ende dieser Seite.
Es werden nun Hooks verwendet. Wie immer bei Subclassing, Hooks und Co. sind Haltepunkte, Syntaxfehler und nicht behandelte Laufzeitfehler in den aufgerufenen Prozeduren fast immer tödlich für die Anwendung. Meist hilft dann nur noch ein Abschießen des Prozesses, nach Änderungen deshalb vor dem Starten immer schön sichern.
Beispieldatei inputboxEx.zip 64 kBVersion XL97
Die wichtigste Funktion ist dabei die zur Ermittlung eines Funktionszeigers.
Ab XL2000 gibt es AdressOf, was die ganze Sache enorm vereinfacht. Dank K. Getz
und M. Kaplan kann man das unter Office 97 nachbilden.
Der Trick, um aus
einer Inputbox eine für die Passwortabfrage zu machen, ist der asynchrone
Aufruf einer Funktion über einen Windows-Timer. Der Timer muss aber vor
dem Anzeigen der Inputbox gestartet werden, denn die Programmausführung
stoppt solange, bis die Inputbox verschwunden ist. In der Timer-Proc wird dann
das Handle der Inputbox gesucht und an das Fenster eine Message gesendet, die
den Stil ändert. Auch andere Ersatzzeichen als der Stern sind möglich.
Man kann den Code von 4 und 5 auch noch etwas kürzen, indem man beispielsweise
FindWindowEx() einsetzt, das vorliegende hatte ich aber schon in der Schublade
liegen und war ganz einfach zu faul, es nochmal zu überarbeiten. In VB
würde man das mit einem Hook erledigen, das funzt aber nicht so richtig
in Office.
Beispieldatei inputbox.zip 16 k
'*************************************
'* AddressOf
'* Ausgeknobelt von K. Getz und M. Kaplan
'*************************************
Private Declare Function GetVbaProjekt _
Lib "vba332.dll" Alias "EbGetExecutingProj"
_
(hVBA As Long) As Long
Private Declare Function GetFunktionsnummerString _
Lib "vba332.dll" Alias "TipGetFunctionId"
_
(ByVal hVBA As Long, ByVal strFuncNameUnicode _
As String, strFunktionsnummer As String) As Long
Private Declare Function GetFunktionsnummerLong _
Lib "vba332.dll" Alias "TipGetLpfnOfFunctionId"
_
(ByVal hVBA As Long, ByVal strFunktionsnummer _
As String, hlngFunction As Long) As Long
'*************************************
'* Der Rest ist von mir
'*************************************
Private Declare Function SetTimer Lib "user32"
_
(ByVal hwnd As Long, ByVal nIDEvent As Long, _
ByVal uElapse As Long, ByVal lpTimerFunc _
As Long) As Long
Private Declare Function KillTimer Lib "user32"
_
(ByVal hwnd As Long, ByVal nIDEvent As Long) As
Long
'************************************
Private Declare Function FindWindow Lib "user32"
Alias _
"FindWindowA" (ByVal lpClassName As String,
_
ByVal lpWindowName As String) As Long
Private Declare Function GetWindow Lib "user32"
_
(ByVal hwnd As Long, ByVal wCmd As Long) As Long
Private Declare Function GetClassName Lib "user32"
_
Alias "GetClassNameA" (ByVal hwnd As Long,
ByVal _
lpClassName As String, ByVal nMaxCount As Long)
_
As Long
Private Declare Function GetWindowText Lib "user32"
_
Alias "GetWindowTextA" (ByVal hwnd As
Long, _
ByVal lpString As String, ByVal cch As Long) _
As Long
Private Declare Function SendMessageBynum& Lib "user32"
_
Alias "SendMessageA" (ByVal hwnd As Long,
ByVal _
wMsg As Long, ByVal wParam As Long, ByVal lParam
_
As Long)
Private Const EM_SETPASSWORDCHAR = &HCC
Private Const GW_CHILD = 5
Private Const GW_HWNDFIRST = 0
Private Const GW_HWNDNEXT = 2
'************************************
Private hlngTimerKennung As Long
Public Function PasswortHolen(Optional Beschriftung
As String) As String
If Beschriftung = "" Then Beschriftung = "Geben sie ihr
Passwort ein!"
TimerSetzen
PasswortHolen = InputBox(Beschriftung)
End Function
Private Sub Passwortchar()
Dim hwnd&, hwnd1&, lngRück&, Klasse$
Dim Stil As Long
hwnd = FindWindow("#32770", "Microsoft Excel")
hwnd1 = GetWindow(hwnd, GW_CHILD)
Do
Klasse = String(255, 0)
lngRück = GetClassName(hwnd1, Klasse, 250)
Klasse = Left$(Klasse, InStr(1, Klasse, _
Chr(0)) - 1)
If LCase(Klasse) = "edit" Then
SendMessageBynum hwnd1, _
EM_SETPASSWORDCHAR, 42, 0
End If
hwnd1 = GetWindow(hwnd1, GW_HWNDNEXT)
Loop While hwnd1 <> 0
End Sub
Private Sub TimerSetzen()
hlngTimerKennung = SetTimer(0, 0, 1000, _
GetFuncAdress("ApiTimer1"))
If hlngTimerKennung = 0 Then MsgBox _
"Fehler beim Initialisieren des Timers"
End Sub
Private Sub TimerZerstören()
If hlngTimerKennung <> 0 Then _
KillTimer 0, hlngTimerKennung
End Sub
Private Sub ApiTimer1(ByVal hwndOwner&, _
ByVal lngWindowMessage&, _
ByVal hlngRückTimerKennung&, _
ByVal lngTickCount&)
TimerZerstören
Passwortchar
End Sub
'*************************************
'* AddressOf
'* Ausgeknobelt von K. Getz und M. Kaplan
'*************************************
Public Function GetFuncAdress&(strFunktion$)
Dim hVBA&, lngRück&, strFunktionsnummer$
Dim hlngFunction&, strFuncNameUnicode$
strFuncNameUnicode = StrConv(strFunktion, vbUnicode)
GetVbaProjekt hVBA
If hVBA <> 0 Then
lngRück = GetFunktionsnummerString(hVBA, _
strFuncNameUnicode, strFunktionsnummer)
If lngRück = 0 Then
lngRück = GetFunktionsnummerLong(hVBA, _
strFunktionsnummer, hlngFunction)
If lngRück = 0 Then GetFuncAdress = _
hlngFunction
End If
End If
End Function