VBA: SendKeys '{BS}' läuft in einer SUB nicht

Hi,
unter Windows XP lief der folgende Code, mit dem verhindert wurde, dass andere Zeichen als die Ziffern 1 - 9, Backspace und Punkte in die Combobox einer Userform eingegeben werden konnten. Unter Windows Vista nicht (Es erscheint die Fehlermeldung: Fehler 70 Zugriff verweigert):

Private Sub cbo1_KeyPress(ByVal KeyAscii As MSForms.ReturnInteger)
Call Datumsformat(ByVal KeyAscii) '*** Datum, also Zahlen und Pkte. ***
End Sub

Public Sub Datumsformat(ByVal KeyAscii As Integer) ’ *** Ziffern, Punkt und Komma ***
Select Case KeyAscii
Case vbKey0 To vbKey9, vbKeyBack, 46 'Pkt
Exit Sub
Case Else
MsgBox „Datumsformat!“, vbExclamation
SendKeys „{BS}“
End Select
End Sub

SendKeys „{BS}“ löschte ein unzulässiges Zeichen wie z.B. ein A. Wie kann ich erreichen, dass ein unzulässiges Zeichen verhindert wird?
Gruß
Wilhelm

Hallo Wilhelm.

Ich kenne mich mit Vista nicht aus, aber normalerweise sollte folgender Code funktionieren:

Public Sub Datumsformat(ByVal KeyAscii As Integer) ' \*\*\*Ziffern, Punkt und Komma \*\*\*
 Select Case KeyAscii
 Case vbKey0 To vbKey9, vbKeyBack, 46 'Pkt
 Case Else
 KeyAscii=0
 MsgBox "Datumsformat!", vbExclamation
 End Select
End Sub

Kannst Du ja 'mal ausprobieren

Viele Grüße
Carsten

Hallo Wilhelm,

ich bin kein Spezi in VBA, aber probiere mal folgende Version.

Private Sub cbo1\_KeyPress(ByVal KeyAscii As MSForms.ReturnInteger)
Keyascii= Datumsformat( KeyAscii) 
End Sub

Public Function Datumsformat(ByVal KeyAscii As Integer) as Integer
Select Case KeyAscii
Case vbKey0 To vbKey9, vbKeyBack, 46 'Pkt
 DatumsFormat=KeyAscii
Case Else
 Datumsformat=0
MsgBox "Datumsformat!", vbExclamation
End Select
End Sub

Senkeys ist immer Mist! Bedenke mal ein blöder Zufall. Genau zu dem Zeitpunkt aktiviert sich ein anderes Program oder ein anderes Steuerelement erhaelt den Focus. Was passiert denn da ? *zwinker*

Sendkeys ist wie Russisch Roulette :wink:

MfG Alex

Public Sub Datumsformat(ByVal KeyAscii As Integer) ’
***Ziffern, Punkt und Komma ***
Select Case KeyAscii
Case vbKey0 To vbKey9, vbKeyBack, 46 'Pkt
Case Else
KeyAscii=0
MsgBox „Datumsformat!“, vbExclamation
End Select
End Sub

Kannst Du ja 'mal ausprobieren

Viele Grüße
Carsten

Hallo Carsten,

ich mag zwar nicht klugscheissern wollen. Aber kennst du den Unterschied zwischen ByVal und ByRef ?

Bei ByVal uebergibst du den Wert als Argument
Bei ByRef halt als Refernz

Ist zumindest bei primitiven Datentypen so. Bei Referenzen ist das ein wenig anders.

Im Klartext heisst es das wenn du einen primitiven Datentyp via ByVal übergibst so kannst du ihn nicht veraendern, da dort lediglich eine Kopie im Stak angelegt wird und diese nach verlassen der sub wieder gelöscht wird! Sprich du aenderst die kopie und nicht den orginalen Wert! Bei Byref übergibst du lediglich einen Zeiger auf die orginalen Daten, die du dann aendern kannst!
Für Deine Variante müsstest du den Wert als ByRef übergeben!

Handelt es sich nicht um primitive Datentypen, so wird es ein wenig anders. Aber das spielt in dem Falle keine Rolle.

Anbei ein kleines Demo, Was ein wenig Licht ins Dunkel bringt :wink:

Private Sub Command1\_Click()
Dim x As Integer
x = 10
Call test(x)
MsgBox x
Call test1(x)
MsgBox x
End Sub

Private Sub test(ByVal vret As Integer)
 vret = 20
End Sub

Private Sub test1(ByRef vret As Integer)
 vret = 20
End Sub

Alternativ koenntest du auch eine Funktion einsetzen, wie in dem Demo von mir :smile:

