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 Chriss Miller on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

WinExec: Problem running xcopy

Status
Not open for further replies.

rianeiromiron

Programmer
Jul 1, 2001
151
GT
Hi everyone:

I'm using WinExec to execute a command several times, each time with different parameters. Actually I'm using WinExec to run xcopy.exe to make backup of several directories. Before the xcopy is executed I create a dummy file in the source directory. After the xcopy I check for that file in the destination directory. If the file is present, the xcopy was succesfull and I move on to the next directory. The problem is that when I isue

WinExec('xcopy',myparameters)

the next line of code is executed even if the xcopy has not finished copying and because of that I cannot check for the existance of the dummy file in the destination directory.

I've been thinking in 2 solutions which I know would work but don't know how to implement:

1) Make WinExec run the command and continue to the next line of code until it has completed the file copy

2) After isuing WinExec, enter a While Do with something like this


Do While .t.
if the last WinExec is still running
do nothing
else
exit
endif
enddo

Does anyone have any ideas for this?

Thank you
 
rianero

There is an MSDN knowledge base article which should provide you with an answer

HOWTO: Determine When a 32-bit Process has Completed (Q191584)

This uses CreateProcess() as opposed to WinExec(), but in reality WinExec() calls CreateProcess().

You may need some code to get xcopy.exe to run hidden - if so please advise.

You can run the process in a loop as you were intending, and each instance of xcopy should complete its task before instantiating the next one.

HTH

Chris :)
 
Dear Chris:

Thanks a lot.

Yes, it worked. However, when the process I'm running (the xcopy), I need to close the D.O.S. window manually and then I get the message box telling me the process finished. In Winexec you can use 7 as your second parameters and the proces will run hidden, so, do you know how to

1) Terminate the process automatically and
2) Hide the D.O.S. window

or

2) Get a handle for the WinExec process and ask if it's still running.

Thank you.
 
rianero

As xcopy is not automatically terminating under CreateProcess(), you will need additional code to both terminate xcopy and hide the process.

As you were already thinking of checking for xcopy running through a WinExec() call, I suggest that is now the simpler way.

You need to put VFP into a wait state with a DO WHILE ... loop or a timer, checking for xcopy through Geoge Tasker's Is_Run32.prg

DO WHILE .T.
[tab]lnHand = IsRunning([xcopy])
[tab]IF lnHand = 0
[tab][tab]EXIT
[tab]ELSE
[tab][tab]WAIT WIND [] TIME 1
[tab]ENDI
ENDD

WAIT WIND [Xcopy terminated]


*******************************
FUNCTION IsRunning
* FUNCTION: Is_Run32.prg
* AUTHOR: George Tasker
* DATE: January 13, 1998 - 8:26 AM
* PURPOSE: Determines if a Windows
* application is running and returns
* the handle of the window if it is,
* otherwise returns 0.
* 32 bit version for Windows 95/NT 3.51-4
* Modified 5/20/2001 - gt

LPARAMETER pctitle

* Parameter list description
*
* pctitle - The title bar of the Window
* Note: The title does not have to be
* the complete title that appears
*
*
* API Declarations
DECLARE INTEGER GetDesktopWindow IN Win32API
DECLARE INTEGER GetWindow IN Win32API;
[tab]INTEGER hwnd,;
[tab]INTEGER dflag
DECLARE INTEGER GetWindowText IN Win32API;
[tab]INTEGER hwnd,;
[tab]STRING @lptstr,;
[tab]INTEGER cbmax

LOCAL lnhwnd,;
[tab]lnnext,;
[tab]lldone,;
[tab]lctitle_bar,;
[tab]lcsearchfor,;
[tab]lntext_len

lcsearchfor = UPPER(ALLTRIM(pctitle))
lnhwnd = GetDesktopWindow()
lnhwnd = GetWindow(lnhwnd, 5) && Get first child window
lnnext = 2
lldone = .F.
lctitle_bar = []

