PDA

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: