Excel workbook only shows after running code is finished - vba

I have a program in an Excel workbook that opens a Powerpoint-File, updates the links within this file and closes it after that. This works fine. Here is my problem: When the links are updated an Excel file with the source data is opened. After Powerpoint is closed this file stays open. I want it to get closed because I repeat this process for many files and I can't end up with hundreds of open Excel files.
I tried the following:
WBKs=Application.Workbooks.count
For i = WBKs to 1 Step -1
If Workbooks(i).Name<>ThisWorkbook.Name then
Workbooks(i).close savechanges:=False
End if
Next i
Now comes the weird part. Whenever I just run my code, WBKs always returns 1 and the Excel file only pops up after the code is finished. If I go through my code in debug mode it works. The workbook pops up as soon as I enter debug mode.
I tried Applicatio.Wait in the hope that the file would show after a second. The file only showed after the code was finished.
I tried a Do While Loop to wait until the file is open. Excel crashes because I never leave the loop.
Edit: I tried DoEvents as suggested. Does not work either.

This is just a workaround, but try using a brute force after x times your macro has run. Store that x somewhere in workbook, save. And kill excel process (all instances, including self) :
Dim sKill As String
sKill = "TASKKILL /F /IM excel.exe"
Shell sKill, vbHide
Found here : VBA script to close every instance of Excel except itself
When running your macro next time, you will use that x as a starting point for next PPT file to update.

Related

Timer on Opening Workbook - VBA

I created a macro that automatically opens a "raw" workbook file, does some calculations/manipulations, then saves it in another location. However, sometimes the original "raw" data file has corrupted characters that causes excel to get stuck while opening the file (even if done manually) -- it will just keep the opening file dialog open, with a full status bar, but will never actually proceed to opening the file or present an error. So, when we run the macro, it runs forever, trying to open the file but it is unable to proceed because the file never opens.
I am trying to add a timer to the function so that "if rawdata.xlsx takes longer than 5 minutes to open, cancel and stop procedure."
So far, I'm trying the following (file names have been changed for placeholders):
Sub wbprocess()
Dim Start, MaxTime, OpenStatus
OpenStatus = False
Start = Timer
MaxTime = 300 '(seconds)
Do While Timer < Start + MaxTime Or OpenStatus = False
Dim wb As Workbook
Set wb = Workbooks.Open(input file here)
' calculations and rest of code here
wb.SaveAs Filename:=output file here, FileFormat:=xlOpenXMLWorkbook
wb.Close
OpenStatus = True
Loop
End Sub
But, it won't leave the while loop until the max time has been reached, even if the file takes less than the allotted 5 minutes to open. What would be the correct logic to make it leave the while loop, and continue the calculations once the file has opened?

A private subroutine in VBA is being activated by other workbooks -- Why?

I have a private subroutine in Workbook A that is running any time I open or close and save other unrelated workbooks. I'm trying to understand why that occurs so I can capture all potential errors that may occur.
The subroutine is an ActiveX ComboBox named TabProg that is supposed to run when the value is changed. I have currently added in a check to see if the active sheet trying to run the code is the "Program Loading" sheet to try and divert any potential errors. See snippet below.
Private Sub TabProg_Change()
MsgBox "Whomp!", vbOKOnly + vbExclamation
If ActiveSheet.Name <> "Program Loading" Then 'do nothing
Else
'Run desired actions on "Program Loading" sheet
End If
End Sub
Any known reasons why this is occurring or other ways to catch it would be helpful. Thanks!
Edit 1: I do not see this problem occur when I open other workbooks in new instances of Excel.
Edit 2: I have modified the code to include a message box whenever the code tries to run, now shown above. It is occurring every time I open or close and save any Excel file, including the file itself. The drop down list in the ActiveX ComboBox is a list of names that correspond to 10 sheets within Workbook A. If I delete the sheet that ComboBox is currently set to, the error will disappear. If I change the ComboBox to a different sheet, the error will reappear.
From what you've wrote in your question, I don't think that your problem can be replicated. I think that your Excel file got corrupted. I had an experience like this: had a file for experimenting with macros, one of the macros used Excel Speech object to say "This is a test file" on opening that particular file. The macro was (as all other macros from this file) not part of my PERSONAL workbook, it was assigned to ThisWorkbook in the bespoke file. At some point a funny thing happened: this "This is a test file" private subroutine got activated every time I opened any Excel file. I did not find any solutions, I just deleted the file where the subroutine was stored. This resolved the problem, but I have no explanation for this. I am afraid the same thing may apply to your file, but maybe other folks have a better idea... maybe it's something in the system registry??? I don't know. Can you manually copy elements / code from this file to a newly created file and just delete the original?

