OLE Automation: How do i copy text between Word do

2019-03-31 01:12发布

问题:

While doing som Word automation from Delphi XE, I have two documents open simultaneously. I want to copy the contents of a given range of one document to another range in the other document. How can I do this?

Consider the following code:

procedure TForm1.ManipulateDocuments;
var
  vDoc1,vDoc2 : TWordDocument;
  vFilename : olevariant;
  vRange1,vRange2 : Range;
begin
  vDoc1 := TWordDocument.Create(nil);
  vDoc2 := TWordDocument.Create(nil);
  try
    vFilename := 'c:\temp\test1.doc';
    vDoc1.ConnectTo(FWordApp.Documents.Open(vFilename,EmptyParam,EmptyParam,EmptyParam,EmptyParam,EmptyParam,EmptyParam,EmptyParam,EmptyParam,EmptyParam,EmptyParam,EmptyParam,EmptyParam,EmptyParam,EmptyParam));

    vFilename := 'c:\temp\test2.doc';
    vDoc2.ConnectTo(FWordApp.Documents.Open(vFilename,EmptyParam,EmptyParam,EmptyParam,EmptyParam,EmptyParam,EmptyParam,EmptyParam,EmptyParam,EmptyParam,EmptyParam,EmptyParam,EmptyParam,EmptyParam,EmptyParam));

    vRange1 := GetSourceRange(vDoc1);
    vRange2 := GetDestinationRange(vDoc2);

    vRange2.CONTENTS := vRange1.CONTENTS; //What should I substitute for CONTENTS?
  finally
    vDoc1.Free;
    vDoc2.Free;
  end;
end;

Is there something I could substitute for CONTENTS? I can't use text, since I want to copy formatting, bookmarks, field codes etc. Do I have to do it another way alltogether? Any suggestions?

回答1:

I don't know a way for earlier versions of Word, but for newer versions (2007 and up) you can export a range from a document to a fragment file, and then import it from another document. If you want early binding, you might need to import the type library (msword.olb), I don't know if Delphi XE has it. Otherwise the code might look like this:

function GetTempFileName(Prefix: string): string;
begin
  SetLength(Result, MAX_PATH);
  GetTempPath(MAX_PATH, PChar(Result));
  windows.GetTempFileName(PChar(Result), PChar(Prefix), 0, PChar(Result));
end;

procedure TForm2.Button1Click(Sender: TObject);
const
//  wdFormatDocument = 0;
  wdFormatRTF = $00000006;
var
  WordApp : OleVariant;
  fragment: string;
  vDoc1, vDoc2: OleVariant;
  vRange1, vRange2: OleVariant;
begin
  try
    WordApp := GetActiveOleObject('Word.Application');
  except
    WordApp := CreateOleObject('Word.Application');
  end;
  WordApp.Visible := True;

  vDoc1 := WordApp.Documents.Open(ExtractFilePath(Application.ExeName) + 'test1.doc');
  vRange1 := vDoc1.Range(20, 120);     // the export range
  fragment := GetTempFileName('frg');
  vRange1.ExportFragment(fragment, wdFormatRTF);
  try
    vDoc2 := WordApp.Documents.Open(ExtractFilePath(Application.ExeName) + 'test2.doc');
    vRange2 := vDoc2.Range(15, 15);    // where to import
    vRange2.ImportFragment(fragment);
  finally
    DeleteFile(fragment);
  end;
end;

With my test, 'document' format threw an error (something like not being able to insert XML formatting), hence usage of RTF format.

edit:

With earlier versions, it seems to be possible to insert a named selection from one document to a selection in another document. The result seems not to be perfect regarding formatting if one of the selections happens to be in the middle of some text. But otherwise it seems to be working good.

  ...
  WordApp.Visible := True;

  vDoc1 := WordApp.Documents.Open(ExtractFilePath(Application.ExeName) + 'test1.doc');
  vRange1 := vDoc1.Range(20, 188);                 // the transfer range
  vDoc1.Bookmarks.Add('TransferSection', vRange1); // arbitrary bookmark name

  vDoc2 := WordApp.Documents.Open(ExtractFilePath(Application.ExeName) + 'test2.doc');
  vRange2 := vDoc2.Range(103, 104);           // where to import the bookmark
  vRange2.Select;
  vDoc2.ActiveWindow.Selection.InsertFile(vDoc1.FullName, 'TransferSection');

  vDoc1.Bookmarks.Item('TransferSection').Delete; // no need for the bookmark anymore
 


回答2:

If you can use the Office Open XML-format (ie. the docx file format that was introduced in Word 2007), then you can do this without automation.

Word versions prior to 2007 must install a compatibility pack which will enable docx-files for Word 2003, 2002 and 2000.

The docx-file is actually a zip-file that contains several xml-files. Try to change the extension of a docx-file from .docx to .zip and open this file in eg. WinZip.

So... Unzip docx-file and grab the xml-part you need. As pure string or as a xml document. Then you can inject this xml-part into the other docx-file. You need to know where in the xml-structure to grab/insert the xml, though. This will depend on how well you know the document structure and how much editing the user is allowed to do in the document.

I don't know how Word will handle duplicate bookmark names etc with this approach.



回答3:

It seems I found the canonical solution to this question while digged into similar problem. The FormattedText property of Range object is the exact what do you need. Just use:

vRange2.FormattedText := vRange1;

and the contents of vRange1 will be copied into vRange2. Also, this works too:

vRange2 := vRange1;

Though, the second statement doesn't copy the formatting.



回答4:

Why not use the clipboard? If all the text is selected in vDoc1, then to copy this to the clipboard involves one simple call: vDoc1.copy. Similarly, copying the contents of the clipboard to the second document requires one simple call: vDoc2.paste. The clipboard buffer will hold all the formatting information.