• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 1235
  • Last Modified:

Launching Winword

Hi,

I'm trying to open a Winword session from C++ with the following code:

if (bNewSession)
{      
 sTemplate = "\"" + sTemplate + "\"";  // for long filenames
 if (bHide)
   hErrorCode = ShellExecute ( hParent, "open", "winword.exe", sTemplate, NULL, SW_MINIMIZE);
 else
   hErrorCode = ShellExecute ( hParent, "open", "winword.exe", sTemplate, NULL, SW_SHOWNORMAL);
}else
 if (bHide)
   hErrorCode = ShellExecute ( hParent, "open", sTemplate, NULL, NULL,SW_MINIMIZE);
 else
   hErrorCode = ShellExecute ( hParent, "open", sTemplate, NULL, NULL,  SW_SHOWNORMAL);

It is working fine except in the case:
bNewSession = true and bHide = true

I have a Word session running, and I am expecting to load the template in the current session but in background.
I also tried  SW_SHOWNA without any success.
The idea is to call winword to print a document within an application.
I also tried GetActiveWindow and SetActiveWindow with no more success.

Any idea?
Serge
 
0
SergeD
Asked:
SergeD
  • 10
  • 5
  • 5
  • +3
1 Solution
 
SergeDAuthor Commented:
Sorry Lads,

you understood I made a mistake. The incorrect case is:
- bNewSession = False
- bHide = True

Sorry for that
Serge
0
 
nietodCommented:
The last parameter is used only when a new application is started.  That parameter is used in the application's start-up information.   (and the application may ignore it.)   If the application is already running, the parameter is going to be ignored.    

what is it that you are hoping to achieve?  perhaps there is another way.
0
 
SergeDAuthor Commented:
I have a Word template that contains macros for editing, printing, ....

From an application, I'd like to launch Winword on a default template (with the macros) and if the required action is EDIT, then I'm expecting Winword to be in the foreground, and if the action is PRINT, then I'm expecting Winword to be in the background.

The different macros are used to replace some fields in the document and then wait for user interaction for EDIT action. Of course, the Minimise action could be include in the macro on PRINT action, but I'd prefer not to see Word in this case instead of to see the macros doing their job, and then to minimse word.

I know it looks a bit complicated, but...

Thanx
Serge

0
VIDEO: THE CONCERTO CLOUD FOR HEALTHCARE

Modern healthcare requires a modern cloud. View this brief video to understand how the Concerto Cloud for Healthcare can help your organization.

 
nietodCommented:
And what exactly is the problem that you are experiencing?  Is a running copy of word becoming activated (which you don't want)?  It is that an active copy is not becoming inactive?
0
 
SergeDAuthor Commented:
Well...

I'd like the active copy of Word to process in background for printing.

0
 
nietodCommented:
But what does that mean?  Do you want the active word window to minimize?  What does it do now, does it become active and then prints?  
0
 
SergeDAuthor Commented:
At the moment, the word session is activated, displayed on the foreground, and the document is processed (fields replaced, and document printed). When the printing process is finished the Word session stays there and my calling application is behind.
I'd like to the same process, but to be able to continue my stuff in my application. In the meantime, the document could be processed in the back.

Kind of spooler...
0
 
nietodCommented:
I don't think you can prvent that, but you can use WaitForInputIdel() to make sure that the target application (word) is done with its initialization (makes sure it already has been activated and moved to the foreground.),  then try to reactivate your own window.  

If you want to use WaitForInputidle, you will need the process handle.  For that you will have to use ShellExecuteEx().
0
 
wpdCommented:
Basically, you have very little control over the application when it is started with ShellExecute(Ex). So you have 2 solutions :



1. Use CreateProcess which will give you better control :



/* begin code */

PROCESS_INFORMATION pi;

STARTUP_INFO si;



memset(&pi,0,sizeof(pi));

memset(&si,0,sizeof(si));

si.cb = sizeof(si);

si.dwFlags = STARTF_USESHOWWINDOW; // whatever you like

si.wShowWindow = SW_SHOW; // SW_MINIMIZE, etc...

if (CreateProcess(NULL, cmdLine, NULL, NULL, FALSE,    

        NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi))

          WaitForInputIdle(pi.hProcess, INFINITE);        

/* end code */



2. But I'd strongly suggest launching Word as an OLE server, instead. It may sound scary at first (if you're not used to it), but I think it makes perfect sense considering your problem. That way, you'll have full control over what's happening.