Excel VBA - Run Macro / Open another file / Run that files macro / Save & Close

This is what I'm trying to do.
Run macro in file x
File x macro opens file y
Then calls file y's macro
File y's macro runs
File y closes
Currently, I'm getting as far as opening file y from file x's macro, but the macro ends once file y is open and doesn't step through past opening.
I had a look and attempted using info from this thread Run excel macro on multiple files, but it didn't help.
Appreciate any help anyone can offer.
Here is the part of my code it gets to before ending. This opens file y, but ends the macro;
Workbooks.Open (MyPath & FileY & ".xls")
I want the macro to then do this;
ActiveWorkbook.Application.Run FileYsMacro
Thanks.
Don't use ActiveWorkbook. Use:
Workbooks("WorkbookName").Application.Run

VBA macro crashing Excel 2010

I have an Excel 2010 spreadsheet that I use to maintain tests for an automated test application written with Selenium WebDriver. The spreadsheet has 6 ActiveX buttons that Upload (to a database), Import (from another sheet), Clear All Sheets, Export (to another sheet), Merge (two sheets), and Restore (a test suite from the database). My problem seem to stem from the macro that runs when the Import button is selected
The macro takes two sheets, source and destination, clears the destination sheet and copies the cells and data from the source workbook to the corresponding sheet in the destination workbook. From there, it can either be uploaded to the database or run from the sheet by the testing app
A couple weeks ago when selecting the Import button Excel crashed. I looked on the web and found a solution that worked. It involved disabling all macros, shutting down trust documents and trusted locations, recompiling, and reopening the workbook. This worked for a couple weeks until the same thing happened again this past weekend and the fix did not work anymore.
Here is the code I have:
numSheets = wrkBook.Sheets.Count
appName = wrkSheet.Cells(2, 3).Value
bkPath = Application.GetOpenFilename("Excel Macro-Enabled Workbook (*.xlsm),*.xlsm")
If bkPath <> "False" Then
Call Clear_All(wrkBook, wrkSheet)
'get the number of sheets in the source workbook
destCnt = wrkBook.Sheets.Count
Set importBook = Workbooks.Open(bkPath)
For a = 1 To destCnt
thsShtName = wrkBook.Worksheets(a).Name
'activate the source workbook
importBook.Activate
'get the numer of sheets in the source workbook
srcCnt = importBook.Sheets.Count
'loop through each sheet checking title
For b = 1 To srcCnt
'get the name of the 'a' sheet
**srcSheetName = importBook.Worksheets(b).Name**
'do more stuff with the source sheet
Next b
Next a
End If
The code is crashing on the following line
srcSheetName = importBook.Worksheets(b).Name
Here are a few things that I have noticed. Once the importBook variable is set, I cannot expand the variable in a watch window even if the code has not reached the offending line. However, the macro will successfully get the srcCount (number of sheet on the source sheet). Once the macro gets to the offending line it crashes consistently with a message stating simply that Excel has encountered an error and needs to close . No information, Excel closes, and sometimes restarts to a blank workbook
OK...I figured this out myself after heeding Siddarth Rout's suggestion about a corrupt spreadsheet. I took the steps that I described above
So here's what I did.
I copied one of the other spreadsheets and cleared it. As soon as I ran the Import macro, it passed through the line where it was failing before, so a corruption seems to have been the issue. I then stepped through the original code and rewrote the Import macro to simplify the code in it. It now seems to be working just fine
Thanks for the suggestions!!

Cancel External Query in Excel VBA

