|
|||||||||||||||||||||
|
|||||||||||||||||||||
|
Announcements
Want a new Job?
Chapters
Services
Feature Zones
|
Note: This is an unedited contribution. If this article is inappropriate,
needs attention or copies someone else's work without reference then please
Report This Article
IntroductionWord Automation is the development of software routines which help optimize or automate existing word procedures. There are mainly two ways to automate word using the technology used. Late Binding and Early Binding. Before coming to the automation, a brief introduction about these technologies are necessary. Early BindingEarly binding is the commonly used technology. Here directly add the reference of the office library to the project.
Then use the namespace in the code file and do code using the classes and interfaces provided by the office library. The coding is almost like: public object CreateWordApplication()
{
Word.Application wordApp = new Word.ApplicationClass();
return wordApp;
}
Early binding has several advantages.
Late BindingLate binding uses another mechanism called reflection for invoking the office libraries and do not need any office reference. It mainly uses two lines to communicate with office library. Type wordType = Type.GetTypeFromProgID("Word.Application");
object wordApplication = Activator.CreateInstance(wordType);
By using this wordApplication object, we can do almost all the operations. The main advantages of late binding is, it will invoke our machine's active word application and is version independent. But its performance is slower than early binding. By using early binding, develop an automation is very easy and in this article, I am giving a brief idea about word automation using late binding and early binding. Problem SpecificationHere I am discussing a simple word automation, which is used for getting a particular word count from a document. Also replace that word with another word. At first we can implement it using early binding using Word 2003 office reference. After that, we can use late binding for solve the same problem in all the word versions. Word Automation: Early BindingThe first step of doing a word automation using early binding is, install a particular version of office version in your machine. Then create a new Class Library named "WordAutomation" and give the reference of the Office Library in your project as mentioned above. Using CodeUsing NamespacesCreate a new class named WordAutomation in your project and use the following namespaces. using Word;
using Office;
Creating a WordApplication ObjectThe following code will create a word application object. public object CreateWordApplication()
{
Word.Application wordApp = new Word.ApplicationClass();
return wordApp;
}
Closing a WordApplicationHere the parameter is an already created word application object using the CreateWordApplication() function. public bool CloseWordApp(object wordApplication)
{
bool isSuccess = false;
if (wordApplication != null)
{
object missing = System.Reflection.Missing.Value;
object saveChanges = Word.WdSaveOptions.wdSaveChanges;
Word.Application wordApp =
wordApplication as Word.ApplicationClass;
wordApp.Quit(ref saveChanges, ref missing, ref missing);
isSuccess = true;
}
return isSuccess;
}
Creating a Word Document ObjectThe CreateWordDoc function mainly requires a word document file name and a word application that we have created using CreateWordApplication() function. We can open it in either read only mode or read write mode. public object CreateWordDoc(object fileName,
object wordApplication, bool isReadonly)
{
Word.Document wordDoc = null;
Word.Application wordApp = null;
if (wordApplication != null)
{
wordApp = wordApplication as Word.ApplicationClass;
}
if (File.Exists(fileName.ToString()) && wordApp != null)
{
object readOnly = isReadonly;
object isVisible = true;
object missing = System.Reflection.Missing.Value;
wordDoc = wordApp.Documents.Open(ref fileName, ref missing,
ref readOnly, ref missing, ref missing, ref missing,
ref missing, ref missing, ref missing, ref missing,
ref missing, ref isVisible);
}
return wordDoc;
}
Closing a Word Document ObjectThe CloseWordDoc() function will close an already created word document using CreateWordDoc() function. public bool CloseWordDoc(object wordDocument, bool canSaveChange)
{
bool isSuccess = false;
if (wordDocument != null)
{
object missing = System.Reflection.Missing.Value;
object saveChanges = null;
if (canSaveChange)
{
saveChanges = Word.WdSaveOptions.wdSaveChanges;
}
else
{
saveChanges = Word.WdSaveOptions.wdDoNotSaveChanges;
}
Word.Document wordDoc = wordDocument as Word.Document;
wordDoc.Close(ref saveChanges, ref missing, ref missing);
isSuccess = true;
}
return isSuccess;
}
Getting Word CountThe word document has different objects inside. The main area of a word document is called Content. Another objects are Shapes, Comments, Header and Footer etc. This function will find out all the word count from the above sections. The main parameters of this function are a word document and a text to search in the document. Here the main function to count the word occurrence is GetCountFromRange() which is explained below. public int GetWordCount(object wordDoc, string word)
{
int count = 0;
do
{
if (wordDoc == null)
{
break;
}
if (word.Trim().Length == 0)
{
break;
}
Word.Document wordDocument = wordDoc as Word.Document;
wordDocument.Activate();
// Get the count from direct text inside the document.
count+= GetCountFromRange(
wordDocument.Content,wordDocument,word);
// Get the word count from the comments
foreach(Word.Comment com in wordDocument.Comments)
{
count+= GetCountFromRange(
com.Range,wordDocument,word);
break;
}
// Get the word count from all headers
foreach(Word.HeaderFooter header in
wordDocument.Sections.Last.Headers)
{
count+= GetCountFromRange(
header.Range,wordDocument,word);
}
// Get the word count from all headers
foreach(Word.HeaderFooter footer in
wordDocument.Sections.Last.Footers)
{
count+= GetCountFromRange(
footer.Range,wordDocument,word);
}
// Get the word count from all shapes
foreach (Word.Shape shape in wordDocument.Shapes)
{
if (shape.TextFrame.HasText < 0)
{
count+=GetCountFromRange( shape.TextFrame.TextRange,wordDocument,word);
}
}
}
while(false);
return count;
}
Getting Word Count from a RangeThe word finds a text inside a range. The range may be the following.
We can search or replace a text by using a range and the sample code is shown. private int GetCountFromRange(
Word.Range range, Word.Document wordDocument,string word)
{
int count = 0;
object missing = System.Reflection.Missing.Value;
object matchAllWord = true;
object item = Word.WdGoToItem.wdGoToPage;
object whichItem = Word.WdGoToDirection.wdGoToFirst;
wordDocument.GoTo(
ref item, ref whichItem,ref missing, ref missing);
range.Find.ClearFormatting();
range.Find.Forward = true;
range.Find.Text = word;
range.Find.Execute(
ref missing, ref missing, ref matchAllWord, ref missing,
ref missing, ref missing, ref missing, ref missing,
ref missing, ref missing,ref missing, ref missing,
ref missing, ref missing, ref missing);
while (range.Find.Found)
{
++count;
range.Find.Execute(
ref missing, ref missing, ref matchAllWord, ref missing,
ref missing, ref missing, ref missing, ref missing,
ref missing, ref missing,ref missing, ref missing,
ref missing, ref missing, ref missing);
}
return count;
}
Find and ReplaceThe find and replace is similar to Find, the only difference is needs to provide the replace text also. The function ReplaceRange() will replace the find text with replace text. public bool FindReplace(object wordDoc,object wordApplication,
string findText, string replaceText)
{
bool isSuccess = false;
do
{
if (wordDoc == null)
{
break;
}
if (wordApplication == null)
{
break;
}
if (replaceText.Trim().Length == 0)
{
break;
}
if (findText.Trim().Length == 0)
{
break;
}
Word.Application wordApp =
wordApplication as Word.ApplicationClass;
Word.Document wordDocument = wordDoc as Word.Document;
ReplaceRange(wordDocument.Content,wordDocument,wordApp,findText,replaceText);
foreach (Word.Comment comment in wordDocument.Comments)
{
ReplaceRange(comment.Range, wordDocument,wordApp,findText,replaceText);
}
foreach (Word.HeaderFooter header in
wordDocument.Sections.Last.Headers)
{
ReplaceRange(header.Range, wordDocument,wordApp,findText,replaceText);
}
foreach (Word.HeaderFooter footer in
wordDocument.Sections.Last.Footers)
{
ReplaceRange(footer.Range, wordDocument,wordApp,findText,replaceText);
}
foreach (Word.Shape shp in wordDocument.Shapes)
{
if (shp.TextFrame.HasText < 0)
{
ReplaceRange(shp.TextFrame.TextRange,
wordDocument,wordApp,findText,replaceText);
}
}
isSuccess = true;
}
while(false);
return isSuccess;
}
Replace within a RangeThis function will replace a find text with replace text. private void ReplaceRange(Word.Range range,
Word.Document wordDocument,
Word.Application wordApp,
object findText, object replaceText)
{
object missing = System.Reflection.Missing.Value;
wordDocument.Activate();
object item = Word.WdGoToItem.wdGoToPage;
object whichItem = Word.WdGoToDirection.wdGoToFirst;
object forward = true;
wordDocument.GoTo(ref item, ref whichItem,
ref missing, ref missing);
object replaceAll = Word.WdReplace.wdReplaceAll;
object matchAllWord = true;
range.Find.ClearFormatting();
range.Find.Replacement.ClearFormatting();
range.Find.Execute(ref findText, ref missing, ref matchAllWord,
ref missing, ref missing, ref missing, ref forward,
ref missing, ref missing, ref replaceText, ref replaceAll,
ref missing, ref missing, ref missing, ref missing);
}
Word Automation: Late BindingThe late binding is not required any references and namespace usage in the project. The coding is very difficult and we have to know the functions and properties. Using CodeUsing NamespacesFor working with late binding, you have to use the reflection mechanism. So the following namespace usage is required. using System.Reflection; Creating a WordApplication ObjectIf at least one of the word version is installed in your machine, this function create a word type using the program id of word. If the system has multiple versions, then at a time only one of the word version is active and this function will create the type of the active version. The create instance method will create the word application object. public object CreateWordApplication()
{
string message = "Failed to create word application. Check whether"
+ " word installation is correct.";
Type wordType = Type.GetTypeFromProgID("Word.Application");
if (wordType == null)
{
throw new Exception(message);
}
object wordApplication = Activator.CreateInstance(wordType);
if (wordApplication == null)
{
throw new Exception(message);
}
return wordApplication;
}
Closing a WordApplicationFor closing the word application, we have to invoke the Quit method of the word application. Here another method is used to invoke the Quit function called InvokeMethod() and is explained in below section. The parameter wordApplication is the instance created from CreateWordApplication() function. public bool CloseWordApp(object wordApplication)
{
bool isSuccess = false;
if (wordApplication != null)
{
object saveChanges = -1; // Save changes
InvokeMember("Quit",wordApplication,new object[]{saveChanges});
isSuccess = true;
}
return isSuccess;
}
Creating a Word Document ObjectHere the wordApplication and filename are required to create a word document. Inside the function, we are using a function GetProperty() for getting the property named Documents from word application and is explained in below section. public object CreateWordDoc(object fileName,
object wordApplication, bool isReadonly)
{
object wordDocument = null;
if (File.Exists(fileName.ToString()) && wordApplication != null)
{
object readOnly = isReadonly;
object isVisible = true;
object missing = System.Reflection.Missing.Value;
object wordDocuments = GetProperty("Documents", wordApplication);
// Open a given Word document.
wordDocument = InvokeMember("Open",wordDocuments,
new object[]{fileName, missing, isReadonly, missing, missing, missing,
missing, missing, missing, missing,
missing, isVisible});
}
return wordDocument;
}
Closing a Word Document ObjectThis function requires mainly two parameters, a word document and save changes information. The main thing to understand here is, we have to pass a save changes information to the Close() function of word document, which is an enum value (Word.WdSaveOptions)and you can refer the same function in early binding for better understanding. But instead of that enum member, we will just pass its integer value here, because we don't have any access to that enum here. Here the value –1 is the integer value of Word.WdSaveOptions.wdSaveChanges and the value 0 is the integer value of Word.WdSaveOptions.wdDoNotSaveChanges. public bool CloseWordDoc(object wordDocument, bool canSaveChange)
{
bool isSuccess = false;
if (wordDocument != null)
{
object saveChanges = null;
if (canSaveChange)
{
saveChanges = -1; // Save Changes
}
else
{
saveChanges = 0; // No changes
}
InvokeMember("Close",wordDocument,new object[]{saveChanges});
isSuccess = true;
}
return isSuccess;
}
Getting Word CountCompare the below function with late binding technology and try to understand how the early binding code is converted to late binding. public int GetWordCount(object wordDoc, string word)
{
int count = 0;
do
{
if (wordDoc == null)
{
break;
}
if (word.Trim().Length == 0)
{
break;
}
InvokeMember("Activate",wordDoc);
object content = GetProperty("Content",wordDoc);
// Get the count from direct text inside the document.
count+= GetCountFromRange(
content,wordDoc,word);
object comments = GetProperty("Comments",wordDoc);
object count1 = GetProperty("Count", comments);
int rangeCount = (int)count1;
for(int i = 1; i <= rangeCount;)
{
object comment = InvokeMember("Item",
comments, new object[] { i });
object range = GetProperty("Range", comment);
count+= GetCountFromRange(
range,wordDoc,word);
break;
}
object sections = GetProperty("Sections",wordDoc);
object last = GetProperty("Last",sections);
object headers = GetProperty("Headers",last);
rangeCount = (int)GetProperty("Count", headers);
for (int i = 1; i <= rangeCount; i++)
{
object header = InvokeMember("Item",
headers, new object[] { i });
object range = GetProperty("Range", header);
count+= GetCountFromRange(
range,wordDoc,word);
}
object footers = GetProperty("Footers",last);
rangeCount = (int)GetProperty("Count", footers);
for (int i = 1; i <= rangeCount; i++)
{
object footer = InvokeMember("Item",
footers, new object[] { i });
object range = GetProperty("Range", footer);
count+= GetCountFromRange(
range,wordDoc,word);
}
object shapes = GetProperty("Shapes",wordDoc);
rangeCount = (int)GetProperty("Count", shapes);
for (int i = 1; i <= rangeCount; i++)
{
object shape = InvokeMember("Item",
shapes, new object[] { i });
object textFrame = GetProperty("TextFrame",shape);
int hasText = (int)GetProperty("HasText",textFrame);
if (hasText < 0)
{
object range = GetProperty("TextRange", textFrame);
count+= GetCountFromRange(
range,wordDoc,word);
}
}
}
while(false);
return count;
}
Getting Word Count from a RangeHere also the logic is same as early binding, only conversion is done. private int GetCountFromRange(
object range, object wordDocument,string word)
{
int count = 0;
object missing = System.Reflection.Missing.Value;
object matchAllWord = true;
object item = 1; // Goto Page
object whichItem = 1;// First page
InvokeMember("GoTo",wordDocument,new object[]{item, whichItem});
object find = GetProperty("Find",range);
InvokeMember("ClearFormatting",find);
SetProperty("Forward",find,true);
SetProperty("Text",find,word);
SetProperty("MatchWholeWord",find,true);
InvokeMember("Execute",find);
bool found = (bool)GetProperty("Found",find);
while (found)
{
++count;
InvokeMember("Execute",find);
found = (bool)GetProperty("Found",find);
}
return count;
}
Find and ReplaceThe find and replace is similar to Find, the only difference is needs to provide the replace text also. The function ReplaceRange() will replace the find text with replace text. public bool FindReplace(object wordDoc,object wordApplication,
string findText, string replaceText)
{
bool isSuccess = false;
do
{
if (wordDoc == null)
{
break;
}
if (wordApplication == null)
{
break;
}
if (replaceText.Trim().Length == 0)
{
break;
}
if (findText.Trim().Length == 0)
{
break;
}
object content = GetProperty("Content",wordDoc);
ReplaceRange(content,wordDoc,wordApplication,findText,replaceText);
object comments = GetProperty("Comments",wordDoc);
object count1 = GetProperty("Count", comments);
int rangeCount = (int)count1;
for(int i = 1; i <= rangeCount; i++)
{
object comment = InvokeMember("Item",
comments, new object[] { i });
object range = GetProperty("Range", comment);
ReplaceRange(range,wordDoc,wordApplication,findText,replaceText);
}
object sections = GetProperty("Sections",wordDoc);
object last = GetProperty("Last",sections);
object headers = GetProperty("Headers",last);
rangeCount = (int)GetProperty("Count", headers);
for (int i = 1; i <= rangeCount; i++)
{
object header = InvokeMember("Item",
headers, new object[] { i });
object range = GetProperty("Range", header);
ReplaceRange(range,wordDoc,wordApplication,findText,replaceText);
}
object footers = GetProperty("Footers",last);
rangeCount = (int)GetProperty("Count", footers);
for (int i = 1; i <= rangeCount; i++)
{
object footer = InvokeMember("Item",
footers, new object[] { i });
object range = GetProperty("Range", footer);
ReplaceRange(range,wordDoc,wordApplication,findText,replaceText);
}
object shapes = GetProperty("Shapes",wordDoc);
rangeCount = (int)GetProperty("Count", shapes);
for (int i = 1; i <= rangeCount; i++)
{
object shape = InvokeMember("Item",
shapes, new object[] { i });
object textFrame = GetProperty("TextFrame",shape);
int hasText = (int)GetProperty("HasText",textFrame);
if (hasText < 0)
{
object range = GetProperty("TextRange", textFrame);
ReplaceRange(range,wordDoc,wordApplication,findText,replaceText);
}
}
isSuccess = true;
}
while(false);
return isSuccess;
}
Replace within a RangeThis function will replace a find text with replace text. private void ReplaceRange(object range,
object wordDocument,object wordApp,
string findText, string replaceText)
{
object missing = System.Reflection.Missing.Value;
InvokeMember("Activate",wordDocument);
object item = 1;
object whichItem = 1;
InvokeMember("GoTo",wordDocument,new object[]{item, whichItem});
object replaceAll = 2;
object matchAllWord = true;
object find = GetProperty("Find",range);
InvokeMember("ClearFormatting",find);
object replacement = GetProperty("Replacement",find);
InvokeMember("ClearFormatting",replacement);
InvokeMember("Execute",find,
new object[]{findText,false,true,missing,missing,missing,true,missing,missing
,replaceText,replaceAll});
}
Reflection UtilitiesInvoking a Methodprivate object InvokeMember(string method, object instance,
object[] parameters)
{
Type type = instance.GetType();
return type.InvokeMember(method,
BindingFlags.InvokeMethod,null,instance,parameters);
}
Invoking a Methodprivate object InvokeMember(string method, object instance)
{
Type type = instance.GetType();
return type.InvokeMember(method,
BindingFlags.InvokeMethod,null,instance,new object[]{});
}
Getting a Propertyprivate object GetProperty(string propertyName, object instance)
{
Type type = instance.GetType();
return type.InvokeMember(propertyName,
BindingFlags.GetProperty,null,instance,new object[]{});
}
Setting a Propertyprivate void SetProperty(string propertyName,object instance, object value)
{
Type type = instance.GetType();
type.InvokeMember(
propertyName,BindingFlags.SetProperty,
null,instance,new object[]{ value} );
}
Points of InterestGetting Office Class DetailsBefore going to late binding type of coding, you have to know the object details of office libraries. For this purpose, one of the better solution is the object browser of office applications. For getting the object browser, open Microsoft Word, open the menu Tools->Macro->Visual Basic Editor or press Alt+F11. This will open a new Microsoft Visual Basic Environment. From this editor, open View menu and click on Object Browser submenu or press F2. You will get the following window and all the object information are listed here.
| ||||||||||||||||||||