Click here to Skip to main content
15,885,546 members
Articles / Desktop Programming / Windows Forms

Word Automation using Late binding - Usage of dynamic Keyword

Rate me:
Please Sign up or sign in to vote.
4.92/5 (15 votes)
9 Nov 2011CPOL2 min read 74.9K   2K   42   8
This article gives a brief idea about word automation using late binding and dynamic keyword in .NET Framework 4.0

Introduction

Do you have a requirement to develop a Word automation application (Microsoft Office Automation), that will support in all the Word versions? What exactly may be the solution?

The answer is Late Binding. In C#, it is possible to implement late-binding using reflection and you can see its implementation in my previous article "Word Automation".

This article gives an idea about the implementation of late binding using dynamic keyword in .NET Framework 4.0

Dynamic Keyword

This is a new keyword introduced in VS 2010 and this type will bypass static check at compile time. That is, an element that is typed as dynamic is assumed to support any operation and the compiler will not give any error. But when executing, the run time will try to invoke the method/property using reflection and if it is found, then that will execute, otherwise will provide run time error. Refer to the MSDN link for getting a better idea about the dynamic keyword.

Late Binding

In the case of early binding, we have to refer (add reference) to the Office Word object library directly to the project. The result is, only that type can be used inside the project and while running, that Word version should be installed in the target machine. But in the case of late binding, just create the Word application instance using the program id as shown below:

C#
Type wordType = Type.GetTypeFromProgID("Word.Application"); 
dynamic wordApplication = Activator.CreateInstance(wordType);  

This will give a Word application object with installed Word version. After that, all the properties, functions, etc. can be invoked using reflection. No need to add any office references statically in the project. See the image below:

Referances.JPG

Is it possible to support all Microsoft Word versions using a single implementation?

The answer is yes up to the current Word version (2010). How? Let us take one example. We have one API provided by Microsoft WordApplication.Documents.Open() for opening a document to the document collection. You can see the syntax below, where all the parameters in bold letters are the same. They will add new parameters when changing one version to the next, by maintaining all the previous version's API parameters (order-wise also) same.

In our implementation, we will only provide the mandatory parameters for opening a document (represented using bold letters). In all the other APIs, the thing is the same. That is the secret for supporting multiple versions.

Office 2000

C#
WordApplication.Documents.Open(FileName,ConfirmConversions,
    ReadOnly,AddToRecentFiles,PasswordDocument, PasswordTemplate,Revert, 
    WritePasswordDocument, WritePasswordTemplate,Format,Encoding, Visible);

Office 2003

C#
WordApplication.Documents.Open(FileName,ConfirmConversions,
    ReadOnly,AddToRecentFiles, PasswordDocument, PasswordTemplate, Revert, 
    WritePasswordDocument,WritePasswordTempate,Format,Encoding, Visible, 
    OpenAndRepair, DocumentDirection,NoEncodingDialog, XMLTransform);

Office 2007

C#
WordApplication.Documents.Open(FileName,ConfirmConversions,
    ReadOnly,AddToRecentFiles,PasswordDocument,PasswordTemplate,Revert,
    WritePasswordDocument,WritePasswordTemplate,Format,Encoding,Visible,
    OpenAndRepair, DocumentDirection, NoEncodingDialog, XMLTransform);  

Office 2010

C#
WordApplication.Documents.Open(FileName,ConfirmConversions,ReadOnly,  
    AddToRecentFiles,  PasswordDocument,PasswordTemplate,Revert,
    WritePasswordDocument,WritePasswordTemplate,Format,Encoding,Visible,
    OpenAndRepair,DocumentDirection,NoEncodingDialog, XMLTransform) 

Using the Code

The below code samples give an idea about how to use dynamic keyword for accessing methods/properties from any type.

Creating Word Application

C#
dynamic _wordApplication = null;
dynamic _wordDoc = null;
public void 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);
    }
      _wordApplication = Activator.CreateInstance(wordType);
      if (_wordApplication == null)
      {
            throw new Exception(message);
      }
}  

Opening Word Document

C#
public void CreateWordDoc(object fileName, bool isReadonly)
{    
    if (File.Exists(fileName.ToString()) && _wordApplication != null)
    {                
        object readOnly = isReadonly;
        object isVisible = true;
        object missing = System.Reflection.Missing.Value;
        // Open a given Word document.
        _wordDoc = _wordApplication.Documents.Open(fileName, missing,
                            isReadonly, missing, missing, missing,
                            missing, missing, missing, missing,
                            missing, isVisible);
    }            
} 

