I am writing a bit of code for a company who will be using it on their network. I'm not on a network like theirs to be able to test, so I'm checking in to try and make sure the code is right first time!
The workbook will interact with other worksheets on their network in various ways, and essentially I need to be able to check if:
The target workbook is already open on their own system, and
ideally:
Whether it is open in the same Excel instance, or
Whether is is open in a separate instance (I believe this could
require different actions for copying and pasting ranges, saving etc
which I need to learn more about)
The target workbook is open by another network user (so presume it would only open as read-only)
I've come across lots of stuff about checking if a workbook is already open, eg: http://support.microsoft.com/kb/291295
But I haven't found anything that clearly differentiates between the two scenarios I explained above.
Thank you
Update
I am now using:
Function IsFileOpenInInstance(FileName As String)
Dim ShortFileName As String
Dim ValidOpenFile As Boolean
Dim ErrNum As Integer
On Error Resume Next
ShortFileName = Dir(FileName)
If FileName = Workbooks(ShortFileName).FullName Then ValidOpenFile = True
ErrNum = Err
On Error GoTo 0
If ErrNum <> 0 Then
IsFileOpenInInstance = False
Else
IsFileOpenInInstance = ValidOpenFile
End If
End Function
Because of how Excel macros are resolved, Excel workbooks within a single instance are distinguished by unqualified name (file name without full path).
Try accessing Workbooks("File Name.xls") to see if the workbook is loaded within current instance, and if it is, check FullName to see if it is indeed the right one.
It does not make a difference, operation-wise, whether or not two workbooks are opened in different instances. Getting a pointer to another instance, however, may be problematic and I don't recommend this (e.g. if you call GetObject("File Name.xls"), it can return your instance of Excel, not the other one, even if you don't have the file opened). You should work on assumption that all required workbooks are opened in the same instance.
It does not make a difference whether you or another user has opened a workbook. If you try to open the same workbook in another instance of Excel, it will tell you it is blocked same way as if it was opened by someone else.
Related
After a bunch of processing, I save my Excel spreadsheet with a script-defined name and a user-chosen path. In other words, I know exactly where the file is.
I want to close down Excel. If I do
Workbook.Close
...the instance of Excel will hang around. If I do
Excel.Application.quit
...it might affect other instances of Excel I have open.
Given I know exactly which Excel I want to close, can I close the workbook AND the associated instance in a single step, without a lot of testing for other workbooks or instances? TIA
If you already have the Workbook Object, you can access the Application that it is running within using Workbook.Application. The .Parent property also returns the object that the current object is a member of, which for Workbooks, is also the Application.
So either of the following will close the specific Excel App that is running the target workbook:
'Given that TargetWorkbook is a valid Workbook Object
TargetWorkbook.Application.Quit
TargetWorkbook.Parent.Quit
I run a lot of macros, and lately had to change to O365, which gave me the "wonderful" 64 bit Excel. One of its most maddening shenanigans is that when I run some macros (working with SAP GUI, creating SAP reports, naming them, saving and downloading to a specific folder where the next macro step opens them and incorporates into a relevant macro sheet), the new Excel opens these in a new instance (which I don't want but cannot prevent - can you help here?), which causes a dialog "File in Use" (which I don't want but cannot prevent - can anyone help me here?) because it tries to open my PERSONAL.XLSB (which I need and therefore don't want to get rid of just because of this) and qualifies it as "locked for editing" by another user, who is myself (which is absolutely stupid but I cannot change it - can anyone help me perhaps with this part?).
Sometimes the macro finishes fine when I don't answer that dialog; sometimes it seems to cause the Excel to hang up in trying to do next steps, sometimes the macro quits on me when I click "Read only" and so on.
All this is maddening also because these files that (possibly) SAP GUI opens, I have to waste my time by closing them after the macro finishes (has anyone a possible solution in stopping SAP opening these files? I was unable to find it anywhere so far.).
In some macros I fixed it by a specific part of code which waits till when these files open up and then it closes them - yet even that wait is wasting my time and it would be better without.
But with O365 and other instances it is even more colorful, because some files open in the same instance (this issue was there even before the O365 though) and I can manually close them after the macro finishes (although again, I have to wait till they open, which is again "vanity and torture of the soul"), but those opening in the new instance (specific to O365) show as blank sheets, I have to click into them, a warning sound follows but no dialog is visible, then I have to click into them again from the Excel icon in the bottom menu bar and then it tells me that I cannot close them because a dialog is open.
Then I need to close this dialog plus the one which appeared about the "File in Use", then I finally wait for the file (that I don't need) to open (because it is already saved inside the folder and already copied and pasted onto the relevant macro sheet) and only then I can close it.
So far I was unable to find anywhere on the internet an advice for how to stop SAP opening these files (when they are already saved where I need them), neither how to stop them opening in new Excel instances. So reluctantly, I think I should aim for at least a tiny little VBA code which will cause my macro to click on the "Read Only" button. Can anyone here help me with any part of this "Excel complaint"?
I do not know how/if SAP can be 'persuaded' to not open the workbooks in discussion... But, I think, you can handle that in a different way the "PERSONAL.XLSB" behavior, from the opening point of view. Of course, if SAP itself has this "bad habit" and not your code opens it in a new session, which have to be solved in a different way...
"PERSONAL.XLSB" is located in "...AppData\Roaming\Microsoft\Excel\XLSTART" folder. All workbooks located in this folder are automatically open when an Excel session starts;
You may change the "PERSONAL.XLSB" workbook path. If you want it to be open when a specific (necessary) workbook needs it, add a reference to it.
addRef. In order to do that, modify the (standard) VBAProject (of "PERSONAL.XLSB"), in, let us say, "VBPersProj", then (being in VBE) trough 'Tools - References...` tick its check box and press 'OK';
If the exported workbook is open in a new session and, even if it does not bother you any more because of the annoying dialogs and you need to identify that specific session, you can use the next function:
Function sameExSession(wbFullName As String, Optional boolClose As Boolean) As Boolean
Dim sessEx As Excel.Application, wb As Workbook
Set sessEx = GetObject(wbFullName).Application
If sessEx.hWnd = Application.hWnd Then
sameExSession = True
Else
sameExSession = False
If boolClose Then
sessEx.Workbooks(Right(wbFullName, Len(wbFullName) - InStrRev(wbFullName, "\"))).Close False
sessEx.Quit: Set sessEx = Nothing
End If
End If
End Function
It offers the possibility to close the workbook and the session itself if it is a different against the one where the code runs. It is an Optional parameter to be used, only if you need that. You can test it in the next way:
Sub testSameExSession()
Dim wbFullName As String
wbFullName = ThisWorkbook.fullName 'use here the full name of the SAP exported workbook
If sameExSession(wbFullName, True) Then
'process the workbook if this is what you need
End If
'if not open in the active session, the workbook could be closed and session quit, choosing the above way
'You can also simply call the function, without the closing parameter, as:
Debug.Print sameExSession(wbFullName)
End Sub
In case of False function retur, it can be closed and reopen in the existing session, if needed...
Edited:
If you need personal.xlsb being used/open when ribbon controls calls some of its Subs/functions, please adapt the code to call it in the next way:
Dim x as Long
x = Run("'C:\Users\your_UserName\AppData\Roaming\Microsoft\Excel\XLSTART\PERSONAL.XLSB'!GiveMeFive", 4, 3)
if calling a function (with parameters), which must return. Or
Run "'C:\Users\your_UserName\AppData\Roaming\Microsoft\Excel\XLSTART\PERSONAL.XLSB'!YourSubName"
when calling a Sub. In such a way, "Personal.xlsb" is opened if it is closed. And, in your specific case nothing will press the ribbon controls.
But, isn't the ribbon added when a specific workbook is open? Probably, the one you are using to make the above mentioned calls... If so, adding the suggested reference at "Personal.xlsb", will configure the new ribbon tab only when it is loaded. And your mentioned "problem" does not exist, in fact. Is it an add-in?
I'm making a macro in excel 2010 and my goal is that everyone can use this macro for their own work. Therefore I have to make it very flexible.
Everyone names their own workbook (I've solved this in the macro by using ThisWorkbook), but everyone also names their own extraction file (from where the new data comes).
However, almost no-one knows how to work with VBA so it's not possible to adjust this reference in the code every time.
Therefore, I've added a new sheet: 'Personalize'
In here, the person can add the name of the extraction file in a specific cell.
Unfortunately I don't know how to open a workbook that has the name of a specific cell in the current workbook.
I tried, for example, Windows("ThisWorkbook.Sheets("Personalize").Range("B3")").Activate but it didn't work.
The same thing with sheets that can be named differently, I don't know how to adjust them in the macro without using VBA in every case.
Does anyone have any ideas or suggestions?
Thank you so much in advance!
Kind Regards,
Hendrik
If you want to open a workbook that's closed you need the full path:
dim wkb as workbook
dim wbPath as String
wbPath = ThisWorkbook.Sheets("Personalize").Range("B3").value
Set wkb = Workbooks.Open(wbPath )
If you want to activate a workbook that's already opened, you don't need the full path but the workbook's name (such as "Workbook1.xlsx"). For this you can shave off the path before reaching "Workbook1.xlsx" (alternatively if the workbook is always going to be opened first, just have them enter the workbook's name directly and skip taking out the rest of the path):
dim wbname as String
wbname = ThisWorkbook.Sheets("Personalize").Range("B3").value
Do until Instr(wbname, "\") = 0
wbname = Mid(wbname, instr(wbname, "\") + 1) 'I didn't test this, just going off the top of my head
Loop
Workbooks(wbName).Activate
Be sure to handle your errors properly. For reference: VBA Excel simple Error Handling
Test what happens when you try to input a name that's incorrect, for example. You want to handle those errors with appropriate messageboxes or the users may feel like your program doesn't work.
I have a simple code that runs upon initialization of a userform that sets a few workbook and worksheet variables so they can be used easily throughout the rest of my modules, and the references can be easily changed in one place if the file moves. I recently migrated my workbooks from my desktop to a separate server/drive, and accordingly updated the file pathways; however, when I try to run the code now I immediately get the message:
"Run-time error '-2147352565 (8002000b)': Can't move focus to the control because it is invisible, not enabled, or of a type that does not accept the focus."
This error occurs on the line
Set ReportWkbk = Workbooks("N:\ rest of file pathway here\QuaRT_Template.xlsm")
Is there anything that can be happening on the server/drive that is causing this issue? If so, is there anything I can do to fix it? I do work with other excel workbooks saved in the same location that seem to have no issue being referenced, though their references are in the workbook itself, not through Visual Basic.
The Subscript Out of Range error occurs because the Excel workbook being referenced is not open (or opening) in the same instance of Excel. You can easily reproduce the error by creating two workbooks (name one WorkBook2.xlsx) and ensuring they open in separate instances of Excel. Then run this code:
Sub OpenWkbkNames()
Dim wbk As Workbook
For Each wbk In Workbooks
Debug.Print wbk.Name
Next
'Hmm. Workbook 2 is not listed, but lets activate it and see what happens.
Workbooks("workbook2.xlsx").Activate
End Sub
You could avoid the issue by looping through the names of the workbooks open in THIS instance of Excel to ensure its available.
I've been researching Run-time error '-2147352565 (8002000b)' and found this: https://msdn.microsoft.com/en-us/library/microsoft.visualstudio.vsconstants.disp_e_badindex.aspx
Apparently, that is VS's way of saying Subscript Out of Range too. I suspect (but have no solid proof) that since the code worked locally but the issue appeared after the file was migrated to a shared drive that Windows is taking a long time to open the file so opens it in a new instance of Excel. Essentially, Excel gets impatient and decides to move on while Windows completes its task. I base this assumption on the detection logic that was added to Excel 2013 (https://blogs.office.com/2013/06/03/opening-workbooks-by-running-separate-instances-of-excel/).
First: I'm using Excel 2007, but the code has to work for Excel 2003 as well.
My problem is the following: I need to access cells in a different workbook, which may be closed. The following code can be found all around the web:
Function Foo()
Dim cell As Range
Dim wbk As Workbook
Set wbk = Workbooks.Open("correct absolute path")
' wbk is Nothing here so the next statement fails.
Set cell = wbk.Worksheets("Sheet1").Range("A1")
Foo = cell.Value
wbk.Close
End Function
sadly, wbk is Nothing after the open statement (I'd love to give a better error message, but no idea how I'd do that; what I'd give for a real IDE and an useful language :/). The absolute path is correct and points to a valid excel xlsx file.
Also I assume the best way to do this, is to "cache" the workbook and not open/close it every time the function is called? Any possible problems with that (apart from having to handle the situation when the workbook is already open obviously)?
Image while stepping through:
I can reproduce this problem. It only happens to me when I attempt to paste this code into a user-defined function.
I believe this is by design (the quote is for XL 2003, but the same thing happens to me on XL 2010)
Using VBA keywords in custom functions
The number of VBA keywords you can use in custom functions is smaller than the number you can use in macros. Custom functions are not allowed to do anything other than return a value to a formula in a worksheet or to an expression used in another VBA macro or function. For example, custom functions cannot resize windows, edit a formula in a cell, or change the font, color, or pattern options for the text in a cell. If you include "action" code of this kind in a function procedure, the function returns the #VALUE! error.
http://office.microsoft.com/en-us/excel-help/creating-custom-functions-HA001111701.aspx
The only workaround I've found is to call this kind of code via a normal macro. Something like selecting the cells to apply it to, then looping over Selection or the like.
You can use this (similar to what Bruno Leite proposed, but much simpler to write):
Dim excelApp As New Excel.Application
excelApp.Visible = False
Set WB = excelApp.Workbooks.Open(FileName, xlUpdateLinksNever, True)
As UDFs are called repeatedly, you should make sure to do an excelApp.Quit before exiting the function (and a WB.close(False) before) to avoid having countless Excel instances running on your box.
I spent some thoughts on it and came to the conclusion that you cannot mess around with the workbooks of the current instance of excel while executing a UDF. On the other hand, opening a second instance of excel will do the job without interference.
To get data from Workbook without is open, you can use this, with ADO connection.
To use in Excel 2007 change this
Microsoft.Jet.OLEDB.4.0
to
Provider=Microsoft.ACE.OLEDB.12.0
and
Extended Properties=\"Excel 8.0;HDR=Yes;\
to
Extended Properties=\"Excel 12.0;HDR=Yes;\
[]'s
The workaround of putting my routine into a separate macro in the workbook module, and calling that macro from the Workbook_BeforeSave code, seems to have done the trick.
I've had a similar issue, but in my case it's a "Workbooks.Open(filename)" command at the start of a small routine embedded in Workbook_BeforeSave. VBA just skips right over the line of code as if it weren't there, it doesn't even report an Err.Code or Err.Description.
The only clue for me was that it's part of the Workbook_BeforeSave routine, and the limits with Functions above seem to indicate that could be a possible cause. So I dug around further to find more details.
It seems that Workbook_BeforeSave disables Excel from opening more files, and I guess there's a good reason for doing that, since the File > Open option is still visible in the File menu, but it can't be clicked. Strangely, the Open toolbar icon/button still works, and so whilst I can manually open the file from there, I wonder if it's because it's impossible to call this action from VBA code and that's why they allowed it?
You don't have to "Set" a cell, It's part of the workbook class (as far as I know). Just use the following...
foo = wbk.Worksheets("Sheet1").Range("A1").Value
I would suggest that you open you the new workbook upon opening the calling workbook, in the worbook_open event.
You then store the new workbook reference in a global variable.
Then the function called by your cell uses the said global variable instead of trying to open a new workbook. This way you go around the limitations.
PS : Of course global variable are to be avoided, some sort of container would be better than a direct global variable.
You can check the error in a proper way by using the following code:
filelocation = c:\whatever\file.xlsx
On Error GoTo Handler 'this is key as if the next row returns an error while opening the file it will jump to the Handler down there.
Set wkb2 = Workbooks.Open(filelocation, ReadOnly)
Handler:
MsgBox "File " & filelocation & " does not exist or cannot be reached, please review and try again"
I know that this does not answer the question (that's why I also landed in this thread, as I cannot open the file and can't understand why is that so)
Cheers,
RV