MfG Alex

Hallo Alex

Ich habe keinen Schimmer, wovon Du redest.
Aber ich weiß auch nicht, warum der Umstand mit dem Extra-Prozedur-Aufruf sein muß, außer vielleicht für die Wiederverwendbarkeit des Codes.

Das hier habe ich jedenfalls ausprobiert und es läuft:

Private Sub ComboBox1\_KeyPress(ByVal KeyAscii As MSForms.ReturnInteger)
 Select Case KeyAscii
 Case vbKey0 To vbKey9, vbKeyBack, 46 'Pkt
 Case Else
 KeyAscii = 0
 MsgBox "Datumsformat!", vbExclamation
 End Select
End Sub

Viele Grüße
Carsten

Hallo Alex, Hi Carsten,

beide Vorschläge laufen, bei Alex allerdings nur, wenn man die Funktion mit End Function schließt *DoppelzwinkeranAlexdermirschonvormeinemSommerschlafimAprilbeiderLösungdesProblemsohneErfolghelfenwollte*

Ich brauche die Unter-Sub bzw. Funktion, weil auf der Form ca. 25 CboCbo sind, die das Datumsformat verlangen.
THX und bis in einigen Tagen, denn ich habe jetzt wieder begonnen, an meinen Programmen zu basteln!
Gruß Wilhelm

[Bei dieser Antwort wurde das Vollzitat nachträglich automatisiert entfernt]

Hallo Carsten,

Ich habe keinen Schimmer, wovon Du redest.

ja, das sah Alex am Code, deshalb versuchte er dir zu helfen.
Probier mal bitte seine Beispielcodes.

Aber ich weiß auch nicht, warum der Umstand mit dem
Extra-Prozedur-Aufruf sein muß, außer vielleicht für die
Wiederverwendbarkeit des Codes.

Kann man später klären, viel wichtiger ist das Verständnis des Unterschiedes zwischen ByVal und ByRef.

Prinzipiell schnell erklärt, geht eine Variable via Byval in eine Sub so ist sie nacher genauso so, geht sie via ByRef in eine Sub kann sie verändert sein.
Insofern ist es völlig gleich ob du KeyAscii = 0 hinschreibst oder die Codezeile wegläßt, KeyAscii hat nach deiner Prozedur den gleichen Wert wie vor deiner Prozedur, da du sie mit ByVal in die Prozedur einlädst.

Der Unterschied ByVal/ByRef wird anfangs des Erlernens von Vba ein Stückchen wichtig, dann schläft das wieder ein. Wenn du dann mit API in Vba anfängst kommt der Unterschied ByVal/ByRef 1000fach verstärkt zum Tragen, da kommt kein netter Debugger mehr der dir sagt, ByVal ist schlecht, nimm lieber ByRef, nö, nö, du hast gerade irgendwo auf deiner Festplatte in irgendeiner Datei irgendwelche Daten umgewandelt, dein Excel ist grußlos beendet worden, vielleicht Windows gleich mit.

Wenn dich das interessiert, Dan Appleman ist sowieso der Guru für API, er hat ein einziges Buch auf Deutsch zu APIs herausgegeben, heißt Das Puzzlebuch o.ä.

Es richtet sich an sehr erfahrene Vbaler und was macht Dan, er erklärt nochmals auf sehr vielen Seiten den genauen Unterschied zwischen ByVal und ByRef usw. Er weiß warum.

Gruß
Reinhard

Das hier habe ich jedenfalls ausprobiert und es
läuft:

Private Sub ComboBox1_KeyPress(ByVal KeyAscii As
MSForms.ReturnInteger)
Select Case KeyAscii
Case vbKey0 To vbKey9, vbKeyBack, 46 'Pkt
Case Else
KeyAscii = 0
MsgBox „Datumsformat!“, vbExclamation
End Select
End Sub

Viele Grüße
Carsten

Hallo Carsten

Ich habe keinen Schimmer, wovon Du redest.

Na dann versuche ich dir das mal nahe zu bringen :wink: Schaden kann es ja nicht :smile:

Aber ich weiß auch nicht, warum der Umstand mit dem
Extra-Prozedur-Aufruf sein muß, außer vielleicht für die
Wiederverwendbarkeit des Codes.

Richtig. Wenn man es richtig anstellt wird sie arg flexibel und kann in jedem Project verwendet werden und nicht nur für Comboboxen :wink:

Das hier habe ich jedenfalls ausprobiert und es
läuft:

Private Sub ComboBox1_KeyPress(ByVal KeyAscii As
MSForms.ReturnInteger)
Select Case KeyAscii
Case vbKey0 To vbKey9, vbKeyBack, 46 'Pkt
Case Else
KeyAscii = 0
MsgBox „Datumsformat!“, vbExclamation
End Select
End Sub

