View Full Version : Solved: SendMessage vs. SendKeys
Howard Kaikow
04-21-2005, 10:59 AM
In another thread, it was suggested that SendMessage could be used instead of SendKeys, e.g., to set the VBA Project Properties.
 
I started a thread on his topic very recently, but cannot find that thread, so here we go again.
 
I have found a way to get the handle of each of the fields in the VBA Project Properties dialog. To do this, one must first display the Project Properties dialog, then use the following code:
 
Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" ( _
ByVal lpClassName As String, _
ByVal lpWindowName As String) As Long
 
Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
 
strCaption = somedocobject.VBProject.Name & " - Project Properties".
lngHWnd - FindWindow(vbNullString, strCaption)
'Get Protection tab
SendKeys "^{TAB}": Sleep 50: DoEvents
EnumChildWindows lngHWnd, AddressOf EnumChildProc, ByVal 0&
 
Along with the following module:
Option Explicit
Public Declare Function GetWindowText Lib "user32" _
Alias "GetWindowTextA" _
(ByVal hwnd As Long, _
ByVal lpString As String, _
ByVal cch As Long) As Long
 
Public Declare Function GetWindowTextLength Lib "user32" _
Alias "GetWindowTextLengthA" _
(ByVal hwnd As Long) As Long
 
Public lngLockProject As Long
Public lngPassword As Long
Public lngConfirmPassword As Long
Public lngOK As Long
 
Public Function EnumChildProc(ByVal hwnd As Long, ByVal lParam As Long) As Long
Dim strTemp As String
strTemp = Space$(GetWindowTextLength(hwnd) + 1)
GetWindowText hwnd, strTemp, Len(strTemp)
'remove Chr$(0)
strTemp = Left$(strTemp, Len(strTemp) - 1)
If strTemp <> "" Then
Debug.Print strTemp & " = " & hwnd
Select Case strTemp
Case "Lock project for &viewing"
lngLockProject = hwnd
Case "&Password"
lngPassword = hwnd
Case "&Confirm password"
lngConfirmPassword = hwnd
Case "OK"
lngOK = hwnd
End Select
End If
EnumChildProc = 1
End Function
 
Using the handles obtained supra, I tried a solution based on the code in MSFT KB article 176058.
 
In a VB 6 Form, I have the code below.
 
The handle passed to the sub is that of the Password field in a VBA Project
Properties dialog.
 
I get a message stating that the password is invalid but the cursor is where
it should be, i.e., in the Password field of the Project Properties dialog, with nothing getting inserted in the Password field.
 
I suspect that I am not using the COPYDATASTRUCT correctly or the string
being passed is not in a format required by VBA. I've also tried passing double byte strings, but got the same error message,
 
What am I missing, other than a good night's sleep?
 
Private Type COPYDATASTRUCT
dwData As Long
cbData As Long
lpData As Long
End Type
 
Private Const WM_COPYDATA = &H4A
 
Private Declare Function FindWindow Lib "user32" Alias _
"FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName _
As String) As Long
 
Private Declare Function SendMessage Lib "user32" Alias _
"SendMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal _
wParam As Long, lParam As Any) As Long
 
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" _
(hpvDest As Any, hpvSource As Any, ByVal cbCopy As Long)
 
PrivateSub SendingApplication(strToSend As String, ThWnd As Long)
' strToSend: String to send
'ThWnd: hWnd of the target application
Dim i As Long
Dim cds As COPYDATASTRUCT
Dim buf(254) As Byte
Call CopyMemory(buf(0), ByVal strToSend, Len(strToSend))
cds.dwData = 3
cds.cbData = Len(strToSend) + 1
cds.lpData = VarPtr(buf(0))
i = SendMessage(ThWnd, WM_COPYDATA, Me.hwnd, cds)
Debug.Print ThWnd, i
End Sub
Howard Kaikow
04-22-2005, 08:16 PM
Turns out that the handles returned in the code I posted are the handles for the labels on the textboxes.
 
 For example, the handle for &Password is the handle for the "Password" label itself, not for the adjacent box provided for the password.
 
 So that may explain why using WM_COPYDATA with SendMessage returns an invalid
 password error.
 
 Using WM_SETTEXT with SendMessage causes the "Password" label itself to be changed to whatever text I send.
 
 Need to find out how to get the handle of the actual input boxes.
