run macro when worksheet is deleted

NMeeker

New Member
Joined
Feb 10, 2009
Messages
31
I have a macro i would like to run whenever a user deletes a worksheet...

I would like the user to be able click as normal to delete,(ie. right clicking the tab and selecting delete worksheet, or selecting delete worksheet from the menu) but would like to run my macro when they select delete.

Any suggestions?

I am using excel 07 if that helps, or i could use another version...
My macro will save a copy of the deleted worksheet as xlveryhidden as a backup..
 
FYI, you can simplify the pre-2007 code using FindControls and simply overriding the built-in ones - something like:
Code:
Sub hookdelete()
   Dim ctl As CommandBarControl
   For Each ctl In Application.CommandBars.FindControls(ID:=847)
      ctl.OnAction = "MySheetDelete"
   Next ctl
End Sub
Sub Unhookdelete()
   Dim ctl As CommandBarControl
   For Each ctl In Application.CommandBars.FindControls(ID:=847)
      ctl.OnAction = ""
   Next ctl
End Sub
Sub MySheetDelete()
   MsgBox "deleting sheet"
End Sub
 
Upvote 0

Excel Facts

Repeat Last Command
Pressing F4 adds dollar signs when editing a formula. When not editing, F4 repeats last command.
...There are certainly those more knowledgeable than me here...

Well... least I got that part right.

Greetings Rory and thank you very much :-)

I (obviously) did not know one could override the button's 'native' actions. Thanks for being so gracious, as "simplify..." is certainly understated.

@NMeeker:

Well, sorry 'bout that, simply something that I did not know in the least. Rory's method is leaps-and-bounds better.

(Where is that dang blushing icon?)

Mark
 
Upvote 0
Just for the sake of completeness, preventing the deletion of workheets in unprotected workbooks can in fact be achieved via a system wide hook. I remember doing something similar a while ago. Having said that, setting a hook comes at the price of slowing the system and any unhandled errors can potentially crash XL.

I'll post back with an example if time allows.

Regards.
 
Upvote 0
Can anybody using an English version of XL post the exact warning message that comes up before deleting a worksheet ?

I will need that as part of the code but I am using a French version of XL that's why I can't get the warning message in English.

Reagards.
 
Upvote 0
For 2000:

TitleBar:
Microsoft Excel

Text:
The selected sheet(s) will be permanently deleted.

(bullet)To delete the selected sheets, click OK.
(bullet)To cancel the deletion, click Cancel.

Bttns:
OK and Cancel
 
Upvote 0
Thanks for responding Mark. I hope it's the same message for all different XL versions.

Regards.
 
Upvote 0
Well, this is more for the sake of learning & streching the bounderies of XL than anything.

Workbook demo : http://www.savefile.com/files/2032567

Anyways, after much debugging,:banghead: i got this code to work for my version of XL in French. I hope i have made the necessary changes so that it works for English versions too.

Basically, the code prevents the deletion of worksheets in unprotected workbooks. I have designed the code in a way that emulates XL native events by placing the event handler in the "ThisWorkbook" Module and by passing a Cancel argument ByRef.

The core ugly code that does the job is encapsulated in a seperate standard module so the user only has to deal with and add code as required to the event handler located in the "ThisWorkbook" module just like with native events. Much more flexibility is gained that way and this can be used not just to prevent the deletion but to fire any code you like.

Drawbacks I have found so far:

*Code uses a system hook meaning potential crashes in case of unhandled errors.

*Using the RaiseEvent VB command would have been a cleaner & better approach but for some obscure reason it doesn't fire inside CallBack procedures !

*The code doesn't work for selected worksheets. Scenario dealt with but not in the best way.

*The code retrieves the text displayed in the worksheet deletion warning window. This makes it Language relevant.


In a Standard Module :

Code:
Option Explicit
 
 
Private Declare Function SetWindowsHookEx Lib _
"user32" Alias "SetWindowsHookExA" _
(ByVal idHook As Long, ByVal lpfn As Long, ByVal hmod As Long, _
ByVal dwThreadId As Long) As Long
 
Private Declare Function UnhookWindowsHookEx _
Lib "user32" (ByVal hHook As Long) As Long
 
