Solved

Disable Word Close Button Programmatically in VB.Net

Posted on 2009-07-07
16
2,353 Views
Last Modified: 2012-06-27
I need the program code to disable Word 2003 Close(X) Button from a VB.Net application.
0
Comment
Question by:CurtHugo
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 10
  • 6
16 Comments
 
LVL 20

Expert Comment

by:alainbryden
ID: 24794752
To do so, you would need to get a handle on the actual word process, you cannot simply do this using the COM interface.
http://support.microsoft.com/kb/258511

--
Alain
0
 
LVL 2

Author Comment

by:CurtHugo
ID: 24795190
I took the sample code in the ms link you provided and copied it into my application. It ran ok. The example referenced PowerPoint. I modified the code to get the handle for Word.

Now that I have the code working to get the handle of the Word Process I'm automating, what is the code to disable the Close button?

Code:
        Dim objWord As Microsoft.Office.Interop.Word.Application = Nothing
        objWord = CType(CreateObject("Word.Application"),  _
                        Microsoft.Office.Interop.Word.Application)
        objWord.Caption = "New Caption Supplied by Program"
        Dim hWndXl As Long
        hWndXl = FindWindow("XLMAIN", objWord.Caption)
        'objWord.Caption = Nothing 'Set the original caption back

        objWord.Visible = True
        MsgBox("hWndXl ( " & Hex(hWndXl) & " ) contains the Window Handle " & _
               "of the Word application created by this program." & vbCr & _
               "You can use this Window Handle in various Win 32 APIs, " & _
               "such as SetForeGroundWindow," & vbCr & _
               "which require a Window Handle parameter to be supplied." & vbCr _
               & vbCr & "All Done.  Click OK to close Word.", vbMsgBoxSetForeground)

        objWord.Quit()
        objWord = Nothing

0
 
LVL 20

Expert Comment

by:alainbryden
ID: 24796381
Another idea occurred to me. What if you didn't hack into the word handle to disable the button, but instead just handled the word Quit event. You could simply prevent allowing the user to quit of close the document.
Event      Object      Description
Close      Document      Occurs when a document is closed.
Quit      Application      Occurs when the user quits Word.

If you handle these two events you could simply set Cancel to true and prevent the user quitting from doing anything.

http://support.microsoft.com/default.aspx?scid=kb;EN-US;302816

--
Alain
Imports Microsoft.Office.Interop
Private WithEvents oWord As Word.ApplicationClass
 
Somewhere...
     oWord = CreateObject("Word.Application")
 
 
Private Sub wdapp_BeforeClose(ByVal Doc As Document, Cancel As Boolean) Handles oWord.ApplicationEvents3_Event_Quit
     Cancel = True
End Sub

Open in new window

0
Salesforce Has Never Been Easier

Improve and reinforce salesforce training & adoption using WalkMe's digital adoption platform. Start saving on costly employee training by creating fast intuitive Walk-Thrus for Salesforce. Claim your Free Account Now

 
LVL 2

Author Comment

by:CurtHugo
ID: 24796918
I'm not sure this would work in my case.
I have a custom macro the user is supposed to use instead of the X button to close the Word document.
Wouldn't the wdapp_BeforeClose event fire if they selected the custom Close macro and prevent the document from closing?
0
 
LVL 20

Expert Comment

by:alainbryden
ID: 24797388
All you have to do is detect the close mode. I don't remember the details, but you'll see when you handle the event, there is a integer that indicates whether the window is closing because the user clicked the button, because the system is shutting down, because code told it to close, etc. All you do is check the state, and if it's because the user clicked it, set cancel to true!

--
Alain
0
 
LVL 20

Expert Comment

by:alainbryden
ID: 24797442
I'm pretty sure I did this before with an excel window, let me see if I can find the exact code...

--
Alain
0
 
LVL 20

Expert Comment

by:alainbryden
ID: 24797482
I used this for before I workbook was closed:

--
Alain
Public Sub CloseBook(ByRef Cancel As Boolean) _
                Handles xlBook.BeforeClose
        If Not ExcernalCellEditedHandler Is Nothing Then
            If InvokeRequired Then
                Invoke(ExternalSaveFileHandler, New Object() {Cancel})
            Else
                ExternalSaveFileHandler(Cancel)
            End If
        Else
            Cancel = True
            MsgBox("Error: The application is not in a state where it can be saved.")
        End If
    End Sub

Open in new window

0
 
LVL 20

Expert Comment

by:alainbryden
ID: 24797551
I see, the way I did this with the excel window is that I stripped away all the elements of the main window from the handle and used SetParent to hijack the application and integrate the excel window into my own application. It was pretty neat actually. I'll attach the form. Maybe you could make a few adjustments and turn it into something that hijacks a word application. I'm sure it would be extremely similar. Just download the zip and remove .txt from the extensions.

