Jump to content
Mark Williams

Trying to turn CKNewInstance into CKUniqueInstance for ms word

Recommended Posts

I am automating Word via TWordApplication. I would like to create the word document in its own unique instance of Word. It is possible to open the document in its own instance by setting the ConnectKind to ckNewInstance, but Word is then a bit of a bandit and if the user open a new word document other than via my app my word instance gets hijacked by the new Word document.

 

It doesn't sound like such a big problem, but these are some of the consequences:

  1. If the pirate document enters modal mode so does my document and my app can't do anything with it. Of course, if the user puts my document into modal mode I have the same issue and I have to respond to OLEExceptions for rejected calls. But in he case of my own document I can pop up it up from my app for the user to deal with the modal dialog before proceeding. If it is a pirate document (and there could well be more than one)  it is a bit yuk to tell the user to either find it themselves and kill the modal dialog or to pop up all possible docs that might be modal.
  2. Even worse than this is that if the user opens a blank document via my exe and then open an existing word document not via my app Word commits high sea piracy and hijacks my blank document completely so that unless I monitor this my app is dealing with the wrong document altogether.

 

There is a registry hack to force Word to open in a new instance every time, but I don't want to force that on users, plus I think it requires admin privileges.

 

In the absence of a ckUniqueInstance constant I have been trying to replicate that effect by monitoring the onDocumentChange event of the Word application and checking if the application's documents count goes above 1. If so I close the pirate document and re-open it in its own instance.

 

I appreciate this is not perhaps the most elegant solution and I also suspect I am going to be told not to interfere with the user experience in this way. I having quite made my mind up as yet. But  it almost works, subject to two issues (so far) one major, one minor and I am curious to see if I can get it to work.

 

First, when my app opens a new blank document I add a custom variable to the document simply so that it is flagged as having content. This stops Word trying to hijack the document when an existing document is subsequently opened.

 Doc.Variables.Add('AnyOldRubbish', 1); //this flags the document as having content

And then in the DocumentChange event:

 

procedure TForm.DocumentChange(ASender: TObject); 
  var 
    i : integer; 
    WordAppT : TWordApplication; 
    Template, FileName : OleVariant; 
begin 
  if wordApp.Documents.Count>1 then 
    begin 
      for i:=WordApp.Documents.Count downTo 1 do 
      begin 
        Template := EmptyParam; 
 
        if WordApp.Documents.Item(i) <> Doc then 
          begin 
            FileName := WordApp.Documents.Item(i).FullName; 
            if not FileExists(FileName) then 
              FileName := ''; 
            Template := WordApp.Documents.Item(i).Get_AttachedTemplate; 
            if Uppercase(Template) = 'NORMAL.DOTM' then 
              Template := EmptyParam; 
 
            //Close the offending document! 
            WordApp.Documents.Item(i).Close(false, emptyParam, emptyParam); {PROBLEM CODE - see below} 
 
            //Reopen it in a separate instance of Word 
            WordAppT := TWordApplication.Create(Nil); 
            WordAppT.ConnectKind := ckNewInstance; 
 
            if FileName<>'' then 
              WordAppT.Documents.open(FileName, EmptyParam, EmptyParam, EmptyParam, EmptyParam, 
              EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, 
              EmptyParam, EmptyParam, EmptyParam, EmptyParam) 
            else 
             WordAppT.Documents.Add(EmptyParam, EmptyParam, EmptyParam, True); 
 
            WordAppT.Visible := true; 
            WordAPPT.ActiveWindow.Activate; 
          end; 
      end; 
    end; 
end; 

The two problems:

  1. By adding the variable I am marking blank documents as having been changed when they should be marked as unchanged. Not a major problem. Would be good not to have to do this, but if I can solve problem 2 I can deal with this issue by checking the word count property of the document to see if there has been any substantive change, Not perfec, but good enough for my purposes.
  2. The above code works neatly when the user tries to open a blank pirate document, but not when they try to open an existing document. In the latter case, whilst the pirate document does get opened in its own new instance, the closing it in the first place causes my original document to become unresponsive and I can't for the life of me work out why.

 

If I break the process down into separate chunks it works fine:

  • Open my document.
  • Open an offending existing document outside of my app.
  • Add a button to close the offending document.
  • Add another button to reopen the offending document.

 

This works leading me to believe it might be a timing issue. So I tried adding a sleep before and after the call to close the pirate document, but made no difference.

Share this post


Link to post

In case any one is interested there is a partial solution to this. Create a dummy document and close it before opening your actual document. Word apparently only latches on to the first instance opened. So:

//Open fake doc
dummyDoc:=WordApp.Documents.Add(EmptyParam, EmptyParam, EmptyParam, True);
dummyDoc.Close(false, emptyParam, emptyParam);

//Open actual document
 WordApp.Documents.Add(EmptyParam, EmptyParam, EmptyParam, True);
RealDoc:=WordApp.ActiveDocument;

Thanks to the guys at Add-In Express for this suggestion. 

 

However, it has some limitations:

  1. It doesn't seem to work if you have a Word window hung somewhere in the background.
  2. It doesn't catch new word windows opened within your instance of Word. So if your users open a new or existing word document  from eg the backstage of your word instance, it will open in your instance. 

To overcome both the problems I have implemented the following:

  1. Disable all functionality within my word instance for opening existing or new documents.
  2. Use the DocumentChange event to catch any documents that may still be loaded in your instance of Word, but do the following to overcome the issues mentioned in the previous post:
  •  In the documentChange event check if there is more than one document in your word instance.
  • If there is, fire a timer event (interval of 100 ms seems to be sufficient) and add similar code to that shown in the first post to the timer event.

Still a clunky last resort, but I really need to stop word documents being opened in my instance.

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×