Solved

Disable Word Close Button Programmatically in VB.Net

Posted on 2009-07-07
16
2,228 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
  • 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
 
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
Highfive + Dolby Voice = No More Audio Complaints!

Poor audio quality is one of the top reasons people don’t use video conferencing. Get the crispest, clearest audio powered by Dolby Voice in every meeting. Highfive and Dolby Voice deliver the best video conferencing and audio experience for every meeting and every room.

 
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

IT, Stop Being Called Into Every Meeting

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

Join & Write a Comment

Since .Net 2.0, Visual Basic has made it easy to create a splash screen and set it via the "Splash Screen" drop down in the Project Properties.  A splash screen set in this manner is automatically created, displayed and closed by the framework itsel…
Microsoft Reports are based on a report definition, which is an XML file that describes data and layout for the report, with a different extension. You can create a client-side report definition language (*.rdlc) file with Visual Studio, and build g…
Sending a Secure fax is easy with eFax Corporate (http://www.enterprise.efax.com). First, Just open a new email message.  In the To field, type your recipient's fax number @efaxsend.com. You can even send a secure international fax — just include t…
Get a first impression of how PRTG looks and learn how it works.   This video is a short introduction to PRTG, as an initial overview or as a quick start for new PRTG users.

708 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

Need Help in Real-Time?

Connect with top rated Experts

16 Experts available now in Live!

Get 1:1 Help Now