--
Alain
ExcelHosterControl.zip
0
 
LVL 20

Expert Comment

by:alainbryden
ID: 24797625
It basically ended up being a whole bunch of special calls such as:
xlApp.ExecuteExcel4Macro("SHOW.TOOLBAR(""Ribbon"",False)") 'Hides all the menu items and the ribbon (fast)
Word probably has an equivalent.

Things that minimized the user's ability to mess around with the application.

You can remove the menu item "Quit" from word with code attached in the snippet. I also have simple code for getting the handle.

I'm sure you could reproduce most of these to configure Word to be unquittable.

--
Alain
'Get application handle'
Try
    'The application.Hwnd property does not exist on earlier versions of excel'
    appHWnd = xlApp.Hwnd
Catch ex As Exception
    'This method works for all versions of excel, but is less reliable because it may find'
    'a window that the user is currently using and not the one created by the application'
    appHWnd = CInt(FindWindow("XLMAIN", xlApp.Caption))
End Try
 
'Remove a menu item'
    Private Declare Function RemoveMenu Lib "user32" (ByVal hMenu As Integer, ByVal nPosition As Integer, ByVal wFlags As Integer) As Integer
    Private Const REMOVE_BYPOSITION As Integer = 5120
'(This code removes all removable menu items)'
Dim hMenu As Integer = GetSystemMenu(appHWnd, False)
        If (hMenu > 0) Then
            Dim menuItemCount As Integer = GetMenuItemCount(hMenu)
            For i As Integer = menuItemCount - 1 To 0 Step -1
                RemoveMenu(hMenu, i, REMOVE_BYPOSITION)
            Next
        End If

Open in new window

0
 
LVL 2

Author Comment

by:CurtHugo
ID: 24797627
It's getting close to quiting time for me so I'll get back to you tomorrow.
I'm thinking I'd prefer to disable the X button rather than do it this way. Either way, there's a bunch of hoops to make it work.
I will think about which way makes more sense to me.
Anyway, thanks for help. You're the only one who's responded to my question.
0
 
LVL 2

Author Comment

by:CurtHugo
ID: 24803814
After thinking about the situation, I'd prefer to go the route of disabling the close button.
I've done this before in VB6 Forms and I think it's a simpler approach then the other approach you offered.
Please let me know if you can provide me with the code to disable the close button. I already have the code to find the handle.
Thanks...
0
 
LVL 20

Expert Comment

by:alainbryden
ID: 24804219
Well, try this, although you might have to experiment with the exact structure and order of unknown menu items.

Good luck with it.

--
Alain
Private Declare Function GetSystemMenu Lib "user32" (ByVal hWnd As Long, _
    ByVal bRevert As Long) As Long
Private Declare Function EnableMenuItem Lib "user32" (ByVal hMenu As _
    Long, ByVal wIDEnableItem As Long, ByVal wEnable As Long) As Long
Const MF_GRAYED = &H1&
Const MF_BYCOMMAND = &H0&
Const SC_CLOSE = &HF060&
 
 
'Disable the Menu Option
    objWord.CommandBars("File").Controls("Exit").Enabled = blnExitState
 
'Disable the CLose Button Option
    Dim hWnd As Long
    Dim wFlags As Long
    Dim hMenu As Long
 
    hWnd = objWord.hWndAccessApp
    hMenu = GetSystemMenu(hWnd, 0)
    wFlags = MF_BYCOMMAND Or MF_GRAYED
    result = EnableMenuItem(hMenu, SC_CLOSE, wFlags)

Open in new window

0
 
LVL 2

Author Comment

by:CurtHugo
ID: 24804669
I get the syntax error message 'Option Strict On disallows late binding' on this line of code;
    hWnd = objWord.hWndAccessApp
In VB.Net 2008, which is the version of VB.Net I'm using, hWndAccessApp is not an option you can choose from and therefore the error. Our standard and what is recommended is to use Option Strict On in our applications.
Also for your information, in VB.Net 2008, I have found you must declare the variables in the API's as Integer instead of Long. If specified as long, the function does not work correctly.  Here's how I have it:

    ' These API's are used to disable the Close Button (X) in Word
    Private Declare Function GetSystemMenu Lib "user32" (ByVal hWnd As Integer, _
    ByVal bRevert As Integer) As Integer
    Private Declare Function EnableMenuItem Lib "user32" (ByVal hMenu As _
        Integer, ByVal wIDEnableItem As Integer, ByVal wEnable As Integer) As Integer
    Public Const MF_GRAYED As Integer = &H1&
    Public Const MF_BYCOMMAND As Integer = &H0&
    Public Const SC_CLOSE As Integer = &HF060&