The pseudo-code below will show you how :



HRESULT    hr;

CLSID      clsId;

LPUNKNOWN  punk;

LPDISPATCH pdisp = NULL;



// Get CLSID for WORD OLE Server

CLSIDFromProgID(OLESTR("WORD.Application"), &clsId);

// see if server's already running

hr = GetActiveObject (cldid, NULL, &punk);

// if hr == NOERROR, then the app is running

if (FAILED(hr)) {

  // Launch process through OLE

  hr = CoCreateInstance (clsId, NULL, CLSCTX_SERVER,

                     IID_IUnknown, (void FAR * FAR *)&punk);

}

// at that point, punk contains a valid IUnknown for word

punk->QueryInterface(IID_IDispatch, (void FAR * FAR *)&pdisp);

// not needed anymore

punk->Release();

// At that point, Word has started but is invisible



OK. Now you have "pdisp", which is the IDispatch for the Word application. This interface gives you access to the complete object model, as described in the docs or the Type Library. "pdisp" is the C++ equivalent of "app" in the following VB statement :



Dim app as Object

Set app = CreateObject("Word.Application")



Once you're here, you can :



Show/Hide the application : app.Visible = True/False

(VB Code, same in C++ but longer !)

Open a file : app.Documents.Open ("foo.doc")

Print the current document : app.Printout (...)

Quit the app : app.quit



You could even do your text editing from your application, without the user ever seeing it.



NB : If you're using MFC, you have wrapper classes that makes the code much easier to read, and shorter too.



Hope it helps.


0
 
viktornetCommented:
Just use SetForgroundWindow(This);

-Viktor
--Ivanov
0
 
SergeDAuthor Commented:
Thanx wpd for the code.

