Handle the event "Excel is closing" - vb.net

I have a vb .net app which opens an Excel file and puts values in it. But if the user (who I have to assume is extremely dumb :) ), closes the workbook, next time my app will try to put a value to the file, it will show an error since there is no excel file open.
How can I either prevent excel from being closed, or disable the buttons when excel is closed?
I'm working with Excel 2003 on Windows 7
EDIT: Here is the code to open Excel
Private Sub Open_button_Click(sender As Object, e As EventArgs) Handles Open_button.Click
OpenFileDialog1.FileName = ""
OpenFileDialog1.Filter = "Excel files (*.xls)|*.xls"
OpenFileDialog1.ShowDialog()
filePath = OpenFileDialog1.FileName
If System.IO.File.Exists(filePath) Then
oExcel = CreateObject("Excel.Application")
oExcel.Visible = True
oBook = oExcel.Workbooks.Open(filePath)
oSheet = oBook.WorkSheets(1)
oExcel.Sheets(1).Select()
oExcel.ScreenUpdating = True
xlDown = Microsoft.Office.Interop.Excel.XlDirection.xlDown
xlUp = Microsoft.Office.Interop.Excel.XlDirection.xlUp
Me.Activate()
End If
End Sub

You can add event handler to detect the close event and disable the button in the handler.
From https://support.microsoft.com/en-us/kb/822750
Private EventDel_BeforeBookClose As Excel.AppEvents_WorkbookBeforeCloseEventHandler
Then after you have created the Excel objects
EventDel_BeforeBookClose = New Excel.AppEvents_WorkbookBeforeCloseEventHandler( _
AddressOf BeforeBookClose)
AddHandler xlApp.WorkbookBeforeClose, EventDel_BeforeBookClose
And add a sub to handle the event
Private Sub BeforeBookClose(ByVal Wb As Excel.Workbook, ByRef Cancel As Boolean)

Related

How to use DocumentBeforeClose event on Application.word in VB.Net

I open Word application in VB.Net by below code:
Dim appWord As New Microsoft.Office.Interop.Word.Application
appWord.Documents.Open("path")
appWord.Visible = True
I wanna subscribe the closing event of msword and run something before closing. I read this question and this article but I really don't know how to use in VB.Net
Add a reference (Project--References...) to Microsoft Word XX.0 Object Library.
XX depends on your version of MS Office i.e. 16.0 if your MS Office is 2016.
Add a command button named Command1 to form.
Add this code to form:
Option Explicit
Public WithEvents moWord As Word.Application
Private Sub Command1_Click()
' open test document
With moWord
.Documents.Open "J:\Test.docx" ' change document path according to actual file
'.WindowState = wdWindowStateNormal
.Visible = True
End With
End Sub
Private Sub Form_Load()
Set moWord = New Word.Application
End Sub
Private Sub Form_QueryUnload(Cancel As Integer, UnloadMode As Integer)
If Not (moWord Is Nothing) Then Set moWord = Nothing
End Sub
Private Sub moWord_DocumentBeforeClose(ByVal Doc As Word.Document, Cancel As Boolean) Handles moWord.DocumentBeforeClose
If MsgBox("Do you really want to close the document?", vbQuestion + vbYesNo + vbDefaultButton2) = vbNo Then Cancel = True
End Sub

Disappearing Toggle Button macro

I have simple Excel macro, for ToggleButton:
Private Sub ToggleButton1_Click()
Dim xAddress As String
xAddress = "L:R"
If ToggleButton1.Value Then
Application.ActiveSheet.Columns(xAddress).Hidden = True
Else
Application.ActiveSheet.Columns(xAddress).Hidden = False
End If
End Sub
When I upload this to OneDrive and download, macro disappear.
I have just ToggleButton definition, so it doesn't work.
How I can store this macro permanently?
Save your file as ".xlsm" or ".xlsb" file, and everything should be fine.

Preventing Excel prompt from Word VBA