Howard Kaikow
04-23-2005, 09:50 PM
I believe, as the song says, that for every drop of rain that falls, a flower grows; and, I believe that I may now know how to access the elusive handles.
I believe that this can be done using spyxx to first get the control ids and then using GetDlgItem API to get the handles and then use SendMessage.
In the interim, I've gotten rid of most of the SendKeys using keybd_event API. This needs further modification to handle Alt, CTRL and Shift.
I believe ...!
MOS MASTER
04-24-2005, 07:15 AM
Hi Howard, :D
 
Seams like you have to thank you're self for solving this question.
 
I was able to track down you're missing thread:
http://groups.google.nl/groups?q=Howard+Kaikow+Password+VBE&hl=nl&lr=&selm=ee7fo%23INFHA.3076%40TK2MSFTNGP14.phx.gbl&rnum=1
 
:thumb
Howard Kaikow
04-24-2005, 03:20 PM
Hi Howard, :D
 
Seams like you have to thank you're self for solving this question.
 
I was able to track down you're missing thread:
http://groups.google.nl/groups?q=Howard+Kaikow+Password+VBE&hl=nl&lr=&selm=ee7fo%23INFHA.3076%40TK2MSFTNGP14.phx.gbl&rnum=1
 
:thumb
That's not the missing thread, I thought that I had posted something in this Forum.
I have found the crux of the problem.
WM_SETTEXT does not do the expected with an Edit control.
I'll get back to this after I visit Wysteria Lane tonight.
Howard Kaikow
04-25-2005, 03:18 PM
So there is now but one SendKeys left, but it is key to everything  else.
            ' Get to the Protection tab
            SendKeys  "^{TAB}": Sleep lngSleep: DoEvents
            I can set focus to the  SysTabControl32 control using:
                SetFocusAPI  lngSysTabControl32
            Is there a message that can be used to  switch the tab?
An alternative might be using a keybd_event to send CTRL TAB.
I'll beat this sucker yet!
8 months to Xmas, hope to be finished by then!
TonyJollans
04-25-2005, 04:02 PM
How are you displaying the project properties dialog?
Howard Kaikow
04-25-2005, 04:19 PM
How are you displaying the project properties dialog?
I'm using the FindWindow API.
When my mind recovers, I'll post sample code, I'm sick and tired of  looking at the critter!
In any case, 
Bingo!
   
  In this app, SendKeys is no more!
   
			'''''''''''''''''''''OLD CODE'''''''''''''''''''''''''''''''''''''''''''''''''''''''
			 'Protection tab
'			SendKeys "^{TAB}": Sleep 50: DoEvents
  '			' Lock project for &viewing
'			SendKeys "%v":  Sleep 50: DoEvents
'
'			' &Password
'			SendKeys  "{TAB}": Sleep 50: DoEvents
'			SendKeys strPassword: Sleep 50:  DoEvents
'
'			' &Confirm Password
'			SendKeys  "{TAB}": Sleep 50: DoEvents
'			SendKeys strPassword: Sleep 50:  DoEvents
'
'			'Enter key
'			SendKeys "~": Sleep 50:  DoEvents
						 
			'''''''''''''''''''''New CODE'''''''''''''''''''''''''''''''''''''''''''''''''''''''
			 'Protection tab
			KeyBoardSendStringCtrl vbTab ' Uses  keybd_event