It works fine but unfortunately it is not what I'm looking forward. I don't want to create a new process, but using the current session (I don't want to launch Winword each single time I have a document to process.). For the OLE server, it is surely a good solution, but honnestly it is too scary. I'm not enough familiar in C++ to go in that way.

As Nietod suggested, I have tried the following functions:
Response = ShellExecuteEx( &sei );
wResponse = WaitForInputIdle( sei.hProcess, INFINITE);
Response = SetForegroundWindow (hParent);

It could work, but the WaitForInputIdle stop its execution when Word is loaded, and unfortunately not when the template is loaded. So, what is happening is:
- I press the PRINT button on my App
- Word is displayed on screen (I supposed I cannot avois that)
- My App is displayed 1 seconde (SetForegroundWindow)
- Word goes back in front to load the document
Final State: Word is in front and my App lost the focus.

I could of course add a Wait(X_Secondes), but the processing time of my document is not  constant and it is not really nice.

I'm still investigate the Waitxxx functions.

Thank you anyway
Serge
0
 
jim_pettinatoCommented:
Wouldn't this work just as you wish if you used the "print" action instead of the "open" action?

0
 
viktornetCommented:
jim_pettinato, even if you use the "print" operation (that's what actually you gotta do) WinWord will still open and your app would be behind... That's why you need to work out the WAIT function that will wait till you get evetything done and you can then set the focus on your app...
0
 
SergeDAuthor Commented:
Victornet,

you are right, but the WaitForIdle funtion waits for Winword to be loaded, not for the document. So, that means:
1. the focus is on MyApp
2. Winword goes in front for loading
3. MyApp goes back in front once Word is loaded
4. Winword goes back in front when the document is loaded

I should apply the WaitForIdle on Word and on its child.
Thanx anyway
Serge
0
 
viktornetCommented:
Use something like this...

ShellExecute(0, "print", "ThePathToTheFile", NULL, NULL, SW_SHOWNORMAL);
//do some waiting....
//SetForegroundWindow(MyHandle);
0
 
SergeDAuthor Commented:
Sure that I could wait for X seconds before to set the App in the foreground.
But the documents to process might be from different sizes and is it reasonable to slow the process of small documents?
I would prefer to get a solution like:
- Wait for Word Idle
- Get Word child
- Wait for Word child Idle
- Set MyAPP in the foreground

Is there a way to do that?

Ta,
Serge
0
 
viktornetCommented:
I'm sure there is a way, but not that i know of :(

well you could try this...

read the name of the file of the DOC...

e.g.

test.doc

then wait until there is a window with the caption that read something like this...

WinWord - test.doc

or something like that.. you just see how it writes it on the caption.. then get that handle and wait for the idle state of that window...

I hope this helps. I can't think of any other way... :(

-Viktor
--Ivanov
0
 
wpdCommented:
I still think the OLE technique I proposed would have been much less trouble, more reliable, and quicker, too :)
0
 
viktornetCommented:
if the OLE technique works, then it's okay to use it, but i don't think it's quicker,,,,

-Viktor
--Ivanov
0
 
calixtoCommented:
You can use OLE to access the WinWord (I supposed that you are using MFC, some changes are needed to run without MFC)

TRy the following code:

{
   COleVariant var1( "My template.dot" );
   COleVariant var2( (short)false, VT_BOOL );

   Application app;

   COleException e;
   CLSID clsid;
   LPUNKNOWN lpUnk;
   LPDISPATCH lpDispatch;

   if ( CLSIDFromProgID( OLESTR( "Word.Application" ), &clsid ) != NOERROR )
      return;

   if ( GetActiveObject( clsid, NULL, &lpUnk ) == NOERROR )
   {
      HRESULT hr = lpUnk->QueryInterface( IID_IDispatch, (LPVOID*)&lpDispatch );
     
      lpUnk->Release();

      if ( hr == NOERROR )
         app.AttachDispatch( lpDispatch, TRUE );
   }

   if ( app.m_lpDispatch == NULL && !app.CreateDispatch( clsid, &e ) )
      return;

   Documents docs( app.GetDocuments() );

   // create a document from a template
   //
   docs.Add( var1, var2 );

   _Document doc = app.GetActiveDocument();

   COleVariant fname( "my document.doc" );
   COleVariant none( DISP_E_PARAMNOTFOUND, VT_ERROR );

   doc.SaveAs( fname, none, none, none, none, none, none, none, none, none,none );
   
   app.Quit( none, none, none );
}

The classes Application, Documents, _Document can be imported from msword8.tbl located in the Office directory.

The documentation of this classes are the same of the Word VBA.

0
 
SergeDAuthor Commented:
Sorry,

WPD already tried this answer. Thanx anyway.
I'll give the points to WPD who gave me the most complete answer.

Ta
Serge

0
 
nietodCommented:
Then you need to reject the current answer.
0
 
wpdCommented:
Calixto's solution is the same as mine except that it needs MFC, type libs, etc. I've followed this thread with a lot of interest but I can't help thinking that OLE is your only way out. I'll re-post the solution if you want :)
0
 
SergeDAuthor Commented:
Sorry Lads,

I sent the comment and forgot to tick "Reject"
Here you go

Thanx
Serge
0
 
wpdCommented:
Answer was already submitted above.
Hope all goes well...

PS : If you're to implement an OLE solution, don't forget the "reference counting" mechanism. Examples using VB code :

Set app = CreateObject("Word.Application")
/* Word is launched, interface is not visible */
Set app = Nothing /* WINWORD process is unloaded */

but

Set app = CreateObject("Word.Application")
/* Word is launched, interface is not visible */
app.Visible = True /* shows the interface */
Set app = Nothing /* reference count is decremented but WINWORD is still alive */
/* User chooses "Quit" in the interface */
/* => Word is unloaded */

Also :

Set app = CreateObject("Word.Application")
/* Word is launched, interface is not visible */
app.Visible = True /* shows the interface */

/* User chooses "Quit" in the interface */
/* => Word is not unloaded because the VB app holds a reference to it */

And so on...



0
 
SergeDAuthor Commented:
Thanx,

I'll try to implement your solution when I will have a bit of time...

Serge
0

Featured Post

Keep up with what's happening at Experts Exchange!

Sign up to receive Decoded, a new monthly digest with product updates, feature release info, continuing education opportunities, and more.

  • 10
  • 5
  • 5
  • +3
Tackle projects and never again get stuck behind a technical roadblock.
Join Now