2

Send Keys to unlock a VBA Project

Any I am somewhat advanced in macro writing, however it was all self-taught off this site and I don’t fully understand the big picture

I am trying to create an Excel spread sheet that will update the VBA code of another Excel spread sheet that has a password protected VBA Project. I am using SendKeys to unlock the VBA Project. I am also solving for SendKey deficiencies by writing scrip to close all open excel documents.

All the code I have written works on its own but when I try to combine it the SendKey macro is placing the password in other lines of code:

This works:

Sub UnprotectProject()
 With Application
 .SendKeys "%{F11}", True
 .SendKeys "^r", True
 .SendKeys "~", True
 .SendKeys "password", True
 .SendKeys "~", True
 End With
 End Sub

This inserts the password in other VBA Code:

Sub UnprotectProject()
 With Application
 .SendKeys "%{F11}", True
 .SendKeys "^r", True
 .SendKeys "~", True
 .SendKeys "password", True
 .SendKeys "~", True
 End With
 Application.VBE.MainWindow.Visible = False
 End Sub

Update:

Both sets of code are the same except for, the following line in the second example

Application.VBE.MainWindow.Visible = False

The full code I am trying to write has five tasks, I created a macro for each task, then another macro to run the five macros. Each macro does the intended job when run independently. However when I try to run the macro that combines the individual tasks, the macro with send keays is failing, and instead of unlocking the VBA Project it sticks the password in the code of one of the individual task macros These are the five tasks

  1. Open (Opens the workbook to change)

  2. Unprotect VBA Project

  3. Update VBA Code

  4. Update a Worksheet

  5. SaveAs new version

This is the macro I wrote to run the individual tasks Sub UsernameCheck()

lastRow = Sheets("update").Range("I" & Rows.count).End(xlUp).Row
Uname = Environ("Username")
Set aCell = Sheets("update").Range("I4:I" & lastRow).Find(What:=Uname, MatchCase:=False)
If aCell Is Nothing Then
    MsgBox ("Not an Authorised User")
    Else
    Open_1
    UnprotectProject
    ChangeDateAddUserCheck
    UpdateDashBoard
    Save

End If

End Sub

This is the code I am using to edit a macro

Sub ChangeDateAddUserCheck()
  Dim VBComp As VBIDE.VBComponent
  Dim CodeMod As VBIDE.CodeModule
  Dim S As String
  Dim LineNum As Long

Set VBComp = ActiveWorkbook.VBProject.VBComponents("Module2")
  'Delete
  VBComp.CodeModule.DeleteLines 15, 4
  'add Code
  Set CodeMod = VBComp.CodeModule
  LineNum = 15
  S = "yr = Format(Now(), ""YYYYMMDD"")" & vbCrLf & _
      "If UCase(Sheets(""DashBoard"").Range(""B21"").Value) =      UCase(Environ(""Username"")) Then" & vbCrLf & _
      "If yr < 20160601 Then B2_Stage Else MsgBox (""Software is Expired"")" & vbCrLf & _
      "Else: MsgBox (""Not Authorized User"")" & vbCrLf & _
     "End If"
  CodeMod.InsertLines LineNum, S
End Sub

The password is being pasted in the code above between the folloing lines, but I think that has more to do with the placement of the Macro in the VBA Editor

Dim LineNum As Long

Set VBComp = ActiveWorkbook.VBProject.VBComponents("Module2")
Community
  • 1
  • 1
Chris M
  • 23
  • 1
  • 4
  • 2
    See [here](http://stackoverflow.com/questions/16174469/unprotect-vbproject-from-vb-code/16176557#16176557) for Sid's brilliant trick. – findwindow Nov 02 '15 at 17:21
  • 1
    @findwindow That *is* brilliant. Thanks for pointing it out. – John Coleman Nov 02 '15 at 17:23
  • @David Zemens I am looking for something specific, and have read all the posts with sendkeys, as I sid the macro works but not when combined with other macros – Chris M Nov 03 '15 at 00:39
  • @findwindow that is a great piece of code and works perfectly however the follow on code for changing a macro doesn't work unless they are in the same excel instance. I am going to tinker with it some more to see if I can get the VBA Edit code to work. I used the Pearson site for that code – Chris M Nov 03 '15 at 00:42
  • Aside from the `Application.VBE.MainWindow...` statement, I don't see any difference in the two code snippets you've provided. -- yet you indicate that one of them is working as-expected, but the second one is inserting text (from the SendKeys) in to the VBE? Can you elaborate? – David Zemens Nov 03 '15 at 00:47
  • Can you also elaborate on this: *the follow on code for changing a macro doesn't work unless they are in the same excel instance* (as that seems to be the actual problem you're facing -- correct me if I'm mistaken :) – David Zemens Nov 03 '15 at 00:50
  • @DavidZemens I expanded on the question above, thank you – Chris M Nov 03 '15 at 04:18
  • I stil don't understand why you have two different "UnprotectProject" functions, as per your own description: one of these methods is working, but the other is not. What is so critical about the one that is *not* working, that you need to use that method over the one that *is* working? And, could you not just move the `Application.VBE.MainWindow...` perhaps to a different point in the code (maybe at the end of the main sub routine?) if it is critical? – David Zemens Nov 03 '15 at 10:28
  • 1
    @DavidZemens Application.VBE.MainWindow... was just used as an example, the code that is working only works on it's own not if there is other code that needs to run after it – Chris M Nov 03 '15 at 18:40
  • OK OK it's making sense now and I see the problem when I try to test it :) – David Zemens Nov 03 '15 at 19:16
  • @DavidZemens thank you, I will work through this and see how it goes, I have been coding off other stack post for a few years now but don't have a great understanding of the big picture, so will take some time to digest – Chris M Nov 04 '15 at 22:00
  • @DavidZemens I agree the problem is solved, works very well. There is a lot you did that I don't understand. I think I will be able to pick your answer apart and learn a few things. Thank you, I also want to try to combine it with Sid's API solution – Chris M Nov 05 '15 at 04:13
  • Cheers, if you need any clarification on the what/why/how, just leave a comment on my answer below and I'll try to give more explanation. The biggest change I made was just ensuring to use a *new* excel instance, and then using the Sleep API function. Otherwise if it's solved the problem do consider marking it as Accepted. :) – David Zemens Nov 05 '15 at 04:21

