Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations gkittelson on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

ShellExecute & IsRunning 1

Status
Not open for further replies.

andrej67

IS-IT--Management
Jan 23, 2007
14
SI
Hi,

simple question with no answer in sight. After starting an external appl from within VFP with Shellexecute i want to make sure, that the appl does its job and only after that, i can query its result. If i use

Shellexecute(0, "", appl, "", "", 1)
DO WHILE isRunning(appl)
ENDDO

it skips the "DO WHILE" loop since it hasn't been started yet. If, on the other hand, i issue a MessageBox, it processes system messages, which in turn starts the appl and program enter "DO WHILE" loop and waits for my appl to finish its job. So, i need something like ProcessMessages Delphi method, that is, something that handles events, that are stil waiting in queue. Any ideas, how can this be accomplished?

Thanks in advance for your suggestions,
Andrej
 
Hi Adrej,

Would that IsRunning() function be one that you downloaded from my site (
If so, it should work as you have coded it, except that it needs time for the application to be launched. Try putting a short delay after the ShellExecute() -- using WAIT or INKEY for example.

Also, make sure that the parameter you pass (appl in your example) matches the actual window title, which is not necessarily the name of the executable.

If you still can't get it to work, you could try a completely different approach:

Code:
oWsh = CREATEOBJECT("wscript.shell")
oWsh.Run(appl, 3, .T.)

The third param (.T.) tell your VFP program to suspend execution while the other application is running.

Mike


__________________________________
Mike Lewis (Edinburgh, Scotland)

My Visual FoxPro site: www.ml-consult.co.uk
 
Mike, yes, it's the one downloaded from your site and it works when appl being tested was not run from within the VFP and no user action was taken from "execution time". I've tried with inkey but to no avail, appl sleeps until it is forwarded from the waiting list, which happens with every "user intervention" (like messagebox). Scripting is not an option since i'm not sure that it's enabled on all the clients and i'm not in a position to decide on that.

Olaf, i'll check your suggestion, hopefully it'll work.
 
Olaf,

your suggestion works great. Thanks for the help,

Andrej
 
Hi Andrej,

thanks, glad it helps. I also had my doubts about WSH, but today it is seldom disabled. And that solution is quite beautiful because of it's shortness. And there are more things you can do, eg Run also has a third outputparameter, which enables you to get back a result.

Bye, Olaf.
 
Mike & Olaf:

I am aware of both the solutions you have provided, But have been wrestling with:

I want the control to return to VFP, but need to know when a user has closed the external app.

Example:
User does stuff in Both VFP and the document, need to know when the document is closed. Is it possible? We have quite a convoluted way to do this was looking for an easier way.

Thanks
 
You need to define an eventhandler class an implement the Word or Excel event model.

It's easier as you think, the object browser creates the class skeleton for you, then you would only change to close/quit event.

Here's a step by step instruction on how to do that:
Insead of selecting the Microsoft Agent Control in step 4 select the Microsoft Office X type library.

and instead of dragging the _AgentEvents to the editor window, drag the Word or Excel Events Interface.

When you have the class, you need to create word or excel, like always, resulting in eg loWord, then create that interface class, eg loWordEvents, then bind them with Eventhandler(loWord, loWordEvents).

You've seen this already in my latest example of the tag cloud.

Bye, Olaf.
 
Thanks Olaf;
Am out of town for the holidays, will try it Tuesday and will post back...
 
Olaf: thanks for the link. It will not work for what I want to do; it would work if I wanted to know what the user was doing in the external app. What I wanted was to be able to know if the external app was closed prior to running some code.
I have figured out an extremely simple way to do it. (Why it did not come to me over the last year or so is beyond me)

In the form, I create a word or excel document, in the QueryUnload() with a Try..Endtry. I check :
For Excel:
if Vartype(This.oExcel.ActiveSheet) = "O"
For word:
if Vartype(This.oWord.Windows) = "O"

If either of them is open, this returns a True. Need to figuire out for other formats i.e. Pdf, Tiff etc
 
Hi Imaginecorp,

there is a Quit or Close event, which you could use. Also a Save event and Sheet Save/Close and others. It has advantages, as you can react to these events too, not only to close. That is of course limited to COM applications.

But in general you coud use the Createprocess call only, you get a processID and can use WMI to find out the list of running processes/ if that process still runs.

VFP6:
Code:
LOCAL lcComputer, lcAdminUserName, lcAdminPassword, loSWbemLocator, loSWbemServices
*lcAdminUserName="sysadmin_name" for remote computers, not needed locally
*lcAdminPassword="password_sysadmina" for remote computers, not needed locally
lcAdminUserName=""
lcAdminPassword=""
*lcComputer = "remote_computer_name" or "." for this computer
lcRemoteComputer = "."

loSWbemLocator = CreateObject("WbemScripting.SWbemLocator")
loSWbemServices = loSWbemLocator.ConnectServer(lcRemoteComputer,"root\cimv2", lcAdminUserName, lcAdminPassword)
lcolSWbemObjectSet = loSWbemServices.InstancesOf("Win32_Process")
FOR EACH loProcess In lcolSWbemObjectSet
    ? "Name "+ TRANSFORM(loProcess.Name)
    ? "Process ID: " + TRANS(loProcess.ProcessID)
NEXT

VFP7:
Code:
lcComputer = "."
loWMIService = Getobject("winmgmts:" ;
    + "{impersonationLevel=impersonate}!\\" + lcComputer + "\root\cimv2")
colProcessList = loWMIService.ExecQuery ;
    ("Select * from Win32_Process")

FOR EACH loProcess In colProcessList
  IF TRANSFORM(loProcess.Name)=lcExeName
    ? "Name "+ TRANSFORM(loProcess.Name)
    ? "Process ID: " + TRANS(loProcess.ProcessID)
    ? "App Command Line: " + TRANS(loProcess.CommandLine)
    ? "CPU usage to date: " + ;
          TRANSFORM((VAL(loProcess.KernelModeTime)+;
          VAL(loProcess.UserModeTime)) / 10000000)
  ENDIF 
NEXT

Here you can even select for the single processID you want to watch: Select * from Win32_Process Where ProcessID=the one you get back from Createprocess.

You can even subscribe to WMI events, eg waiting for an app to start or to quit:

Bye, Olaf.
 
Hello Olaf:

I am sorry I did not explain better.

We save documents (doc, xls, pdf, tiff etc) in a table using filetostr(), Strtofile(). The user has the capability to view, create and/or delete. Once created /viewed, it is saved back to the table and the physical copy erased.

Lots of times the user may create , let’s say, a word document Outside our app (“A.Doc”), he then opens a word doc from inside our app (“B.Doc”), the Excellent code you wrote would not work as it would always return True, even if the “B.Doc” was closed but “A.Doc” was still open. And A.doc could be open for a long time. I am only interested in B.Doc as I need to erase the physical copy once it’s closed.

That’s why the objects, oWord & oExcel (posted above) are useful, as they hold B.doc.

For other documents (pdf, tiff etc.), which we open with “wscript.Shell” I have not figured out a way, based upon the above scenario. I need to store this in an object so I can identify which was opened within the app and which was opened outside.

Thank you for the excellent code.

We have been using the following function in Foxtools.fll. It’s pretty neat as you do not have to know the .Exe name; it accepts words like “Microsoft Word”, “Windows Media Player”, etc.

Code:
Parameters WindowName
IsAppRunning = .F.
Set Library To foxtools.fll
* Register the Windows API functions that will be called
mGetWinTxt = RegFn("GetWindowText", "I@CI", "I")
mGetWindow = RegFn("GetWindow", "II", "I")
mIsWinVis =  RegFn("IsWindowVisible", "I", "I")
* Get the HWND (handle) to the main FoxPro window
foxhwnd = MAINHWND()
* Produce a list of all windows
hwndNext = CallFn(mGetWindow,foxhwnd,0)
Do While hwndNext <> 0
	If (hwndNext <> foxhwnd) .And. ;
			CallFn(mGetWindow,hwndNext,4) = 0 .And. ;
			CallFn(mIsWinVis,hwndNext) <> 0
		cHolder = Space(64)
		x = CallFn(mGetWinTxt,hwndNext,@cHolder,64)
		* check to see if this is the specified window. 
		If Len(Trim(WindowName)) <> 0 And WindowName $ cHolder
			IsAppRunning = .T.
			Exit
		Endif
	Endif
	hwndNext = CallFn(mGetWindow,hwndNext,2)
Enddo
Return IsAppRunning
 
Hi Imaginecorp,

I did not know this is your task...

If that's what you need to do: copy a known file back to your dbf with filetostr() after the user closed/saved it, then you simply could try to fopen(...,12) the file you want to put back. If you get a handle (read/write mode) to the file, it is not open anymore by the external app (whatever that is) and can be stored. You would not even need to know the window title then.

It's not very reliable, as that can change, eg if you replace Adobe Reader by foxreader as the default pdf file reader.

Bye, Olaf.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top