PDA

View Full Version : Eliminate "Save changes?" and "Empty clipboard?" dialogs. (Word 2003)



marco75
01-15-2009, 10:50 PM
I want to eliminate the following dialogs from Microsoft Word 2003:

?Do you want to save changes to this document??


?There is a large amount of data on the clipboard. Do you want this to be available to other applications??


I want to write a VBA macro that will automatically save a document when it is closed, and empty the clipboard when the last MS Word window is closed.
This is what I have so far:

Enter VBE. (Alt + F11)

Click on the Normal Project. This makes the macro global, in effect at all times.

Insert > Class Module, rename it to oAppClass
Paste the following code into oAppClass :


Option Explicit
Public WithEvents oApp As Word.Application
Private Sub oApp_DocumentBeforeClose(ByVal Doc As Document, Cancel As Boolean)
SaveAndCloseDocument
Cancel = False
End Sub


Insert > Module :


Option Explicit
Dim oAppClass As New oAppClass
Public Sub AutoOpen()
Set oAppClass.oApp = Word.Application
End Sub


? Why do I need the snippet above? I don?t remember why I put that there.

Insert > Module :


Option Explicit
Public Sub SaveAndCloseDocument()
If ActiveDocument.ReadOnly = True Then
ClearClipBoard
Application.Quit
End If
ActiveDocument.Close SaveChanges:=wdSaveChanges
If Application.Documents.Count = 0 Then
ClearClipBoard
Application.Quit
End If
End Sub


! Following an application crash, when a recovered document is closed, there?s a Run-time error `4198` at line ?ActiveDocument.Close SaveChanges:=wdSaveChanges?

Insert > User Form (this is to make sure you can use the DataObject Object)
Insert > Module :


Option Explicit
Public Sub ClearClipBoard()
Dim oData As New DataObject 'object to use the clipboard
oData.SetText Text:=Empty 'Clear
oData.PutInClipboard 'take in the clipboard to empty it
End Sub


! Occasionally, after closing the application window, the following VBA error appears:
?Run-time error ?-2147221040 (800401d0)?: DataObject: PutInClipboard Failed"
The debugger highlights the following as the offending line:
oData.PutInClipboard

How do I eliminate the error messages?

Thanks to anyone taking the time to read this!

CreganTur
01-16-2009, 06:24 AM
I want to eliminate the following dialogs from Microsoft Word 2003:

“Do you want to save changes to this document?”

I got around this by using the following code as a part of the Document_Close event:

ActiveDocument.Saved = True

This sets the save flag to be true, which tells Word that the document is not dirty (there are no unsaved changes tot he document). Just make sure that no code changes the document after this statement; if it does, then the saved flag will revert to False because another change was made.

HTH:thumb

fumei
01-16-2009, 11:09 AM
1. Seems to me you are doing more work than is involved with simply clicking the dialogs.

You may want to check out the order/timing of your Application.Quit

2. It is generally NOT a good idea to put code into Normal.

Paul_Hossler
01-17-2009, 07:49 AM
2. It is generally NOT a good idea to put code into Normal.


If there are general purpose macros that I like to keep readily at hand for every MS Word session, what is the downside of using Normal? More importantly, are there any risks?

Paul

lucas
01-17-2009, 10:44 AM
I used to keep macros in normal because as you say it is convenient but I had several occations where normal became corrupt and I had to start over adding the same macro's back into normal again.

A template file that loads at startup is better for me. It's easy to backup and edit.

dikdikdik
01-18-2009, 08:49 PM
Hi all of you. I have the same question as marco_75. I write an app to open MS word, do something and close it. Because the app cut a large amount of text, so when it close MS word, I always receive a message “There is a large amount of data on the clipboard. Do you want this to be available to other applications?”
What the code I need to use to avoid the message? I try with "ClearClipBoard" but it not run

Example code :


Documents.Open FileName:="Testcase.doc"
Selection.WholeStory
Selection.Cut
Selection.PasteAndFormat (wdFormatPlainText)
ActiveDocument.SaveAs FileName:="Test case.txt", FileFormat:=wdFormatText

ClearClipBoard

ActiveWindow.Close
Application.Quit

fumei
01-19-2009, 10:46 AM
dikdikdik, what is the code for your ClearClipboard? The same as macro75? If so, when and where are you calling it?

macro75, as Steve (lucas) mentions, Normal.dot has a very real tendency to get corrupted.

"what is the downside of using Normal? More importantly, are there any risks?"

Yes, there are risks. Even Microsoft itself suggests NOT to put essential code in Normal, as it can get corrupted.

Why does it get corrupted? Because every time you use Word, and change anything, there are I/O (read/write) operations to Normal.dot. The more times you have I/O to any file, the more chance the file can get corrupted.

This is why, personally, I have virtually NOTHING in Normal.dot. All my procedures (macros) and all my Styles are in separate .dot files.

Note that global templates (the ones loaded from Startup) do NOT, repeat NOT, have access to Styles...only code.

Styles should come from the document AttachedTemplate.

None of my documents - and let me repeat that, NONE of my documents - use Normal.dot as the attached template.

I would like to add that globals can be dynamic. Steve mentions having his global in Startup, which is a good idea. I have one global that is in Startup, and thus is always loaded. However, in THAT global template are procedures that can dynamically load and unload other globals, temporary globals.

In other words, I have ONE global template always loaded. I have other globals that I can bring in, and use the code therein for as long as I want them. Then when i do NOt want them, I can unload them.

