Ole для word на c

Overview

OLE, Object Linking and Embedding, that is, object linking and embedding. When we are designing programs, OLE can be used to create compound documents, combining text, sound, images, tables, applications and other types of information together. In Word, we can use OLE to achieve the combination of the above element information. The following example will introduce how to operate OLE in Word through C#. The content of the example contains the following points:

  • Insert OLE into Word
  • Edit OLE in Word
  • Read OLE in Word

use tools

  •  Spire.Doc for .NET

Note: Download and install the library, when editing the code, pay attention to add a reference to Spire.Doc.dll in the program (the dll file can be obtained in the Bin folder under the installation path)

Sample code (for reference)

[Example 1】Insert OLETo Word

Step 1: Add the using directive

using Spire.Doc;
using Spire.Doc.Documents;
using Spire.Doc.Fields;
using System.Drawing;

Step 2: Create a document

//Instantiate a Document class object
Document doc = new Document();
//Add a Section object to the document and add paragraphs
Section sec = doc.AddSection();
Paragraph p = sec.AddParagraph();

Step 3: Load the picture

//Instantiate a DocPicture class object and load the picture
DocPicture picture = new DocPicture(doc);
Image image = Image.FromFile(@"chart1.png");
picture.LoadImage(image);

Step 4: Insert OLE

//Insert a worksheet in the document, the OleLinkType enumeration value controls whether the OLE is linked or embedded
DocOleObject obj = p.AppendOleObject(@"testfile.xlsx", picture, OleLinkType.Link);
//DocOleObject obj = p.AppendOleObject(@"testfile.xlsx", picture, OleLinkType.Embed);

Step 5: Save the document

//Save and open the document
doc.SaveToFile("Add OLE.docx");
System.Diagnostics.Process.Start("Add OLE.docx");

After finishing the code, debug and run the program and generate the documentation.

The test result is as shown in the figure below:

All codes:


using Spire.Doc;
using Spire.Doc.Documents;
using Spire.Doc.Fields;
using System.Drawing;

namespace InsertOLE_Doc
{
    class Program
    {
        static void Main(string[] args)
        {
            //Instantiate a Document class object
            Document doc = new Document();
            //Add a Section object to the document and add paragraphs
            Section sec = doc.AddSection();
            Paragraph p = sec.AddParagraph();

            //Instantiate a DocPicture class object and load the picture
            DocPicture picture = new DocPicture(doc);
            Image image = Image.FromFile(@"chart1.png");
            picture.LoadImage(image);

            //Insert a worksheet in the document, the OleLinkType enumeration value controls whether the OLE is linked or embedded         
            DocOleObject obj = p.AppendOleObject(@"testfile.xlsx", picture, OleLinkType.Link);
            //DocOleObject obj = p.AppendOleObject(@"testfile.xlsx", picture, OleLinkType.Embed);

            //Save and open the document
            doc.SaveToFile("Add OLE.docx");
            System.Diagnostics.Process.Start("Add OLE.docx");
        }
    }
}

View Code

[Example 2] Edit OLE in Word

(Here, the documents generated in the above text are test files)

Step 1: Add the using directive

using Spire.Doc;
using Spire.Doc.Documents;
using Spire.Doc.Fields;
using System.Drawing;
using System.IO;

Step 2: Load the document

//Instantiate a Document object and load a Word document containing OLE
Document doc = new Document();
doc.LoadFromFile("test.docx");

Step 3: Get all the OLE in the section, and change the OLE type and link object as needed

//Get the first section
Section sec = doc.Sections[0];

//Traverse all the child elements in this Section and find the OLE object under the paragraph
foreach (DocumentObject obj in sec.Body.ChildObjects)
{
    if (obj is Paragraph)
    {
        Paragraph par = obj as Paragraph;
        foreach (DocumentObject paraObj in par.ChildObjects)
        {
            //Find the OLE object and change it according to the type
            if (paraObj.DocumentObjectType == DocumentObjectType.OleObject)
            {
                DocOleObject Ole = paraObj as DocOleObject;
                //If it is a link, modify the link path of the object
                if (Ole.LinkType == OleLinkType.Link)
                {
                    //At the same time, you need to manually change the OLE picture
                    DocPicture pic = Ole.OlePicture;
                    pic.LoadImage(Image.FromFile("Img.png"));
                    Ole.LinkPath = @"sample.docx";
                }
                //If it is embedded, just change the data
                byte[] bys = File.ReadAllBytes(@"sample.docx");
                if (Ole.LinkType == OleLinkType.Embed)
                {
                    DocPicture pic = new DocPicture(doc);
                    pic.LoadImage(Image.FromFile(@"Img.png"));
                    Ole.ObjectType = "Word.Document.12";
                    Ole.SetOlePicture(pic);
                    Ole.SetNativeData(bys);
                }
            }
        }
    }
}

Step 4: Save the document

 //Save the modified document and open
 doc.SaveToFile("Modify OLE.docx", Spire.Doc.FileFormat.Docx2010);
 System.Diagnostics.Process.Start("Modify OLE.docx");

After debugging and running the program, a document is generated. When opening the document, the original OLE pictures and linked documents in the generated document have been changed, as shown below:

 

All codes:


using Spire.Doc;
using Spire.Doc.Documents;
using Spire.Doc.Fields;
using System.Drawing;
using System.IO;

namespace EditOLE_Doc
{
    class Program
    {
        static void Main(string[] args)
        {
            //Instantiate a Document object and load a Word document containing OLE
            Document doc = new Document();
            doc.LoadFromFile("test.docx");

            //Get the first section
            Section sec = doc.Sections[0];

            //Traverse all the child elements in this Section and find the OLE object under the paragraph
            foreach (DocumentObject obj in sec.Body.ChildObjects)
            {
                if (obj is Paragraph)
                {
                    Paragraph par = obj as Paragraph;
                    foreach (DocumentObject paraObj in par.ChildObjects)
                    {
                        //Find the OLE object and change it according to the type
                        if (paraObj.DocumentObjectType == DocumentObjectType.OleObject)
                        {
                            DocOleObject Ole = paraObj as DocOleObject;
                            //If it is a link, modify the link path of the object
                            if (Ole.LinkType == OleLinkType.Link)
                            {
                                //At the same time, you need to manually change the OLE picture
                                DocPicture pic = Ole.OlePicture;
                                pic.LoadImage(Image.FromFile("Img.png"));
                                Ole.LinkPath = @"sample.docx";
                            }
                            //If it is embedded, just change the data
                            byte[] bys = File.ReadAllBytes(@"sample.docx");
                            if (Ole.LinkType == OleLinkType.Embed)
                            {
                                DocPicture pic = new DocPicture(doc);
                                pic.LoadImage(Image.FromFile(@"Img.png"));
                                Ole.ObjectType = "Word.Document.12";
                                Ole.SetOlePicture(pic);
                                Ole.SetNativeData(bys);
                            }
                        }
                    }
                }
            }
            //Save the modified document and open
            doc.SaveToFile("Modify OLE.docx", Spire.Doc.FileFormat.Docx2010);
            System.Diagnostics.Process.Start("Modify OLE.docx");
        }
    }
}

View Code

[Example 3] Read OLE in Word

Step 1: Add the using instruction

using Spire.Doc;
using Spire.Doc.Documents;
using Spire.Doc.Fields;
using System.IO;

Step 2: Load the file

//Instantiate a Document object, load a document with an OLE object
Document doc = new Document();          
doc.LoadFromFile(@"test.docx");

Step 3: Traverse the section and read OLE

//Traverse all sections of the document          
            foreach (Section sec in doc.Sections)
            {
                //Traverse all the child elements under Section
                foreach (DocumentObject obj in sec.Body.ChildObjects)
                {
                    if (obj is Paragraph)
                    {
                        Paragraph par = obj as Paragraph;
                        //Traverse the paragraphs below this section
                        foreach (DocumentObject o in par.ChildObjects)
                        {
                            //Find the OLE object and extract it according to the type
                            if (o.DocumentObjectType == DocumentObjectType.OleObject)
                            {
                                DocOleObject Ole = o as DocOleObject;
                                /*The ObjectType property can get the specific type of the ole object.
                                                                 Note that if you use Spire.Doc to add the ole object, you need to add it in AppendOleObject
                                                                 Declare OleObjectType first, otherwise you won’t get the specific type here, you will only get the Package*/
                                string s = Ole.ObjectType;
                                //"AcroExch.Document.11" refers to the ProgID corresponding to the PDF object
                                if (s == "AcroExch.Document.11")
                                {
                                    File.WriteAllBytes("Result.pdf", Ole.NativeData);
                                }
                                //"Excel.Sheet.12" refers to the ProgID corresponding to the worksheet after Excel03
                                else if (s == "Excel.Sheet.12")
                                {
                                    File.WriteAllBytes("Result.xlsx", Ole.NativeData);
                                }
                                //"Word.Document.12" refers to the ProgID corresponding to Word after 03
                                else if (s == "Word.Document.12")
                                {
                                    File.WriteAllBytes("Result.docx", Ole.NativeData);
                                }
                            }
                        }
                    }
                }
            }

Debug and run the program and generate documents as follows:

 

All codes:


using Spire.Doc;
using Spire.Doc.Documents;
using Spire.Doc.Fields;
using System.IO;

namespace ReadOLE_Doc
{
    class Program
    {
        static void Main(string[] args)
        {
            //Instantiate a Document object, load a document with an OLE object
            Document doc = new Document();          
            doc.LoadFromFile(@"test.docx");

            //Traverse all sections of the document          
            foreach (Section sec in doc.Sections)
            {
                //Traverse all the child elements under Section
                foreach (DocumentObject obj in sec.Body.ChildObjects)
                {
                    if (obj is Paragraph)
                    {
                        Paragraph par = obj as Paragraph;
                        //Traverse the paragraphs below this section
                        foreach (DocumentObject o in par.ChildObjects)
                        {
                            //Find the OLE object and extract it according to the type
                            if (o.DocumentObjectType == DocumentObjectType.OleObject)
                            {
                                DocOleObject Ole = o as DocOleObject;
                                /*The ObjectType property can get the specific type of the ole object.
                                                                 Note that if you use Spire.Doc to add the ole object, you need to add it in AppendOleObject
                                                                 Declare OleObjectType first, otherwise you won’t get the specific type here, you will only get the Package*/
                                string s = Ole.ObjectType;
                                //"AcroExch.Document.11" refers to the ProgID corresponding to the PDF object
                                if (s == "AcroExch.Document.11")
                                {
                                    File.WriteAllBytes("Result.pdf", Ole.NativeData);
                                }
                                //"Excel.Sheet.12" refers to the ProgID corresponding to the worksheet after Excel03
                                else if (s == "Excel.Sheet.12")
                                {
                                    File.WriteAllBytes("Result.xlsx", Ole.NativeData);
                                }
                                //"Word.Document.12" refers to the ProgID corresponding to Word after 03
                                else if (s == "Word.Document.12")
                                {
                                    File.WriteAllBytes("Result.docx", Ole.NativeData);
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}

View Code

The above is all about the OLE in the C# operation word. The example method is for reference.

End of this article.

For reprint, please indicate the source!

Word OLE (Object Linking and Embedding) object is used to make contents, created in one program, available in Word document. For example, users can insert an Excel worksheet in a Word document.

Both Linked object and Embedded object can be used between Word and other programs. The data of Embedded objects is saved in Word and should be updated manually, while data of Linked object remains as separate file and will be updated when source data is changed.

Spire.Doc for .NET , a professional component to manipulate Word documents with .NET, enables users to insert OLE objects in Word by using C#/VB.NET. This guide will show you how to insert one kind of OLE objects, Linked objects (an Excel Worksheet) in Word.

Users can invoke paragraph.AppendOleObject(string pathToFile, olePicture, OleObjectType) method to insert OLE objects in Word. The parameters, olePicture and OleObjectType are properties of DocOleObject class which is provided by Spire.Doc for .NET.

The following steps present details to insert OLE Objects in Word. The following demonstrate a document before insert the OLE Object into Word, and at the bottom, you can find the result screenshot after inserting. Before starting with the steps, download and install Spire.Doc for .NET on system.

Insert Word OLE Objects

Code Detail:

Step 1: Define a GetExcelImage(string ExcelFile) method to get olePicture. Actually, the olePicture is image of data information in original Excel worksheet. The image is generated from Excel through Spire.XLS for .NET that will be shown in documents after inserting OLE object in Word. Double click this picture and you can get the original worksheet.

[C#]

 
private static Image GetExcelImage(String ExcelFile)
 {
  //Load Excel File
  Workbook workbook = new Workbook();
  workbook.LoadFromFile(ExcelFile);
  Worksheet sheet = workbook.Worksheets[0];

  //Set Image Range
  int lastRow = sheet.LastRow;
  int lastColumn = sheet.LastColumn;
  return workbook.Worksheets[0].SaveToImage(1, 1, lastRow, lastColumn);
}

[VB.NET]

   
Private Shared Function GetExcelImage(ByVal ExcelFile As String) As Image
  'Load Excel File
  Dim workbook As New Workbook()
  workbook.LoadFromFile(ExcelFile)
  Dim sheet As Worksheet = workbook.Worksheets(0)

  'Set Image Range
  Dim lastRow As Integer = sheet.LastRow
  Dim lastColumn As Integer = sheet.LastColumn
  Return workbook.Worksheets(0).SaveToImage(1, 1, lastRow, lastColumn)
End Function
 

Step 2: Insert OLE object. After adding paragraph in Word document, declare a new DocPicture. Then, use GetExcelImage(string ExcelFile) method which is defined in step 1 to get image source and then use picture.LoadImage(Image) method to load this image. Finally, insert OLE objects.

[C#]

       
para = mysec.AddParagraph();
DocPicture picture = new DocPicture(mydoc);
Image image = GetExcelImage(@"E:workDocumentsExcelFilesCustomers.xlsx");//Get Image Source
picture.LoadImage(image);//Load Image
DocOleObject obj = para.AppendOleObject(@"E:workDocumentsExcelFilesCustomers.xlsx", picture, Spire.Doc.Documents.OleObjectType.ExcelWorksheet);

[VB.NET]

  
'Insert OLE Object
para = mysec.AddParagraph()
Dim picture As New DocPicture(mydoc)
Dim image As Image = GetExcelImage("E:workDocumentsExcelFilesCustomers.xlsx") 'Get Image Source
picture.LoadImage(image) 'Load Image
Dim obj As DocOleObject = para.AppendOleObject("E:workDocumentsExcelFilesCustomers.xlsx", picture, Spire.Doc.Documents.OleObjectType.ExcelWorksheet)

After this coding, you can run this application and a result will show up as below:

Insert Word OLE Objects

Spire.Doc is a Microsoft Word component, which enables users to perform a wide range of Word document processing tasks directly, such as generate, read, write and modify Word document in WPF, .NET and Silverlight.

  • Download demo project- 37.6 KB
  • Download demo executable- 167 KB

Introduction

“MS Office Automation using C++” — this is what I started searching over the internet a few weeks back to plot a graph in an Excel sheet through my program. Fortunately, I got a few — actually very few — inputs from the cyber world, might be because I am a poor searcher in the internet. This article is for those who are still searching the internet with the same keywords.

OLEAutoWord.JPG

OLEAutoExcel.JPG

Object Linking and Embedding

In early days, I wondered how much easier it was to use Visual Basic than any other programming language. Create a media player just by including the Media Player component with our project, create a web browser just by including the Web Browser component to the project etc. When I tried the same with Visual C++, I realized it is not as easy as in VB. I met with lot of linker errors as I was a newbie. The above story is eight years old. Object Linking and Embedding, popularly called as OLE, is a COM based architecture which provides flexibility and reusability while developing software applications. As I said, if you need to develop a media player application, there is not much to do with code. Include the needed components which are already developed by experts. With OLE, we are linking to the component and embedding it to our application. OLE in Windows is everywhere, you can copy paste images, videos, or music files to a Word document, you can open a PDF, Excel, or Word file in Internet Explorer, and so on…

ComponentesInterfacesClients.JPG

You can find lots of registered components of different applications under HKEY_CLASSES_ROOTCLSID{<___CLSID___>}, where {<___CLSID___>} is variant (unique class ID for each registered component).

COM and Interfaces

I won’t be able to say anything new about COM and interfaces here. A COM object, as its name suggests, is a component which can be easily attached to any application using its interfaces. A COM component may have any number of interfaces, and it is not necessary for an application to use all its interfaces. An interface is nothing but a pure virtual class. It has no implementation code, and is used only for communication between applications with a COM object.

Let’s start with what Microsoft has to say about “MS Office Automation using C++”:

“Automation (formerly OLE Automation) is a technology that allows you to take advantage of an existing program’s functionality and incorporate it into your own applications.”

  • With MFC, use the Visual C++ ClassWizard to generate «wrapper classes» from the Microsoft Office type libraries. These classes, as well as other MFC classes, such as COleVariant, COleSafeArray, and COleException, simplify the tasks of Automation. This method is usually recommended over the others, and most of the Microsoft Knowledge Base examples use MFC.
  • #import, a new directive that became available with Visual C++ 5.0, creates VC++ «smart pointers» from a specified type library. It is very powerful, but often not recommended because of reference-counting problems that typically occur when used with the Microsoft Office applications.
  • C/C++ Automation is much more difficult, but sometimes necessary to avoid overhead with MFC, or problems with #import. Basically, you work with such APIs as CoCreateInstance(), and COM interfaces such as IDispatch and IUnknown.

The above statements are purely taken from the Microsoft website Office Automation Using Visual C++. This article is all about the third point mentioned above, i.e., C/C++ Automation using COM interfaces, and the article only takes MS Word to explain in detail. Refer to the demo source code for similar MS Excel stuff.

Initialize an MSWord Application

CoInitialize(NULL);
CLSID clsid;
HRESULT hr = CLSIDFromProgID(L"Word.Application", &clsid);

IDispatch *pWApp;
if(SUCCEEDED(hr))
{
    hr = CoCreateInstance(clsid, NULL, CLSCTX_LOCAL_SERVER, 
                          IID_IDispatch, (void **)&pWApp);
}

Call CoInitialize() to initialize the COM library for the current thread, i.e., the current thread will be loaded with COM library DLLs. Later, we need to call CoUnInitialize() to unload the loaded COM DLLs from memory. As I mentioned earlier, all registered components can be found under “HKCRCLSID” in the Registry. You can also find the PROGID (program ID) for the component. Use the CLSIDFromProgID() API to get the class ID for the component, since we can get the COM object only by using the CLSID. For MS Word, “Word.Application.xx” is the version dependent PROGID, where xx is version of the MS Word installed in the system. For our convenience, to write code independent of the version, MSWord provides another PROGID “Word.Application”, which is under “VersionIndependentProgID”. Call CoCreateInstance() with the MS Word CLSID to get an instance of an MS Word application. pWApp (IDispatch interface) should receive a valid MS Word component interface object.

IDispatch Interface

IDispatch is an interface derived from IUnknown (the base interface), using which applications will expose methods and properties to other applications (our program) to make use of its features. Simply, the IDispatch pointer we got using CoCreateInstance() for MS Word is the interface object which will help us to use MS Word methods and properties through our program. In addition to the IUnknown members, IDispatch has four more member functions to support OLE Automation.

  • GetTypeInfoCount()
  • GetTypeInfo()
  • GetIDsOfNames()
  • Invoke()

The client application (our program) will use the IDispatch::Invoke() method to call MS Word (or any other component) methods and properties. But, IDispatch::Invoke() cannot receive or understand the actual method names or property names of the MS Word component. It can understand only the DISPID. A DISPID is a 32-bit value which represents the actual methods or properties of a component. GetIDsOfName() is the function we can use to get the DISPID for a method or property of the component. For example, refer to the following code which sets the “Visible” property of an MS Word object:

DISPID dispID;
VARIANT pvResult;
LPOLESTR ptName=_T("Visible");
hr = pWApp->GetIDsOfNames(IID_NULL, &ptName, 1, LOCALE_USER_DEFAULT, &dispID);
if(SUCCEEDED(hr))
{
    VARIANT x;
    x.vt = VT_I4;
    x.lVal =1;     DISPID prop=DISPATCH_PROPERTYPUT;

    DISPPARAMS dp = { NULL,NULL,0,0 };
    dp.cArgs =1;
    dp.rgvarg =&x;
    dp.cNamedArgs=1;
    dp.rgdispidNamedArgs= ∝
    hr = pWApp->Invoke(dispID, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYPUT, 
                          &dp, &pvResult, NULL, NULL);
}

Get the DISPID of “Visible”, use the DISPID with Invoke() to set the “Visible” property to true. ptName will be the actual name of a method or a property, used with the GetIDsOfNames() method to get an equivalent DISPID. DISPPARAMS has the parameters for the DISPID (including the method parameter or the property value), used with the Invoke() method which is the actual call for the method or property.

To make the code easier to use, following is the generic function to call an OLE method or to set/get an OLE property:

HRESULT OLEMethod(int nType, VARIANT *pvResult, 
                  IDispatch *pDisp,LPOLESTR ptName, int cArgs...)
{
    if(!pDisp) return E_FAIL;

    va_list marker;
    va_start(marker, cArgs);

    DISPPARAMS dp = { NULL, NULL, 0, 0 };
    DISPID dispidNamed = DISPID_PROPERTYPUT;
    DISPID dispID;
    char szName[200];

        WideCharToMultiByte(CP_ACP, 0, ptName, -1, szName, 256, NULL, NULL);

        HRESULT hr= pDisp->GetIDsOfNames(IID_NULL, &ptName, 1, 
                             LOCALE_USER_DEFAULT, &dispID);
    if(FAILED(hr)) {
        return hr;
    }
        VARIANT *pArgs = new VARIANT[cArgs+1];
        for(int i=0; i<cArgs; i++) {
        pArgs[i] = va_arg(marker, VARIANT);
    }

        dp.cArgs = cArgs;
    dp.rgvarg = pArgs;

        if(nType & DISPATCH_PROPERTYPUT) {
        dp.cNamedArgs = 1;
        dp.rgdispidNamedArgs = &dispidNamed;
    }

        hr = pDisp->Invoke(dispID, IID_NULL, LOCALE_SYSTEM_DEFAULT, 
                       nType, &dp, pvResult, NULL, NULL);
    if(FAILED(hr)) {
        return hr;
    }

        va_end(marker);
    delete [] pArgs;
    return hr;
}

The above function is actually named as AutoWrap() in a Microsoft support article. Nothing new to explain about the function as I have already explained about the GetIDsOfName() and Invoke() calls, except that they are separated to a function. Additionally, the function uses variable arguments to handle different number of parameters for different methods/properties. Now, to set the Visible property of the MS Word object, it is more simpler to use this generic function:

VARIANT x;
x.vt = VT_I4;
x.lVal = 1;        hr=OLEMethod(DISPATCH_PROPERTYPUT, NULL, pWApp, L"Visible", 1, x);

Note that, OLEMethod receives variable parameters. I.e., you can pass any number of parameters depending on the property/method. Following is a summary of the OLEMethod() parameters,

  • nType – Type of call to make, which can be any of the following values:
    • DISPATCH_PROPERTYPUT — Set property value
    • DISPATCH_PROPERTYGET — Get property value
    • DISPATCH_METHOD — Call a method
  • pvResult – Return value for the call made; it can be another IDispatch object, or an integer value, or a boolean, or so on..
  • pDispIDispatch interface object for which the call is to be made.
  • ptName – Property or method name.
  • cArgs – Number of arguments followed after this parameter.
  • … parameters in reverse order for the call (it can be values of a property, or parameters of a method for the IDispatch object).

Methods and Properties

The MS Word application has a number of properties and methods, and everything cannot be explained here. I will explain a couple of functions here; refer to the source code for more functions, because the code for all the method/property calls will look similar. At the end of this section, I will tell you how to find a method name or property name and its parameters or values whenever needed.

To open a word document:

HRESULT CMSWord::OpenDocument(LPCTSTR szFilename, bool bVisible)
{
    if(m_pWApp==NULL) 
    {
        if(FAILED(m_hr=Initialize(bVisible)))
            return m_hr;
    }
    COleVariant vFname(szFilename);
    VARIANT fname=vFname.Detach();
        {
        VARIANT result;
        VariantInit(&result);
        m_hr=OLEMethod(DISPATCH_PROPERTYGET, &result, m_pWApp, 
                       L"Documents", 0);
        m_pDocuments= result.pdispVal;
    }
        {
        VARIANT result;
      VariantInit(&result);
        m_hr=OLEMethod(DISPATCH_METHOD, &result, m_pDocuments, 
                       L"Open", 1, fname);
        m_pActiveDocument = result.pdispVal;
    }
    return m_hr;
}

To open a new document, replace “Open” with “Add” and change the parameters count to 0. Note that, “result” is the output parameter which holds the IDispatch object for “Documents” and “Open” (active document) in the above code.

To close all the opened Word documents:

HRESULT CMSWord::CloseDocuments()
{
    if(m_pWApp==NULL) return E_FAIL;
    {
        VARIANT result;
            VariantInit(&result);
        m_hr=OLEMethod(DISPATCH_METHOD, &result, m_pDocuments, 
                       L"Close", 0);
        m_pDocuments=NULL;
        m_pActiveDocument=NULL;
    }
    return m_hr;
}

The following code will set the font for the selected text in the active document:

HRESULT CMSWord::SetFont(LPCTSTR szFontName, int nSize, 
                 bool bBold, bool bItalic,COLORREF crColor)
{
    if(!m_pWApp || !m_pActiveDocument) return E_FAIL;
    IDispatch *pDocApp;
    {  
        VARIANT result;
        VariantInit(&result);
        OLEMethod(DISPATCH_PROPERTYGET, &result, 
                  m_pActiveDocument, L"Application", 0);
        pDocApp= result.pdispVal;
    }
    IDispatch *pSelection;
    {
        VARIANT result;
        VariantInit(&result);
        OLEMethod(DISPATCH_PROPERTYGET, &result, 
                  pDocApp, L"Selection", 0);
        pSelection=result.pdispVal;
    }
    IDispatch *pFont;
    {
        VARIANT result;
        VariantInit(&result);
        OLEMethod(DISPATCH_PROPERTYGET, &result, 
                  pSelection, L"Font", 0);
        pFont=result.pdispVal;
    }
    {
        COleVariant oleName(szFontName);
        m_hr=OLEMethod(DISPATCH_PROPERTYPUT, NULL, pFont, 
                       L"Name", 1, oleName.Detach());
        VARIANT x;
        x.vt = VT_I4;
        x.lVal = nSize;
        m_hr=OLEMethod(DISPATCH_PROPERTYPUT, NULL, pFont, L"Size", 1, x);
        x.lVal = crColor;
        m_hr=OLEMethod(DISPATCH_PROPERTYPUT, NULL, pFont, L"Color", 1, x);
        x.lVal = bBold?1:0;
        m_hr=OLEMethod(DISPATCH_PROPERTYPUT, NULL, pFont, L"Bold", 1, x);
        x.lVal = bItalic?1:0;
        m_hr=OLEMethod(DISPATCH_PROPERTYPUT, NULL, pFont, L"Italic", 1, x);
    }
    pFont->Release();
    pSelection->Release();
    pDocApp->Release();
    return m_hr;
}

To insert a picture into the active document:

HRESULT CMSWord::InserPicture(LPCTSTR szFilename)
{
    if(!m_pWApp || !m_pActiveDocument) return E_FAIL;
    IDispatch *pDocApp;
    {  
        VARIANT result;
        VariantInit(&result);
        OLEMethod(DISPATCH_PROPERTYGET, &result, m_pActiveDocument, 
                  L"Application", 0);
        pDocApp= result.pdispVal;
    }
    IDispatch *pSelection;
    {
        VARIANT result;
        VariantInit(&result);
        OLEMethod(DISPATCH_PROPERTYGET, &result, pDocApp, L"Selection", 0);
        pSelection=result.pdispVal;
    }
    IDispatch *pInlineShapes;
    {
        VARIANT result;
        VariantInit(&result);
        OLEMethod(DISPATCH_PROPERTYGET, &result, pSelection, L"InlineShapes", 0);
        pInlineShapes=result.pdispVal;
    }
    {
        COleVariant varFile(szFilename);
        COleVariant varLink((BYTE)0);
        COleVariant varSave((BYTE)1);
        OLEMethod(DISPATCH_METHOD,NULL,pInlineShapes,L"AddPicture",3, 
                  varSave.Detach(),varLink.Detach(),varFile.Detach());
    }
    return m_hr;
}

How can we identify a method/property which we need for an MS Word application? The answer is simple, look at the above functions, they can be simply explained as:

Application.Documents.Open(szFilename)
Documents.Close()
ActiveDocument.Application.Selection.Font.Name=szFontName
ActiveDocument.Application.Selection.Font.Size=nSize
ActiveDocument.Application.Selection.Font.Color=crColor
ActiveDocument.Application.Selection.Font.Bold=bBold
ActiveDocument.Application.Selection.Font.Italic=bItalic
ActiveDocument.Application.Selection.InlineShapes.AddPicture(szFilename,false,true)

Does this resemble something familiar to us? Yes, we used to see this often when creating macros in MS Word or MS Excel. These are VBA scripts. So, don’t you think it is much easier to get a method or property name you need for your application?

What to do when you need to know how to insert a picture into a Word document?

  • Open MS Word
  • Open a new document
  • Go to Tools->Macro->Record New Macro
  • Choose a keyboard macro and assign a key for the macro
  • After the macro recording has been started, go to Insert->Picture->From File
  • Choose an image file
  • Stop macro
  • Go to Tools->Macro->Visual Basic Editor (F11)
  • Under “NewMacros”, you can see your recorded macro at the end
  • Look at the code:
  • Selection.InlineShapes.AddPicture FileName:=<your-image>, 
                           LinkToFile:=False, SaveWithDocument=True

Now, compare this with the above InsertPicture() function so that you can understand how it is being coded in C++.

So, whatever the task you want to do with MS Word through automation, first do that with a sample macro in MS Word itself, and you will get to know the methods and their parameters, the property names, and their values. The job will be easy then with the OLEMethod() call.

Points to Note

  • VARIANT is a structure union which means literally “not sure”. Yes, we are not sure about the data type, so it can be anything. Using VARIANT, we can get a BYTE value or an unsigned long value, or an IUnknown object, or whatever is needed in the case. Those who are already familiar with COM should know about this.
  • Make sure about the DISPATCH_METHOD, DISPATCH_PROPERTYPUT, and DISPATCH_PROPERTYGET usage in OLEMethod() or in the Invoke() call. We need to decide which needs to be used where, depending on the method or property we use with OLEMethod().
  • Try to understand GetIDsOfNames() and Invoke() explained in the «IDispatch Interface» section of this article, which are more important than other information provided here.
  • COleVariant is the class version of the VARIANT structure (union), which makes our job easier to initialize a variant with a value.
  • The OLEMethod() function receives variable parameters in reverse order. For example, the AddPicture parameters are actually <filename, > in order, whereas in OLEMethod, call <savewithdocument, />
  • If your Word or Excel application remains in memory (check with Task Manager) after closing your client application, make sure you have released all the used IDispatch objects.

Conclusion

All the concepts explained above are same for Excel as well. Refer to the demo source code for Excel usage. The aim of this article is not to give you the complete set of methods and properties for MS Word and MS Excel Automation, but to give you a hand to help you do the Automation yourself. All the best.

    Naren started coding during 1999 with FORTRAN, then COBOL, PASCAL, C, C++, VC++ ….. C#, Java, ASP so on, till today. He claims himself as techie who loves coding, but he is not even sure which technology or platform he loves, most of the time Windows, some time WinCE, some time Linux, nowadays Android and embedded platforms. He can do some query stuffs with Oracle, SQL Server, MySQL. He strongly believes that C/C++ is a must for all programmers, «if you know C/C++, you can do programming on any language». He is an electronic gadget guy who likes to buy small gadgets all the time, at least he will do window shopping on a gadget shop. His interest always have been with Automation in any form, call it a little automated program sitting in his Laptop or a home automation program runs on his mobile. Apart from coding, he likes to do…???

    Приветствую.

    Попытался тоже подключиться к экселю из Visual Studio. Использую OFFICE 2016 и VS 2019.

    Сейчас имею следующий код, который почему-то не работает:

    C++
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    
    /* Программа для работы с экселем
    *  напрямую из с++
    */
     
    #import "MSO.DLL" 
        rename("IAccessible", "IAccessibleXL")
    using namespace Office;
     
    #import "Vbe6ext.olb" 
        rename("Events", "EventsVB")
    using namespace VBIDE;
     
    #import "excel.exe" 
        rename("DialogBox", "DialogBoxXL") 
        rename("RGB", "RGBXL") 
        rename("DocumentProperties", "DocumentPropertiesXL")
    using namespace Excel;
     
    int main()
    {
     
        HRESULT hr = CoInitializeEx(0, COINIT_MULTITHREADED);
        Excel::_ApplicationPtr pXL;
        pXL->Workbooks->Open(L"D:\testOle.xls");
        pXL->Visible = true;
     
        return 0;
    }

    Собственно, почему я вообще считаю что он должен работать:
    1. Все файлы из #import я поместил в папку с проектом и он успешно создал версии .tlh и .tli
    (к сожалению не знаю как тут пользоваться спойлерами и картинками, так что вот ссылка https://prnt.sc/12w9od5)
    2. Эти файлы не пустые и вполне себе читаемые:
    (рандомный кусок кода из mso.tlh

    Код

          virtual HRESULT __stdcall get_Parent (
            /*[out,retval]*/ IDispatch * * Parent ) = 0;
          virtual HRESULT __stdcall get_Extended (
            /*[out,retval]*/ BSTR * Extended ) = 0;
          virtual HRESULT __stdcall put_Extended (
            /*[in]*/ BSTR Extended ) = 0;
          virtual HRESULT __stdcall get_Id (
            /*[out,retval]*/ BSTR * Id ) = 0;
          virtual HRESULT __stdcall put_Id (
            /*[in]*/ BSTR Id ) = 0;
          virtual HRESULT __stdcall get_Language (
            /*[out,retval]*/ enum MsoScriptLanguage * Language ) = 0;
          virtual HRESULT __stdcall put_Language (
            /*[in]*/ enum MsoScriptLanguage Language ) = 0;
          virtual HRESULT __stdcall get_Location (
            /*[out,retval]*/ enum MsoScriptLocation * Location ) = 0;
          virtual HRESULT __stdcall raw_Delete ( ) = 0;
          virtual HRESULT __stdcall get_Shape (
            /*[out,retval]*/ IDispatch * * Object ) = 0;
          virtual HRESULT __stdcall get_ScriptText (
            /*[out,retval]*/ BSTR * Script ) = 0;
          virtual HRESULT __stdcall put_ScriptText (
            /*[in]*/ BSTR Script ) = 0;

    3. Странное поведение IDE, а именно HRESULT в main из этих файлов смог определиться, но при этом висит ошибка, которая звучит как С1083 «Не удаётся открыть файл библиотека типов: MSO.DLL: No such file or directory,»
    При этом красным подчёркивается единственная строчка с pXL->Visible = true; (примерно 25 строка кода)
    4. Помимо этого имеется 500+ ошибок E0102 «нестандартное опережающее объявление перечисляемого типа», но выглядят эти ошибки так, что сами пропадут, как только будет решена основная из пункта 3.

    Помогите, пожалуйста, разобраться в этом. Настройки VS стоят исходные, ничего не менял (разве что поменял кодировку с Юникода на многобайтовую, но что-то мне подсказывает, что не в этом проблема), установил буквально позавчера после сноса системы. В Visual Studio Installer постарался выбрать все пункты, которые могут быть связанны с MS Office, даже не относящиеся к с++

    Добавлено через 10 минут
    Последняя проблема была решена!
    Всё что нужно было сделать — это дать абсолютное имя файла в #import.
    Теперь новая ошибка
    Звучит она так:
    «Возникло необработанное исключение по адресу 0x00007FF9B225A839 в работа с экселем.exe: исключение Microsoft C++: _com_error по адресу памяти 0x000000BB1E0FF700.»
    (25 строка отказывалась компилироваться, так что я её удалил, а эта ошибка возникла в 24 строке)

    The challenge is that the binary file you extract from the EmbeddedObjectPart is not your ZIP file. It is a structured storage file that contains your ZIP file.

    The following unit test shows how you can extract a ZIP file (e.g., ZipContents.zip) that was embedded into a Word document ("Resources\ZipContainer.docx") as an OLE object, using Microsoft Word. Note the usage of the Ole10Native.ExtractFile() method, which extracts the ZIP file from the structured storage file (e.g., oleObject1.bin) embedded in your Word document.

    using System.IO;
    using CodeSnippets.Windows;
    using DocumentFormat.OpenXml.Packaging;
    using Xunit;
    
    namespace CodeSnippets.Tests.OpenXml.Wordprocessing
    {
        public class EmbeddedObjectPartTests
        {
            private static void ExtractFile(EmbeddedObjectPart part, string destinationFolderPath)
            {
                // Determine the file name and destination path of the binary,
                // structured storage file.
                string binaryFileName = Path.GetFileName(part.Uri.ToString());
                string binaryFilePath = Path.Combine(destinationFolderPath, binaryFileName);
    
                // Ensure the destination directory exists.
                Directory.CreateDirectory(destinationFolderPath);
    
                // Copy part contents to structured storage file.
                using (Stream partStream = part.GetStream())
                using (FileStream fileStream = File.Create(binaryFilePath))
                {
                    partStream.CopyTo(fileStream);
                }
    
                // Extract the embedded file from the structured storage file.
                Ole10Native.ExtractFile(binaryFilePath, destinationFolderPath);
    
                // Remove the structured storage file.
                File.Delete(binaryFilePath);
            }
    
            [Fact]
            public void CanExtractEmbeddedZipFile()
            {
                const string documentPath = "Resources\ZipContainer.docx";
                const string destinationFolderPath = "Output";
                string destinationFilePath = Path.Combine(destinationFolderPath, "ZipContents.zip");
    
                using WordprocessingDocument wordDocument =
                    WordprocessingDocument.Open(documentPath, false);
    
                // Extract all embedded objects.
                foreach (EmbeddedObjectPart part in wordDocument.MainDocumentPart.EmbeddedObjectParts)
                {
                    ExtractFile(part, destinationFolderPath);
                }
    
                Assert.True(File.Exists(destinationFilePath));
            }
        }
    }
    

    Here’s the gist of the Ole10Native class, which was once published by Microsoft but is a bit hard to find nowadays:

    using System;
    using System.IO;
    using System.Runtime.InteropServices;
    using System.Runtime.InteropServices.ComTypes;
    using System.Text.RegularExpressions;
    
    namespace CodeSnippets.Windows
    {
        public class Ole10Native
        {
            public static void ExtractFile(string sourceFilePath, string destinationFolder)
            {
                StgOpenStorage(sourceFilePath, null, STGM.READWRITE | STGM.SHARE_EXCLUSIVE, IntPtr.Zero, 0, out IStorage iStorage);
                ProcessPackage(iStorage, destinationFolder);
                Marshal.ReleaseComObject(iStorage);
            }
    
            private static void ProcessPackage(IStorage pStg, string destinationFolder)
            {
                uint numReturned;
                pStg.EnumElements(0, IntPtr.Zero, 0, out IEnumSTATSTG pEnumStatStg);
                var ss = new STATSTG[1];
    
                // Loop through the STATSTG structures in the storage.
                do
                {
                    // Retrieve the STATSTG structure
                    pEnumStatStg.Next(1, ss, out numReturned);
                    if (numReturned != 0)
                    {
                        //System.Runtime.InteropServices.ComTypes.STATSTG statstm;
                        var bytT = new byte[4];
    
                        // Check if the pwcsName contains "Ole10Native" stream which contain the actual embedded object
                        if (ss[0].pwcsName.Contains("Ole10Native"))
                        {
                            // Get the stream objectOpen the stream
                            pStg.OpenStream(ss[0].pwcsName, IntPtr.Zero, (uint) STGM.READ | (uint) STGM.SHARE_EXCLUSIVE, 0,
                                out IStream pStream);
    
                            //pStream.Stat(out statstm, (int) STATFLAG.STATFLAG_DEFAULT);
    
                            IntPtr position = IntPtr.Zero;
    
                            // File name starts from 7th Byte.
                            // Position the cursor to the 7th Byte.
                            pStream.Seek(6, 0, position);
    
                            var ulRead = new IntPtr();
                            var filename = new char[260];
                            int i;
    
                            // Read the File name of the embedded object
                            for (i = 0; i < 260; i++)
                            {
                                pStream.Read(bytT, 1, ulRead);
                                pStream.Seek(0, 1, position);
                                filename[i] = (char) bytT[0];
                                if (bytT[0] == 0) break;
                            }
    
                            var path = new string(filename, 0, i);
    
                            // Next part is the source path of the embedded object.
                            // Length is unknown. Hence, loop through each byte to read the 0 terminated string
                            // Read the source path.
                            for (i = 0; i < 260; i++)
                            {
                                pStream.Read(bytT, 1, ulRead);
                                pStream.Seek(0, 1, position);
                                filename[i] = (char) bytT[0];
                                if (bytT[0] == 0) break;
                            }
    
                            // Unknown 4 bytes
                            pStream.Seek(4, 1, position);
    
                            // Next 4 byte gives the length of the temporary file path
                            // (Office uses a temporary location to copy the files before inserting to the document)
                            // The length is in little endian format. Hence conversion is needed
                            pStream.Read(bytT, 4, ulRead);
                            ulong dwSize = 0;
                            dwSize += (ulong) (bytT[3] << 24);
                            dwSize += (ulong) (bytT[2] << 16);
                            dwSize += (ulong) (bytT[1] << 8);
                            dwSize += bytT[0];
    
                            // Skip the temporary file path
                            pStream.Seek((long) dwSize, 1, position);
    
                            // Next four bytes gives the size of the actual data in little endian format.
                            // Convert the format.
                            pStream.Read(bytT, 4, ulRead);
                            dwSize = 0;
                            dwSize += (ulong) (bytT[3] << 24);
                            dwSize += (ulong) (bytT[2] << 16);
                            dwSize += (ulong) (bytT[1] << 8);
                            dwSize += bytT[0];
    
                            // Read the actual file content
                            var byData = new byte[dwSize];
                            pStream.Read(byData, (int) dwSize, ulRead);
    
                            // Create the file
                            var bWriter = new BinaryWriter(File.Open(Path.Combine(destinationFolder, GetFileName(path)),
                                FileMode.Create));
                            bWriter.Write(byData);
                            bWriter.Close();
                        }
                    }
                } while (numReturned > 0);
    
                Marshal.ReleaseComObject(pEnumStatStg);
            }
    
            private static string GetFileName(string filePath)
            {
                return Regex.Replace(filePath, @"^.*[\]", "");
            }
        }
    }
    

    You can find the full source code (including the Ole10Native class) in my CodeSnippets GitHub repository.

    This article demonstrates how to save OLE objects in commonly used file formats such as .pdf, .doc, .docx, .ppt, .pptx, .xls and .xlsx, out of a Word document by using Free Spire.Doc for .NET.

    INSTALL SPIRE.DOC

    Download the latest version of Free Spire.Doc from this link, and manually add the DLL files in your .NET application as references. Or, you can install it directly via NuGet.

    USING THE CODE

    [C#]

    using System.IO;
    using Spire.Doc;
    using Spire.Doc.Documents;
    using Spire.Doc.Fields;
    
    namespace ExtractOleObjects
    {
        class Program
        {
            static void Main(string[] args)
            {
                //Create a Document object
                Document doc = new Document();
    
                //Load a Word file
                doc.LoadFromFile("OleObject.docx");
    
                //Loop through the sections
                foreach (Section sec in doc.Sections)
                {
                    //Loop through the child objects
                    foreach (DocumentObject obj in sec.Body.ChildObjects)
                    {
                        //Detect if the object is a paragraph
                        if (obj is Paragraph)
                        {
                            Paragraph par = obj as Paragraph;
    
                            //Loop through the child objects within paragraph
                            foreach (DocumentObject o in par.ChildObjects)
                            {
                                //Detect if the object is an OLE object
                                if (o.DocumentObjectType == DocumentObjectType.OleObject)
                                {
                                    DocOleObject Ole = o as DocOleObject;
                                    string s = Ole.ObjectType;
    
                                    //Detect if the object type is in .pdf file format
                                    if (s == "AcroExch.Document.11")
                                    {
                                        //Save to file
                                        File.WriteAllBytes("Result.pdf", Ole.NativeData);
                                    }
    
                                    //Detect if the object type is in .xls file format
                                    if (s == "Excel.Sheet.8")
                                    {
                                        //Save to file
                                        File.WriteAllBytes("Result.xls", Ole.NativeData);
                                    }
    
                                    //Detect if the object type is in .xlsx file format
                                    if (s == "Excel.Sheet.12")
                                    {
                                        //Save to file
                                        File.WriteAllBytes("Result.xlsx", Ole.NativeData);
                                    }
    
                                    //Detect if the object type is in .doc file format
                                    if (s == "Word.Document.8")
                                    {
                                        //Save to file
                                        File.WriteAllBytes("Result.doc", Ole.NativeData);
                                    }
    
                                    //Detect if the object type is in .docx file format
                                    if (s == "Word.Document.12")
                                    {
                                        //Save to file
                                        File.WriteAllBytes("Result.docx", Ole.NativeData);
                                    }
    
                                    //Detect if the object type is in .ppt file format
                                    if (s == "PowerPiont.Show.8")
                                    {
                                        //Save to file
                                        File.WriteAllBytes("Result.ppt", Ole.NativeData);
                                    }
    
                                    //Detect if the object type is in .pptx file format
                                    else if (s == "PowerPiont.Show.12")
                                    {
                                        //Save to file
                                        File.WriteAllBytes("Result.pptx", Ole.NativeData);
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    

    [VB.NET]

    Imports System.IO
    Imports Spire.Doc
    Imports Spire.Doc.Documents
    Imports Spire.Doc.Fields
    
    Namespace ExtractOleObjects
        Class Program
            Shared  Sub Main(ByVal args() As String)
                'Create a Document object
                Document doc = New Document()
     
                'Load a Word file
                doc.LoadFromFile("OleObject.docx")
     
                'Loop through the sections
                Dim sec As Section
                For Each sec In doc.Sections
                    'Loop through the child objects
                    Dim obj As DocumentObject
                    For Each obj In sec.Body.ChildObjects
                        'Detect if the object is a paragraph
                        If TypeOf obj Is Paragraph Then
                            Dim par As Paragraph = obj as Paragraph
     
                            'Loop through the child objects within paragraph
                            Dim o As DocumentObject
                            For Each o In par.ChildObjects
                                'Detect if the object is an OLE object
                                If o.DocumentObjectType = DocumentObjectType.OleObject Then
                                    Dim Ole As DocOleObject = o as DocOleObject
                                    Dim s As String = Ole.ObjectType 
     
                                    'Detect if the object type is in .pdf file format
                                    If s = "AcroExch.Document.11" Then
                                        'Save to file
                                        File.WriteAllBytes("Result.pdf", Ole.NativeData)
                                    End If
     
                                    'Detect if the object type is in .xls file format
                                    If s = "Excel.Sheet.8" Then
                                        'Save to file
                                        File.WriteAllBytes("Result.xls", Ole.NativeData)
                                    End If
     
                                    'Detect if the object type is in .xlsx file format
                                    If s = "Excel.Sheet.12" Then
                                        'Save to file
                                        File.WriteAllBytes("Result.xlsx", Ole.NativeData)
                                    End If
     
                                    'Detect if the object type is in .doc file format
                                    If s = "Word.Document.8" Then
                                        'Save to file
                                        File.WriteAllBytes("Result.doc", Ole.NativeData)
                                    End If
     
                                    'Detect if the object type is in .docx file format
                                    If s = "Word.Document.12" Then
                                        'Save to file
                                        File.WriteAllBytes("Result.docx", Ole.NativeData)
                                    End If
     
                                    'Detect if the object type is in .ppt file format
                                    If s = "PowerPiont.Show.8" Then
                                        'Save to file
                                        File.WriteAllBytes("Result.ppt", Ole.NativeData)
                                    End If
     
                                    'Detect if the object type is in .pptx file format
                                    Private Function if(ByVal As s = "PowerPiont.Show.12" ) As else
                                        'Save to file
                                        File.WriteAllBytes("Result.pptx", Ole.NativeData)
                                    End Function
                                End If
                            Next
                        End If
                    Next
                Next
            End Sub
        End Class
    End Namespace
    

    Понравилась статья? Поделить с друзьями:
  1. Ole в delphi для excel
  2. Ole word c builder
  3. Ole package что это word
  4. Ole objects in word
  5. Ole objects in excel