I'm working with VBA on Word and Excel. I have the Word running a userform which will automatically open an Excel file. User should fill some data in Excel file and then go back to the Word userform. When user finish filling all fields in Word userform, it will run some VBA code on Word that copy data from Excel to Word. After finished, the Excel file will be closed automatically. Therefore, I need to prevent user from closing the Excel app manually.
In order to do that, I use these code in Excel VBA in Sub Workbook_BeforeClose. If user close the Excel application window, it will show a message box that ask whether the user is still working with the Word userform. The code as follows:
Private Sub Workbook_BeforeClose(Cancel As Boolean)
answer = MsgBox("Are you still working with Word userform?", vbYesNo)
If answer = vbYes Then
Cancel = True
MsgBox "This workbook should not be closed. It will be automatically closed when you finish working with Ms. Word Template Userform."
Else
Application.ThisWorkbook.Saved = True
End If
End Sub
In the Word VBA, I have code to close the Excel file:
Sub closeExcelApp()
If Not excelApp Is Nothing Then
excelApp.DisplayAlerts = False
excelWb.Close savechanges:=False
Set excelWb = Nothing
excelApp.Quit
End If
Set excelApp = Nothing
End Sub
This Sub will be called when the Word VBA code done copying data from Excel to Word. However, calling this Sub will cause the Workbook_BeforeClose called. Meanwhile, I don't want the Workbook_BeforeClose called when I call this closeExcelApp sub from Word VBA.
Any suggestion?
You can just disable events:
Sub closeExcelApp()
If Not excelApp Is Nothing Then
excelApp.DisplayAlerts = False
excelApp.EnableEvents = False
excelWb.Close savechanges:=False
Set excelWb = Nothing
excelApp.Quit
End If
Set excelApp = Nothing
End Sub
As explained in comments, add this line on top of the modules : Public ClosingFromWord As Boolean and set this boolean to False when you start your code execution.
As you are working between apps, the easiest way will be to write the value of the boolean in a cell in Excel from Word and read/laod this value in the Workbook_BeforeClose to avoid going through the whole code of that routine.
And modify you code to look like this :
Sub closeExcelApp()
ClosingFromWord = True
excelApp.Workbooks(1).Sheets(1).Cells(1,1) = ClosingFromWord
If Not excelApp Is Nothing Then
excelApp.DisplayAlerts = False
excelWb.Close savechanges:=False
Set excelWb = Nothing
excelApp.Quit
End If
Set excelApp = Nothing
ClosingFromWord = False
excelApp.Workbooks(1).Sheets(1).Cells(1,1) = ClosingFromWord
End Sub
So while you execute closeExcelApp, the boolean will be set to True and the Workbook_BeforeClose won't be executed in his totality as it'll be exited with If ClosingFromWord Then Exit Sub :
Private Sub Workbook_BeforeClose(Cancel As Boolean)
ClosingFromWord = Workbooks(1).Sheets(1).Cells(1,1)
If ClosingFromWord Then Exit Sub
answer = MsgBox("Are you still working with Word userform?", vbYesNo)
If answer = vbYes Then
Cancel = True
MsgBox "This workbook should not be closed. It will be automatically closed when you finish working with Ms. Word Template Userform."
Else
Application.ThisWorkbook.Saved = True
End If
End Sub

Code in the workbook module still runs after the file is saved as xlsx

Using a button in VB.NET form, an .xlsm file is opened and saved as .xlsx. The code in the .xlsx file (Workbook_BeforeClose event) is NOT deleted after the file is saved, therefore, when I want to close the file the code runs! After reopening the file there is no code left.
This is my VB.NET class:
Imports Excel = Microsoft.Office.Interop.Excel
Public Class Form1
Dim xlApp As Excel.Application
Dim xlWorkBook As Excel.Workbook
Dim xlWorkbooks As Excel.Workbooks
Private Sub btnSave_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnSave.Click
xlApp = New Excel.Application
xlWorkbooks = xlApp.Workbooks
xlWorkBook = xlWorkbooks.Open("C:\Temp\testTemplate.xlsm")
xlApp.DisplayAlerts = False
xlWorkBook.SaveAs(Filename:="C:\Temp\testTemp.xlsx", FileFormat:=51) '51=xlOpenXMLWorkbook
xlApp.DisplayAlerts = True
xlApp.Visible = True
'Clean Up
releaseObject(xlWorkBook)
releaseObject(xlWorkbooks)
releaseObject(xlApp)
End Sub
Private Sub releaseObject(ByVal obj As Object)
Try
System.Runtime.InteropServices.Marshal.ReleaseComObject(obj)
obj = Nothing
Catch ex As Exception
obj = Nothing
Finally
GC.Collect()
GC.WaitForPendingFinalizers()
End Try
End Sub
End Class
This is in the Excel file, workbook module:
Private Sub Workbook_BeforeClose(Cancel As Boolean)
MsgBox "event still runs"
End Sub
How to save the file properly so that NO code would remain in it?
You're right, the code isn't deleted until after it's closed. I suppose you could set some type of flag in the xlsm's BeforeClose event that checks its file type and only runs if it's xlsm. Or you could do a Worksheets.Copy instead of the SaveAs and save the resulting workbook (which won't contain the VBA) as an xlsx, but there could be reference issues to clean up. Or you could set xlApp.EnableEvents=False, close the newly saved xlsx, set it back to True and re-open the xlsx.
Here's a post I wrote on the second option: http://yoursumbuddy.com/copy-an-xlsm-xlsx/

Excel - How to create button upon Workbook_Open event

