Excel Workbook opens as Read-Only because Connection Files - excel

I have an group of 3 interconnected workbooks that pull data from each other. The way I have it pulling data is through Connections. All files are located in the same drive. What I am trying to do is to open all three files at the same time to make edits. However, once I open workbook "A", I can not properly open files "B" or "C" as it displays that the file is in use by another user and only gives the option to open as Read-Only.
Is there a work around to open all files at the same time in Write mode? I have tried including a ChangeFileAccess to Read but that does not work.
Thanks!
UPDATE:
I linked all files using Data > Connections. To further expand, these files open as UserForms on startup and I have added this code under "ThisWorkbook" to do that (open the UserForm automatically and hide the workbook). I am beginning to think that my problem of not being able to open more than one of this documents at the same time is because of this piece of code, but I am not sure what it might be or if there's a better way to open Userform & hide workbook. Any advice is appreciated. Below is the code I have under "ThisWorkbook":
Private Sub Workbook_BeforeClose(Cancel As Boolean)
'important to reset this
Application.IgnoreRemoteRequests = False
End Sub
Private Sub Workbook_Open()
'need to use ontime to allow xl to initialise fully
Application.OnTime Now, "ThisWorkbook.OnlyOneOfMe"
End Sub
Private Sub OnlyOneOfMe()
Dim xlApp As Excel.Application
On Error GoTo BAD
With Application
If Me.ReadOnly Or .Workbooks.Count > 1 Then
Me.ChangeFileAccess Mode:=xlReadOnly
Set xlApp = New Excel.Application
xlApp.Visible = True
xlApp.Workbooks.Open (Me.FullName)
GoTo BAD
Else
'stop opening from explorer (but not from excel)
.Visible = False
.IgnoreRemoteRequests = True
UserForm1.Show
.Visible = True
'.Quit
End If
Exit Sub
End With
BAD: If Err Then MsgBox Err.Description, vbCritical, "ERROR"
Set xlApp = Nothing
Me.Close False
End Sub

Related

How to run macro from another spreadsheet and wait for execution?