1 Answers1

0

Try this. The hint, for me at least, was in your comment:

[the] code for changing a macro doesn't work unless they are in the same excel instance

I modified it to deal with a different instance of Excel, which may be technically necessary here. I have observed some wonky things in the past using VBE extensibility, such as inserting text within the executing module at runtime (basically what you describe).

I also previously noticed some timing issues as the "Wait" argument of SendKeys method wasn't waiting, so I additionally use the WinAPI Sleep function to introduce a half-second lag after the SendKeys calls.

Note: You will need to modify your other functions to receive the wb Workbook argument, explicitly, and change references from ActiveWorkbook to wb, etc. (See how I changed ActiveWorkbook.VBProject to wb.VBProject, etc.)

Option Explicit
Public Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
Const slp As Long = 500

Sub Main()
Dim wb As Workbook
Dim xlApp As Application
Call Open_1("C:\debug\stack\protected.xlsm", xlApp, wb)
Call UnprotectProject(xlApp)
Call ChangeDateAddUserCheck(wb)

Set wb = Nothing
Set xlApp = Nothing

End Sub
Sub Open_1(filename$, xlApp As Excel.Application, wb As Workbook)
    Set xlApp = CreateObject("Excel.Application")
    Set wb = xlApp.Workbooks.Open(filename)
    xlApp.Visible = True
End Sub

Sub UnprotectProject(xlApp As Object)

    With xlApp
        .SendKeys "%{F11}", True
        Sleep slp
        .SendKeys "^r", True
        Sleep slp
        .SendKeys "~", True
        Sleep slp
        .SendKeys "password", True
        Sleep slp
        .SendKeys "~", True
        Sleep slp
    End With
End Sub
Sub ChangeDateAddUserCheck(wb As Workbook)
  Dim VBComp As Object 'VBIDE.VBComponent
  Dim CodeMod As Object 'VBIDE.CodeModule
  Dim S As String
  Dim LineNum As Long

Set VBComp = wb.VBProject.VBComponents("Module2")
  'Delete
  VBComp.CodeModule.DeleteLines 15, 4
  'add Code
  Set CodeMod = VBComp.CodeModule
  LineNum = 15
  S = "yr = Format(Now(), ""YYYYMMDD"")" & vbCrLf & _
      "If UCase(Sheets(""DashBoard"").Range(""B21"").Value) =      UCase(Environ(""Username"")) Then" & vbCrLf & _
      "If yr < 20160601 Then B2_Stage Else MsgBox (""Software is Expired"")" & vbCrLf & _
      "Else: MsgBox (""Not Authorized User"")" & vbCrLf & _
     "End If"
  CodeMod.InsertLines LineNum, S
End Sub

Pics or it didn't happen:

Here, you can see that your function ChangeDateAddUserCheck has introduced the S code string in to my workbook Protected.xlsm!Module2:

enter image description here

Follow-up:

I've declared wb and xlApp in the Main() sub. Then pass these objects to the Open_1 procedure, which will open the new Excel and the specified workbook path.

Then, any other subroutine which needs to operate on this wb or xlApp objects (such as ChangeDateAddUserCheck) you will modify so that it accepts a workbook object, for example:

Sub ChangeDateAddUserCheck(wb As Workbook)

And likewise, modifying the UnprotectProject signature so that it accepts the xlApp object:

Sub UnprotectProject(xlApp As Object)

how would I reference the workbook that this macro is living

As in my code, wb is scoped to the Main procedure (so is xlApp). If you need other procedures to handle these objects, you pass them to those procedures per above examples. You're basically saying, "[some procedure] will now take this wb object and do something with it"

David Zemens
  • 53,033
  • 11
  • 81
  • 130
  • thank you for the input, this will take some time to digest but will let you know how it goes. – Chris M Nov 04 '15 at 21:57
  • The main reason why I needed VBE extensibility to work in a new instance was because Sid's API solution opened a new instance. But your send key solution work in the new instance so I am going to run with it. I am trying to figure out how to convert the rest of the code to work with wb. vs ActiveWorkbook. I have never put anything in the "( )" after the sub, so trying to figure out why some were (wb As Workbook) and (xlApp As Object) – Chris M Nov 05 '15 at 05:30
  • Also where do you declare what wb. is, and how would I reference the workbook that this macro is living on? – Chris M Nov 05 '15 at 05:31
  • made some additional comments in my answer, hope it helps :) – David Zemens Nov 05 '15 at 15:33
  • @findwindow I figured you might wanna see this, an alternative to Sids approach – David Zemens Nov 07 '15 at 19:34