'			SetFocusAPI lngSysTabControl32 ' I'd like to use this instead.
  			' Lock project for &viewing
			SendMessage  lngLockProject, BM_SETCHECK, BST_CHECKED, 0
   
  			' &Password
			SendMessageAsString lngPassword, EM_REPLACESEL, vbTrue, strPassword
   
  			' &Confirm password
			SendMessageAsString lngConfirmPassword, EM_REPLACESEL, vbTrue, strPassword
   
  			'Enter key on OK
			SetFocusAPI ByVal  lngOK
		 KeyBoardSendString vbCr	 ' Uses keybd_event, I'd like to use something other than keybd_event here
Howard Kaikow
04-25-2005, 07:23 PM
How are you displaying the project properties dialog?
i fergot 2 mention that i am running the dialog minimized.
the code runs so fast without sendkeys that minimization is unnecessary, but i'll use minimizationanyway.
MOS MASTER
04-26-2005, 11:26 AM
That's not the missing thread, I thought that I had posted something in this Forum.
Ah..sorry my BAD! :banghead:
Howard Kaikow
04-26-2005, 10:36 PM
In case anyone's interested.
   
  The following is the code I ended up with.
I've run the code using Word  97, Word 2000, Word 2002 and Word 2003.
I used a VB 6 Form. Code might  work in a Word VBA Userform, but I have no
interest in running such code from  within Word.
The code will create a template in Word's default template directory.
 The password is "my".
-----------------------------
3 References are  required:
Office object Library
Word object Library
VBA for  EXtensibity 5.3 (or equivalent in Word 97)
I expect to post versions of  this code for at least Excel and Word at my web
site
Put the following  in a code module:
Option Explicit
	Public hWndProjectProperties As  Long
Public Function EnumChildProc(ByVal hWnd As Long, ByVal lParam As  Long) As
Long
	hWndProjectProperties = hWnd
	' Do not  recurse
	EnumChildProc = 0