A combination of both permanently loaded globals (in Startup), and temporary globals (loaded dynamically) is the best. The best that is if you have a lot of code. If you have just a a wee bit, then a Startup global works fine.

The point being is that by keeping your code out of the hands of Normal, you control things. You control access, you control editing, you control how and when it is saved.

Trusting Microsoft to take care of things is iffy at best.

lucas
01-19-2009, 11:44 AM
I would like to add that globals can be dynamic. Steve mentions having his global in Startup, which is a good idea. I have one global that is in Startup, and thus is always loaded. However, in THAT global template are procedures that can dynamically load and unload other globals, temporary globals.


Gerry what code would you use to call the temp globals? and how to unload them when done with them. This is a new and interesting twist. Another question.....since they are not in startup(temp.globals) can they be wherever you want them on the hard drive?

Still learning from you....

fumei
01-19-2009, 12:54 PM
1. YES! Your temp globals can be ANY folder, anywhere. You need to use full paths though.

2. Here is some code.
AddIns.Add FileName:="C:\YaddaTemplates\Other\MyCodeCrap.dot", _
Install:=True

Note the Install parameter.

This adds the .dot file to the available list - it is listed under Templates and add-ins.

The Install:=True loads it.

Addins("C:\YaddaTemplates\Other\MyCodeCrap.dot").Installed:=False


unloads it, but does NOT de-list it. In other words, you can load it up again dynamically with:

AddIns("C:\YaddaTemplates\Other\MyCodeCrap.dot").Installed = True

Note that to use .dot code cotainers from other than the Startup, you MUST use full path for ALL instructions on the .dot file itself. You do NOT need a full path to execute procedures, you use .Run.

Note also the change between Install:= and Installed =!!! One has the colon ( : ) and one does not. One has "ed" and one does not!

To remove it from the list - although having an add-in (a global .dot) uses very little memory as being a member of the Add-ins Collection simply makes a pointer - use .Delete.

Do not use .Remove (unless you really want to) as it removes ALL add-ins from the list.

Here is an example of adding a .dot file as a global to the list, installing (loading) it, and then immediately executing a procedure in that newly added/installed global.

Sub Install_And_Execute()
AddIns.Add "c:\zzz\DemoMe.dot", Install:=True
Application.Run Macroname:="Blah"
End Sub
The above adds "DemoMe.dot", installs it, and then executes the Sub "Blah".

In fact...if you wanted to, you could add it, install it, execute a Sub, and then immediately remove it.

Sub Install_And_Execute()
AddIns.Add "c:\zzz\DemoMe.dot", Install:=True
Application.Run Macroname:="Blah"
AddIns("c:\zzz\DemoMe.dot").Delete
End Sub


You can also use arguments.

' this is a Function in the DemoMe.dot file
' being loaded and installed as a global
Function ReturnStr(InputStr As String) As String
ReturnStr = "whatever " & InputStr
End Function

' this is a Sub that can be in any module
Sub Install_And_Execute()
AddIns.Add "c:\zzz\DemoMe.dot", Install:=True
MsgBox Application.Run(Macroname:="ReturnStr", _
varg1:=InputBox("type something"))
End Sub

It will:

1. add the add-in to the available list
2. install it
3. fire the Function ReturnStr that is in DemoMe.dot

Result? The Function in #3 has an argument - varg1:=InputBox("type something"). So you get...an Inputbox with the prompt "type something". Suppose you type "yadda yadda".

The result will be a messagebox "whatever yadda yadda"

The "whatever" (hard coded in the Function), plus the "yadda yadda" from the Inputbox. Although technically,
MsgBox Application.Run(Macroname:="ReturnStr", _
varg1:=InputBox("type something"))

displays the returned value of the Function ReturnStr in the global.




Are we having fun yet?

fumei
01-19-2009, 12:59 PM
Final note...

Please notice the difference in syntax when using arguments.

Application.Run Macroname:="Blah"



and...

MsgBox Application.Run(Macroname:="ReturnStr", _
varg1:=InputBox("type something"))

If your procedure uses arguments you must put the call to it in parenthesis.

Hopefully I am not insulting you by pointing that out, as that is standard syntax use in VBA.

fumei
01-19-2009, 01:15 PM
As a final, final, note. This is a way to pass arguments to a starting up macro. Say you have a document and you want to execute a procedure but do NOT want the code to be visible (or rather, easily accessible) to the user.
Sub Document_Open()
AddIns.Add "whatever\path\blahblah.dot", Install:=True
Application.Run(Macroname:="DoSomething", _
varg1:=some_argument)
AddIns("whatever\path\blahblah.dot").Delete

When the document is opened:

1. add-in (a .dot file with code) is added
2. the .dot file is loaded
3. a procedure is executed (in this case with an argument)
4. the .dot file is deleted

The actual code in the procedure (DoSomething) is not in the document.

Hmmmmmmm, I have to test this. Not test if the above works, it does. But I wonder about Permissions. Can you add/load a .dot file that the user does not have Permissions to get at? I wonder. Hmmmm. If they opened the VBE, and saw the code in Document_Open, can you prevent them from getting the .dot file? Well, yes.

The original .dot file could be:

1. read-only
2. if they user must have permission to read it - I mean OS permissions - you could easily hard code its Document_Open to only allow you to open it. BECAUSE, remember adding and loading a global template is NOT opening the file. The file itself is not opened!

Yes...it would work.




Now I am having fun.

lucas
01-19-2009, 01:37 PM
Great reference thread Gerry, thanks.

fumei
01-19-2009, 02:05 PM
A bit of a divergence and hijacking from the thread, but you are welcome.