I have created an Excel Spreadsheet which helps with data analysis from an Oracle database.
The user enters then clicks the "Refresh Query" button which generates a query for Oracle to execute. The query takes a minute or so to complete. Although the VBA code does not hang on ".Refresh", all Excel windows remain frozen until the query completes.
Sub refreshQuery_click()
Dim queryStr as String
' Validate parameters and generate query
' ** Code not included **
'
' Refresh Query
With ActiveWorkbook.Connections("Connection").OLEDBConnection
.CommandText = queryStr
.Refresh
End With
End Sub
Is there a way for the user to manually cancel the query (calling .CancelRefresh) while the Excel user-interface is frozen?
EDIT I don't know if the following is worth noting or regular behavior. While the query is executing, all open Excel windows (including the VBA Editor) become "Not Responding" in Task Manager. Neither pressing Esc nor Ctrl+Break will cancel the script. Also, calling DoEvents (either before or after .Refresh) does not change this behavior.
Here's a method that I know will work. However, there are some complications.
Here's how it's done:
Put the spreadsheet with the data in a separate workbook. This worksheet should execute the refresh query when it's opened and then close once the data is updated.
Create a batch file to call the "Data" Excel file.
Within a different workbook, create a procedure (macro) for the user to call. This procedure will call the batch file, which subsequently calls the Excel file. Since you are calling a batch file and not Excel directly, the Excel procedure will continue because the command shell is released so quickly and opens the other Excel file in a different thread. This allows you to continue working within the main Excel file.
Here are some complications:
I included a method to alert the user that the data has been udpated. There are timing issues where it's possible to try to check if the data has been update when the workbook is not accessible, which forces the user to try to update values. I included a method called my time which pauses the execution of the code so it only checks every so many seconds.
The updated worksheet will pop up in a new window, so the user will need to click on their original worksheet and keep working. You could learn to hide this if you're comfortable with Windows scripting (I haven't learned that yet).
Here are some files and code. Be sure to read the comments in the code for why some things are there.
FILE: C:\DataUpdate.xls
We'll make a workbook called "DataUpdate.xls" and put it in our C:\ folder. In cell A1 of Sheet1, we'll add our QueryTable which grabs external data.
Option Explicit
Sub UpdateTable()
Dim ws As Worksheet
Dim qt As QueryTable
Set ws = Worksheets("Sheet1")
Set qt = ws.Range("A1").QueryTable
qt.Refresh BackgroundQuery:=False
End Sub
Sub OnWorkbookOpen()
Dim wb As Workbook
Set wb = ActiveWorkbook
'I put this If statement in so I can change the file's
'name and then edit the file without code
'running. You may find a better way to do this.
If ActiveWorkbook.Name = "DataUpdate.xls" Then
UpdateTable
'I update a cell in a different sheet once the update is completed.
'I'll check this cell from the "user workbook" to see when the data's been updated.
Sheets("Sheet2").Range("A1").Value = "Update Table Completed " & Now()
wb.Save
Application.Quit
End If
End Sub
In the ThisWorkbook object in Excel, there's a procedure called Workbook_Open(). It should look like the following so it executes the update code when it is opened.
Private Sub Workbook_Open()
OnWorkbookOpen
End Sub
NOTE: I found a bug when this file closed if 1) you accessed the file from the command line or shell and 2) you have the Office Live Add-in installed. If you have the Office Live Add-in installed, it will throw an exception on exit.
FILE: C:\RunExcel.bat
Next, we're going to create a batch file that will open the Excel file we just made. The reason that call the Excel file from within the batch file and not directly from the other Excel file using Shell is because Shell will not continue until the other application closes (at least when using Excel.exe "c:\File.xls"). The batch file, however, runs its code and then immediately closes, thus allowing the original code that called it to continue. This is what will let your uses continue working in Excel.
All this file needs is:
cd "C:\Program Files\Microsoft Office\Office10\"
Excel.exe "C:\DataUpdate.xls"
If you're handy with Windows Scripting, you do fancy things like open the window in a hidden mode or pass a parameter of the file name or Excel location. I kept it simple with a batch file.
FILE: C:\UserWorkbook.xls
This is the file that the user will open to "do their work in." They'll call the code to update the other workbook from within this workbook and they'll still be able to work in this workbook while this one is updating.
You need a cell in this workbook where you'll check the "Update Table Completed" cell from the DataUpdate workbook. I chose cell G1 in Sheet1 for my example.
Add the following code to a VBA module in this workbook:
Option Explicit
Sub UpdateOtherWorkbook()
Dim strFilePath As String
Dim intOpenMode As Integer
Dim strCallPath As String
Dim strCellValue As String
Dim strCellFormula As String
Dim ws As Worksheet
Dim rng As Range
Set ws = Worksheets("Sheet1")
Set rng = ws.Range("G1")
strCellFormula = "='C:\[DataUpdate.xls]Sheet2'!A1"
'This makes sure the formula has the most recent "Updated" value
'from the data file.
rng.Formula = strCellFormula
strFilePath = "C:\RunExcel.bat"
intOpenMode = vbHide
'This will call the batch file that calls the Excel file.
'Since the batch file executes it's code and then closes,
'the Excel file will be able to keep running.
Shell strFilePath, intOpenMode
'This method, defined below, will alert the user with a
'message box once the update is complete. We know that
'the update is complete because the "Updated" value will
'have changed in the Data workbook.
AlertWhenChanged
End Sub
'
Sub AlertWhenChanged()
Dim strCellValue As String
Dim strUpdatedCellValue As String
Dim strCellFormula As String
Dim ws As Worksheet
Dim rng As Range
Set ws = Worksheets("Sheet1")
Set rng = ws.Range("G1")
strCellFormula = "='C:\[DataUpdate.xls]Sheet2'!A1"
strCellValue = rng.Value
strUpdatedCellValue = strCellValue
'This will check every 4 seconds to see if the Update value of the
'Data workbook has been changed. MyWait is included to make sure
'we don't try to access the Data file while it is inaccessible.
'During this entire process, the user is still able to work.
Do While strCellValue = strUpdatedCellValue
MyWait 2
rng.Formula = strCellFormula
MyWait 2
strUpdatedCellValue = rng.Value
DoEvents
Loop
MsgBox "Data Has Been Updated!"
End Sub
'
Sub MyWait(lngSeconds As Long)
Dim dtmNewTime As Date
dtmNewTime = DateAdd("s", lngSeconds, Now)
Do While Now < dtmNewTime
DoEvents
Loop
End Sub
As you can see, I constantly updated the formula in the "Listening Cell" to see when the other cell was updated. Once the data workbook has been updated, I'm not sure how you'd force an update in code without rewriting all the cells. Closing the workbook and reopening it should refresh the values, but I'm not sure of the best way to do it in code.
This whole process works because you're using a batch file to call Excel into a different thread from the original file. This allows you to work in the original file and still be alerted when the other file has been updated.
Good luck!
EDIT: Rather than include a more complete answer in this same answer, I've created a separate answer dedicated entirely to that solution. Check it out below (or above if it gets voted up)
Your users can break the VBA function by pressing Ctrl+Break on the keyboard. However, I've found that this can cause your functions to randomly break until each time any function is run. It goes away when the computer is restarted.
If you open this file in a new instance of Excel (meaning, go to Start > Programs and open Excel from there), I think that the only workbook that will be frozen will be the one executing the code. Other intances of Excel shouldn't be affected.
Lastly, you might research the DoEvents functions, which yields execution back to the Operating System so that it can process other events. I'm not sure if it would work in your case, but you could look into it. That way you can do other things while the process is being completed (It's kind of dangerous because the user can then change the state of your application while the process is working).
I believe I know a way that actually will work, but it's complicated and I don't have the code in front of me. It involves creating a separate instance of the Excel application in code and attaching a handler to the execution of that instance. You include the DoEvents part of the code in a loop that releases once the application closes. The other instantiated Excel application has the sole purpose of opening a file to execute a script and then close itself. I've done something like this before so I know that it works. I'll see if I can find the code tomorrow and add it.
Well, you could consider the old-fashion way -- split the query into smaller batches and use Do Events in between batches.
You could try XLLoop. This lets you write excel functions (UDfs) on an external server. It includes server implementations in many languages (eg. Java, Ruby, Python, PHP).
You could then connect to your oracle database (and potentially add a caching layer) and serve up the data to your spreadsheet that way.
The XLL also has a feature to popup a "busy" GUI that lets the user cancel the function call (which disconnects from the server).
BTW, I work on the project so let me know if you have any questions.