DO WHILE !lldone
[tab]IF !EMPTY(lnhwnd)
[tab][tab]lctitle_bar = SPACE(200) + CHR(0)
[tab][tab]lntext_len = GetWindowText(lnhwnd, @lctitle_bar, 200)
[tab][tab]lctitle_bar = UPPER(LEFT(lctitle_bar, lntext_len))
[tab][tab]lldone = (lcsearchfor $ lctitle_bar)
[tab][tab]IF !lldone
[tab][tab][tab]lnhwnd = GetWindow(lnhwnd, lnnext)
[tab][tab]ENDIF
[tab]ELSE
[tab][tab]lldone = .T.
[tab]ENDIF
ENDDO

RETURN lnhwnd && If 0, not found()

HTH

Chris :)
 
Thank you Chris, I'll try it at work on monday. It's very helpfull.
 
Dear Chris:

So I finally got it done but with some drawbacks.

The code I'm using is the following

#DEFINE SW_HIDE 0
#DEFINE SW_SHOWNORMAL 1
#DEFINE SW_SHOWMINIMIZED 2
#DEFINE SW_SHOWMAXIMIZED 3
#DEFINE SW_SHOWNOACTIVATE 4
#DEFINE SW_SHOW 5
#DEFINE SW_MINIMIZE 6
#DEFINE SW_SHOWMINNOACTIVE 7
#DEFINE SW_SHOWNA 8
#DEFINE SW_RESTORE 9
#DEFINE SW_SHOWDEFAULT 10

DECLARE INTEGER FindWindow IN user32;
STRING lpClassName, STRING lpWindowName

#DEFINE WM_QUIT 18

DECLARE SHORT PostMessage IN user32;
INTEGER hWnd, INTEGER Msg,;
STRING @wParam, INTEGER lParam

DECLARE INTEGER CloseHandle IN kernel32 INTEGER hObject
DECLARE INTEGER WinExec IN kernel32;
STRING lpCmdLine,;
INTEGER nCmdShow

lpCmdLine="c:\windows\command\xcopy32.exe c:\source\*.* c:\destination /s"
WHANDLE=WinExec (lpCmdLine, 0)
do while .t.
hWindow=FindWindow (.NULL.,"XCOPY32")
if hWindow>0
loop
else
exit
endif
enddo
&& for some miseteriouse reason I had to verify twice
&& and make a dummy wait, otherwise it wouldn't work

WAIT '' WINDOW TIME 1
lwindow_=0
lwindow=0
DO WHILE .t.
lwindow_=lwindow
lwindow=FindWindow (.NULL.,"XCOPY32")
if lwindow=0
exit
endif
ENDDO
=MESSAGEBOX('XCOPY32 REALLY FINISHED XCOPY32')
= PostMessage(WHANDLE, WM_QUIT, 0,0)

Now, the drawback is the following:

I'm running Win98b, and every time I run XCOPY32 (using the parameter 7 in WinExec) and then press CTRL+ALT+DEL a copy of WinOldAp appears in the list of proceses running. I found WinOldAp is the application that runs DOS commands. I tried to free it using PostMessage but didn't success. Please taka a look at my code and give me some hints. I'll really appreciate it.

Rianeiro
 
rianero

I suspect the reason that you need the first WAIT WIND is that xcopy.exe has not been instantiated before the DO WHILE... loop begins, and so the window cannot be found.

If you are going to use the DO WHILE... loop only once in an application, then placing it in the main code is fine - otherwise, IMHO, it would be better in a function like ISRUNNING().

One advantage of calling a function is that you can make the VFP wait state more efficient by using WAIT WIND [] TIME n, the WinAPI call Sleep or use a timer.

In your code you are constantly looping in the DO WHILE... loop and this can be demanding on resources.

The term "hidden" can be confusing - it appears to mean that the relevant window does not appear on the desktop in either a minimized, normal or maximized WindowState, but always seems to appear in the list of open Windows applications under one name or another.

FI, if you instantiate Word as a COM object but run it without opening a existing file and making it visible, it appears in the list of open Windows applications list as "winword".

What should happen is that "WinOldAp" should disappear after all the xcopy processes have finished - are you saying it is remaining in the open Windows applications list?

The fact that it is in the list of open Windows applications can be positive rather than negative.

In your help file, you could explain that if the application freezes during this xcopy process, there is a way out, by pressing CTRL+ALT+DEL and closing the application.

