Alex said:
FYI:
It won't work. It will ignore the dialog box and move on. I guess
it is because it's not the same thread.
If you moved it into Word, yes, of course it's the same thread! That's the whole
point of doing it there. But you also need to set the parameters for
SetWindowsHookEx back to how they were when we started. I need to you to *think*
along with me here, okay? <g>
This (kinda) works:
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 GetCurrentThreadId Lib "kernel32" () As Long
Private Declare Function UnhookWindowsHookEx Lib "user32" (ByVal hHook As Long)
As Long
Private Declare Function PostMessage Lib "user32" Alias "PostMessageA" (ByVal
hWnd As Long, ByVal wMsg As Long, ByVal wParam As Long, ByVal lParam As Long) 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 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 Const WH_CBT = 5
Private Const WM_CLOSE = &H10
Private Const HCBT_ACTIVATE = 5
Private m_hHook As Long
Public Sub AvoidCorruptDoc()
' Set the CBT hook. (Parameters set to be used in-process, within VBA!)
m_hHook = SetWindowsHookEx(WH_CBT, AddressOf CloseCorrupt, 0&,
GetCurrentThreadId())
' Take step(s) here to Open document
CreateDocument
' Hook no longer needed.
If m_hHook Then Call UnhookWindowsHookEx(m_hHook)
End Sub
Private Function CloseCorrupt(ByVal nCode As Long, ByVal wParam As Long, ByVal
lParam As Long) As Long
' We only want to react to activations.
If nCode = HCBT_ACTIVATE Then
' Handle to be activated window is in wParam.
' IMPORTANT: Need to test for specific dialog!
Debug.Print Hex$(wParam), """"; Classname(wParam); """", """";
WindowText(wParam); """"
If IsFileSaveDialog(wParam) Then
' Press the [X] button!
Call SendMessage(wParam, WM_CLOSE, 0&, ByVal 0&)
' Hook no longer needed.
Call UnhookWindowsHookEx(m_hHook)
m_hHook = 0
End If
End If
End Function
Private Function IsFileSaveDialog(ByVal hWnd As Long) As Boolean
If Classname(hWnd) = "#32770" Then
If WindowText(hWnd) = "Microsoft Office Word" Then
' Pretty good odds this is the File-Save dialog.
' Could also test button text?
IsFileSaveDialog = True
End If
End If
End Function
Private Function Classname(ByVal hWnd As Long) As String
Dim nRet As Long
Dim Class As String
Const MaxLen As Long = 256
' Retrieve classname of passed window.
Class = String$(MaxLen, 0)
nRet = GetClassname(hWnd, Class, MaxLen)
If nRet Then Classname = Left$(Class, nRet)
End Function
Private Function WindowText(ByVal hWnd As Long) As String
Dim nRet As Long
Dim Buffer As String
Const MaxLen As Long = 256
' Retrieve caption of passed window.
Buffer = String$(MaxLen, 0)
nRet = GetWindowText(hWnd, Buffer, MaxLen)
If nRet > 0 Then
WindowText = Left$(Buffer, nRet)
End If
End Function
Public Sub CreateDocument()
Documents.Open "c:\temp\test.doc"
Selection.Text = "test"
Documents.Close
End Sub
I say "kinda" because (again!) I don't have a corrupt document to test against, and
my knowledge of the Word object model is severely constrained. The Documents.Close
call is failing when I send it the WM_CLOSE message. Yes, *that* part is working
just fine!
I think if you figure out the proper parameters for the dialog test, you'll be in
business. But as I said, I can't do all the thinking for ya here. You gotta become
a real participant in the process!