How to store variables within Word Add-In accessible only for the current Word instance

Add-in Express™ Support Service
That's what is more important than anything else

How to store variables within Word Add-In accessible only for the current Word instance
 
Mark Williams




Posts: 21
Joined: 2020-06-08
I am trying to control interaction between my own Delphi exe and a word add-in.

My add-in tlb has an InitializeAddIn method. Various variables can be passed to the add-in using this method which are then stored by the add-in and used for determining availability of ribbon tabs, task panes etc.

My exe creates a new instance of Word (ckNewInstance), adds a new word document to that word instance and calls the InitializeAddIn method before making the word app visible to make sure the required variables are set prior to loading the ribbon and task panes.

Within the AddIn these variables are stored under the TAddInModule.

If I open Word documents via my exe it works as expected. If I open more than one word document via my exe the Add-In seems to maintain two different sets of the variables set by the InitializeAddIn method and as such operates as I would expect/hope.

However, if I open a Word document via my exe and then open a second word document in the usual way, the second word document Add-In seems to have access to the variables set for the first Word document's add-in in other words the second document functions as if it has been opened via my exe.

Presumably, the reason two documents created by my exe behave properly is because I have created a new instance of word for each document. Whereas when I create a word document via my exe and then create a second document in the normal way, Word is not creating a new instance of itself, but piggybacking on the existing one and at the same time stealing all my variables!

That being so, is it possible to set variables in the Add-In module that are accessible only for the current document and not for all documents opened with the specific word instance?

If not, do you know if there is any way I can programmatically force Word to open a new instance of itself for any new documents?
Posted 04 Aug, 2020 16:11:40 Top
Andrei Smolin


Add-in Express team


Posts: 17361
Joined: 2006-05-11
Hello Mark,

The add-in could request settings whenever a new document opens. Or, when Office raises the PropertyChanging event on the Ribbon tab (or whatever Ribbon control(s) you have) and you find out that you do not have settings for the corresponding document.

Mark Williams writes:
If not, do you know if there is any way I can programmatically force Word to open a new instance of itself for any new documents?


I don't think this is possible to do. Note that Word doesn't create a new instance if you repeatedly start it from the Start menu.

Regards from Belarus (GMT+3),

Andrei Smolin
Add-in Express Team Leader
Posted 05 Aug, 2020 03:45:19 Top
Mark Williams




Posts: 21
Joined: 2020-06-08
Hi Andrei,

Thanks for the reply. I have come up with some partial solutions myself. The Add-in has a task pane and when initializing it I set it's tag to 1. I then make ribbons etc visible depending on whether the tag=1. Seems to work okay and I don't need to keep going back to my exe for settings.

The inability to open a unique instance of word as opposed to merely a new instance is a real design flaw in the com model as far as I am concerned!

It means your exe interacting with Word has to be ready to anticipate any other documents owned by the Word instance which are in a modal state.

Worse if the user opens a blank Word document via your exe and then open a docx file rather than a new blank document the docx file grabs your blank document (assuming it has not yet been changed). This is a real problem (for me at least).

I am working on solutions to overcome these problems. I thought I would post what I have come up with her in case anyone might find it useful.

I thought I would monitor the WordApp's onDocumentChange event and at any point the Documents count is higher than 1, close the offending window and reopen it in a new instance of Word.

This is the code I came up with:

First, open a new word document:
vDoc:=WordApp.Documents.Add(EmptyParam, EmptyParam, EmptyParam, True);
  vDoc.Variables.Add('PlaceHolder', 1); //this flags the document as changed


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) <> vDoc then //vDoc is a variable representing the document opened by my exe
          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;


This works well provided the user tries to open only new blank documents. However, if they try and open an existing file the call to close which I have flagged as "PROBLEM CODE" above causes the vDoc window to hang. I have no idea why.

The other problem is adding the document variable to bamboozle Word into thinking the document has changed when it hasn't to stop Word stealing the document window when a docx file is opened. As the document is flagged as changed, even if there have been no "real" changes to the document the user is prompted to save it.