This may be stating the obvious, but now you know a particular xcopy process has terminated and how many individual processes there are, you could help the user, if you have not already done so, by providing a progressbar.

HTH

Chris :)
 
rianero

Since making the last post, I have been able to use your code and now see your problem.

"oldwinap" does not disappear from the active Windows list even though XCOPY32.exe has finished and adds another instance each time you run the code.

I have unsuccessfully tried various WinAPI calls to remove it.

One workaround would be to abandon the use of XCOPY32.exe and instead use VFP's COPY FILE command in a loop.

As you are using the '/s' parameter with XCOPY32, you are going to need to determine the folder structure of your application to simulate the way XCOPY32 copies to subfolders.

The following code will do a recursive search and return paths\subfolders depending on where you start the search. This data can be used for target folders as well as source folders.

You could then rework the code by COPY FILE ... within SCAN... ENDSCAN through the cursor DIRLIST.

*****************************
lcFolder = SYS(5) + SYS(2003)

CREATE CURSOR DIRLIST (folder C(240)) && Create dirlist
INSERT INTO DIRLIST (folder) ;
[tab]VALUES ([C:\windows\]) && Start at C:\windows

DO WHILE !EOF([DIRLIST])
[tab]lnRec = RECN()
[tab]SET DEFAULT TO ALLT(DIRLIST.folder)
[tab]lnNo = ADIR(laDirList,[],[D]) && Copy all the current dirs nto an array
[tab]IF lnNo # 0
[tab][tab]FOR i = 1 TO ALEN(laDirList,1)
[tab][tab][tab]IF RIGHT(laDirList[i,5],1) = [D] ;
[tab][tab][tab][tab][tab]AND laDirList[i,1] # [.]
[tab][tab][tab][tab]INSERT INTO DIRLIST (folder) ;
[tab][tab][tab][tab][tab]VALUES (SYS(5) ;
[tab][tab][tab][tab][tab][tab]+ LOWE(CURDIR()) ;
[tab][tab][tab][tab][tab][tab]+ LOWE(laDirList[i,1]);
[tab][tab][tab][tab][tab][tab]+ [\])
[tab][tab][tab]ENDIF
[tab][tab]ENDFOR
[tab]ENDI
[tab]GOTO lnRec
[tab]SKIP IN DIRLIST
ENDDO

SET DEFA TO (lcFolder)

BROW

It may be removing "oldwinap", even if it is possible, may be more trouble than it is worth, and a rethink may be a better way to go.

You will still be able to implement a progressbar bar as you have RECC() and RECN() to provide the necessary data as you progress through the loop.

HTH

Chris :)
 
Dear Chris:

You don't know how I really appreciate your comments. They have all been very helpful.

I'm going to talk "only" about your last helpfull response.

There are some very special subject in my application:

1) On how to get the directory tree or list of sub-directories in a directory: The directories included (to be backed up) in the backup program are selected one by one, meaning I do not include this directory and all of its directories, but instead, I select one by one (that's regarding the xcopy's /s parameter). In the example above I used /s as a means of example only, but in the real application I do not.

2) The main and maybe only reason I'm using xcopy is it's ability to only copy files that have changed since the last backup (/d parameter) or are new. Unfortunately if you want to copy these kind of files using the COPY FILE command in VFP you would have to load the list of files in that directory, compare dates and/or time between the source and destination files, and so on. In fact, I do use the VFP's COPY FILE, and that is when the parameter list (including directory names) is larger than 100 characters. The reason is that in Win98b (but not win Win2000) the maximum lenght allowed seems to be 100. (That's incredible since there's no documentation available for that)

I don't know, I've been working with this for months. Next Christmas I'll ask for an API function that does just what xcopy does.

This thread is becoming really interesting. And believe me Chris, I've learned and understood a lot about the WinApi to work with VFP.

Your comments are always welcome. Thanks a lot.

Rianeiro
 
rianero

The last code posted created a cursor populated with the folder names below the root folder of your app.

By adding to this code, you can extract the paths\filenames of those files with the archive attribute on which will need backing up.

**********

lcFolder = SYS(5) + SYS(2003)