Private Declare Function CallNextHookEx Lib "user32" _
(ByVal hHook As Long, ByVal ncode As Long, _
ByVal wParam As Long, lParam As Any) As Long
 
Private Const WH_CBT As Long = 5
Private Const HCBT_ACTIVATE = 5
Private lhHook As Long
Private bHookEnabled As Boolean
 
Private Declare Function GetClassName _
Lib "user32" Alias "GetClassNameA" _
(ByVal hwnd As Long, ByVal lpClassName As String, _
ByVal nMaxCount As Long) As Long
 
 
Private Declare Function GetWindowText Lib "user32" Alias "GetWindowTextA" _
(ByVal hwnd As Long, ByVal lpString As String, ByVal cch As Long) As Long
 
Private Declare Function GetCurrentThreadId Lib "kernel32" () As Long
 
Private Declare Function FindWindowEx Lib "user32" Alias "FindWindowExA" _
(ByVal hWnd1 As Long, ByVal hWnd2 As Long, ByVal lpsz1 As String, _
ByVal lpsz2 As String) As Long
 
 
Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" _
(ByVal hwnd As Long, ByVal wMsg As Long, _
ByVal wParam As Long, lParam As Any) As Long
 
Const BM_CLICK = &HF5
 
'French version
'Const WARNING_MESSAGE1 = _
'"Les feuilles sélectionnées peuvent contenir des données."
'English Version
Const WARNING_MESSAGE2 = _
"The selected sheet(s)will be permanently deleted."
 
Sub StartEvent()
 
    'install a cbt hook to monitor for the activation of a window
    If Not bHookEnabled Then
        lhHook = SetWindowsHookEx _
        (WH_CBT, AddressOf HookProc, 0, GetCurrentThreadId)
        bHookEnabled = True
    Else
        MsgBox "The Event is already active.", vbInformation
    End If
 
End Sub
 
Sub TerminateEvent()
 
    'important to unhook when done!
    UnhookWindowsHookEx lhHook
    bHookEnabled = False
 
End Sub
 