Ja klar. Aber hast du mehr wie nur eine Combobox etc. die du prüfen magst ist das umstaendlich hoch 4!

Aber nun mal kurz zu Byref und Byval.

Wenn du eine Sub oder Function aufrufst kannst du ihr doch Parameter übergeben. Standard ist glaube ByRef

Das bedeutet eine Declaration alla

Public Sub Test(x as Long)
ist das selbe wie
Public Sub Test(Byref x as long)

Dim x as DeineKlasse
Set x = New DeineKlasse

oder

Dim x as New DeineKlasse

Das selbe zaehlt für Objecte!

Du kannst dir merken, alles was du nicht mit New instanzierst sind primitive Datentypen!

Soweit alles klar?

Wenn wir uns nun einmal den Speicher anschauen.
Stelle ihn dir einmal vor als 2 Vierecke die nebeneinander sind
Das linke Viereck ist der sogenannte Heap. das rechte Viereck dagegen der Stack!

 SPEICHER
------------------------
| | |
| | | 
| HEAP | STACK |
| | |
| | |
------------------------

Wenn du nun eine Variable anlegst, so wird diese doch im „Speicher“ gespeichert. Dazu wird sie im Stack abgelegt. Handelt es sich hierbei um primitive Datentypen so wird deren Wert dort direkt abgelegt. Handelt es sich aber um einen Verweis ( Refernz) so wird im Stack lediglich eine Adresse angelegt. Diese Adresse ist die Position der Daten im Heap. Also eine Verweis nach dem Heap! Im Heap selbst stehen dann die Daten

'primitiver Datentyp
Dim X as long
X=10
' Verweis
Dim Z as New Deine Klasse

HEAP | Stack
 |
daten von | 10 'Daten von X
DeineKlasse | 1232431232'Adresse im Heap 
an Adresse xyz im Heap |

es lässt sich nen bissl blöd erklären, aber ich hoffe du hast den Sinn verstanden.

So, nun gehen wir mal davon aus das du eine Variable ByVal übergibst. Byval selbst legt eine Kopie der Daten im Stack an und auf diese kannst du dann zugreifen.
In dem Bsp wird für X welchen den wert 10 hat eine kopie im Stack angelegt.In der Sub / Function wo du die Variable Byval uebergeben hast, liegt ja nun die Kopie vor. Diese kannst du in der Sub / Function veraendern wie du willst. Jedoch verlaesst du die Sub / Function wird diese Kopie wieder gelöscht. Der orginal Wert bleibt aber! das heisst eine Variable die du via Byval übergibst kannst du nicht aendern in der Sub. Sofern es sich um einen primitiven Datentyp handelt. Übergibst du sie aber ByRef so übergibst du sie als Verweis. Das heisst die Sub bekommt lediglich einen zeiger auf die Variable im Stack. Diese zeigt jedoch auf den orginal Wert. Wenn du diesen nun aenderst, so aenderst du den orginalen Wert. Beim verlassen der Sub / Function dann, wird zwar der Verweis gelöscht aber die orginalen Daten liegen geaendert im Stack vor!

Soweit alles klar?

So nun schauen wir uns mal die Refernzen an.

Du übergibst eine Referenz Byref. Das heisst du bekommst einen Zeiger direkt auf den Stack und von da aus auf dem Heap auf die orginalen Daten! Du änderst diese nun! Beim verlassen der Sub / function verfaellt der zeiger, aber die Daten sind geaendert. Eigentlich logisch oder?

So nun kommt ein kleines Phaenomaen! Du uebergibst eine Referenz Byval! Es wird ja eine Kopie von den Daten im Stack angelegt. Im Stack steht aber nun der zeiger ( Die Adresse) wo sich die Daten im Heap befinden. Nun aenderst du etwas. Du greifst ueber den kopierten zeiger im Stack auf die Orginalen daten im Heap zu, da ja nur der Inhalt vom stack kopiert wird und nicht die vom Heap!

HEAP Stack

DeineDaten XYZ'Zeiger auf den Heap 
Adresse XYZ Kopie von XYZ

Beide Daten im Stack zeigen auf die selben Daten im Heap!!!

Nun aenderst du etwas. Das bedeutet das du ueber die Kopie im Stack auf die orginalen Daten im Heap zugreifst und somit die orginalen Daten aenderst!
Wenn du nun die Sub / Function verlaesst so wird die Kopie gelöscht aber die Daten im Heap sind geaendert!