Let me know if you have any suggestions around this syntax error.
Thanks, Curt
0
 
LVL 20

Expert Comment

by:alainbryden
ID: 24804916
hmm, in that case maybe use the hWnd that you already got for the word object using more complicated methods. I should have seen hWndAccessApp couldn't possibly be a property for a Word application :p. I was trying to adapt some similar code for an Access application as you can see. If you can't see any properties of objWord that would give you the handle in a straightforward way, go ahead and try the above code with the handle you got using FindWindow.

--
Alain
0
 
LVL 2

Accepted Solution

by:
CurtHugo earned 0 total points
ID: 24851521
Alain,
Sorry for the delay getting back to you. I was under the gun and once I had this solution working, I was already onto the next task. I am going to award you the 250 points because you provided most of what I needed however in multiple comments.

Below is the code I used to disable the Word Close Button for a Word Document. In one comment it has everything needed to accomplish this task.

The FindWindow function was the key. Once I was able to get the handle for the Word Document, it worked like a charm.

Unless there's another way to do it easier, why not copy the solution below into a new comment and submit it. I'll then accept that as the solution and you get the points.

Thanks for your help.  Curt Hugo.

PS I have done a lot of work in MS Access. My email is Hugoc@PPNPF.com if you would like to communicate with me via email.

' These objects are declared in the Declaration section
' They are used to disable the Close(X) Button in Word

Public Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String,  ByVal lpWindowName As String) As Integer

Private Declare Function GetSystemMenu Lib "user32" _
                        (ByVal hWnd As Integer, _
                         ByVal bRevert As Integer) As Integer

Private Declare Function EnableMenuItem Lib "user32" _
                        (ByVal hMenu As Integer, _
                         ByVal wIDEnableItem As Integer, _
                         ByVal wEnable As Integer) As Integer

    Public Const MF_GRAYED As Integer = &H1&
    Public Const MF_BYCOMMAND As Integer = &H0&
    Public Const SC_CLOSE As Integer = &HF060&

    Public m_inthWnd As Integer
    Public m_intwFlags As Integer
    Public m_inthMenu As Integer
    Public m_intwResult As Integer
.
.
.

 Public Sub OpenWordDoc
' Opens a Word document and disables the Close Button
     Dim objWord As Microsoft.Office.Interop.Word.Application
     Dim objDoc As Microsoft.Office.Interop.Word.Document = Nothing
       Dim strWordCaption As String
       Dim strMyWordDoc As String = "C:\MyDoc.doc"
 
      objWord = CType(CreateObject("Word.Application"),  _
                Microsoft.Office.Interop.Word.Application)
      objDoc = objWord.Documents.Open(strMyWordDoc, , ReadOnly:=objReadOnly)

       strWordCaption = objWord.ActiveDocument.Windows(1).Caption ' Example: MyDoc.doc

       strWordCaption += " - " & objWord.Caption    ' Example: MyDoc.doc - Microsoft Word

 ' Disable Word's Close(X) Button to force user to close document with NLW's custom Exit buttons
            m_inthWnd = FindWindow(vbNullString, strWordCaption)
            m_inthMenu = GetSystemMenu(m_inthWnd, 0)
            m_intwFlags = MF_BYCOMMAND Or MF_GRAYED
            m_intwResult = EnableMenuItem(m_inthMenu, SC_CLOSE, m_intwFlags)
.
.
     objWord.Visible = True
.
.
End Sub
0
 
LVL 20

Assisted Solution

by:alainbryden
alainbryden earned 250 total points
ID: 24854064
Sounds great. Glad you finally got it working.

--
Alain
0

Featured Post

Instantly Create Instructional Tutorials

Contextual Guidance at the moment of need helps your employees adopt to new software or processes instantly. Boost knowledge retention and employee engagement step-by-step with one easy solution.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Well, all of us have seen the multiple EXCEL.EXE's in task manager that won't die even if you call the .close, .dispose methods. Try this method to kill any excels in memory. You can copy the kill function to create a check function and replace the …
The ECB site provides FX rates for major currencies since its inception in 1999 in the form of an XML feed. The files have the following format (reducted for brevity) (CODE) There are three files available HERE (http://www.ecb.europa.eu/stats/exch…
This tutorial will teach you the special effect of super speed similar to the fictional character Wally West aka "The Flash" After Shake : http://www.videocopilot.net/presets/after_shake/ All lightning effects with instructions : http://www.mediaf…
Visualize your data even better in Access queries. Given a date and a value, this lesson shows how to compare that value with the previous value, calculate the difference, and display a circle if the value is the same, an up triangle if it increased…

632 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question