Until I can solve these issues (if it's possible to do so), I am using a halfway house ie I am closing and reopening the word window only if the the user opens a blank word document in the same word instance as my exe. If they open an actual file I am escaping from the DocumentClose procedure before the call to close the document. That means it is still necessary to deal with some documents opened in the same instance and it is still necessary add the document variable.

I appreciate this is beyond the remit of AIE. However, I posted the code in case anyone might be interested in it and also, in case someone has a possible solution. If I can find one I will post it.
Posted 05 Aug, 2020 09:10:46 Top
Andrei Smolin


Add-in Express team


Posts: 17361
Joined: 2006-05-11
Hello Mark,

I don't understand what "modal state" is.

This approach is wrong strategically. An add-in shouldn't control Word. Instead, Word *and the user* control everything: they decide whether to use your add-in or not. Any restriction you impose causes a bad user experience.

Adding a variable *is* a change of the document. You may set theDocument.Dirty := false to prevent the dialog but this won't save the variable (if it was added or changed).

Regards from Belarus (GMT+3),

Andrei Smolin
Add-in Express Team Leader
Posted 06 Aug, 2020 04:09:16 Top
Mark Williams




Posts: 21
Joined: 2020-06-08
Hi Andrei,

"Modal state" (to me) means that the user has opened a modal dialog in the word document and the document becomes unresponsive until they deal with that dialog for example the insert a link dialog. In fact, not only does that document become unresponsive all documents opened in that Word instance become unresponsive because the whole application is in a modal state. That makes obvious sense from a programming perspective, but from a user point of view I think it is pretty awful.

I am not sure what you mean by theDocument.Dirty. I can't see any property in Word. However, if it's purpose is to suppress a dialog that is not what I am looking for. The problem is this. If I open a new blank document via Com via my app and before the user makes any changes they open an existing word document via the Word shortcut, Word hijacks my blank document and replaces it with the document the user has just opened. As my app allows users to open blank documents I have to prevent that standard behaviour from happening and the only way I can see to do that is to make a hidden change to the blank document as soon as it is opened so that Word will not then hijack it.

I appreciate what you say about strategy. I am not trying to control Word via the add-in. I am trying to control it via COM and via my own exe.

What I want is a word processor as part of my own application (rather than a stand alone word processor) that opens in a form in my application and is therefore part of my application. I originally created a word processor using WPTools for this purpose. It was fine and did the job although not as fully functional as Word. Users however complain that they don't want to use a different word processor they want Word. So I am trying to replace my word processor with Word and trying it to get it to operate as far as possible as my own word processor works ie as part of my application. If modern Word worked well in an OLE container that would probably work for me. But it doesn't. So I have to work with Word as a stand alone solution and control it as best I can for the purpose of my app using COM. Using an add-in is a real help and AIE is excellent. But I could really do with the ability to open a unique instance of Word for my application and not just a new one which can then be hijacked by Word for other documents.

If the COM model offered a connectKind of ckUniqueInstance rather than just ckNewInstance that would be fine, but it doesn't. I accept I may be forced to live with this COM limitation if I cannot get my rather dirty workaround to function. If I can get it to function I will obviously need to make an assessment of whether it is desirable or not to use it. It works fine when the user tries to open a new blank document from the Word shortcut save that they experience a slightly longer delay in opening it (because it is in effect opened twice), but if that is the only interference with the user experience I may be prepared to run with it.

But as I said previously, I do appreciate that this is not an AIE issue in any way and I do not expect you to try and answer it. But some of your forum subscribers may have come across this issue in the past and may have some useful insight, which I would appreciate.
Posted 06 Aug, 2020 05:03:39 Top
Andrei Smolin


Add-in Express team


Posts: 17361
Joined: 2006-05-11
Mark Williams writes:
I am not sure what you mean by theDocument.Dirty.


My fault. This should be theDocument.Saved.

Mark Williams writes:
Word hijacks my blank document and replaces it with the document the user has just opened


Word (and Excel) does this only with the first document (Document1, Book1) created by default. Create a new document and get rid of Document1.

Mark Williams writes:
What I want is a word processor as part of my own application (rather than a stand alone word processor) that opens in a form in my application and is therefore part of my application.


All I know suggests that Word doesn't support this scenario: you can't control it. On the other hand, you can embed a document in your container and activate the document there. But this topic is complete out of scope here. You can start with https://social.msdn.microsoft.com/Forums/vstudio/en-US/50b83cae-3f13-42ea-aa30-e725c34a4762/embedding-a-word-document-into-an-application?forum=vbgeneral to find more details.

Regards from Belarus (GMT+3),

Andrei Smolin
Add-in Express Team Leader
Posted 06 Aug, 2020 05:20:10 Top
Mark Williams




Posts: 21
Joined: 2020-06-08
Hi Andrei,

Thanks.

Unfortunately, setting saved to false doesn't work. When deciding whether to hijack a document it seems Word doesn't check the saved state it seems to check if there's any content. If there isn't it hijacks it. Can't understand why there is a different approach when the user opens a second blank document.

Word (and Excel) does this only with the first document (Document1, Book1) created by default. Create a new document and get rid of Document1.


That sounds like an interesting solution. However, for some reason Word has stopped hijacking instances created by my app so I can't test it! Try as I might I can't get it to repeat the previous behaviour. I create an instance via my app an then open a new/existing word document for which Word creates a new instance and it continues to use that new instance for all docs opened subsequently. Try as I might I can't get it to offend again.

Thanks for the suggestion re embedding Word. That would have been a nifty solution, but I have had a quick look into it and I can't see it being a practical solution

It is, however, simple to implement. Merely Windows.SetParent(wordHandle, myForm.Handle) does the job.

However, you lose the Word main menu, and ribbon, pressing Ctrl+F1 does not produce the search functionality etc.

I've tried to get the main menu back (and presumably the ribbon) with SetWindowLong and removing WS_Popup, but that doesn't bring the menu back nor does toggleribbon for the active window.
Posted 06 Aug, 2020 09:48:49 Top
Andrei Smolin


Add-in Express team


Posts: 17361
Joined: 2006-05-11
Hello Mark,


Mark Williams writes:
Word has stopped hijacking instances created by my app so I can't test it!


Make sure this flag is unchecked: File | Options | General | "Show the Start screen when this application starts". Also make sure you have no extra WINWORD.EXE hanging in processes.

Mark Williams writes:
It is, however, simple to implement. Merely Windows.SetParent(wordHandle, myForm.Handle) does the job.


No-no! I meant embedding a Word document the same way you embed it in an Excel worksheet. Note that nowadays, SetParent requires extra things to do: search "different DPI awareness" at https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setparent.

Regards from Belarus (GMT+3),

Andrei Smolin
Add-in Express Team Leader
Posted 07 Aug, 2020 01:37:52 Top
Mark Williams




Posts: 21
Joined: 2020-06-08
I've unchecked splash screen, but it still shows. I probably need to repair /reinstall Word.

What mechanism does Microsoft use to embed Word in Excel.? I always assumed it was OLE.
Posted 08 Aug, 2020 09:33:30 Top
Andrei Smolin


Add-in Express team


Posts: 17361
Joined: 2006-05-11
Hello Mark,

Start screen, not splash screen.

OLE.

Regards from Belarus (GMT+3),

Andrei Smolin
Add-in Express Team Leader
Posted 11 Aug, 2020 04:20:29 Top