![]() |
Languages »
C# »
General
Intermediate
License: The Code Project Open License (CPOL)
Word AutomationBy Prathapachandran.vWord Automation using Early Binding and Late Binding |
C# 2.0, Windows, .NET 2.0VS2005, Dev
|
||||||||
|
Advanced Search |
|
|
|
||||||||||||||||
Word 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 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 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.
Here 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.
The 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.
Create a new class named WordAutomation in your project and use the following namespaces.
using Word;
using Office;
The following code will create a word application object.
public object CreateWordApplication()
{
Word.Application wordApp = new Word.ApplicationClass();
return wordApp;
}
Here 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;
}
The 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;
}
The 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;
}
The 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;
}
The 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;
}
The 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;
}
This 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);
}
The 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.
For working with late binding, you have to use the reflection mechanism. So the following namespace usage is required.
using System.Reflection;
If 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;
}
For 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;
}
Here 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;
}
This 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;
}
Compare 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;
}
Here 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;
}
The 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;
}
This 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});
}
private object InvokeMember(string method, object instance,
object[] parameters)
{
Type type = instance.GetType();
return type.InvokeMember(method,
BindingFlags.InvokeMethod,null,instance,parameters);
}
private object InvokeMember(string method, object instance)
{
Type type = instance.GetType();
return type.InvokeMember(method,
BindingFlags.InvokeMethod,null,instance,new object[]{});
}
private object GetProperty(string propertyName, object instance)
{
Type type = instance.GetType();
return type.InvokeMember(propertyName,
BindingFlags.GetProperty,null,instance,new object[]{});
}
private void SetProperty(string propertyName,object instance, object value)
{
Type type = instance.GetType();
type.InvokeMember(
propertyName,BindingFlags.SetProperty,
null,instance,new object[]{ value} );
}
Before 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.

General
News
Question
Answer
Joke
Rant
Admin
|
PermaLink |
Privacy |
Terms of Use
Last Updated: 8 Nov 2007 Editor: |
Copyright 2007 by Prathapachandran.v Everything else Copyright © CodeProject, 1999-2009 Web19 | Advertise on the Code Project |