Auf Deutsch heisst das, das es keine Rolle spielt ob du eine Referenz Byval oder ByRef übergibst! Bei Primitiven Datentypen schon :smile:

Viele Grüße
Carsten

MfG Alex

beide Vorschläge laufen, bei Alex allerdings nur, wenn man die
Funktion mit End Function schließt

Hi Wilhelm,

tja nun, das wäre Rainer und mir nicht passiert, wir haben gemeinsam die Bauinformatikprüfung gut bestanden:

/t/fehler-im-programmtest-finden/4791765

Alex war da wieder mal auf dem Klo oder sonstwo, kein Wunder das er das nicht erkannte *kicher*

Was meinst du eigentlich mit diesen 25 Boxen oder was das war vor deinem Aprilurlaub?

Gruß
Reinhard

Hallo Reinhard,

Alex war da wieder mal auf dem Klo oder sonstwo …

*gg* Alex steckt jetzt mehr in .NET :smile: Deshalb hat er nicht mehr so viel Zeit für uns.

Gruß Rainer

Hallo Alex.

Vielen Dank für die Erläuterungen. Ich hatte nun endlich mal die Geduld, mir Deinen Beitrag richtig durchzulesen.

Ich habe das mit „ByVal“ und „ByRef“ und „Heap“ und „Stack“ durchaus verstanden. Aber bei einer Sache habe ich definitiv noch kein Aha-Erlebnis:
Wenn ich, wie Du sagst, einen (Original-)Wert nicht verändern kann, sofern er ByVal übergeben wird, warum funktioniert dann die Prozedur mit der Anweisung „KeyAscii = 0“ ?.

Das würde ich gerne noch verstehen.

Viele Grüße
Carsten

ByVal ByRef

Wenn ich, wie Du sagst, einen (Original-)Wert nicht verändern
kann, sofern er ByVal übergeben wird, warum
funktioniert dann die Prozedur mit der Anweisung „KeyAscii =
0“ ?.

Hallo Carsten,

was meinst du mit „funktioniert“ ?

Sofern der Else-Zweig zutrifft hat in dem Moment nach „KeyAscii =
0“ die nur innerhalb der Sub Datumsformat (zu deren Laufzeit) bekannte Variable „keyascii“ den Wert 0.

Option Explicit
'
Sub Test()
Dim K As Integer
K = 17
Call Datumsformat1(K)
MsgBox K
K = 17
Call Datumsformat2(K)
MsgBox K
K = 17
Call Datumsformat3(K)
MsgBox K
End Sub
'
Public Sub Datumsformat1(KeyAscii As Integer)
Select Case KeyAscii
 Case vbKey0 To vbKey9, vbKeyBack, 46 'Pkt
 Case Else
 KeyAscii = 0
 MsgBox "Datumsformat! " & KeyAscii, vbExclamation
End Select
End Sub
'
Public Sub Datumsformat2(ByVal KeyAscii As Integer)
Select Case KeyAscii
 Case vbKey0 To vbKey9, vbKeyBack, 46 'Pkt
 Case Else
 KeyAscii = 0
 MsgBox "Datumsformat! " & KeyAscii, vbExclamation
End Select
End Sub
'
Public Sub Datumsformat3(ByRef KeyAscii As Integer)
Select Case KeyAscii
 Case vbKey0 To vbKey9, vbKeyBack, 46 'Pkt
 Case Else
 KeyAscii = 0
 MsgBox "Datumsformat! " & KeyAscii, vbExclamation
End Select
End Sub

Gruß
Reinhard

Hallo Carsten,

meintest du das

Public Sub Datumsformat(ByVal KeyAscii As Integer) ' \*\*\*Ziffern, Punkt und Komma \*\*\*
 Select Case KeyAscii
 Case vbKey0 To vbKey9, vbKeyBack, 46 'Pkt
 Case Else
 KeyAscii=0
 MsgBox "Datumsformat!", vbExclamation
 End Select
End Sub

wenn ja, dann teste es mal aus. Du wirst sehen das das nicht klappt.
Änderst du dagegen Byval in Byref so klappt es dann wie gewuenscht :wink:

Anbei mal ein kleines Demo. Rufe dazu einfach die Sub Test auf :smile:

Private Sub test()
Dim x As Integer
Dim Y As Integer
x = 5
Y = 5
MsgBox "x: " & x & vbNewLine & "y: " & Y
Call testeByRef(x)
Call testeByVal(Y)
MsgBox "x: " & x & vbNewLine & "y: " & Y
End Sub

Private Sub testeByRef(ByRef vRet As Integer)
 vRet = 10
End Sub

Private Sub testeByVal(ByVal vRet As Integer)
 vRet = 10
End Sub

MfG Alex