End Function
Put the following in a  VB 6 Form with 2 command buttons, as indicated in the
code:
Option  Explicit
	' Constants for SendMessage messages
	Private Const  BM_CLICK As Long = &HF5&
	Private Const BM_SETCHECK As Long =  &HF1&
	Private Const BST_CHECKED As Long = &H1&
	 Private Const EM_REPLACESEL As Long = &HC2&
	Private Const  KEYEVENTF_EXTENDEDKEY As Long = &H1&
	Private Const  KEYEVENTF_KEYUP As Long = &H2&
	' Needed if dialog is to be  minimized in code below
	'Private Const SW_SHOWMINIMIZED As Long =  2
	Private Const TCM_SETCURFOCUS As Long = &H1330&
	'  API functions and subs
	Private Declare Function EnumChildWindows Lib  "user32" _
		(ByVal hWndParent As Long, ByVal lpEnumFunc As Long, ByVal  lParam 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 GetDlgItem  Lib "user32.dll" _
		(ByVal hDlg As Long, ByVal nIDDlgItem As Long) As  Long
	Private Declare Sub keybd_event Lib "user32.dll" _
		 (ByVal bVk As Byte, ByVal bScan As Byte, ByVal dwFlags As Long,
ByVal  dwExtraInfo As Long)
	Private Declare Function SendMessage Lib  "user32" Alias "SendMessageA" _
		(ByVal hWnd As Long, ByVal wMsg As  Long, ByVal wParam As Long,
lParam As Any) As Long
	Private Declare  Function SetFocusAPI Lib "user32" Alias "SetFocus" _
		(ByVal hWnd As  Long) As Long
	' Needed if dialog is to be minimized
	'Private  Declare Function ShowWindow Lib "user32" _
	'	(ByVal hWnd As Long,  ByVal nCmdShow As Long) As Long
	Private Declare Function VkKeyScan  Lib "user32" Alias "VkKeyScanA" _
		(ByVal cChar As Byte) As  Integer
Private Sub btnByeBye_Click()
	Unload Me
End  Sub
Private Sub GetPassword(strPassword As String)
	' Set  password
	strPassword = "my"
End Sub
Private Sub  KeyBoardSendString(strToSend As String)
	' This sub may not handle all  character codes correctly
	Dim i As Long
	Dim VirtualKey As  Integer
	For i = 1 To Len(strToSend)
		VirtualKey =  VkKeyScan(Asc(Mid$(strToSend, i, 1))) And &HFF
		keybd_event VirtualKey, 0, KEYEVENTF_EXTENDEDKEY, 0	' key down
		keybd_event  VirtualKey, 0, KEYEVENTF_EXTENDEDKEY Or KEYEVENTF_KEYUP,
0	' key up
	 Next i
End Sub
Private Sub btnCreateTemplate_Click()
	' spy++  was used to get Control IDs in Project Properties dialog
	Const  ControlIDConfirmPassword As Long = &H1556&
	Const  ControlIDLockProject As Long = &H1557&
	Const ControlIDOK As Long  = &H1&
	Const ControlIDPassword As Long = &H1555&
	 Const ControlIDSysTabControl32 As Long = &H3020&
	Const strProject  As String = "HKNewTemplate"
	Dim ctrl As  Office.CommandBarControl
	Dim appWord As Word.Application
	Dim  docWord As Word.Document
	Dim hWndLockProject As Long
	Dim  hWndPassword As Long
	Dim hWndConfirmPassword As Long
	Dim hWndOK As  Long
	Dim hWndSysTabControl32 As Long
	Dim strCaption As  String
	Dim strNewTemplate As String
	Dim strPassword As  String
	Dim strPath As String
	Set appWord = New  Word.Application
	With appWord
		.Visible = False
		 .ScreenUpdating = False
		.WindowState =  wdWindowStateMinimize
		strPath =  .Options.DefaultFilePath(wdUserTemplatesPath) &
.PathSeparator
		 strNewTemplate = strPath & strProject & ".dot"
		Set docWord =  .Documents.Add(NewTemplate:=True)
		With docWord
			With  .VBProject
				' Rename template project
				.Name  = "HowardKaikowTest"
				With .VBE
					' Find Project Properties dialog
					Set ctrl = .CommandBars.FindControl(ID:=2578)
				 ' Displat Project Properties dialog
					ctrl.Execute
					Set ctrl = Nothing
				End With
				strCaption = .Name & " - Project Properties"
			End With
			' Get  hWnd for Project Properties dialog
			hWndProjectProperties = FindWindow(vbNullString, strCaption)
			If hWndProjectProperties = 0  Then
				Exit Sub
			End If
			 GetPassword strPassword
			' Get hWnd for OK button in Project  Properties dialog
			hWndOK = GetDlgItem(hWndProjectProperties,  ControlIDOK)
			' Get hWnd for Tab Control in Project Properties  dialog
			hWndSysTabControl32 =  GetDlgItem(hWndProjectProperties,
ControlIDSysTabControl32)
			 'Move to Protection tab
			SendMessage hWndSysTabControl32, TCM_SETCURFOCUS, 1, ByVal 0&
			' Must reset hWndProjectProperties probably because tab changed.
			 EnumChildWindows ByVal hWndProjectProperties, AddressOf
EnumChildProc, ByVal  0
			' Get hWnd for Password Edit control in Project Properties
dialog
			hWndPassword =  GetDlgItem(hWndProjectProperties,
ControlIDPassword)
			' Get  hWnd for Confirm Password Edit control in Project
Properties  dialog
			hWndConfirmPassword =  GetDlgItem(hWndProjectProperties,
ControlIDConfirmPassword)
			'  Get hWnd for Lock Project checkbox control in Project
Properties  dialog
			hWndLockProject =  GetDlgItem(hWndProjectProperties,
ControlIDLockProject)
			'  Minimize Project Properties dialog
			' May cause problems if done before showing Protection tab
			' Anyway, causes problem in Word  97, Word 2000
			' Needed if dialog is to be  minimized
			'ShowWindow hWndProjectProperties,  SW_SHOWMINIMIZED
			' Lock project for  &viewing
			SendMessage hWndLockProject, BM_SETCHECK, BST_CHECKED, 0
			' &Password
			SendMessage  hWndPassword, EM_REPLACESEL, vbTrue, ByVal
strPassword
			'  &Confirm password
			SendMessage hWndConfirmPassword, EM_REPLACESEL, vbTrue, ByVal
strPassword
			'OK  button
			SetFocusAPI hWndOK
			SendMessage hWndOK,  BM_CLICK, 0, 0
			On Error Resume Next
			Kill  strNewTemplate
			On Error GoTo 0
			.SaveAs  strNewTemplate, addtorecentfiles:=False
			.Close
		End  With
		.ScreenUpdating = True
		.Quit
	End  With
	Set appWord = Nothing
	Set docWord = Nothing
	 btnCreateTemplate.Visible = False
End Sub
MOS MASTER
04-27-2005, 10:57 AM
Hi Howard, :D
 
Totaly interested!!!! 
 
I've been wandering before if it was posible to set a password to the VBE project. Didn't find an easy sollution at the time and didn't need to have it after a while...
 
But you did it whit some Excellent coding! :clap: 
 
I rarly use VB at the moment when there's time I'm learnig .NET. So I just added you're code to a VBA UserForm and it runs perfectly!!! (Mide I mention it's extremly fast..in my humble opinion)
 