Function HookProc _
(ByVal idHook As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
 
    Dim sBuffer1 As String
    Dim sBuffer2 As String
    Dim lRetVal As Long
    Dim lhwndText As Long
    Dim lhwndDelete As Long
    Dim lhwndCancel As Long
    Dim bCancel As Boolean
 
''    On Error Resume Next
    'check if a window has been activated.
    If idHook = HCBT_ACTIVATE Then
        'if so,get it's class name.
        sBuffer1 = Space(256)
        lRetVal = GetClassName(wParam, sBuffer1, 256)
        'if it is a #32770 window that is being activated
        'retrieve its text to ensure it's the sh deletion warning window.
        If Left(sBuffer1, lRetVal) = "#32770" Then
            lhwndText = FindWindowEx(wParam, ByVal 0&, "MSOUNISTAT", vbNullString)
            sBuffer2 = Space(256)
            GetWindowText lhwndText, sBuffer2, 256
            'if it is, get the "Cancel" button hwnd and
            'send a BM_CLICK to it before the window gets a chance to appear.
            If InStr(1, Left(sBuffer2, Len(sBuffer2) - 1), _
                WARNING_MESSAGE2, vbTextCompare) Then
                lhwndDelete = FindWindowEx(wParam, 0, "BUTTON", vbNullString)
                lhwndCancel = FindWindowEx(wParam, lhwndDelete, "BUTTON", vbNullString)
                'call our event and return the "bCancel" argument ByRef
                Call thisWorkbook.ThisWorkbook_SheetBeforeDelete _
                (ActiveSheet, bCancel)
                'if the "bCancel" argument is True- ie: the user set
                'the "Cancel" argument in the Event handler to True
                'then abort the activation of the warning window.
                If VariousSheetsSelected Then _
                MsgBox "You may only delete worksheets one at a time.", vbInformation
                If bCancel Or VariousSheetsSelected Then
                       HookProc = 1
                       Call TerminateEvent
                       SendMessage lhwndCancel, BM_CLICK, 0, 0
                       Call StartEvent
                End If
            End If
        End If
    End If
Xit:
    'Call next hook
    HookProc = CallNextHookEx(lhHook, idHook, ByVal wParam, ByVal lParam)
 
End Function
 
Private Function VariousSheetsSelected() As Boolean
 
    If ActiveWindow.SelectedSheets.Count > 1 _
    Then VariousSheetsSelected = True
 
End Function


In the ThisWorkbook Module :

- This will prevent from deleting Sheet1- Change as required.
Code:
Option Explicit
 
Public Sub ThisWorkbook_SheetBeforeDelete(ByVal Sh As Worksheet, ByRef Cancel As Boolean)
 
    If Sh Is Sheet1 Then
 
        Cancel = True
 
        MsgBox "The worksheet: """ & Sh.Name & """" & "  is protected." _
        & vbCrLf & "You may not delete it.", vbCritical
 
    End If
 
End Sub

Hoping to get feedback from users of XL (English version ).

Regards.
 
Upvote 0
I've just run the code in a version of XL 2003 in English on another computer and found a couple of discrepancies which i have amended.

Here is an update of the workbook demo which will now hopefully work: http://www.savefile.com/files/2032813

Regards.
 
Upvote 0
Thank you Jaafar :-)

What I know reference API could be written on a matchbook cover; so this is very nice to study.

Only tested a bit so far, but seems to work nicely!

Mark
 
Upvote 0
Thank you Jaafar :-)

What I know reference API could be written on a matchbook cover; so this is very nice to study.

Only tested a bit so far, but seems to work nicely!

Mark


Thanks for testing the code Mark.

Glad it worked in XL2000. Ideally, one wouldn't use a hook as it renders XL a bit shaky at best but i can't think of another workaround to fire code before the deletion of a worksheet.

Anyways, here is the code for future reference (for when the test link expires)

In a Standard Module :

Code:
Option Explicit
 
 
Private Declare Function SetWindowsHookEx Lib _
"user32" Alias "SetWindowsHookExA" _
(ByVal idHook As Long, ByVal lpfn As Long, ByVal hmod As Long, _
ByVal dwThreadId As Long) As Long
 
Private Declare Function UnhookWindowsHookEx _
Lib "user32" (ByVal hHook As Long) As Long
 
Private Declare Function CallNextHookEx Lib "user32" _
(ByVal hHook As Long, ByVal ncode As Long, _
ByVal wParam As Long, lParam As Any) As Long
 
Private Const WH_CBT As Long = 5
Private Const HCBT_ACTIVATE = 5
Private lhHook As Long
Private bHookEnabled As Boolean
 
Private Declare Function GetClassName _
Lib "user32" Alias "GetClassNameA" _
(ByVal hwnd As Long, ByVal lpClassName As String, _
ByVal nMaxCount As Long) As Long
 
 
Private Declare Function GetWindowText Lib "user32" Alias "GetWindowTextA" _
(ByVal hwnd As Long, ByVal lpString As String, ByVal cch As Long) As Long
 
Private Declare Function GetCurrentThreadId Lib "kernel32" () As Long
 
Private Declare Function FindWindowEx Lib "user32" Alias "FindWindowExA" _
(ByVal hWnd1 As Long, ByVal hWnd2 As Long, ByVal lpsz1 As String, _
ByVal lpsz2 As String) As Long
 
 
Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" _
(ByVal hwnd As Long, ByVal wMsg As Long, _
ByVal wParam As Long, lParam As Any) As Long
 
Private Const BM_CLICK = &HF5
'French version
'Const WARNING_MESSAGE1 = _
'"Les feuilles sélectionnées peuvent contenir des données."
 
'English Version
Private Const WARNING_MESSAGE2 = "sheet(s)"
 
Sub StartEvent()
 
    'install a cbt hook to monitor for the activation of a window
    If Not bHookEnabled Then
        lhHook = SetWindowsHookEx _
        (WH_CBT, AddressOf HookProc, 0, GetCurrentThreadId)
        bHookEnabled = True
    Else
        MsgBox "The Event is already active.", vbInformation
    End If
 
End Sub
 
Sub TerminateEvent()
 
    'important to unhook when done!
    UnhookWindowsHookEx lhHook
    bHookEnabled = False
 
End Sub
 
Function HookProc _
(ByVal idHook As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
 
    Dim sBuffer1 As String
    Dim sBuffer2 As String
    Dim lRetVal As Long
    Dim lhwndText As Long
    Dim lhwndDelete As Long
    Dim lhwndCancel As Long
    Dim bCancel As Boolean
 
  On Error Resume Next
    'check if a window has been activated.
    If idHook = HCBT_ACTIVATE Then
        'if so,get it's class name.
        sBuffer1 = Space(256)
        lRetVal = GetClassName(wParam, sBuffer1, 256)
        'if it is a #32770 window that is being activated
        'retrieve its text to ensure it's the sh deletion warning window.
        If Left(sBuffer1, lRetVal) = "#32770" Then
            lhwndText = FindWindowEx(wParam, ByVal 0&, "MSOUNISTAT", vbNullString)
            sBuffer2 = Space(256)
            GetWindowText lhwndText, sBuffer2, 256
            'if it is, get the "Cancel" button hwnd and
            'send a BM_CLICK to it before the window gets a chance to appear.
            If InStr(1, Left(sBuffer2, Len(sBuffer2) - 1), _
                WARNING_MESSAGE2, vbTextCompare) Then
                lhwndDelete = FindWindowEx(wParam, 0, "BUTTON", vbNullString)
                lhwndCancel = FindWindowEx(wParam, lhwndDelete, "BUTTON", vbNullString)
                'call our event and return the "bCancel" argument ByRef
                Call ThisWorkbook.ThisWorkbook_SheetBeforeDelete _
                (ActiveSheet, bCancel)
                'if the "bCancel" argument is True- ie: the user set
                'the "Cancel" argument in the Event handler to True
                'then abort the activation of the warning window.
                If VariousSheetsSelected Then _
                MsgBox "You may only delete worksheets one at a time.", vbInformation
                If bCancel Or VariousSheetsSelected Then
                       HookProc = 1
                       Call TerminateEvent
                       SendMessage lhwndCancel, BM_CLICK, 0, 0
                       Call StartEvent
                End If
            End If
        End If
    End If
Xit:
    'Call next hook
    HookProc = CallNextHookEx(lhHook, idHook, ByVal wParam, ByVal lParam)
 
End Function
 
Private Function VariousSheetsSelected() As Boolean
 
    If ActiveWindow.SelectedSheets.Count > 1 _
    Then VariousSheetsSelected = True
 
End Function


In the ThisWorkbook Module

Code:
Option Explicit
 
Public Sub ThisWorkbook_SheetBeforeDelete(ByVal Sh As Worksheet, ByRef Cancel As Boolean)
 
    'prevent worksheet "sheet1" from deletion
    If Sh Is Sheets("sheet1") Then
        Cancel = True
        MsgBox "The worksheet: """ & Sh.Name & """" & "  is protected." _
        & vbCrLf & "You may not delete it.", vbCritical
    End If
 
End Sub

Regards.
 
Upvote 0

Forum statistics

Threads
1,224,890
Messages
6,181,612
Members
453,057
Latest member
LE102024

We've detected that you are using an adblocker.

We have a great community of people providing Excel help here, but the hosting costs are enormous. You can help keep this site running by allowing ads on MrExcel.com.
Allow Ads at MrExcel

Which adblocker are you using?

Disable AdBlock

Follow these easy steps to disable AdBlock

1)Click on the icon in the browser’s toolbar.
2)Click on the icon in the browser’s toolbar.
2)Click on the "Pause on this site" option.
Go back

Disable AdBlock Plus

Follow these easy steps to disable AdBlock Plus

1)Click on the icon in the browser’s toolbar.
2)Click on the toggle to disable it for "mrexcel.com".
Go back

Disable uBlock Origin

Follow these easy steps to disable uBlock Origin

1)Click on the icon in the browser’s toolbar.
2)Click on the "Power" button.
3)Click on the "Refresh" button.
Go back

Disable uBlock

Follow these easy steps to disable uBlock

1)Click on the icon in the browser’s toolbar.
2)Click on the "Power" button.
3)Click on the "Refresh" button.
Go back
Back
Top