I have a spreadsheet (let's name it Spr1) that I need to refresh periodically. I don't want to refresh it every time I open it, because it takes time.
I created another "launcher spreadsheet" (Spr2) to start Spr1 with macro in argument.
Spr2 is opening with:
Private Sub Workbook_Open()
Application.Visible = True
Application.Run "'\\path\Spr1.xlsm'!Refresh_data"
Workbooks("Spr1.xlsm").Close SaveChanges:=True
Application.Quit
End Sub
Macro in Spr1:
Sub Refresh_data()
ThisWorkbook.RefreshAll
End Sub
The first macro is not waiting for second one to finish the refresh. It is terminating Excel right after opening Spr1.
How can I wait to finish "Refresh_data"?
It depends on type of default you have set.
Find your Query Properties, and set 'Enable background refresh' to False (Microsoft use True as default).
Then in your code you need to call for RefreshAll and wait for the data to load with the DoEvents. If you want your update of data to run on open you can use this in 'ThisWorkbook' Object.
Private Sub Workbook_Open()
For Each q In ThisWorkbook.Connections
q.Refresh
DoEvents
Next
End Sub
If you like to launch your update from a VBScript you can call your macro without even se Excel doing the work in the background. Use Notepad and past this code in and save as MyStarter.vbs where you can start it with a double click.
Dim xlApp
Dim xlWkb
Dim MyParam
MyParam = InputBox("Input your Parameter:","Enter parameter to the service")
if MyParam <> false then
Set xlApp = CreateObject("excel.application")
Set xlWkb = xlApp.Workbooks.Open("\\path\Spr1.xlsm",true,true)
xlApp.Run "Spr1.xlsm!Refresh_data", CStr(MyParam)
Set xlWkb = Nothing
Set xlApp = Nothing
end if
Then you can use your input parameter sent to the instance of your workbook like this :
Sub Refresh_data(MsgFromVBScript As String)
MsgBox ("This is your parameter from VBScript:" & MsgFromVBScript)
ThisWorkbook.RefreshAll
End Sub

How to keep a userform open when closing other instances of Excel

Hope someone can help - I have a Userform that opens on launch of the excel file (Test.xlsm) and hides the workbook from prying eyes. The workbook can become visible for editing by a button click and password entry from the userform. Everything is working fine - UNTIL - you open another instance of excel. Once you finish with it and close any secondary instance of excel, it also either
1. closes the userform, or
2. shows the excel workbook behind the userform. Neither of these is what I want. I need the userform to remain open and I need the workbook associated with it to remain hidden until called.
Question - is there some code that will prevent other instances of excel from doing what it is doing, or am I dreaming.
I found some code (below) that the writer said done exactly what i am after, but all I got was global errors.
Private Sub WorkBook_Open()
If Workbooks.Count = 1 Then Application.Visible = False
Workbooks("test.xlsm").Windows(1).Visible = False
UserForm1.Show vbModeless
End Sub
Any help greatly appreciated.
BTW - the code for the workbook open is
Private Sub Workbook_Open()
Set Thiswb = Me.Application
Application.Visible = False
Staff_Contacts.Show vbModeless
End Sub
For usecase you described, I would propose you something similar to this solution:
Sub Workbook_open()
Dim wrong_attempts As Integer
wrong_attempts = 0
Dim sheet As Worksheet
Set sheet = ActiveWorkbook.Sheets.Add(After:=ActiveWorkbook.Worksheets(ActiveWorkbook.Worksheets.Count))
ActiveWorkbook.Sheets("Sheet2").Visible = xlSheetVeryHidden
Start1:
If wrong_attempts > 4 Then
MsgBox "You've entered wrong password too many times. File will now be closed"
Application.DisplayAlerts = False
sheet.Delete
ActiveWorkbook.Close
Application.DisplayAlerts = True
End If
InputBoxVariable = InputBox(Prompt:="Please enter password to access this document", Title:="Authorization required", Default:="")
If InputBoxVariable = "12345" Then
ActiveWorkbook.Sheets("Sheet2").Visible = xlSheetVisible
Application.DisplayAlerts = False
sheet.Delete
Application.DisplayAlerts = True
Else
wrong_attempts = wrong_attempts + 1
GoTo Start1
End If
End Sub
In this example it is assumed that sheet2 should be protected. On the very load of the document, phantom sheet will be created, and after successful login, it will be deleted and sheet2 should be visible again. In addition, if someone enters wrong password 5 times, file closes automatically.
Fair notice, I've used InputBox out here, so password is not masked, if you want to mask password as well, you will have to make brand new form with button and textbox.

GetOpenFilename behaviour different for version earlier than Excel 2016

I have the following code:
Private Sub CommandButton3_Click()
Dim SPATH As String
Dim myFileName As Variant
If MsgBox("Some Question...", vbYesNo) = vbNo Then Exit Sub
'Used to open the VO2 report taken from the Metabolic Cart. Will close later
myFileName = Application.GetOpenFilename(FileFilter:="Excel Files,*.xls*")
If myFileName <> False Then
Application.ScreenUpdating = True
Workbooks.Open Filename:=myFileName
End If
MultiPage2.Value = 3
End Sub
It's job is to search for and open another Excel file while a userform is being run with the workbook it's currently tied to is visible=False with this:
Private Sub Workbook_Open()
Application.ScreenUpdating = False
ThisWorkbook.Application.Visible = False
UserForm2.Show
Windows(ThisWorkbook.Name).Visible = True
Application.Visible = True
Application.ScreenUpdating = True
End Sub
Also important note: Showmodal for this userform is True
When this is run using Excel 2016, it works no problem (i.e. I am able to use a refedit on the userform while the loaded Excel file is visible to use it on).
On my work computer which has an older version of Excel, the loaded Excel file is invisible (meaning I can't use the refedit I have on the userform).
Does anyone know what may be going on or have a possible solution?

Unable to open Excel from Explorer when userform is opened

Hi I am working on a project where I have to let users open excel while the Userform is opened.I can navigate through other excel files but not the one from Explorer.Please help.It would be of great help for me.
Option Explicit
Private Sub Workbook_Open()
Application.OnTime Now, "ThisWorkbook.OnlyOneOfMe"
Dim wks As Worksheet
For Each wks In ActiveWorkbook.Worksheets
wks.Protect Password:="Nothing", _
UserInterfaceOnly:=True
Next wks
End Sub
Private Sub Workbook_BeforeClose(Cancel As Boolean)
'important to reset this
Application.IgnoreRemoteRequests = False
End Sub
Private Sub OnlyOneOfMe()
Dim XlApp As Excel.Application
On Error Goto BAD
With Application
If Me.ReadOnly Or .Workbooks.Count > 1 Then
Me.ChangeFileAccess Mode:=xlReadOnly
Set XlApp = New Excel.Application
XlApp.Visible = True
XlApp.Workbooks.Open (Me.FullName)
Goto BAD
Else
'stop opening from explorer (but not from excel)
.Visible = False
.IgnoreRemoteRequests = True
UserForm1.Show
.Visible = True
.Quit
End If
Exit Sub
End With
BAD: If Err Then MsgBox Err.Description, vbCritical, "ERROR"
Set XlApp = Nothing
Me.Close False
End Sub
If the UserForm is modal it will lock the instance of excel until the form is closed.
You need to make the UserForm non Modal or close it after the workbook is opened.
Or you may Disable events before opening the workbook, that will prevent the UserForm to popup.
Just put this before the line where you open the workbook
Application.EnableEvents = False
And after the opening line enable the evens again
Application.EnableEvents = True
And you need to Enable/Disable the events for correct instance/application since you are opening new.
like this:
XlApp.EnableEvents = False
XlApp.Workbooks.Open (Me.FullName)
XlApp.EnableEvents = True
But you probably wont need to open new excel instance if this would work.
Also put the events enabling line to the ErrorHandling
If Err Then Application.EnableEvents = True: MsgBox Err.Description, vbCritical, "ERROR"
You can also try to hide all opened UserForms.
Put this right after you open the workbook:
For Each Object In VBA.UserForms
Object.Hide
Next
I would rather use a visual basic script to open up the userform in your VBA project. Put the following code on a plain text file and save it with the '.vbs' extension.(This must be in the same folder as the excel file containing the userform)
Option Explicit
Dim fso, curDir
Dim xlObj, file
Dim fullPath
Const xlMaximized = -4137 'constant to maximizes the background excel window
On Error Resume next
Set fso = CreateObject("Scripting.FileSystemObject")
curDir = fso.GetAbsolutePathName(".")
file ="\~$YourExcelFileName.xlsm"
fullPath = curdir & File
If fso.FileExists(fullPath) Then ' checks if the project is open or not
MsgBox "The project is in use!",64, "Notificación"
Else
file ="\YourExcelFileName.xlsm"
fullPath = curdir & file
Set fso = Nothing
Set xlObj = CreateObject("Excel.Application")
With xlObj
.WindowState = xlMaximized
.Visible = False
.Workbooks.Open fullPath
.IgnoreRemoteRequests = True
.Run "mainMethod"
End With
set xlObj=Nothing
End If
... then add a public subroutine in your vba project to listen the call from the previous VBScript (name the subroutine as above, I've called mainMethod)
Public Sub mainMethod()
UserForm1.Show vbModeless
End Sub
... you also have to attach an userform_terminate event to indicate that when you close the userform it must quit the current and active instance of excel:
Private Sub UserForm_Terminate()
Application.Quit
End Sub
... and of course you have to write a workbook's before_close event to reset the .IgnoreRemoteRequests to false, as follow (You can also write this in the previous userform_terminate event handler, but I believe this a tidier way to do it):
Private Sub Workbook_BeforeClose(Cancel As Boolean)
ThisWorkbook.Saved = True 'if needed
Application.IgnoreRemoteRequests = False
End Sub
Once you manage to do that, you'll have a very clean stand alone application, no one will notice it comes from an excel file and it won't interfeers with any other excel instance. Good Luck.
Andrés Alejandro García Hurtado

Excel VBA - always show worksheet on open

How can the following conditions be met with VBA code?
A particular worksheet is always displayed on open, even if the worbook is opened without enabling macros.
A workbook user may save the workbook while working on any worksheet.
The save must not interfere with the user - no navigating away to a different sheet, no messageboxes, etc.
The regular save functions (Ctrl-S, clicking Save) must remain available and when used must obey the criteria above.
I'd like to avoid the attempted solutions I've listed at the bottom of this question.
Details:
The workbook is created using Office 2007 on a Windows 7 machine. It is an .xlsm workbook with 2 worksheets, "Scheduler" and "Info." Sheet tabs are not visible. Not all users will enabled macros when the workbook is opened.
Upon opening the workbook, a user will only be exposed to one sheet as follows:
"Info" shows up if macros are disabled, and basically tells anyone who opens the workbook that macros need to be enabled for full workbook functionality. If macros are enabled at this point, "Scheduler" is activated.
"Scheduler" is where data is stored and edited, and is automatically shown if macros are enabled. It is not presented to the user when the workbook is opened without macros enabled.
"Info" must show up first thing if the workbook is opened and macros are disabled.
Attempted Solutions (I'm looking for better solutions!):
Placing code in the Workbook.BeforeSave event. This saves with "Info" activated so it shows up when the workbook is opened. However, if the user is in "Scheduler" and not done, I cannot find a way in this event to re-activate "Scheduler" after the save.
Using Application.OnKey to remap the Ctrl-s and Ctrl-S keystrokes. Unfortunately this leaves out the user who saves using the mouse (clicking File...Save or Office Button...Save).
Checking during every action and if needed activating "Scheduler". In other words, inserting code in something like the Workbook.SheetActivate or .SheetChange events to put "Scheduler" back into focus after a save with "Info" activated. This runs VBA code constantly and strikes me as a good way to get the other code in the workbook into trouble.
Placing code in the Worksheet("Info").Activate event, to change focus back to "Scheduler". This leads to the result of "Scheduler", not "Info", showing when the workbook is opened, even with macros disabled.
Will this not work? Updated to handle Saving gracefully
Private Sub Workbook_Open()
ThisWorkbook.Worksheets("Scheduler").Activate
End Sub
Private Sub Workbook_BeforeClose(Cancel As Boolean)
ThisWorkbook.Worksheets("Info").Activate
If (ShouldSaveBeforeClose()) Then
Me.Save
Else
Me.Saved = True ' Prevents Excel Save prompt.
End If
End Sub
Private Function ShouldSaveBeforeClose() As Boolean
Dim workbookDirty As Boolean
workbookDirty = (Not Me.Saved)
If (Not workbookDirty) Then
ShouldSaveBeforeClose= False
Exit Function
End If
Dim response As Integer
response = MsgBox("Save changes to WorkBook?", vbYesNo, "Attention")
ShouldSaveBeforeClose= (response = VbMsgBoxResult.vbYes)
End Function
I don't have time to test this out, but you might be able to do this using Application.OnTime in your BeforeSave event handler. Something like:
Private Sub Workbook_BeforeSave(ByVal SaveAsUI As Boolean, Cancel As Boolean)
Dim objActiveSheet
Set objActiveSheet = Me.ActiveSheet
If objActiveSheet Is InfoSheet Then Exit Sub
If Module1.PreviousSheet Is Nothing Then
Set Module1.PreviousSheet = objActiveSheet
InfoSheet.Activate
Application.OnTime Now, "ActivatePreviousSheet"
End If
End Sub
Then in Module1:
Public PreviousSheet As Worksheet
Public Sub ActivatePreviousSheet()
If Not PreviousSheet Is Nothing Then
PreviousSheet.Activate
Set PreviousSheet = Nothing
End If
End Sub
Edit 2: Here is a re-write that does not utilize AfterSave. You may need to tweak the dialog created from GetSaveAsFilename according to your needs.
This relies on overriding default save behavior and handling the save yourself.
Private actSheet As Worksheet
Private Sub Workbook_BeforeSave(ByVal SaveAsUI As Boolean, Cancel As Boolean)
Cancel = True
PrepareForSave
manualSave SaveAsUI
AfterSave ThisWorkbook.Saved
End Sub
Private Sub PrepareForSave()
Set actSheet = ThisWorkbook.ActiveSheet
ThisWorkbook.Sheets("Info").Activate
hidesheets
End Sub
Private Sub manualSave(ByVal SaveAsUI As Boolean)
On Error GoTo SaveError 'To catch failed save as
Application.EnableEvents = False
If SaveAsUI Then
If Val(Application.Version) >= 12 Then
sPathname = Application.GetSaveAsFilename(FileFilter:="Excel Files (*.xlsm), *.xlsm")
If sPathname = False Then 'User hit Cancel
GoTo CleanUp
End If
ThisWorkbook.SaveAs Filename:=sPathname, FileFormat:=52
Else
sPathname = Application.GetSaveAsFilename(FileFilter:="Excel Files (*.xls), *.xls")
If sPathname = False Then
GoTo CleanUp
End If
ThisWorkbook.SaveAs Filename:=sPathname, FileFormat:=xlNormal
End If
Else
ThisWorkbook.Save
End If
SaveError:
If Err.Number = 1004 Then
'Cannot access save location
'User clicked no to overwrite
'Or hit cancel
End If
CleanUp:
Application.EnableEvents = True
End Sub
Private Sub AfterSave(ByVal bSaved As Boolean)
showsheets
If actSheet Is Nothing Then
ThisWorkbook.Sheets("Scheduler").Activate
Else
actSheet.Activate
Set actSheet = Nothing
End If
If bSaved Then
ThisWorkbook.Saved = True
End If
End Sub
Private Sub hidesheets()
For Each ws In ThisWorkbook.Worksheets
If ws.Name <> "Info" Then
ws.Visible = xlVeryHidden
End If
Next
End Sub
Private Sub showsheets()
For Each ws In ThisWorkbook.Worksheets
ws.Visible = True
Next
End Sub
Private Sub Workbook_Open()
AfterSave True
End Sub
The only way to make Info display first without macros enabled is if that is how the workbook was saved. This is most reasonably handled when saving.
Unless I misunderstood your issue, not using BeforeSave seems misguided. Just make sure to use AfterSave as well. Here's an example:
Private actSheet As Worksheet
Private Sub Workbook_AfterSave(ByVal Success As Boolean)
showsheets
actSheet.Activate
Set actSheet = Nothing
Thisworkbook.Saved = true 'To prevent save prompt from appearing
End Sub
Private Sub Workbook_BeforeSave(ByVal SaveAsUI As Boolean, Cancel As Boolean)
Set actSheet = ThisWorkbook.activeSheet
ThisWorkbook.Sheets("Info").Activate
hidesheets
End Sub
Private Sub Workbook_Open()
showsheets
ThisWorkbook.Sheets("Scheduler").Activate
End Sub
Private Sub hidesheets()
For Each ws In ThisWorkbook.Worksheets
If ws.Name <> "Info" Then
ws.Visible = xlVeryHidden
End If
Next
End Sub
Private Sub showsheets()
For Each ws In ThisWorkbook.Worksheets
ws.Visible = True
Next
End Sub
The use of the private object actSheet allows the "ActiveSheet" to be reselected after save.
Edit: I noticed you had more requirements in the comments. The code has been updated so that now upon saving, only the Info sheet will be visible, but when opened or after saving, every sheet will reappear.
This makes it so that any user opening the file without macros will not be able to save with a different sheet activated, or even view the other sheets. That would certainly help motivate them to enable macros!
This problem has been flogged to death in the past, its just hard to find a solution that actually works. Take a look at this code which should do what you need. Basically it shows a splash screen, with all other sheets hidden if the user does not enable macros. It will still save normally if the user clicks save and wont interfere with their work. If they save with there worksheet open it will still show only the splash screen when next opened. Download the sample file below and you can test for yourself, make sure you download the file posted by Reafidy it has over 400 views. If you need it modified further let me know.
Private Sub Workbook_BeforeClose(Cancel As Boolean)
bIsClosing = True
End Sub
Private Sub Workbook_BeforeSave(ByVal SaveAsUI As Boolean, Cancel As Boolean)
Dim wsArray() As Variant
Dim iCnt As Integer
Application.ScreenUpdating = 0
Splash.Visible = True
For Each wsSht In ThisWorkbook.Worksheets
If Not wsSht.CodeName = "Splash" Then
If wsSht.Visible = True Then
iCnt = iCnt + 1: Redim Preserve wsArray(1 To iCnt)
wsArray(iCnt) = wsSht.Name
End If
wsSht.Visible = xlSheetVeryHidden
End If
Next
Application.EnableEvents = 0
ThisWorkbook.Save
Application.EnableEvents = 1
If Not bIsClosing Then
For iCnt = 1 To UBound(wsArray)
Worksheets(wsArray(iCnt)).Visible = True
Next iCnt
Splash.Visible = False
Cancel = True
End If
Application.ScreenUpdating = 1
End Sub
Private Sub Workbook_Open()
Dim wsSht As Worksheet
For Each wsSht In ThisWorkbook.Worksheets
wsSht.Visible = xlSheetVisible
Next wsSht
Splash.Visible = xlSheetVeryHidden
bIsClosing = False
End Sub
A sample file can be found here.
How about using a 'proxy workbook'.
The 'proxy workbook'
is the only workbook which is directly opened by the users
contains the info sheet
contains VBA to open your 'real workbook' using Workbooks.Open (As I've checked with Workbooks.Open documentation by default it will not add the file name to your recent files history unless you set the AddToMru argument to true)
if required the VBA code could even make sure that your 'target workbook' is trusted (I found some sample code here)
The 'target workbook'
contains your Schedule and any other sheets
is only opened if the VBA code in 'proxy workbook' was executed
can be saved by the user at any time as usual
I've got no Office 2007 at hand to test this but think it should do.

Resources