How to use a single VBA procedure to read or write both custom and built-in Document Properties .... How can I prevent Word from running macros automatically when I create a new instance
Trang 1LẬP TRÌNH VBA FOR WORD
(Tiếng Anh)
Nguồn tư liệu : http://word.mvps.org/FAQs/MacrosVBA/index.htm Người sưu tầm : Phan Tự Hướng
Nơi phát hành: : www.giaiphapexcel.com
Trang 2CHAPTER 1: Beginners' tips 10
1 How to modify a recorded macro 10
1.1 What's wrong with the recorder, anyway? 10
1.2 Cleaning out unneeded dialog arguments 10
1.3 Making a macro more general 11
2 Making toggle macros 13
2.1 Fixing broken Replace macros 13
2.2 Naming and storing macros 14
2.3 More fun to come 15
3 Creating a macro with no programming experience using the recorder 15
4 Getting to grips with VBA basics in 15 minutes 17
5 Making the transition from WordBasic to VBA 22
6 Organizing your macros 23
6.1 Getting yourself organized 23
6.2 Editing Macros 24
6.3 Organizing your Global Templates 24
7 When to use parentheses to enclose subroutine and function arguments 26
8 The art of defensive programming 27
Recommended further reading 30
9 How to cut out repetition and write much less code, by using subroutines and functions that take arguments 31
10 How to use a single VBA procedure to read or write both custom and built-in Document Properties 34
10.1 Writing Document Properties 34
10.2 Reading Document Properties 36
11 How to get the username of the current user 37
12 How can I get a list of the available printer names? 38
13 How to find out, using VBA, how many replacements Word made during a Find & Replace All 40
14 Finding and replacing symbols 43
14.1 Basic Latin symbols listed under “(normal text)” 44
14.2 Upper Unicode characters and symbols which use decorative fonts 44
14.3 How to write your own macro to do the job 45
15 Distributing macros to other users 48
CHAPTER 2: Returning information 50
1 How to check whether Word is open 50
2 Control Word from Excel 50
3 Determine whether the insertion point is located at the end of a document 52
Trang 33.1 Start Property 52
3.2 End Property 52
3.3 Are We There Yet? 53
4 Detect whether a table cell is empty 54
4.1 Method 1 54
4.2 Method 2 54
4.3 Method 3 55
5 Determine the page number at the current cursor position 55
6 Determine the number of pages in a document 55
7 How to get the column number of the selection (in a document containing snaking, or newspaper-style, columns) 56
8 Getting help with calling Word's built-in dialogs using VBA (and why doing so can be much more useful than you'd think) 56
8.1 Where to find Help on them 56
8.2 Why use built-in dialogs? 56
9 Determine the position of the cursor on the page in points 58
9.1 Important caveat 58
10 Determine whether the selection or range is at the start of a paragraph 59
11 Detect whether the first character in a selection is alphanumeric 59
12 How to find out whether the current document is running in another application (such as Internet Explorer, Outlook, etc.) 59
CHAPTER 3: Working with Bookmarks in VBA 61
1 Working with Bookmarks in VBA 61
1.1 Types of Bookmarks 61
(1) Placeholder Bookmarks 61
(2) Enclosing Bookmarks 61
1.2 Inserting and retrieving text from a Bookmark 61
2 Inserting text at a bookmark without deleting the bookmark 62
2.1 The problem 62
2.2 The solution 63
3 How to create a menu to navigate to the non-hidden bookmarks in a document 63
CHAPTER 4: Working with built-in dialogs 66
1 Calling FileOpen dialog in VBA does not allow opening of multiple files 66
2 How to change the directory of the Save As dialog 67
3 How to set the default suggested filename to be displayed by the Save As dialog the first time a user saves a new document 68
4 Passwords not saved when calling FileSaveAs dialog from VBA 69
5 Force the user to save documents into a particular folder or a subfolder of that folder 70
Trang 46 How to get the full path from the SaveAs dialog 71
7 Force the File New dialog to display in List view 71
CHAPTER 5: Working with events 72
1 Running a macro automatically when Word starts or quits 72
1.1 Global templates 72
2 Running a macro automatically when a document is created, opened or closed 72
2.1 Using Document events 72
2.2 Using Auto macros 73
2.3 Using Application Events 73
3 Writing application event procedures 73
3.1 How to set up code that will respond to application events 73
3.2 Summary of set-up procedure 76
3.3 oApp_Quit 77
3.4 oApp_DocumentChange 77
3.5 oApp_DocumentBeforeClose, oApp_DocumentOpen, oApp_NewDocument, oApp_ WindowActivate, oApp_WindowDeactivate 78
3.6 oApp_DocumentBeforePrint, oApp_DocumentBeforeSave 78
3.7 oApp_WindowBeforeDoubleClick, oApp_WindowBeforeRightClick, oApp_WindowSelectionChange 78
4 How to create global event procedures similar to AutoOpen, AutoNew and AutoClose, without using Normal.dot 78
1 78
Using Word 97 78
2 81
Using Word 2000 and later versions 81
5 Intercepting events like Save and Print 82
5.1 Intercepting commands 82
5.2 Intercepting events (Word 2000 or later) 83
6 A Pseudo DocumentBeforeClose Event 84
7 How can I prevent Word from running macros automatically when I create a new instance of Word, open a Word document or create a new one? 85
8 Assigning a macro to the tab key 86
9 Change the behavior of the TAB key inside a table cell 86
10 Prevent a file from showing up on the recently used files list 89
11 How can I prevent users from editing the header of a document in Word 2000 or higher?90 CHAPTER 6: Working with properties 91
1 Using VBA, how can I get access to the Document Properties of a Word file without opening the document? 91
Trang 52 How to use a single VBA procedure to read or write both custom and built-in Document
Properties 91
2.1 Writing Document Properties 92
2.2 Reading Document Properties 93
3 Using VBA, how can I get access to the Document Properties of a Word file without opening the document? 95
4 How to use a single VBA procedure to read or write both custom and built-in Document Properties 95
4.1 Writing Document Properties 95
4.2 Reading Document Properties 97
5 Highlight any misspelled words, so that unrecognized words stand out prominently on a printout 98
6 Clear all highlighting from a document 99
7 Turning “Allow spacing between cells” off with VBA in a Word 2000 table 99
7.1 Workaround 1 99
7.2 Workaround 2 100
CHAPTER 7: Working with ranges and selections (not including Tables) 101
1 How to select – or set a Range object – to the page that the cursor is on 101
2 Determine the index number of the current paragraph, table, section 101
1 102
In order to operate on the currently selected paragraph, table, section, etc 102
2 102
Get the index number, by setting a range to the start of the document 102
3 How to move a range variable to the end of an inserted file after using [range].InsertFile 103 3.1 Workaround 1 103
3.2 Workaround 2 103
4 Delete any paragraph that is an exact duplicate of the preceding paragraph, using a Range object 103
5 Delete any paragraph that is an exact duplicate of the preceding paragraph, using a Selection object 104
6 How to find out whether a range is off-screen 105
7 How to get the column number of the selection (in a document containing snaking, or newspaper-style, columns) 105
CHAPTER 8: Working with Tables 106
1 Maximising the performance of Word tables 106
As a user 106
In code 108
2 How can I resize a table to fit the page's width? 114
Trang 62.1 Word 2000 114
2.2 Word 97 114
3 Deleting duplicate rows in a table 116
3.1 Method 1 116
3.2 Method 2 118
4 Detect whether a table cell is empty 118
4.1 Method 1 118
4.2 Method 2 119
4.3 Method 3 119
5 Delete all rows of a table that contain a particular text string in the first column 120
6 Determine the index number of the current paragraph, table, section 120
1 120
In order to operate on the currently selected paragraph, table, section, etc 120
2 121
Get the index number, by setting a range to the start of the document 121
7 Apply changes to all cells in a table 122
8 Apply changes to an individual cell in a table 122
9 Select all but the first two cells in a table column 122
10 Display in a message box the contents of each cell in a table column 123
11 Select a range of cells within a table 123
12 Select all rows of a table except the first row 123
13 How to centre a left-justified table (or left or right-justify a centred one) 124
14 How to get the Rowspan and Colspan of a table cell using VBA 124
14.1 Rowspan 124
14.2 Colspan 125
15 Change the behavior of the TAB key inside a table cell 125
16 Turning “Allow spacing between cells” off with VBA in a Word 2000 table 128
16.1 Workaround 1 128
16.2 Workaround 2 128
CHAPTER 9: Working with strings, dates and Find & Replace 129
1 How do I return the date of the previous month using VBA? 129
2 Using a macro to replace text where ever it appears in a document 130
2.1 Step 1 130
2.2 Step 2 131
2.3 Step 3 132
3 How to Find & ReplaceAll on a batch of documents in the same folder 133
4 Clear settings from Find and Replace dialog to prevent unexpected results from future Find or Replace operations 136
Trang 75 Flush bad karma from Word's find facility after an unsuccessful wildcard search 137
6 How to prevent the built-in BrowseNext and RepeatFind commands from creating bad karma for wildcard searches 138
6.1 Workaround 1: Intercepting the RepeatFind, BrowseNext and BrowsePrev commands 138
6.2 Workaround 2: Executing the Find & Replace dialog to clear the bug 139
6.3.1 Notes regarding Workaround 2 141
7 How to find out, using VBA, how many replacements Word made during a Find & Replace All 142
8 Finding out how many times some text appears in a document 145
9 How to use Edit Find to select everything from where the cursor is to the first found item145 9.1 Doing it programmatically 146
10 How to replace text in quotation marks with italic or highlighted text minus the quotes 147 10.1 To do it manually 147
10.2 To do it with a macro 148
11 Replace each instance of the text string “Document One” with the contents of a file called c:\test\Doc1.doc 150
12 Replace one character with another wherever it appears in a string 151
13 Remove the underline attribute from characters with descenders 152
14 Remove all empty paragraphs from a document 152
CHAPTER 10: Working with files and directories 156
1 How to allow the user to browse to and select a folder 156
2 How to Find & ReplaceAll on a batch of documents in the same folder 157
3 Skipping Password-Protected Documents in a Batch Process 160
4 How to check if a file has already been opened by another user 162
5 How to copy an open file using VBA 162
6 How to create a copy of an open document 163
7 How to save a document using a filename that gets incremented by 1 each time if the filename already exists 164
8 Insert into a document the names of all files in a selected folder 164
9 How to retrieve Word's default Documents path or Pictures path setting 165
10 How to delete files using VBA, including files which may be readonly 166
11 How to read the filenames of all the files in a directory into an array 167
12 How to get the names of all the folders in the folder tree, starting from a specified folder 168
Notes 172
13 How to ensure (using VBA) that all your Word add-ins are installed in the correct path 172
Trang 8CHAPTER 11: Miscellaneous 175
1 How can I get the mousewheel working in the VBA editing window? 175
2 How to do a mail merge to the printer using VBA, without displaying the Print dialog 176
3 How to do a screen capture using VBA 177
3.1 Using SendKeys 177
3.2 Using API calls 177
4 How to get the username of the current user 178
5 Useful WordBasic commands that have no VBA equivalent 180
5.1 SortArray 180
5.2 FileNameInfo$() 181
5.3 DisableAutoMacros 182
5.4 ToolsBulletsNumbers 182
5.5 FileCopy/FileCopyA 183
5.6 FileProperties 184
5.7 SendKeys 184
6 How to send an email from Word using VBA 184
6.1 Using the Routing Slip method 184
6.2 Automating Outlook 185
7 How to get the most recently used document to be opened automatically when you open Word 187
1 187
Using a command line switch to open the most recently used document 187
2 188
Using a macro to open the most recently used document whenever Word opens 188
3 189
Using a macro to open the most recently used document whenever Word is opened from its icon, but not when you open Word by launching a file 189
4 190
The Application.GoBack bug and how to get round it 190
8 Creating upside down or rotated text in Word 190
8.1 Using two table cells 190
8.2 Word 2002 only: Rotating a picture of your text 191
8.3 Pasting from PowerPoint into Word as a picture 191
8.4 Pasting from other vector graphics applications 192
8.5 Using Word Art 192
8.6 And just for the sake of completeness 193
9 How can I tile documents vertically in Word 2000? 194
10 “Invalid Page Fault” message when running a macro 196
10.1 Obtaining the Word Code Cleaner 197
11 Creating sequentially numbered documents (such as invoices) 198
12 Sequentially numbering multiple copies of single document using a macro 199
Trang 913 How to remove manually typed numbering from a document 200
14 I want the numbers in my footnotes not to be superscripted, and I want the numbers to be followed by a dot and a tab 201
15 How to speed up Word Automation by hiding the application 203
Chapter 12: Working with other objects and collections 206
1 How to convert the hyperlinks in a document to plain text 206
Preserving hyperlinks within your Table of Contents 207
2 Size the text in a textbox to fill the textbox 208
3 When I position a floating object (such as a text box or graphic) “Relative to Page” in Word 2000, it doesn’t end up where it should – why doesn’t it? 209
4 Move shape anchors away from heading paragraphs 210
5 The simplest way, using VBA, to reset part of a style definition (e.g the font name), so it inherits the definition of the style it is based on 211
6 Cycle a paragraph through all available paragraph styles, eventually returning to the style the paragraph started with 213
7 How to safely update a document's styles from its template without using the Organizer (and how to make the Tools + Templates and Add-ins dialog safe) 215
7.1 Overview of updating styles and template strategy 215
7.2 When should styles be updated? 215
7.3 “Gotchas” to be aware of, and their workarounds 216
If you want to be able to update the styles of Word 97 documents that are attached to Normal.dot 219
8 Scroll all open documents the same percentage as the active document 220
Trang 10CHAPTER 1: Beginners' tips
1 How to modify a recorded macro
Article contributed by Jay Freedman Lots of articles advise you to use the macro recorder to get started with Word macros An example is our own Creating a macro with no programming experience using the recorder Although it's good advice, the macros you record often need tweaking—and sometimes they don't work at all
1.1 What's wrong with the recorder, anyway?
The macro recorder's job is to translate your actions into programming code, using a language called Visual Basic for Applications (VBA) That's easy to do when you simply type some words into a document, or when you give a simple command such as Edit > Copy It's harder for the recorder to make good code for more complicated commands such as File > Open or Edit > Replace
Sometimes the recorded code doesn't do exactly what you want it to do, and in a few situations the code is incorrect
1.2 Cleaning out unneeded dialog arguments
You can record a macro while you make a change in the document by using a dialog, such as
Format > Font or Format > Paragraph You might think the macro recorder should capture only the settings that you changed, but it doesn't—instead, it throws into the macro every setting in the whole dialog! Look at what you might get if you just change the font size to 10 pt:
Trang 11The lines that say "With Selection.Font" and "End With" mean that the lines between them set the properties of the font of the selected text This is a shorthand way to refer to many properties of the same item, instead of writing "Selection.Font" at the beginning of each property's name
In this macro the one property that you changed during recording is buried among the many that you didn't change, which take extra storage and extra execution time That's bad enough But suppose you replay the macro after you select text that's in a different font or a different color, thinking that it will just change the size to 10 pt In fact, the macro will also change the text to Times New Roman and Automatic color, because the recorder captured every setting in the dialog That probably isn't what you intended the macro to do
Whenever you record the result of a dialog, you should inspect the code and delete all the extra settings In this case, to apply only the size change, all you need is this:
1.3 Making a macro more general
Suppose you record a macro that opens a document and then does something to it, such as changing the view The beginning of the macro may look like this: 1
The recorded macro for opening a document, like the recording of the Format > Font dialog,
contains unneeded things In this case, they are parameters that contain information about the file, such as a password The only parameter that's necessary is the FileName You can remove the other parameters from the command, and Word will use its default values for them
Trang 12A more important problem is that every time you run the recorded macro, it will open the same document This may be what you intend, but more likely you want the macro to let you choose which document to open
One way to get the file's name into the macro is to display an input box, where you can type it in The InputBox function shows a message box with a text entry field, and its result is the name that you type into the field
Sub Macro2A()
Dim MyFileName As String
MyFileName = InputBox( "Enter file name to open:" , _
The word ".Show" refers to a method of the dialog A method is an action that can be done—the Show method causes the dialog to appear and execute (carry out its function)
Many methods also have a value after they execute, which tells the macro something about what just happened (this is called "returning" the value) In this case, if you click the OK button in the dialog then the Show method returns the value –1, but if you click the Cancel button or the X in the title bar then Show returns the value 0 The VBA help topic for each method tells you what values that method can return and what they mean You can use the returned value in an If statement, as in Macro2B, to decide what to do
Similar changes to recorded code let you make macros that save files to variable locations, search for variable strings, and many other unrecordable variations
Trang 132 Making toggle macros
The Italic, Bold, and Underline buttons on the toolbar are toggles—click the button once to turn it
on, and again to turn it off If you want to make your own toggle for something else, you can record separate macros for turning it on and off, but how do you combine them into one?
As an example, let's make a macro to toggle the font's outline property on and off If you record the change to turn it on, and remove the unnecessary properties, you get this:
2.1 Fixing broken Replace macros
One of the most common actions to record is a Replace operation A macro can be a great saver, since setting up the same Replace over and over can be time-consuming and it's easy to make
time-a misttime-ake In one circumsttime-ance, though, the recorder cretime-ates time-a mtime-acro thtime-at simply doesn't work
Suppose you record the replacement of all italic text with the same text in bold italic While you're recording this operation, it works perfectly well If you replay the macro on another document, though, nothing happens! What's the matter?
A look at the recorded code reveals the problem:
Sub Macro4()
Selection.Find.ClearFormatting
Selection.Find.Replacement.ClearFormatting
Trang 14.Font.Italic = True
.Replacement.Font.Bold = True
The first of these lines tells Word to search for italic text The second line tells it to make the
replacement text bold—because it's already italic, it will become bold italic
Besides making the macro correct, I like to make it consistent The "With" and "End With"
statements are meant to replace the references to Selection.Find; that both speeds up the macro and makes it easier to read You can pull the ClearFormatting and Execute statements inside the With clause as well, to get this code:
2.2 Naming and storing macros
When you record a macro, Word suggests a name like Macro1 By default, it puts the macro in a module named NewMacros in the Normal.dot template You should make it a habit to rename your
Trang 15macros to give them descriptive names, and macros that are useful enough to keep should be stored
in a more organized manner
To rename a macro, all you need to do is change the word that follows "Sub" in the first line For example, you could change the first line of Macro4A to
To move a macro from one module to another, cut its code from the editing pane, double-click the destination module in the Project Explorer, and paste the code into the destination's editing pane Unfortunately, the Project Explorer doesn't support drag-and-drop movement of macros
You can move an entire module from one template to another by using the Organizer (Tools > Macro > Macros > Organizer) Macros that are useful for a specific type of document should be stored in modules in the template used to create that type of document Macros that are generally useful should be stored in a global template, as explained in What do Templates and Add-ins store?
2.3 More fun to come
There are lots of other situations in which the macro recorder gives you code that's inefficient or doesn't do what you want or expect, and you should practice by improving it
As you learn more about macros, sometimes you'll find it useful to record an action just to discover what statements in VBA are involved Then you can throw away the recorded macro and write good code of your own
There are times when you can't get the recorder to record anything useful because the commands you want to use are grayed out Then you can usually find out more by looking at articles here, or asking questions in the VBA newsgroups
3 Creating a macro with no programming experience using the recorder
Article contributed by Bill Coan Word's macro recorder can help you acquaint yourself with macros and with Office 97's vba
programming language
Trang 16Let's assume that you create several documents from scratch on a daily basis After composing the text, you press Ctrl+Home to position the cursor at the start of the document Then you click the Center tool on the Formatting toolbar to center the first paragraph of your document Eventually, it occurs to you that you should be able to record a macro for these last actions That is, a macro that will automatically position the cursor at the start of the document and center the first paragraph
Technically, this scenario may call for a solution involving paragraph styles But for now let's create
a macro solution and let's use the macro recorder to create it
Proceed as follows:
1 Choose Macro|Record New Macro on the Tools menu (or simply double-click
the REC button in the Status bar.)
2 Enter a macro name (sorry, no spaces or punctuation allowed)
3 Click the Assign Macro To Keyboard button
4 Press Ctrl+F8, then click Assign and click Close Word will display the Stop Recording toolbar,
which lets you know that Word is recording your actions and will continue recording them until you click the Stop Recording button on this toolbar (Don't click it yet!)
5 Press Ctrl+Home to position your cursor at the start of your document
6 Click the Center tool on the Formatting toolbar
7 Click the Stop Recording tool on the Stop Recording toolbar
8 Open some other document
9 Position the cursor in the middle of the document (doesn't matter where)
10 Press Ctrl+F8
11 Smile with satisfaction as Word moves the cursor to the start of the
document and centers the first paragraph
So far, so good Now to see what the macro looks like under the hood:
1 Choose Macro|/Macros on the Tools menu
2 Select the macro that you just created
Trang 17F1 to view a related help topic
5 Close the help window and repeat Step 4 with the cursor in other locations
6 On the vba editor File menu, choose Close and Return to Microsoft Word
If you have the patience to try these steps, you'll be well on your way The rest is just details There are lots of them but you can always search the vba help system and return to the newsgroups for additional information
4 Getting to grips with VBA basics in 15 minutes
Article contributed by Bill Coan
I can't turn you into a VBA expert but I can suggest a way to explore VBA that you may find
helpful Below, I've listed 22 steps that can be completed in approximately 15 minutes, assuming someone is kind enough to read them to you as you sit at your keyboard If you have to read them by yourself and turn your attention alternately to the keyboard and back to the steps, then you may need
a half hour or longer to complete the steps Either way, the steps should give you a feel for what it's like to program in Word
Before starting, launch Word, then press Alt+F11 to launch the VBA editor, and then maximize the
VBA editor window
Ready? Let's start:
1 In the VBA editor, choose Options on the Tools menu and make sure the following checkboxes
are checked:
Auto List Members
Auto Quick Info
2 Press F7 to view a code window, if not already displayed
Then type “Sub Test” and hit the enter key The VBA editor will create a subroutine for you that looks as follows:
Notice that the cursor is already flashing inside the subroutine, ready for you to type a
4 If you want to work on the active document (and who doesn't) then you have to start this way
(Think Jesus: No one gets to the father except through me!) When you type the "." at the end of
Trang 18the expression a list will pop up This list is the most amazing guide you can imagine Each item on the list is either a method or a property of the ActiveDocument or else it's an object unto itself that belongs to the ActiveDocument For now let's deal with methods Toward the end of this message we'll deal with properties At the very end of the message, we'll deal with other objects besides the currently active document
A method is something you can DO to the ActiveDocument, like print it out The way to think
is this: “I'm trying to do something to the active document as a whole, namely, print it If I'm patient and scroll through this list, I'll almost certainly find a method that will do this for me.” When you first think this way, the word “method” will stick in your throat like a fishbone Later it will feel more like burnt toast and later still it will feel like candy when you were young Indeed, in this case, if you're patient, you will scroll far enough to encounter a method called PrintOut Eureka! You know it's a method (as opposed to a property) because it has an icon next to it that looks like a green brick flying through the air and at this stage the whole concept of method makes you feel like throwing a brick Nice mnemonic, eh?
Let's give it a try Since you've scrolled down and selected PrintOut, you can press Tab to accept it or you can double-click it right there in the list Either way, your statement now looks
as follows
5 So far so good Most people easily get this far But now what?
6 One possibility is that you're done After all, you've specified an object (the active document)
and you've selected something that you want to do to it (print it out) Indeed, if you're willing to
let Word print out the document in whatever way it chooses, then you are done
7 Another possibility is that you want to control how the print job will be carried out In this case,
you must provide some “arguments” (another fishbone, at first) Not to worry To enter one or more arguments, all you do is type a space after the method and let the editor help you enter them Arguments are equivalent to the choices you make in the Print dialog box So go ahead, type a space after PrintOut, so your screen looks like this (the pipe character “|” represents your flashing insertion point after you've typed a space):
8 But wait! When you type the space, the VBA editor suddenly gets very helpful again, showing
you something like the following:
9 Aha! Aha! These are the arguments for the PrintOut method Most of them are immediately
recognizable if you've ever paid attention to the Print dialog box If you're wondering about one
or more of them, simply press F1 and you'll call up a help topic that tells you all about each one
of them
10 Now that you can see all the arguments, you can enter values for as many
of them as you desire
Trang 19One way to do this is to type the name of the argument and then a value,
using “:=” to connect them and a comma to separate one argument/value from
the next, like so:
ActiveDocument.PrintOut Background:=False, Copies:= 2
Another, boneheaded (in my opinion) way to do this is to enter values for ALL of the
arguments, in which case you don't have to type the names of the arguments but you do have to account for ALL of them, as follows:
ActiveDocument.PrintOut False, , , , , , 2, , , , , , ,
11 Let's go with the named-argument approach:
ActiveDocument.PrintOut Background:=False, Copies:= 2
12 That wasn't so painful, was it? Now press F5 to run the subroutine Or return to Word and choose Tools|Macro|Macros |Test|Run (Pressing F5 is easier!)
A quick review before we plunge on to properties You think: “I'm trying to do something to the active document as a whole, namely, print it So I start by typing “ActiveDocument.” and a list pops up I scroll through the list and select the PrintOut method Then I type a space and enter some arguments In this case, I want background printing off and I want two copies, so I type the names of those arguments and values for each of them I connect each argument name
to its value by using “:=” because I'm part of the cognoscenti.”
Take a big breather here because now it's time to explore properties instead of methods
13 Let's go back to our original assumption, namely, that you want to work on the active document
as a whole In this case, though, let's assume you want to change one of the properties of the document, rather than hit it with a brick
14 Once again, click in a code window and type the following, making special note of the dot at
the end of the expression:
ActiveDocument
15 Remember, if you want to work on the active document (and who doesn't) then you have to
start this way (No one gets to the father except through me!) When you type the “.” at the end
of the expression a list will pop up This list is the most amazing guide you can imagine Each item on the list is either a method or a property of the ActiveDocument or else it's an object unto itself that belongs to the ActiveDocument For now let's deal with properties
A property is a single characteristic One of the properties of a document is its password The way to think is this: “I'm trying to change a property of the active document as a whole,
namely, its password If I'm patient and scroll through this list, I'll almost certainly find a password property.” Indeed, in this case, if you're patient, you will scroll far enough to
encounter a property called Password Eureka! You know it's a property (as opposed to a
method) because it has an icon next to it that looks like a finger pointing at a piece of
information Pretty useless mnemonic, eh?
Let's give it a try Since you've scrolled down and selected Password, you
Trang 20can press Tab to accept it or you can double-click it right there in the
list Either way, your statement now looks as follows:
ActiveDocument.Password
16 So far so good Many people easily get this far But now what?
17 In this case, the next step is to specify a value for the property To do this, all you do is type a
space and an equals sign after the name of the property, so your screen looks like this (the pipe character “|” represents your flashing insertion point after you've typed a space):
ActiveDocument.Password = |
18 Since the VBA editor has no idea what value you want to use for a password, it can't offer any
suggestions Instead, you simply have to come up with an idea on your own and type it in, perhaps as follows:
ActiveDocument.Password = "billcoan"
That's it! That's it! Now press F5 to run the subroutine Or return to Word and choose
Tools|Macro|Macros |Test|Run (Pressing F5 is easier!)
When this statement runs, it will assign “billcoan” to be the password for the currently active document You might wonder how you were supposed to know that the password had to be enclosed in quotation marks Well, experience ought to be worth something, oughtn't it? In any case, if you had any question, all you would have had to do is position the cursor anywhere in the name of the property (“Password”) and press F1 This would display a help topic that tells you that a password requires a string, which is to say, a bunch of characters inside some
But what about working on a particular part of a document, such as the first paragraph all by itself, or on a collection of parts, such as all the paragraphs? Here lies opportunity! After all, documents aren't just objects unto themselves; they're composed of hundreds of other, smaller objects And you can work on each of those objects individually or as parts of collections
Let's dig deeper and find out how
19 The good news is that you can “reach” any part of a document that you want to work on by
starting out as though you were going to work on the document itself In other words, you can start as you almost always start That is, once again click in a code window and type the
following, making special note of the dot at the end of the expression:
ActiveDocument
20 If you want to work on a part of the active document then you have to start this way
(Remember: No one gets to the father except through me!) When you type the “.” at the end of
Trang 21the expression a list will pop up This list is the most amazing guide you can imagine Each item on the list is either a method or a property of the ActiveDocument or else it's an object unto itself that belongs to the ActiveDocument For now let's deal with objects that belong to the ActiveDocument
An object is something that you can “work on” by applying methods to it or by changing its properties Word documents contain lots of different types of objects When multiple objects of the same type exist (or can exist) in the same document, they are treated as “collections.” For example, a word document has, or can have, multiple paragraphs Each paragraph is an object All of the paragraph objects, together, form the “paragraphs collection.”
The easiest document objects (and collections) to think about are paragraphs, words, and
characters The way to think is this: “I'm trying to work on an object that belongs to the active document, namely a paragraph Since a document can contain more than one paragraph, I'll have to locate the collection of paragraphs and then specify the specific paragraph that I want
to work on If I'm patient and scroll through this list, I'll almost certainly find the collection.”
When you first think this way, the words “object” and “collection” will stick in your throat like
a fishbone Later it will feel more like burnt toast and later still it will feel like love when you hit puberty Indeed, in this case, if you're patient, you will scroll far enough to encounter a collection called Paragraphs Eureka! You know it's not a method because it doesn't have an icon next to it that looks like a green brick flying through the air You don't think of it as a property, either, but you quickly find out that the VBA editor *does* think of it as a property
OK, OK So one of the “properties” of a Word document is that it contains a collection of paragraphs Great So “Paragraphs” is a collection of paragraph objects and “Paragraphs” is a property of a Word document This is confusing, so quit worrying about it Focus on the list! Find the item you want to work on!
Let's give it a try Since you've scrolled down and selected Paragraphs, you can press Tab to accept it or you can double-click it right there in the list Either way, your statement now looks
as follows:
ActiveDocument.Paragraphs
21 So far so good Most people easily get this far But now what? A major wrinkle, that's what!
But hold on, it's easy to deal with Since “Paragraphs” is a collection, you have to tell the VBA editor which paragraph you're interested in You do this with a number in parentheses For example, (1) refers to the first paragraph Let's assume you want to work on the first paragraph
in the collection Type until your statement looks like this:
ActiveDocument.Paragraphs(1)
22 Guess what? You've just “drilled down” from the ActiveDocument object to the Paragraphs
collection to the first Paragraph That is, you've “reached” or “specified” an object that you want to work on From here on out, life is easy Why? Because working on a paragraph object
is just like working on a document object As soon as you type the dot after the object, the VBA editor shows you a list of all the methods, properties, and objects (or collections of
objects) that belong to *your* object Simply select the method that you want to carry out on your object, or select the property that you want to change, or keep drilling down by selecting
Trang 22an object or collection that belongs to your object That's all there is to it
A possible 23rd step would be to repeat Steps 1 -22 but replace all occurrences of
“ActiveDocument.” with “Selection.” This allows you to drill down from the Selection object and discover the various methods, properties, and collections associated with the Selection object
A possible 24th step would be to repeat Steps 1 -22 but replace all occurrences of
“ActiveDocument.” with “Application.” This allows you to drill down from the Word
application object and discover the various methods, properties, and collections associated with that object
5 Making the transition from WordBasic to VBA
Article contributed by Bill Coan The most difficult part about making the transition from WordBasic to VBA is learning to use Word's object model (instead of Word's user interface) as an organizing principle
As a WordBasic programmer, you could view macros as silent, behind-the-scenes users of Word Indeed, if you read through a WordBasic macro, you find a series of commands that might just as well have come from a user When Word 6 or 7 executed a macro, it behaved exactly as it would have behaved if a user were sitting at the keyboard and executing the commands manually
Sure, WordBasic offered looping and branching, but these were merely tools for managing the order
in which commands were to be executed The commands themselves might just as well have come from a user
As a Visual Basic programmer, you can view your macros as object manipulators You can drill down to a particular object and then manipulate it In the end, the types of manipulations that you can perform aren't all that different from what the user can accomplish via the user interface But your macros can “reach” objects in more powerful and flexible ways than user interface commands could ever do Moreover, your macros can reach those objects without regard to the current position
of the selection Plus, you can branch and loop in new, more powerful ways than you could with WordBasic
Consider the following statement, which allows you to manipulate the entire range of text found in the second row of the first table of the active document:
ActiveDocument.Tables(1).Rows(2).Range.Bold = True
Now consider this looping structure, which allows you to manipulate the same range within all
tables in the document:
Dim oTable As Table
For Each oTable In ActiveDocument.Tables
Trang 23oTable.Rows(2).Range.Bold = True
Next oTable
Powerful stuff, eh?
6 Organizing your macros
Article contributed by Beth Melton Many of us have a multitude of macros I have too many to want them all cluttering up my menus,
or to remember keyboard shortcuts for them all Rather than having to look though your list of macros in the Macros dialog, or in the VB Editor, to find the one you want, the following methods may help to make it easier to find/edit/run the macro you want with a few clicks of the mouse; and if you have as many macros as me, will speed up Word as well
6.1 Getting yourself organized
First categorize the macros into different Templates and Add-ins
Macros that are intended for use with specific document types should be placed in the template you base those documents on
I suggest creating at least two add-ins One for those you frequently use and want to have loaded automatically when Word starts and one for those you don't use as often
If you want to further categorize the macros within the add-ins, you can create various modules:
1 With your add-in open, go into the VB Editor (Alt+F11)
2 In the Project pane, select the “project” (add-in)
3 Select Insert/Module
4 Change the name of the Module in the Properties pane (if it isn't visible, press F4)
In each add-in, add each of the macro names and descriptions in the document itself, in a two
column Word table
For each macro you can also include a MacroButton field so you can double-click it to run the macro:
{ MACROBUTTON EmailCleanup Run Macro }
For example:
EmailCleanup
Run Macro
To clean up email that has been copied/pasted into Word;
removes > and | characters, manual line breaks, multiple spaces, etc
FolderContents Creates a new document that lists the file names in a
Trang 24Run Macro specified folder, defaulting to current folder but displaying
a dialog to let you choose the path
Figure 1
Or create a custom toolbar or menu in the add-in, that
invokes the macros, as in Figure 2 See How to assign a
Word command or macro to a toolbar or menu
Figure 2
6.2 Editing Macros
If you want a very quick way to get to the macro you want in order to edit it, store the following macro in Normal.dot, or in an add-in that automatically loads; it opens the VBA editor and takes you directly to the macro whose name you have selected in the current document:
6.3 Organizing your Global Templates
Global templates fall into three categories, Normal.dot; add-ins that automatically load when Word starts, and add-ins that you manually load as needed
To automatically load an add-in when Word starts, place either it, or a shortcut to it, in Word's
Startup folder This location can be obtained from Tools/Options/File Locations
Add-ins you wish to manually load can be placed in any folder except Word's Startup folder I place
all my add-ins in the same folder as each other, and put a shortcut in Word's Startup folder to those Add-ins that I want Word to load automatically Only loading addins when they are needed can speed up Word
To manually load an add-in:
Trang 251 Go to Tools/Templates and Add-Ins
2 Click the Add button and locate the add-in
When Word restarts they will still be in the list,
just unloaded When you need them all you have
to do is go back to Tools/Templates and Add-Ins,
and tick the appropriate one
In my setup, I also include a couple of templates
that come with Microsoft Word as global
templates too Since I have several, I have created
a UserForm to make loading them easier:
I have named my option buttons, respectively
(from top to bottom): optWordMcr, optMacros
and optSupport
Here are the necessary macros for this UserForm:
In your project module:
Sub ShowGlobalTemplates()
frmGlobal.Show
End Sub
In your UserForm module:
Private Sub cmdCancel_Click()
Unload Me
End Sub
Private Sub cmdOK_Click()
If optWordMcr = True Then
Trang 267 When to use parentheses to enclose subroutine and function arguments
Article contributed by Jonathan West
The rules are confusing concerning the use of parentheses to enclose argument lists I have even seen MS Knowledgebase articles that have got it wrong The rules are as follows
1 For the initial line of a Sub or Function, you use parentheses to enclose the arguments (if any),
e.g
Sub MySubroutine(a As Long , b As String )
or
Function MyFunction(a As Long , B As String ) As Long
2 When calling a function, you use parentheses to enclose the arguments, like this
x = MyFunction(a:=1, b:="abc")
or
x = MyFunction(1, "abc")
3 When calling a sub directly, you don't use parentheses, like this
MySub a:=1, b:="abc"
Call MySub(1, "abc")
Note that it doesn't matter whether or not you use the Call keyword to call a subroutine The effect on the program is identical whether or not you use Call (assuming you have the
parentheses right) Most of the code on this site doesn't use Call, simply because it is fewer words to type
5 When dealing with methods of an object, use the same rules, depending on whether you are
obtaining a return value from the method (like a function), or just applying the method to the
Trang 27object without a return value (like a subroutine) For example, on the one hand:
Selection.GoTo What:=wdGoToPage, Name:="5"
But on the other hand (because you're now using a function which returns a value):
Dim MyRange As Range
Set MyRange = ActiveDocument.Range
Set MyRange = MyRange.GoTo(What:=wdGoToPage, Name:="5")
8 The art of defensive programming
Or how to write code that will be easy to maintain
Article contributed by Jonathan West The source code that you write has two quite separate purposes
1 It is a set of instructions to the computer, telling it to perform a particular task
2 It is a description of the task and how you have gone about executing the task, for yourself
and/or other programmers
This article is mainly about the second purpose, which a surprising number of people don't really think about
If you can't understand a program, then you can't debug it Even with code that you have written yourself, if you come back to it six months or a year later, you may find yourself wondering “Why
on earth did I write that? What was it for?” It doesn't take long to forget the details of a program when you aren't working on it any more Make life easier for yourself, and write programs as clearly
as possible Also, provide such defences as you can against the possibility that VBA might change between versions of Word
The following example is based on a real question that came up in the newsgroups in 1999 The questioner wanted a macro that would do the following for a particular set of styles named “AGR1”
to “AGR5”:-
1 Check to see if the style already exists in the document
2 If it does, then do nothing
3 If it doesn't, then copy it from a central template
This is a fairly common kind of problem that Word VBA macros are excellent at solving The
following code was suggested in response to the question
Dim StyleArray As Variant
Dim StyleExists As Boolean
Sub Numbering()
With ActiveDocument
Trang 28For Each oStyle In ActiveDocument.Styles
If oStyle = StyleArray(x) Then
Source:="C:\Program Files\Microsoft Office\" & _
"Templates\Final Numbering TDS.dot", _
1 There is no need for the Dim statements to be module-level declarations They aren't needed in any other routine Therefore they should come after the Sub Numbering() statement, so that there is no confusion with any other routine which might happen to use the same variable names People tend to re-use favored and meaningful names, so this problem can be significant
2 “Numbering” isn't a very meaningful name for the routine It's always a good idea give a routine
a name that clearly describes its purpose Perhaps ImportAGRStyles would be better
3 You should have the “Require Variable Declaration” checkbox set in the VBA Options dialog,
so that there is an Option Explicit statement at the start of every module That way, there is much less chance that VBA will interpret a typographical error in one of your variable
assignments as the creation of an entirely new variable of type Variant This being so, you would need Dim statements for x and oStyle, as Long & Style respectively See also Why
variables should be declared properly
4 With–End With statements are a good idea, but there isn't really any need to use them if you are
only putting one item in between If you have two or more lines between them, then yes, great idea, use them whenever you can
5 When updating the code at some point in the future, you might want to add another one or two
items to StyleArray It would be quite likely that you would forget (at least initially) to change the limit value of x, so that it reflects the new size of the array It is much easier to let VBA to remember for you, by using UBound(StyleArray) That will adjust automatically to the number
of items you include using the Array function
6 A speedup trick In the For Each oStyle loop, once you find a case where you set StyleExists = True, you don't need to go round the loop any more times Therefore you can add an Exit For
Trang 29statement to drop you out of the loop
7 Using If StyleExists = False Then is not very efficient Better would be If Not StyleExists Then
In fact, for readability, better still would be to rename the variable StyleIsMissing and swap the True/False values round, so the line can now read If StyleIsMissing Then
8 You can reset the value of StyleExists (or StyleIsMissing, as we would be renaming it) just once
at the start of the loop, rather than once outside and once at the end of the loop Fewer lines of code means fewer places that bugs can creep in!
9 I don't trust default properties of objects It would be too easy for MS to change them between
versions of Word and break something in my code Therefore I would prefer to use
oStyle.NameLocal rather than just oStyle when comparing its value with StyleArray(x) We haven't been through enough versions of Word with VBA to find out whether this may be a common problem with MS, though a few things did change between Word 97 and Word 2000 I would simply prefer to minimize the risk, especially as I think it improves readability Similarly,
in the OrganizerCopy command, it would be better to use ActiveDocument.FullName instead of just ActiveDocument
10 Indenting the code so that it follows the structure of the If–Then statements and For–Next loops
makes it much easier to understand where the loops and branches are Also, it is a good idea to indent continuations of lines (perhaps by double the normal amount), so that it is quite clear that they are not new instructions Also, it is common to indent everything except for comments and the lines which begin and end a routine
11 When a few lines of code together are needed for a specific task, keep them together, and then
put a blank line below to show that you are starting on a new task That way, the eye more clearly sees what your design is
That's quite a few comments, they are longer than the code I've been discussing! However, with every line of code that you publish (by which I mean that will ever be seen by anyone other than yourself), you should be able to justify it as being about the best way it could be written I have learned the need for this kind of coding discipline the hard way, by having to fix extremely nasty and obscure bugs Also, I have learned that for longer running macros (though this particular macro should be over quickly) every speedup trick in the book is worth applying The code with these changes would now look like this
Option Explicit
Sub ImportAGRStyles()
Dim StyleArray As Variant
Dim StyleIsMissing As Boolean
For Each oStyle In ActiveDocument.Styles
If oStyle.NameLocal = StyleArray(x) Then
StyleIsMissing = False
Trang 30There's one other thing to notice I didn't put any comments in For a routine that's as simple as this,
if the code is written well, there shouldn't be any need for comments At most, you might want a line
or two at the start to describe the purpose of the routine (You might also have other “standard” comments describing the author and revision level of the routine, but that's another issue altogether.) For a longer routine or a particularly obscure bit of code, it's probably a good idea to include a few extra comments at strategic points These comments should aim to explain what you are trying to
do, rather than how you are doing it Once you know the what, the how should be clear from the code itself, and so doesn't need duplication
Recommended further reading
If you want a much fuller treatment of this subject, I would very strongly recommend that you buy a
copy of Code Complete, by Steve McConnell, published 1993 by Microsoft Press, ISBN
1-55615-484-4 The book was written before Word VBA existed, and even before Visual Basic existed, and
so its examples are mainly in C, Pascal and some in BASIC Despite this, it contains the best set of guidelines I have come across for writing good code in any language It covers most of the points I have made in this article, in much greater depth than I possibly could here The book is beautifully clear (just like code should be!), and you will have no difficulty following the examples, whichever language they are written in
Trang 319 How to cut out repetition and write much less code, by using subroutines and functions that take arguments
Article contributed by Dave Rado Most of us write routines that do similar operations more than once It makes your code much less cumbersome and much easier to follow if you hive off all such repetitive chunks of code into
separate subroutines or functions
The difference between a sub and a function is that a function can return a value Within the
function itself, you can treat the function name like a variable, and give it a value and then you can call the function and get that value Here's a ridiculously simple (and not very useful!) example:
Function GetResult As Long
So you could have:
Function SumTwoValues(FirstNum As Long , SecondNum As Long ) As Long
'Any sub or function can have variables passed to it,
'and these variables, which need to be declared as shown here,
enclosed in brackets
'are called arguments
SumTwoValues = FirstNum + SecondNum
Trang 32That's exactly the same as MyResult = 2 + 2, but if the function contained more than just one line of code, and was called often, using a function like that would greatly reduce the amount of code needed and also make your code easier to follow
Let's take a more complex example Supposing you had a macro that needs to do many Find and Replace operations, one after another By using a subroutine that takes arguments to do the Find and Replaces it means you only need to have a single line of code for each and Replace Here's a simple example:
Let's suppose that all your Find & Replace operations are identical except for the find text and replacement text Then you could have a sub or function that gets called, which looks something like this:
Sub DoFindReplace(FindText As String , ReplaceText As String )
Trang 33So only one extra line is needed for each Find and Replace operation
You can make it a bit more flexible by making any other parameters (such as MatchCase) that might change from one find & replace operaration to the next into arguments that you can pass values to, instead of hard coding them
For instance, if MatchCase is always set to False in your macro, you can just hard code it as shown above; but if it's True for some and False for others then you could use:
Sub DoFindReplace(FindText As String , ReplaceText As String , _
Call DoFindReplace("Ibm", "IBMI", True)
Call DoFindReplace("laserjet", "LaserJet", False)
End Sub
With Subs and Functions that take arguments – as with Word's Methods (which are actually built-in functions) – you can choose whether or not to specify the variable names when you call them In the examples given so far I haven't used the variable names in the calling statements; and in the first few examples, there was no real point, because it's obvious what's going on in them
But in the last example, it's not so obvious what the third variable is (without looking at
the function) – so it's better to specify the variable names in this case, which you do like this:
That is much easier to follow, for anyone maintaining your code, than
Call DoFindReplace("Ibm", "IBMI", True)
Call DoFindReplace("laserjet", "LaserJet", False)
Sometimes you might want to specify an argument in a sub or function, but you might want it to be optional Most built-in Word functions include some optional arguments (such as the Background argument of the Printout method, for instance)
To make an argument optional, you simply prefix it with the keyword Optional when you declare it,
for example:
Trang 34Sub DoFindOrReplace(FindText As String , Optional ReplaceText As String )
In this example, the procedure could check the value of the optional ReplaceText variable; and do a Find & Replace if it had a value, but a Find if it didn't
With optional arguments, you can also specify what value the variable should have if no value is specified In other words you can specify its default value If you do that, it's not optional in quite the same sense as before; it's actually mandatory, but you just don't have to specify its value in your code If you don't specify its value, it will use the default value So for example, you could have:
Sub DoFindReplace(FindText As String , ReplaceText As String , _
Optional bMatchCase As Boolean = False )
Then you'd only have to specify the value of the bMatchCase argument if you wanted it to be set to True This can save a lot of typing!
10 How to use a single VBA procedure to read or write both custom and built-in Document Properties
Article contributed by Astrid Zeelenberg
When you work with Document Properties in code, most people end up with two functions or
subroutines, one to write built-in Document Properties and one for custom Document Properties; because in each case the object used to refer to the Document Properties is different – you have to use the CustomDocumentProperties and BuiltinDocumentProperties collection as appropriate But this can be very inconvenient
10.1 Writing Document Properties
However, you can write a procedure which checks whether the property you want to write the value
for is custom or built-in, and then uses the appropriate collection (Note: If you are not familiar with
calling subroutines with arguments, see: How to cut out repetition and write much less code, by using subroutines and functions that take arguments)
This is how to do it:
Public Sub WriteProp(sPropName As String , sValue As String , _
Optional lType As Long = msoPropertyTypeString)
'In the above declaration, "Optional lType As Long =
Trang 35On Error GoTo ErrHandlerWriteProp
'Try to write the value sValue to the custom documentproperties
'If the customdocumentproperty does not exists, an error will occur 'and the code in the errorhandler will run
ActiveDocument.BuiltInDocumentProperties(sPropName).Value = sValue 'Quit this routine
Exit Sub
Proceed:
'We know now that the property is not a builtin documentproperty,
'but a custom documentproperty, so bCustom = True
bCustom = True
Custom:
'Try to set the value for the customproperty sPropName to sValue
'An error will occur if the documentproperty doesn't exist yet
'and the code in the errorhandler will take over
ActiveDocument.CustomDocumentProperties(sPropName).Value = sValue
Exit Sub
AddProp:
'We came here from the errorhandler, so know we know that
'property sPropName is not a built-in property and that there's
'no custom property with this name
'e,g an invalid date was used
Debug.Print "The Property " & Chr(34) & _
sPropName & Chr(34) & " couldn't be written, because " & _
Chr(34) & sValue & Chr(34) & _
" is not a valid value for the property type"
'bCustom is a boolean variable, if the code jumps to this
'errorhandler for the first time, the value for bCustom is False
If Not bCustom Then
'Continue with the code after the label Proceed
Resume Proceed
Else
Trang 36'The errorhandler was executed before because the value for
'the variable bCustom is True, therefor we know that the
'customdocumentproperty did not exist yet, jump to AddProp,
'where the property will be made
'Author is a built-in property
Call WriteProp(sPropName:="Author", sValue:="William Shakespeare") 'Date Updated is a custom document property
Call WriteProp(sPropName:="Date Updated", sValue:="11 Mar 2001", _ lType:=msoPropertyTypeDate)
End Sub
10.2 Reading Document Properties
The same principle can be used when reading Document Properties:
Function ReadProp(sPropName As String ) As Variant
Dim bCustom As Boolean
Dim sValue As String
On Error GoTo ErrHandlerReadProp
'Try the built-in properties first
'An error will occur if the property doesn't exist
sValue = ActiveDocument.BuiltInDocumentProperties(sPropName).Value ReadProp = sValue
'The boolean bCustom has the value False, if this is the first
'time that the errorhandler is runned
If Not bCustom Then
'Continue to see if the property is a custom documentproperty
Resume ContinueCustom
Else
Trang 37'The property wasn't found, return an empty string
11 How to get the username of the current user
Article contributed by Astrid Zeelenberg
If you want a routine that works for all types of networks, you'll have to use an API call to show the username of an user The following code, which needs to be in a Module, was taken from Microsoft Knowledge Base article Q161394
Option Explicit
'Declare for call to mpr.dll
Declare Function WNetGetUser Lib "mpr.dll" _
Alias "WNetGetUserA" ( ByVal lpName As String , _
ByVal lpUserName As String , lpnLength As Long ) As Long
Const NoError = 0 'The Function call was successful
Function GetUserName() As String
'Buffer size for the return string
Const lpnLength As Long = 255
'Get return buffer space
Dim status As Integer
'For getting user information
Dim lpName, lpUserName As String
Trang 38'Assign the buffer size constant to lpUserName
lpUserName = Space$(lpnLength + 1)
'Get the log-on name of the person using product
status = WNetGetUser(lpName, lpUserName, lpnLength)
'See whether error occurred
If status = NoError Then
'This line removes the null character Strings in C are null-
'terminated Strings in Visual Basic are not null-terminated
'The null character must be removed from the C strings to be used 'cleanly in Visual Basic
lpUserName = Left$(lpUserName, InStr(lpUserName, Chr(0)) - 1)
Note that if the user is not logged in, the function will return nothing (an empty string)
12 How can I get a list of the available printer names?
Article contributed by Astrid Zeelenberg
It requires some Windows API trickery, because VBA (unlike VB) does not have a Printers
collection Paste the following into a separate module The function ListPrinters returns a variant containing an array of printer names
Option Explicit
Const PRINTER_ENUM_CONNECTIONS = &H4
Const PRINTER_ENUM_LOCAL = &H2
Private Declare Function EnumPrinters Lib "winspool.drv" Alias
Trang 39( ByVal RetVal As String , ByVal Ptr As Long ) As Long
Private Declare Function StrLen Lib "kernel32" Alias "lstrlenA" _
( ByVal Ptr As Long ) As Long
Public Function ListPrinters() As Variant
Dim bSuccess As Boolean
Dim iBufferRequired As Long
Dim iBufferSize As Long
Dim iBuffer() As Long
Dim iEntries As Long
Dim iIndex As Long
Dim strPrinterName As String
Dim iDummy As Long
Dim iDriverBuffer() As Long
Dim strPrinters() As String
iBufferSize = 3072
ReDim iBuffer((iBufferSize \ 4) - 1) As Long
'EnumPrinters will return a value False if the buffer is not big enough
bSuccess = EnumPrinters(PRINTER_ENUM_CONNECTIONS Or _
PRINTER_ENUM_LOCAL, vbNullString, _
1, iBuffer(0), iBufferSize, iBufferRequired, iEntries)
If Not bSuccess Then
If iBufferRequired > iBufferSize Then
iBufferSize = iBufferRequired
Debug.Print "iBuffer too small Trying again with "; _
iBufferSize & " bytes."
ReDim iBuffer(iBufferSize \ 4) As Long
If Not bSuccess Then
MsgBox "Error enumerating printers."
Exit Function
Else
ReDim strPrinters(iEntries - 1)
For iIndex = 0 To iEntries - 1
'Get the printername
strPrinterName = Space$(StrLen(iBuffer(iIndex * 4 + 2)))
iDummy = PtrToStr(strPrinterName, iBuffer(iIndex * 4 + 2))
strPrinters(iIndex) = strPrinterName
Next iIndex
Trang 40Public Function IsBounded(vArray As Variant ) As Boolean
'If the variant passed to this function is an array, the function will return True;
'otherwise it will return False
On Error Resume Next
IsBounded = IsNumeric( UBound(vArray))
End Function
13 How to find out, using VBA, how many replacements Word made during a Find & Replace All
Or: How to find out how many occurrences there are of a particular word in a document
Article contributed by Bart Verbeek and Dave Rado
When you click Replace All in the Find & Replace dialog, Word shows the number of replacements
on the Status bar after the operation is completed To the regret of many it is impossible to query this number in VBA But that does not mean you cannot determine the number of replacements if you want to The following VBA code sample does just that (If you are not familiar with using