Closing Word Document

C#
public bool CloseWordDoc(bool canSaveChange)
{
    bool isSuccess = false;
    if (_wordDoc != null)
    {        
        object saveChanges = null;                
        if (canSaveChange)
        {
            saveChanges = -1; // Save Changes
        }
        else
        {
            saveChanges = 0; // No changes
        }
        _wordDoc.Close(saveChanges);
        _wordDoc = null;
        isSuccess = true;
    }
    return isSuccess;
} 

Closing Word Application

C#
public bool CloseWordApp()
{
    bool isSuccess = false;
    if (_wordApplication != null)
    {                                
        object saveChanges = -1; // Save changes
        _wordApplication.Quit(saveChanges);
        _wordApplication = null;
        isSuccess = true;
    }
    return isSuccess;
} 

Getting a Word Count

C#
public int GetWordCount(string word)
{
    object wordDoc = _wordDoc;
    int count = 0;
    do
    {
        if (_wordDoc == null)
        {
            break;
        }
        if (word.Trim().Length == 0)
        {
            break;
        }
        _wordDoc.Activate();
        dynamic content = _wordDoc.Content;
        // Get the count from direct text inside the document.
        count += GetCountFromRange(_wordDoc.Content, word);    
        int rangeCount = _wordDoc.Comments.Count;
        for(int i = 1; i <= rangeCount;)
        {
            count += GetCountFromRange(_wordDoc.Comments.Item(i), word);
            break;
        }
        rangeCount = _wordDoc.Sections.Last.Headers.Count;
        for (int i = 1; i <= rangeCount; i++)
        { 
            count += GetCountFromRange(
            _wordDoc.Sections.Last.Headers.Item(i).Range, word);
        }
        rangeCount = _wordDoc.Sections.Last.Footers.Count;
        for (int i = 1; i <= rangeCount; i++)
        {
            count += GetCountFromRange(
            _wordDoc.Sections.Last.Footers.Item(i).Range, word);
        }
        rangeCount = _wordDoc.Shapes.Count;
        for (int i = 1; i <= rangeCount; i++)
        { 
            dynamic textFrame = _wordDoc.Shapes.Item(i).TextFrame;
            int hasText = textFrame.HasText;
            if (hasText < 0)
            {
                count += GetCountFromRange(textFrame.TextRange, word);
            }
        }            
    }
    while(false);
    return count;
} 

Getting Word Count from a Range

C#
private int GetCountFromRange(dynamic range,string word)
{
    int count = 0;
    object missing = System.Reflection.Missing.Value;
    object matchAllWord = true;
    object item = 1; // Goto Page
    object whichItem = 1;// First page    
    _wordDoc.Goto(item, whichItem);            
    dynamic find = range.Find;
    find.ClearFormatting();
    find.Forward = true;
    find.Text = word;
    find.MatchWholeWord = true;
    find.Execute();
    bool found = find.Found;
    while (found)
    {
        ++count;
        find.Execute();
        found = find.Found;
    }
    return count;
}  

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Founder Appfabs
India India
- C#, WPF, WCF
- Java, J2EE, jersey, Spring MVC, Spring Boot, Spring Security, Hibernate, Spring Data
- NoSQL with Cassandra
- C, C++, Qt, OpenGL, Python
- SQL Server, Oracle, PostgreSQL
- Glassfish, Apache, Tomcat, NodeJS and IIS
- Hadoop, Flume, Spark
- Amazon Web Services (AWS) & Jenkins
- Big Data and Analytics

Comments and Discussions

 
SuggestionNamed arguments Pin
Yves Goergen14-Feb-17 4:26
Yves Goergen14-Feb-17 4:26 
QuestionIs it possible to create table of Images at run time and add same table to the Work document opened. Pin
Member 892570625-Jun-13 23:30
Member 892570625-Jun-13 23:30 
QuestionHow can I use this code in 3.5 where dynamic keyword is not available? Pin
Member 305775617-Dec-12 19:57
Member 305775617-Dec-12 19:57 
QuestionInteresting Pin
Slacker0079-Nov-11 0:21
professionalSlacker0079-Nov-11 0:21 
GeneralMy vote of 1 Pin
Selvin8-Nov-11 21:34
Selvin8-Nov-11 21:34 
GeneralThis is worth way more than 1 Pin
Pascal Ganaye8-Nov-11 23:06
Pascal Ganaye8-Nov-11 23:06 
GeneralRe: This is worth way more than 1 Pin
Selvin9-Nov-11 0:26
Selvin9-Nov-11 0:26 
"Going late binding make it work with absolutely every version of word."