So thank you for showing it can be done...not knowing when it will be off use to me but there's lot's of stuff in there to play with.
 
:whistle:
Howard Kaikow
04-27-2005, 12:33 PM
Hi Howard, :D
 
Totaly interested!!!! 
 
I've been wandering before if it was posible to set a password to the VBE project. Didn't find an easy sollution at the time and didn't need to have it after a while...
 
But you did it whit some Excellent coding! :clap: 
 
I rarly use VB at the moment when there's time I'm learnig .NET. So I just added you're code to a VBA UserForm and it runs perfectly!!! (Mide I mention it's extremly fast..in my humble opinion)
 
So thank you for showing it can be done...not knowing when it will be off use to me but there's lot's of stuff in there to play with.
 
:whistle:
Setting a password is in many ways a pointless task, as it is all to easy to bypass the password protection. However, setting the password does prevent most users from viewing the code.
The main reason I spent so much time on this is that I don't know when to quit! But the exercise does demonstrate how to avoid the use of SendKeys, so the techniques are useful for other purposes.
Not knowing when to quit, I'm now trying to adapt the code to Excel.
Other than the few Word/Excel  specific statements, I expect that all the
other code will work as  is.
But, there is one problem.
When Word displays the Project  Properties dialog, Word plops it on the
screen in plain view.
However,  Excel minimizes the dialog and flashes the task bar icon.
I've tried a  few of the messages with ShowWindow, but no success.
I've not yet figured out how to restore the dialog to display normally.
I have one lead on how to do this, not tried yet.
Regarding VB .NET.
Threre should be no problem using the code from within VB .NET.
Indeed, early on, I had a native VB .NET version of an earlier version of the code. But for purposes of password protection, it is rather pointless to use VB .NET since the source code cannot be protected. With VB 6, one can compile to a DLL.
MOS MASTER
04-27-2005, 12:49 PM
Hi Howard, :D
 
I do agree it's a pointless task but however there can be so much fun in doing something that VBA doesn't provide a method for. (Like you for me it's also hard to stop..but because off a heavy task shedule at the moment there's to little time to tinker with code...) :mkay 
 
Like I said I've only used you're sollution in Word and you don't have the problems you discribed over there..(Word plops it on the
screen in plain view)
 
In Word the code runs so damn fast it's done in a blink off an eye! (can't test in VB because that would take me to much time to set it up)
 
VB.NET is just a hobby for now (Done things in ASP.NET though) I'm shure its gona be a slow bussines in the summer so I'll catch up on the new BETA (2005)
 
Good luck on you're Excel adventure...pointless or not have fun! :rofl:
Howard Kaikow
04-27-2005, 02:07 PM
Hi Howard, :D
 
In Word the code runs so damn fast it's done in a blink off an eye! (can't test in VB because that would take me to much time to set it up)
 