I'm trying to make an Excel Add-In to create a simple button when any workbook is opened, but I'm getting
Object variable or With Block variable not set
I think this is happening because technically there is no 'ActiveWorkbook' yet.
First thing I want to do is delete any buttons currently on the sheet. Then I want to place a button.
Anyone know how to make that happen?
Code
Private Sub Workbook_Open()
ActiveWorkbook.ActiveSheet.Buttons.Delete
Dim CommandButton As Button
Set CommandButton = ActiveWorkbook.ActiveSheet.Buttons.Add(1200, 100, 200, 75)
With CommandButton
.OnAction = "Test_Press"
.Caption = "Press for Test"
.Name = "Test"
End With
End Sub
I then have a Private Sub Test_Press() to display a MsgBox. The button is not being created though.
Credit goes to http://www.jkp-ads.com/Articles/FixLinks2UDF.asp
Note: I have another module I didn't post below, which houses the macro Project_Count I tied to the button I place on the workbook only if the workbook name is TT_GO_ExceptionReport
I also have a VBScript that downloads the Add-In, places it in the users addin folder, and installs it. If you want to know how to do that, leave a comment.
Code of Add-In that solved the problem:
ThisWorkbook
Option Explicit
Private Sub Workbook_Open()
' Purpose : Code run at opening of workbook
'-------------------------------------------------------------------------
'Initialise the application
InitApp
modProcessWBOpen.TimesLooped = 0
Application.OnTime Now + TimeValue("00:00:03"), "CheckIfBookOpened"
End Sub
Module 1 named modInit
Option Explicit
'Create a module level object variable that will keep the instance of the
'event listener in memory (and hence alive)
Dim moAppEventHandler As cAppEvents
Sub InitApp()
'Create a new instance of cAppEvents class
Set moAppEventHandler = New cAppEvents
With moAppEventHandler
'Tell it to listen to Excel's events
Set .App = Application
End With
End Sub
Module 2 named modProcessWBOpen
Option Explicit
'Counter to keep track of how many workbooks are open
Dim mlBookCount As Long
'Counter to check how many times we've looped
Private mlTimesLooped As Long
' Purpose : When a new workbook is opened, this sub will be run.
' Called from: clsAppEvents.App_Workbook_Open and ThisWorkbook.Workbook_Open
'-------------------------------------------------------------------------
Sub ProcessNewBookOpened(oBk As Workbook)
If oBk Is Nothing Then Exit Sub
If oBk Is ThisWorkbook Then Exit Sub
If oBk.IsInplace Then Exit Sub
CountBooks
'This checks to make sure the name of the new book matches what I
'want to place the button on
If oBk.Name = "TT_GO_ExceptionReport.xlsm" Then
Dim CommandButton As Button
Set CommandButton = Workbooks("TT_GO_ExceptionReport.xlsm").Sheets(1).Buttons.Add(1200, 100, 200, 75)
With CommandButton
.OnAction = "Project_Count"
.Caption = "Press for Simplified Overview"
.Name = "Simplified Overview"
End With
End If
End Sub
Sub CountBooks()
mlBookCount = Workbooks.Count
End Sub
Function BookAdded() As Boolean
If mlBookCount <> Workbooks.Count Then
BookAdded = True
CountBooks
End If
End Function
' Purpose : Checks if a new workbook has been opened
' (repeatedly until activeworkbook is not nothing)
'-------------------------------------------------------------------------
Sub CheckIfBookOpened()
If BookAdded Then
If ActiveWorkbook Is Nothing Then
mlBookCount = 0
TimesLooped = TimesLooped + 1
'May be needed if Excel is opened from Internet explorer
Application.Visible = True
If TimesLooped < 20 Then
Application.OnTime Now + TimeValue("00:00:01"), "CheckIfBookOpened"
Else
TimesLooped = 0
End If
Else
ProcessNewBookOpened ActiveWorkbook
End If
End If
End Sub
Public Property Get TimesLooped() As Long
TimesLooped = mlTimesLooped
End Property
Public Property Let TimesLooped(ByVal lTimesLooped As Long)
mlTimesLooped = lTimesLooped
End Property
Class Module named cAppEvents
' Purpose : Handles Excel Application events
'-------------------------------------------------------------------------
Option Explicit
'This object variable will hold the object who's events we want to respond to
Public WithEvents App As Application
Private Sub App_WorkbookOpen(ByVal Wb As Workbook)
'Make sure newly opened book is valid
ProcessNewBookOpened Wb
End Sub
Private Sub Class_Terminate()
Set App = Nothing
End Sub
something like this?
Option Explicit
Sub Button()
Dim cButton As Button
Dim rng As Range
Dim i As Long
ActiveSheet.Buttons.Delete
For i = 2 To 3 Step 2
Set rng = ActiveSheet.Range(Cells(i, 2), Cells(i, 2))
Set cButton = ActiveSheet.Buttons.Add(rng.Left, rng.Top, rng.Width, rng.Height)
With cButton
.OnAction = "Test_Press"
.Caption = "Press for Test " & i
.Name = "Test" & i
End With
Next i
End Sub
See Example here