in framework 4.0 static will do the same ...


fx.:
0. create new project
1. add references to Microsoft.Office.Interop.Word
2. add button with click event

C#
private void button1_Click(object sender, EventArgs e)
{
    var app = new Microsoft.Office.Interop.Word.Application();
    app.Visible = true;
    Thread.Sleep(3000);
    app.Quit();
}

3. compile
there will be no static reference to Microsoft.Office.Interop.Word.dll
compiler will generate code like this:

C#
namespace Microsoft.Office.Interop.Word
{
    [ComImport, DefaultMember("Name"), TypeIdentifier, CompilerGenerated, Guid("00020970-0000-0000-C000-000000000046")]
    public interface _Application
    {
        void _VtblGap1_26();
        bool Visible { [DispId(23)] get; [param: In] [DispId(23)] set; }
        void _VtblGap2_85();
        [DispId(1105)]
        void Quit([In, Optional, MarshalAs(UnmanagedType.Struct)] ref object SaveChanges, [In, Optional, MarshalAs(UnmanagedType.Struct)] ref object OriginalFormat, [In, Optional, MarshalAs(UnmanagedType.Struct)] ref object RouteDocument);
    }

    [ComImport, CoClass(typeof(object)), Guid("00020970-0000-0000-C000-000000000046"), TypeIdentifier, CompilerGenerated]
    public interface Application : _Application, ApplicationEvents4_Event
    {
    }

    [ComImport, Guid("000209F7-0000-0000-C000-000000000046"), TypeIdentifier, InterfaceType(ComInterfaceType.InterfaceIsIDispatch), CompilerGenerated]
    public interface ApplicationEvents
    {
    }

    [ComImport, TypeIdentifier("00020905-0000-0000-c000-000000000046", "Microsoft.Office.Interop.Word.ApplicationEvents_Event"), CompilerGenerated, ComEventInterface(typeof(ApplicationEvents), typeof(ApplicationEvents))]
    public interface ApplicationEvents_Event
    {
    }

    [ComImport, TypeIdentifier, InterfaceType(ComInterfaceType.InterfaceIsIDispatch), Guid("000209FE-0000-0000-C000-000000000046"), CompilerGenerated]
    public interface ApplicationEvents2
    {
    }

    [ComImport, TypeIdentifier("00020905-0000-0000-c000-000000000046", "Microsoft.Office.Interop.Word.ApplicationEvents2_Event"), ComEventInterface(typeof(ApplicationEvents2), typeof(ApplicationEvents2)), CompilerGenerated]
    public interface ApplicationEvents2_Event
    {
    }

    [ComImport, CompilerGenerated, Guid("00020A00-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIDispatch), TypeIdentifier]
    public interface ApplicationEvents3
    {
    }

    [ComImport, CompilerGenerated, ComEventInterface(typeof(ApplicationEvents3), typeof(ApplicationEvents3)), TypeIdentifier("00020905-0000-0000-c000-000000000046", "Microsoft.Office.Interop.Word.ApplicationEvents3_Event")]
    public interface ApplicationEvents3_Event
    {
    }

    [ComImport, Guid("00020A01-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIDispatch), CompilerGenerated, TypeIdentifier]
    public interface ApplicationEvents4
    {
    }

    [ComImport, CompilerGenerated, TypeIdentifier("00020905-0000-0000-c000-000000000046", "Microsoft.Office.Interop.Word.ApplicationEvents4_Event"), ComEventInterface(typeof(ApplicationEvents4), typeof(ApplicationEvents4))]
    public interface ApplicationEvents4_Event
    {
    }
}


there is no need for dynamic ... keep in mind that C# is rather static type language
and using dynamic can provide to runtime errors(fx. especially if you do typo in methode/properties)


anyway, this is not the only reason for my vote ... i really don't like articles like this on CP, the ones, which provide so basic information ... you can find it in MSDN and 1001 websites already

edit: fx check MSDN mag article (http://msdn.microsoft.com/en-us/magazine/ff714583.aspx[^])
...works fascinates me...i can stare it for hours...

GeneralRe: This is worth way more than 1 Pin
Pascal Ganaye9-Nov-11 1:45
Pascal Ganaye9-Nov-11 1:45 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.