Down below is my function, for some reason it does not work. If I place a breakpoint in the last if statement and removes sending "%{F11}" it does work. So my guess is that "%F11" is not working. Does anyone have an idea?
Sub UnprotecPassword(wb As Workbook, ByVal projectPassword As String)
Dim currentActiveWb As Workbook
If wb.VBProject.Protection <> vbext_pp_locked Then
Exit Sub
End If
Set currentActiveWb = ActiveWorkbook
wb.Activate
SendKeys "%{F11}"
SendKeys "^r" ' Set focus to Explorer
SendKeys "{TAB}" ' Tab to locked project
SendKeys "~" ' Enter
SendKeys projectPassword
SendKeys "~" ' Enter
If (wb.VBProject.Protection = vbext_pp_locked) Then
MsgBox ("failed to unlock")
End If
currentActiveWb.Activate
End Sub
To test this, Let's create a new workbook called Book2.xlsm.
for testing purpose paste this code in the Book2 Module.
Sub Book2Macro()
End Sub
Protect it with a password say a and then close it. This is necessary for the Locking to take effect.
Now create a new workbook say Book1 and in the module paste this code.
Sub Sample()
UnprotecPassword Workbooks("Book2.xlsm"), "a"
End Sub
Sub UnprotecPassword(wb As Workbook, ByVal projectPassword As String)
Dim currentActiveWb As Workbook
If wb.VBProject.Protection <> 1 Then
Exit Sub
End If
Set currentActiveWb = ActiveWorkbook
wb.Activate
SendKeys "%{F11}"
SendKeys "^r" ' Set focus to Explorer
SendKeys "{TAB}" ' Tab to locked project
SendKeys "~" ' Enter
SendKeys projectPassword
SendKeys "~" ' Enter
If (wb.VBProject.Protection = vbext_pp_locked) Then
MsgBox ("failed to unlock")
End If
currentActiveWb.Activate
End Sub
Now open the 1st workbook that we created; Book2.xlsm. Check the VBA Editor for Book2 and you will notice that it is password protected. You will also notice that it is the active workbook. Activate Book1 by clicking the View Tab | Switch Workbooks | Book1
Now click on Developer tab | Macros If you can't see Developer tab then I would recommend going through this link.
Click on the the Sample Macro in the Macro Dialog Box and you are done.
If you check the VBA Editor, you will notice that the VBA Editor for Book2 is now unlocked/accessible.
Sendkeys are unreliable depending on your use of it. If you use it correctly then that are pretty much reliable :)
There is one more way to unlock the VBA Password but that is pretty complex and involves invoking the API like FindWindow etc...
Check out these posts for code samples:
http://www.mrexcel.com/archive/VBA/29825.html
http://www.vbaexpress.com/forum/showthread.php?t=30687
And these posts are for info:
http://www.excelforum.com/excel-programming/490883-why-doesnt-sendkeys-work-consistently.html
http://www.ozgrid.com/forum/showthread.php?t=13006
They discuss why using Sendkeys is not very reliable in a multitasking environment and many discourage the use for commercial purpose.
However, for unprotecting VBA projects, it appears to be the only solution.
Hope it helps!
Related
I am trying to come up with some vba code to open an input box automatically as soon as the workbook is opened and have the user enter a date and then have the date placed in the A1 cell. I have written the code below but the input box is not pulling up at all it just opens the workbook and moves on.. not sure what is happening. Any and all help is appreciated.
Thanks!
Option Explicit
Private Sub workbook_open()
Dim cellvalue As Variant
Dim ws As Worksheet
Set ws = Worksheets("Workbench Report")
ReShowInputBox: cellvalue = Application.InputBox("Please Enter Todays Date (dd/mm/yyyy)")
If cellvalue = False Then Exit Sub
If IsDate(cellvalue) And CDate(cellvalue) < Date Then
ws.Range("A1").Value = DateValue(cellvalue)
Else: MsgBox ("Invalid Date!")
GoTo ReShowInputBox
End If
End Sub
Your code triggers upon the Workbook opening for me. Try these steps.
Open up Excel and Save As, changing the extension to .XSLM
Open up the VBA Editor (ALT + F11)
In the left-hand window, locate your macro file (the one you just created and named - it's in brackets after "VBA Project"), drilldown to "This Workbook" and double-click it.
Paste your code into the right-hand window
Save the file and re-open.
See attached diagram.
By the way, "cellValue = false" should probably be cellValue = "" since InputBox is returning a string and not a boolean value.
For Workbook_Open events the script needs to reside in the private module (ThisWorkbook)
From Ozgrid:
the Workbook_Open event is a procedure of the Workbook Object and as
such, the Workbook_Open procedure MUST reside in the private module of
the Workbook Object (ThisWorkbook).
I have an excel macro that does two very simple things:
It displays the current date and time in a little window.
It copies the display as a text string for pasting into other apps as needed.
The cell that is displayed has the following formula in it:
=TEXT(NOW(),"yyyy.MM.dd hh:mm:ss")
Every 5 seconds, the macro refreshes the time and the clock ticks.
My problem is that when I copy the time from the cell, I don't consistently get the contents pasted to the clipboard. Sometimes the cell contents are posted to the clipboard. I can't figure out why it works sometimes and not others as there isn't a lot going on. It should just always work.
I know the data aren't on the clipboard because I can try pasting the clipboard into different programs like notepad and other text apps and nothing happens.
The entire code is in a single module.
Dim stopSwitch As Integer
Dim NextTick
Sub myupdate()
If ActiveCell.Address = "$B$1" Then
growWindow ' resize window beyond just clock display
stopTime '
Exit Sub ' stop updating
End If
Range("a1").Select
Calculate
DoEvents
If ActiveWorkbook.Name = "calendar clock.xlsb" Then shrinkWindow
NextTick = Now + TimeValue("00:00:05") ' give me 5 seconds to copy/paste
Application.OnTime NextTick, "myupdate"
ThisWorkbook.Save ' futile attempt to prevent save dialog
End Sub
Sub auto_open()
' to stop clock, tap right arrow to select cell b1 when workbook is active
Range("a1").Select
myupdate
End Sub
Sub growWindow()
Application.Width = 768
Application.Height = 621.75
ThisWorkbook.Save
End Sub
Sub shrinkWindow()
' strip decorations so window is as small as possible
Application.DisplayFormulaBar = False
ActiveWindow.DisplayGridlines = False
ActiveWindow.DisplayHeadings = False
' move window to second monitor and size to single cell display
Application.WindowState = xlNormal
Application.Top = 0
Application.Left = -720
Application.Width = 174
Application.Height = 127
ActiveWindow.WindowState = xlMaximized
End Sub
Sub stopTime() ' called when workbook is closed
On Error Resume Next
Application.OnTime NextTick, "myupdate", schedule:=False
Range("b1").Select
End Sub
Sub copyTime()
Range("a1").Copy ' copy time
Range("f5").PasteSpecial xlPasteValues ' strip formatting
Range("f5").Copy ' copy time as text
DoEvents ' hack to attempt to make copy work consistently
End Sub
The above code sizes the window and updates the clock every 5 seconds.
To copy the clock as text to the clipboard, I have the following code in the workbook
Private Sub Workbook_Activate()
Application.OnKey "^c", "module1.copyTime"
End Sub
Private Sub Workbook_Deactivate()
Application.OnKey "^c"
End Sub
Private Sub Workbook_BeforeClose(Cancel As Boolean)
' turn off auto update
Module1.stopTime
' resize window so if I open another spreadsheet, it's a reasonable size
Application.WindowState = xlNormal
Application.Width = 768
Application.Height = 621.75
Application.OnKey "^c"
ThisWorkbook.Save ' try to prevent save dialog at close
End Sub
I modified the copyTime function to verify the ^C is seen by selecting the unformatted cell and I can see that the data consistently go to the cell so I know my problem isn't with the Range("a1").copy step in copytime or the pastespecial to cell f5.
That leaves the range("a5").copy command as the bad actor when the copy fails which is weird. It's as if copy works as long as the data are kept inside the spreadsheet but fails to update the external clipboard consistently.
That observation led me to try setting application.cutcopymode to xlcopy, true and false to see if that helped. The only effect I saw from trying all the settings is whether I saw f5 get highlighted with a marquee or not - none of the setting forced a copy to the external clipboard.
I tried waiting for a clock tick before copying to see if something was clearing the clipboard following the copy if it was time to update the clock. That appeared to help somewhat but, again not consistently.
So why does the copy fail to always update the clipboard? And why does it not work when it doesn't and does when it does? Even better, how can I modify this code so it always exports to the external clipboard?
Try using this method, it's always reliable for me
Dim TimeInClip As MSForms.DataObject
Set TimeInClip = New MSForms.DataObject
TimeInClip.SetText Range("A1").Value
TimeInClip.PutInClipboard
Try
Sub copyTime()
Range("a1").Copy ' copy time
Range("f5").PasteSpecial xlPasteValues ' strip formatting
Application.CutCopyMode = False ' Clear Excel clipboard
Range("f5").Copy ' copy time as text
DoEvents ' hack to attempt to make copy work consistently
End Sub
You said that you tried Application.CutCopyMode, but have you tried it that way?
It only forces the application to clear the clipboard before copying something else, which should then copy properly on the fresh clipboard.
I've written a program in Excel VBA which uses a UserForm to enter data by the users. Specifically, it is a Telemarketing Tracker tool: the user fills in the details of the call in a text box on the UserForm and then clicks the relevant button to indicate whether it was a good or bad call, and can then continue with the next call.
This data is stored on a worksheet and our users often prefer to hide the workbook and just view the UserForm. I have developed a couple of methods of hiding the workbook. If there is just one workbook open, I hide the Excel Application. If there is more than one workbook open, I just hide the window. Here is the code I use for this:
Private Sub HideUnhideButton_Click() 'User clicks Hide/Unhide button
If Workbooks.Count > 1 Then
Windows(ThisWorkbook.Name).Visible = Not Windows(ThisWorkbook.Name).Visible
HideUnhideButton.Tag = Windows(ThisWorkbook.Name).Visible
Else
ThisWorkbook.Application.Visible = Not ThisWorkbook.Application.Visible
HideUnhideButton.Tag = ThisWorkbook.Application.Visible
End If
ThisWorkbook.Activate
End Sub
This works well but obviously certain issues arise when the user has the workbook hidden and then open a different Excel Workbook. I've worked round most of these issues, but there is one thing I can't seem to work out: if the Telemarketing workbook is hidden, with another workbook open, if I click the Close button, both workbooks try to close.
I've tried creating a class module with an Application Level event tracker so that all workbooks' close events are monitored. But my problem is that when I click the close button, the first workbook that tries to close is the hidden workbook. So I can catch the close event and prevent the hidden workbook from closing but if I set Cancel to True, it prevents all the workbooks from closing!
The only workaround I can think of is when the user tries to close a workbook, I cancel the Close Event and Unhide the hidden workbook. But I don't know how to identify which workbook the user was attempting to close - so I can't work out how to automatically close the correct workbook.
I have currently set up the WorkbookBeforeClose event as follows:
Public WithEvents A As Excel.Application
Private Sub A_WorkbookBeforeClose(ByVal Wb As Workbook, Cancel As Boolean)
If Workbooks.Count > 1 Then
If Windows(ThisWorkbook.Name).Visible = False Then
If Wb.Name = ThisWorkbook.Name Then
Cancel = True
End If
End If
End If
End Sub
If I step through this code, I find that the Wb.Name is the name of the Telemarketing Workbook (even though it's hidden) and the name of the workbook that the user is actually trying to close does not appear at all - as far as I can work out.
Can anyone make any further suggestions?
The other thing I should mention is that it needs to work over Excel 2013 and Excel 2010.
I'm sorry to post an answer to my own question so quickly. It sort of indicates I didn't do quite enough research beforehand. However, for anyone who has a similar problem, here's my solution. This code needs to be posted in a class module and an instance of the class needs to be created before it will work, of course.
Note: in the below example, "TT" relates to the Telemarketing Tracker
Public WithEvents A As Excel.Application
Private Sub A_WorkbookBeforeClose(ByVal Wb As Workbook, Cancel As Boolean)
Dim VIS As Boolean, myAW As Workbook
If Workbooks.Count > 1 Then 'if there is more than one workbook open...
If Windows(ThisWorkbook.Name).Visible = False Then 'and if TT is invisible...
If ActiveWorkbook.Name = ThisWorkbook.Name Then 'and if the workbook being closed is the TT.
Windows(ThisWorkbook.Name).Visible = True
Else 'more than one wb open, TT is invisible, and the workbook being closed is NOT the TT.
Set myAW = ActiveWorkbook
Cancel = True
Windows(ThisWorkbook.Name).Visible = True
Application.EnableEvents = False
myAW.Close
Application.EnableEvents = True
If TelesalesForm.HideUnhideButton.Tag = "False" Then 'NB: I use a tag on the Hide/Unhide button on the UserForm to store whether the workbook should be hidden or not.
If Workbooks.Count > 1 Then
Windows(ThisWorkbook.Name).Visible = False
Else
ThisWorkbook.Application.Visible = False
End If
End If
Exit Sub
End If
ElseIf ActiveWorkbook.Name <> ThisWorkbook.Name Then
'more than one workbook open and the TT is visible and the workbook being closed is NOT the TT
Exit Sub
End If
End If
'code gets to this point ONLY under the following circumstances:
'There is only one workbook open (i.e. the TT) OR
'There is more than one WB open and the WB being closed is the TT.
'The rest of the code goes here for managing the closing of the TT.
End Sub
If anyone thinks of any other embellishments or improvements to the code then I would be very glad to hear of them!
I have a VBA script in several workbook templates that unlocks the current (active) worksheet. I use the same hotkey so that the users who are permitted to use the macro don't have to remember which hotkey allows them to unlock the workbook.
This generally causes no headaches as most users don't have more than one workbook open at a time (and in all likelihood don't use the hotkeys anyway). The issue is if I have more than one workbook open and try to run the VBA script with the hotkey, I'm currently getting a random instance of the VBA script. This causes problems because the password does vary between the workbooks, so if the hotkey kicks off the VBA script in WB X and I'm in WB Y, I get an error.
Getting to the point, is there a way I can make it so that the VBA script from the active workbook on that hotkey is the one that's used?
Per Alter's request here's a sanitized version of my lock_unlock VBA script
Sub Lock_Unlock()
Dim CurrentUser As String 'holds the current users Windows login
Dim Approved As String
Approved = "|user1|user2|user3|"
'Give CurrentUser it's value
CurrentUser = Environ$("username")
'Check if the user is approved
If InStr(1, Approved, CurrentUser) > 0 Then
'The user can use this macro. Check if the sheet is currently locked
If ActiveSheet.ProtectContents = True Then
'It is, unlock
ActiveSheet.Unprotect Password:=PW()
Else
'It isn't, relock
ActiveSheet.Protect DrawingObjects:=False, Contents:=True, Scenarios:= _
False, Password:=PW()
End If
'Not a user approved to use this macro, don't do anything
End If
End Sub
Function PW() As String
PW = "password"
End Function
This is how I would do it, modularize the password to a getFunction.
ex.
Function getPassword()
getPassword = "password1"
End Function
Now, when you want the password call Application.Run(ActiveWorkbook.Name & "!getPassword") This will make sure the password is retrieved from the active workbook, regardless of the workbook your macro is being run from
Ex.
Sub test()
MsgBox Application.Run(ActiveWorkbook.Name & "!getPassword")
End Sub
Function getPassword()
getPassword = "hello"
End Function
Option 2: check if ThisWorkbook is the ActiveWorkbook, if it isn't then call the macro from the activeworkbook using the same method I used to get the password.
I used this vba code in the ThisWorkbook module to disable the right click menu in an Excel workbook.
Private Sub Workbook_Activate()
With Application.CommandBars.FindControl(ID:=847)
.Visible = False
End With
End Sub
Private Sub Workbook_Deactivate()
With Application.CommandBars.FindControl(ID:=847)
.Visible = True
End With
End Sub
Works like a charm.
Problem is, I can't access the right click menu on tabs in ANY workbook now.
The second part of the code is supposed to turn it back on, I assumed? Yet it doesn't.
Even when I remove the code entirely, no workbook, not even a new one, has a menu when I click right on one of the tabs.
Is there a general vba codesnippet that "resets" excel maybe? Or a general "enable all menus" thing?
REVISION:
This code posted here doesn't disable the rightclick menu, it removes the "delete" option from that specific menu.
omg
Application.CommandBars("Ply").Enabled = True
-.-
Started googling different keywords after the last edit and BAM.
Late again as usual, but tackled with the same problem today. Here's the solution to get your right-click functionality back:
Option Explicit
'
Sub tester()
'
Dim cBar As CommandBar
'
For Each cBar In CommandBars
Debug.Print cBar.Name
If (cBar.Type = msoBarTypePopup) Then cBar.Enabled = True
Next
End Sub
Also note that the below also exist. Some macro from work had them all disabled in my Excel.
Application.CommandBars("Cell").Enabled = True
Application.CommandBars("Row").Enabled = True
Application.CommandBars("Column").Enabled = True