It is amazing how much faster it is to use the API calls than to use SendKeys.
I find it is no harder to set up a VB project than to insert code into VBA.
a. create a directory for the project.
b. start VB with a new project, which automatically creates the Form.
c. In this case, add two butons to the Form.
d. paste the code into the Form.
e. create a module.
f. paste the code into the module
steps c-f would be the same in vba.
in vba, steps a-b would be:
a. Open a .dot file.
b.1. open the VB editor
b.2. insert a Userform
MOS MASTER
04-27-2005, 02:14 PM
Hi Howard, :D
 
Totally agree on the API Calls to Sendkeys approach..(Will make use off that in the future)
 
The funny thing you misunderstood me on the VB Set up part :rofl: 
Because I have Visual Studio .NET installed to practice I removed VB6 for now...so I was talking about a true Set up (And that takes up tonnes of time)
 
Sorry about that! :whistle:
Howard Kaikow
04-27-2005, 08:44 PM
Hi Howard, :D
 
Totally agree on the API Calls to Sendkeys approach..(Will make use off that in the future)
 
The funny thing you misunderstood me on the VB Set up part :rofl: 
Because I have Visual Studio .NET installed to practice I removed VB6 for now...so I was talking about a true Set up (And that takes up tonnes of time)
 
Sorry about that! :whistle:
You should not have removed VB 6, even MSFT recommends in the .NET installation instructions to retain the critter.
Although I have not yet found a way to restore the minimized and  flashing
icon, I found a way to prevent the Project Properties dialog from  being
minimized in the first place.
In the original Word version of  the code, Word itself was minimized. Even
so, Word did not minimize the  Project Properties dialog.
In the Excel version, apparently Excel does  minimize the Project Properties
dialog if Excel is minimized.
Yet another example of the Office component developers not talking to each other. Clearly a lack of co-ordination at MSFT.
Turns  out, at this point, it is not necessary to minimize either Word or
Excel, so  after pulling the WindowState setting, Excel does not minimize  the
dialog.
However, I'd still like to know how to restore such a  minimized dialog.
I have some ideas, so I'll play with that separately later.
Howard Kaikow
04-28-2005, 04:14 AM
I now have an improved version of the code that works with Excel, Word and Powerpernt.
MOS MASTER
04-28-2005, 10:23 AM
I now have an improved version of the code that works with Excel, Word and Powerpernt.
Hi Howard, :D
 
Congrats!!!
 
Can we see it? (or is it on you're website?)
 
You should not have removed VB 6, even MSFT recommends in the .NET installation instructions to retain the critter.
Didn't know that...I thought the critters of MS ussualy bite each other! ;)
 
Doing a full cleanup of the pc soon so then I'll install them both to see if no problems arrise...
 
Looking forward to you're improved code! :whistle:
Howard Kaikow
04-28-2005, 02:34 PM
Hi Howard, :D
 
Congrats!!!
 
Can we see it? (or is it on you're website?)
I just added support for Access too.
Hope to post the code this weekend.
 
Didn't know that...I thought the critters of MS ussualy bite each other! ;)
See the .NET release notes.
MOS MASTER
04-28-2005, 02:38 PM
I just added support for Access too.
 
Hope to post the code this weekend.
Great..love to see it! 
See the .NET release notes.
Great...another thing I have to read about .NET...Please let there be more than 24 hours in a day so I can catch-up! :whistle:
Howard Kaikow
04-30-2005, 09:49 AM
It was a difficult birth.
 
 http://www.standards.com/index.html?SetVBAProjectPassword
MOS MASTER
04-30-2005, 11:35 AM
It was a difficult birth.
 
http://www.standards.com/index.html?SetVBAProjectPassword
Hi Howard, :D
 
Looks great thanks for that!
I will go and experiment with the API's you used because it seams mighty usefull..
 
Greetings! :whistle:
Powered by vBulletin® Version 4.2.5 Copyright © 2025 vBulletin Solutions Inc. All rights reserved.