CREATE CURSOR DIRLIST (folder C(240)) && Create dirlist
CREATE CURSOR FILELIST (filename C(240)) && Create filelist

INSERT INTO DIRLIST (folder);
[tab]VALUES ([C:\MyApp\data]) && Root folder

SELE DIRLIST
DO WHILE !EOF([DIRLIST])
[tab]lnRec = RECN()
[tab]SET DEFAULT TO ALLT(DIRLIST.folder)
[tab]lnNo = ADIR(laDirList,[],[D]) && Copy all the current dirs into an array
[tab]IF lnNo # 0
[tab][tab]FOR i = 1 TO ALEN(laDirList,1)
[tab][tab][tab]IF RIGHT(laDirList[i,5],1) = [D];
[tab][tab][tab][tab][tab]AND laDirList[i,1] # [.]
[tab][tab][tab][tab]INSERT INTO DIRLIST (folder);
[tab][tab][tab][tab][tab]VALUES (SYS(5);
[tab][tab][tab][tab][tab]+ LOWE(CURDIR());
[tab][tab][tab][tab][tab]+ LOWE(laDirList[i,1]);
[tab][tab][tab][tab][tab]+ [\])
[tab][tab][tab]ENDIF
[tab][tab]ENDFOR
[tab]ENDI

[tab]lnNo = ADIR(laDirList,[*.*]) && Copy all the files into an array
[tab]IF lnNo # 0
[tab][tab]FOR i = 1 TO ALEN(laDirList,1)
[tab][tab][tab]IF [A] $ UPPE(laDirList[i,5])
[tab][tab][tab][tab]INSERT INTO FILELIST (filename) ;
[tab][tab][tab][tab][tab]VALUES (SYS(5) ;
[tab][tab][tab][tab][tab]+ LOWE(CURDIR()) ;
[tab][tab][tab][tab][tab]+ LOWE(laDirList[i,1]))
[tab][tab][tab]ENDI
[tab][tab]ENDFOR
[tab]ENDI

[tab]GOTO lnRec
[tab]SKIP IN DIRLIST
ENDDO

SELE FILELIST

SET DEFA TO (lcFolder)

brow

**********

You then need to set the archive attribute of those files off.

It's important that none of these files are in use, otherwise "attrib" will fail, and you will not know about it.
If that happens, all it means is that you will copy files that have not been modified.

*********

SET DEFA TO C:\MyApp

DECLARE INTEGER FindWindow IN User32 ;
[tab]STRING lpClassName ,;
[tab]STRING lpWindowName

DECLARE INTEGER WinExec IN kernel32 ;
[tab]STRING lpCmdLine ,;
[tab]INTEGER nCmdShow

lpCmdLine = [attrib -a ] + SYS(5) + SYS(2003) + [\data\*.* /s]
WinExec(lpCmdLine,0)

WAIT WINDOW [] TIME 1
DO WHILE .T.
[tab]lwindow = 0
[tab]lwindow = FindWindow(.NULL.,[attrib])
[tab]IF lwindow = 0
[tab][tab]EXIT
[tab]ENDI
ENDDO

MESSAGEBOX([attrib finished!])

**********

You will not be left with "oldwinap" in the active Windows list as with XCOPY32.exe.

Then you can SCAN - ENDSCAN through the cursor FILELIST with COPY FILE ALLT(FILELIST.filename) TO lcTargetFilename. You can also create lcTargetFilename from FILELIST.filename, using a mix of SUBSTR(), JUSTFNAME(), JUSTPATH(), JUSTSTEM() and so on.

What this code is doing is providing the functionality of Xcopy32 within VFP.

Chris :)

 
rianero

One aspect I did not make very clear was that if the "attrib" routine fails through a sharing violation for example, then your program will stay in the loop indefinately and there will be no indication as what the problem is.

You could add some form of a timeout routine if you considered it worth it, to cancel the whole process.

You will also need to remove the window from the active Windows list with CTRL+ALT+DEL.

If your code ensures the closing of any files liable to be backed up, then this aspect of the application can be basically ignored.

You were also concerned to copy new files - by default, a new file will have the archive attribute turned on.

Chris :)
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top