?
Solved

CreateProcess to start, suspend and continue a program

Posted on 2005-04-23
37
Medium Priority
?
1,190 Views
Last Modified: 2013-11-20
Hi experts,
I am working on a CDialog based application which automates
a program I am using extensively. Since the programm running
eats all the resources and it might run for days and days at a time,
I want to be able to suspend it and of course after I am done whatever
I did i want to continue at the point I left of.
It would also be fine to set the processing priority to very very low so
that everything else works alongside with the program.
Here is the call I am using at the moment:
      DWORD dwCode;
      SHELLEXECUTEINFO sei;
      ZeroMemory (&sei, sizeof (SHELLEXECUTEINFO));
      sei.cbSize = sizeof (SHELLEXECUTEINFO);
      sei.lpFile = javaLoc;      //Full path to java.exe
      sei.lpVerb = "OPEN";
      sei.lpDirectory = workDir.c_str();
      CString pars = "";
      //All paths are full paths except for paths in the worDir (peaks.jar, tmp.xml)
      pars.Format(" -Xmx%dM -jar %s %s %s %s %s \"%s\" %f %f %d %d",
                        memoryLimit,instrument,pL.c_str(),outDir.c_str(),outDir.c_str(),
                        configFile,enzyme,parErrTol,fragErrTol,outputNum,procNum);
      sei.lpParameters = pars;
      //Don't show the console with the raw file extraction, only disturbs everybody
      if(showDosBox)
            sei.nShow = SW_NORMAL;
      else
            sei.nShow = SW_HIDE;
      //Return here when done and don't display any errors while your at it.
      sei.fMask = SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_NO_UI;      
      ShellExecuteEx(&sei);
      WaitForSingleObject(sei.hProcess, INFINITE);
      int ret = GetExitCodeProcess(sei.hProcess, &dwCode);

How can I control this process from a button in the dialog?
I guess I have to switch to CreateProcess, right?
Or is the process above already encapsulated in a thread that I can suspend and continue?
Thanks
Jens
0
Comment
Question by:allmer
  • 19
  • 13
  • 5
37 Comments
 
LVL 9

Expert Comment

by:rcarlan
ID: 13849750
Use CreateProcess. It returns the handle of the main thread running in the newly started process. You can call SuspendThread and ResumeThread on this handle.

Radu
0
 
LVL 5

Author Comment

by:allmer
ID: 13852618
If I look at Create process, there are numerous parameters
I can set. Which one would represent the handle I need.
Could you give a short example?

BOOL CreateProcess(
  LPCTSTR lpApplicationName,
  LPTSTR lpCommandLine,
  LPSECURITY_ATTRIBUTES lpProcessAttributes,
  LPSECURITY_ATTRIBUTES lpThreadAttributes,
  BOOL bInheritHandles,
  DWORD dwCreationFlags,
  LPVOID lpEnvironment,
  LPCTSTR lpCurrentDirectory,
  LPSTARTUPINFO lpStartupInfo,
  LPPROCESS_INFORMATION lpProcessInformation
);

Thanks
Jens
0
 
LVL 9

Expert Comment

by:rcarlan
ID: 13852707
CreateProcess returns the process and thread handles in lpProcessInformation.
0
Concerto's Cloud Advisory Services

Want to avoid the missteps to gaining all the benefits of the cloud? Learn more about the different assessment options from our Cloud Advisory team.

 
LVL 5

Author Comment

by:allmer
ID: 13852749
So this is the Process info:
typedef struct _PROCESS_INFORMATION {
  HANDLE hProcess;
  HANDLE hThread;
  DWORD dwProcessId;
  DWORD dwThreadId;
} PROCESS_INFORMATION;

Should I use the hThread handle directly to manipulate
the state of the thread?
0
 
LVL 9

Expert Comment

by:rcarlan
ID: 13854000
Yes, you should be able to call SuspendThread and ResumeThread on this handle (TerminateThread, as well, though obviously not recommended).

Btw, you have to close these handles when you're done with them.

Radu
0
 
LVL 5

Author Comment

by:allmer
ID: 13857377
Hi I am starting the process like this:
      ZeroMemory( &si, sizeof(si) );
      si.cb = sizeof(si);
      si.dwFlags = STARTF_USESHOWWINDOW;
      if(showDosBox)
            si.wShowWindow = SW_SHOW;
      else
            si.wShowWindow = SW_HIDE;
      PROCESS_INFORMATION  pi;
      ZeroMemory( &pi, sizeof(pi) );
      // Start the child process.
      if( !CreateProcess(
            javaLoc,            // Location of executable
            pars.GetBuffer(),                  // Command line parameters.
            NULL,           // Process handle not inheritable.
            NULL,           // Thread handle not inheritable.
            FALSE,          // Set handle inheritance to FALSE.
            0,              // No creation flags.
            NULL,           // Use parent's environment block.
            workDir.c_str(),        // Use parent's starting directory.
            &si,            // Pointer to STARTUPINFO structure.
            &pi )           // Pointer to PROCESS_INFORMATION structure.
      ) {
            AfxMessageBox("Couldn't start Java.exe");
      } else {
            peaksProcessInfo = pi;
            peaksRunning = true;
      }
      pars.ReleaseBuffer();
      // Wait until child process exits.
      WaitForSingleObject( pi.hProcess, INFINITE );
      int ret = GetExitCodeProcess(pi.hProcess, &dwCode);
      peaksRunning = false;
      // Close process and thread handles.
      CloseHandle( pi.hProcess );
      CloseHandle( pi.hThread );
Any idea why it doesn't work?
I am lost.
Did I forget to set a var did I set it at the wrong point,..
Jens
0
 
LVL 14

Expert Comment

by:wayside
ID: 13857789
What are the values of the strings you are passing in (javaLoc, pars, workDir)?

What is the value returned by GetLastError() if CreateProcess() returns false?

The only other program I see might be that since you are using both lpApplicationName and lpCommandLine, you didn't repeat the application name as the first command line argument (what would be argv[0] to the program). From MSDN:

"If both lpApplicationName and lpCommandLine are non-NULL, the null-terminated string pointed to by lpApplicationName specifies the module to execute, and the null-terminated string pointed to by lpCommandLine specifies the command line. The new process can use GetCommandLine to retrieve the entire command line. Console processes written in C can use the argc and argv arguments to parse the command line. Because argv[0] is the module name, C programmers generally repeat the module name as the first token in the command line."

Don't know if this would apply to java.exe, but it probably does.

One way around this would be to combine everything into one string (use double quotes in case javaLoc has embedded spaces):

     CString cmdLine;
     cmdline.Format("\"%s\" %s", javaLoc, (LPCTSTR) pars);

     if( !CreateProcess(
          NULL,          // Location of executable
          cmdLine,     // Command line parameters.
          NULL,          // Process handle not inheritable.
          NULL,           // Thread handle not inheritable.
          FALSE,          // Set handle inheritance to FALSE.
          0,              // No creation flags.
          NULL,           // Use parent's environment block.
          workDir.c_str(),        // Use parent's starting directory.
          &si,            // Pointer to STARTUPINFO structure.
          &pi )           // Pointer to PROCESS_INFORMATION structure.
     ) {
        DWORD err = GetLastError();
        CString msg;
        msf.Format("CreateProcess failed with error %ld", err;
        AfxMessageBox(msg);
     }
      ...
 
0
 
LVL 14

Expert Comment

by:wayside
ID: 13857794
Oops!

> The only other program

I meant

"The only other problem"...
0
 
LVL 5

Author Comment

by:allmer
ID: 13858264
The problem might be spaces within the paths.
Can I just quote the paths to fix the problem?
Jens
0
 
LVL 14

Expert Comment

by:wayside
ID: 13858314
Yes, that should solve any problems with spaces in your paths.

0
 
LVL 5

Author Comment

by:allmer
ID: 13859069
Hmm,
I am still not there.
So here is what I have on the comandline now.
"C:\Program Files\Java\jre1.5.0_02\bin\java.exe" -Xmx256M -jar peaksbatch.jar -i D:\cw15\TestMatchResult\A1A01\ D:\cw15\TestMatchResult\A1A01\ tmp.xml "cw15trypsin" 1.000000 0.500000 5 1
If I paste it into the MsDos editor it works.
Of course I set the path to the right directory (containing peaksbatch.jar, tmp.xml)
The problem is it doesn't work when I use create process.
My workDir var contains:
"c:\Program Files\PEAKS\"
which in fact is the right path.
Any ideas?
0
 
LVL 5

Author Comment

by:allmer
ID: 13859223
OK,
I got that part done.
The problem is that when I pass the string as above
probably the \" gets processed and the dir:
c:\Program Files\PEAKS"
is searched which of course doesn't exist. The fix was
to get rid of the terminating dir separator.
Thanks so far,
Jens
0
 
LVL 14

Accepted Solution

by:
wayside earned 1000 total points
ID: 13860238
Great! Glad you got it working.

You can change the priority of a program using the SetPriorityClass() api.

Suspending a process is tougher; as far as I know the only ways to do it are to enumerate all the threads in a process and suspend them all; this can cause crashes if there are interactions between the threads. Here's a link showing code for thismethod: http://www.codeproject.com/threads/pausep.asp

The other method I think is to act like a debugger - attach to the process (DebugActiveProcess), stop the process (DebugBreakProcess?) and then continue it (DebugActiveProcessStop?). I've never used these apis, so I'm not really sure if this will work.
0
 
LVL 5

Author Comment

by:allmer
ID: 13861709
Good point,
just now I wanted to ask this exact question.
I have PROCESS_INFORMATION as a class var
now so I can easily access it from all over the
dialog.
The plan is to have only one thread (not counting those I didn't start expicitly)
running at any given time. This thread doesn't need to interact with
any other threads and it only works with prior existing files.

There shouldn't be a problem.
I didn't get to test that part yet, though.
I'll check SuspendThread(%ThreadHandle%) and ResumeThread(%ThreadHandle%)
tomorrow.
Thanks,
Jens
0
 
LVL 5

Author Comment

by:allmer
ID: 13865001
Hi,
So the process starts and runs the executable just
fine. The problem is now to leave the method I am
calling CreateProcess, from.
Here is the call:

      if( !CreateProcess(
            NULL,//jl.c_str(),            // Location of executable
            //Cannot take full path (spaces) MAybe with \"\"
            pars.GetBuffer(),// Command line parameters.
            NULL,           // Process handle not inheritable.
            NULL,           // Thread handle not inheritable.
            FALSE,          // Set handle inheritance to FALSE.
            0,              // No creation flags.
            NULL,           // Use parent's environment block.
            workDir.c_str(),        // Use parent's starting directory.
            &si,            // Pointer to STARTUPINFO structure.
            &pi )           // Pointer to PROCESS_INFORMATION structure.
      ) {
            AfxMessageBox("Couldn't start Java.exe");
      } else {
            peaksProcessInfo = pi;
            peaksRunning = true;
      }
      pars.ReleaseBuffer();
      // Wait until child process exits.
      WaitForSingleObject( pi.hProcess, INFINITE );
      int ret = GetExitCodeProcess(pi.hProcess, &dwCode);
      peaksRunning = false;
      // Close process and thread handles.
      CloseHandle( pi.hProcess );
      CloseHandle( pi.hThread );

And WaitForSingleObject is probably the problem.
How can I check if the process finished so I can get the ExitCode and
close the handles?
I have an OnTimer function that I could check these things in.
Thanks,
Jens
0
 
LVL 9

Expert Comment

by:rcarlan
ID: 13865627
WaitForSingleObject(pi.hProcess, INFINITE) should return after the launched process finishes. Is this not what you want?

If it isn't, don't use INFINITE. You can call WaitForSingleObject in a loop (even if it's the message loop - directly or indirectly, through a timer), with a more reasonable timeout value (e.g. 500ms). If the return value is WAIT_TIMEOUT, the spawned process has not finished yet.
You can also spawn another thread of your own and wait there - so that your main thread can carry on doing things. It's really down to whether or not you have anything you want to do in the mean time. Presumably you do, because you mentioned initially you want to suspend and then restart the spawned process.

Radu
0
 
LVL 5

Author Comment

by:allmer
ID: 13883214
My application still freezes and doesn't get the control back.
What can I do about it?

Here is what I did:
This function calls a thread:
void RunPeaks() {
   while(peaksRunning||pause)
       Sleep(2000);
//That just makes sure that only one process runs at a time.
        ......
      AfxBeginThread(WorkThread,this,THREAD_PRIORITY_BELOW_NORMAL,0);
      return;
}
Here is part of the workthread, then:
void WorkThread() {
          if( !CreateProcess(
            NULL,//jl.c_str(),            // Location of executable
            //Cannot take full path (spaces) MAybe with \"\"
            pDlg->commandLineParams.GetBuffer(),// Command line parameters.
            NULL,           // Process handle not inheritable.
            NULL,           // Thread handle not inheritable.
            FALSE,          // Set handle inheritance to FALSE.
            0,              // No creation flags.
            NULL,           // Use parent's environment block.
            pDlg->workDirectory,        // Use parent's starting directory.
            &pDlg->si,            // Pointer to STARTUPINFO structure.
            &pi )           // Pointer to PROCESS_INFORMATION structure.
      ) {
            AfxMessageBox("Couldn't start Java.exe");
      } else {
            pDlg->peaksProcessInfo = pi;
            pDlg->peaksRunning = true;
            SetPriorityClass(pi.hThread,pDlg->priority);
      }
      pDlg->commandLineParams.ReleaseBuffer();
      // Wait until child process exits.
      WaitForSingleObject( pi.hProcess, INFINITE);
      DWORD dwCode;
      int ret = GetExitCodeProcess(pi.hProcess, &dwCode);
      // Close process and thread handles.
      CloseHandle( pi.hProcess );
      CloseHandle( pi.hThread );
      pDlg->peaksRunning = false;
      pDlg->clean = false;
}
Where could the problem be?
I would like the GUI to be updated every once in a while so that
the Pause button and the priority combo can actually be used.
Any ideas?
Thanks,
Jens
0
 
LVL 9

Assisted Solution

by:rcarlan
rcarlan earned 1000 total points
ID: 13883484
I think your application freezes because it doesn't exit this loop:

while (peaksRunning || pause)
    Sleep(2000);

I think it keeps looping and sleeping until the previous WorkThread exits.

WorkThread spawns a process, sets peaksRunning to true, and then waits for the spawned process to finish. After the spawned process finishes, WorkThread wakes up and sets peaksRunning to false. Thus, unless you set peaksRunning to false from somewhere else, when you call RunPeaks a second time, it'll wait for the spawned process to finish.

You have to change this logic. If you do not want to launch more than one process at a time, return from RunPeaks immediately, instead of waiting in a loop.

I don't know under what conditions RunPeaks gets called, but if it's as a result of some user input (e.g. button click), then you can just return and wait in the message loop for the next click. If RunPeaks is called because of some other condition and you want it to basically restart the process once the previous instance finishes, you'll have to use yet another thread.

Btw, you shouldn't call AfxMessageBox (or display any other U/I) from a worker thread.

Radu
0
 
LVL 5

Author Comment

by:allmer
ID: 13884612
You are right about AfxMessageBox and I will delete the line as soon as everything ist properly tested.
How can I handle the problem with the freezing application?
The function RunPeaks() which might be the problem because of the Sleep (how about SleepEx?) call
is in turn called from a function called Process().
There I use CFileFind to get all subdirectories of a parentdirectory.
For each directory found I call RunPeaks().

At which point do I have to Sleep or do something else in order to return control to the GUI?
Maybe I should get a list of all the paths and then process them in the thread one by one so I don't have
to sleep in the CDialog anymore. Does that sound like a resolution?
Thanks
Jens
0
 
LVL 9

Expert Comment

by:rcarlan
ID: 13884999
You cannot use Sleep (or SleepEx) "to return control to the GUI". Sleep stops the current thread. If it is the GUI thread, the GUI will appear frozen.

What is it that you're trying to achieve? You said that RunPeaks is called in a loop (i.e. you iterate through subdirectories and call RunPeaks for each one). What do you want to happen? The first call starts a process. Then what? When RunPeaks is called for the next subdirectory, do you want it to spawn another process, or do you want it to wait for the first one to finish? If you want it to wait for the first spawned process to finish, what do you want to be able to do from the GUI while waiting?

You have to get all these thoughts organised and decide on the desired behaviour. Sleep is not the answer. You have to allow the message loop to pump messages in order to have a responsive GUI. You can implement a local message pump, but a better solution would be to return to the main message loop and wait for a message from the worker thread as to when to move on to the next subdirectory. You could also use a timer message and poll on a status flag of some sort. Anything but Sleep-ing in a loop in the GUI thread.

Radu
0
 
LVL 5

Author Comment

by:allmer
ID: 13885229
You seem to understand the problem quite exactly ;)
The thing is that I don't want to spawn several processes at any given time.
Whenever I start a process the control should return to the GUI. The next
process should be started when the first one has completed.

The GUI should be responsive in order to use the pause, resume,
and the set thread priority controls.
That's all there is too it.

What happens if I simply use a filel ist and hand it to the workerthread, which
then starts one process at a time (for loop) and waits for its completion. Would that
interfere with the GUI?

I thougt of using
            while(WaitForSingleObject( pi.hProcess, 1000 ) == WAIT_TIMEOUT) {
                  SleepEx(1000,FALSE);
            }
in the for loop to wait for completion.

How does that look?

I have to asks these questions, since I cannot control the program locally.
I don't have a licence to use it so I have to test it on a remote machine,
which prooves quite tedious because of my slow internet connection.

Thanks,
Jens
0
 
LVL 9

Expert Comment

by:rcarlan
ID: 13885469
Forget about Sleep already!

Iterate through subdirectories in the worker thread. For each subdirectory:
- start process
- populate shared data structure (so that you can operate on the process from GUI)
- wait for process to finish
- move on to the next subdirectory

Radu
0
 
LVL 5

Author Comment

by:allmer
ID: 13885823
I am trying to forget but it keeps spooking through my mind.

I am fine with your schema just one question:

How do I wait for the process to finish?
for(..) {
  if(CreateProcess())
  {
    How do I wait here?
  }
  else {}
  //Or how to wait here?
}

If I understand that I will jump with joy!
0
 
LVL 9

Expert Comment

by:rcarlan
ID: 13890407
You've done already:

WaitForSingleObject(pi.hProcess, INFINITE);

Radu
0
 
LVL 5

Author Comment

by:allmer
ID: 13892299
Hmm,
The SuspendThread() doesn't seem to work with this setup.
Otherwise it works fine.
The GUI gets the control, and once the actual process (java.exe)
finishes the next process in the queue will not run if paused.
For me that is good enough for the time being, but it seems as
if the SuspendThread message doesn't reach the thread, which
is again unresponsive because of the WaitForSingeObject(..) call.

Is there something like:
While thread still active {
   make thread process messages;
   let the thread do some processing again;
}
Jens
0
 
LVL 9

Expert Comment

by:rcarlan
ID: 13892373
What thread handle are you calling SuspendThread on? It should pi.hThread (or with your setup, peaksProcessInfo.hThread in the dialog class).


>>Is there something like:
>>While thread still active {
>>   make thread process messages;
>>   let the thread do some processing again;
>>}

Yes, it's called a message loop :-) You have one in your GUI thread. If you want to do "background" processing in your GUI thread you could use a timer.

Radu
0
 
LVL 5

Author Comment

by:allmer
ID: 13892461
So I changed my code a little.
I am no longer making a copy of hThread,
but have only one peaksProcessingInfo in my class.
I am calling SuspendThread and even TerminateThread() on that thread,
but it doesn't work.
Seems like the message isn't processed in the thread.
It uses up all processing time until it is finished. When it finishes Terminate
thread is called.
At least the GUI is responsive at all times now.
Any other ideas than WaitForSingleProcess in the thread?
Jens
0
 
LVL 9

Expert Comment

by:rcarlan
ID: 13892482
>>I am calling SuspendThread and even TerminateThread() on that thread

What thread is that? We have three: the GUI thread, the spawning thread and the main thread of the spawned process. Which one are you trying to suspend?

I take it's not the GUI thread because you said the GUI is responsive. So which of the other two are you trying to suspend?

If it's the spawning thread, it's pointless. This thread is already suspended waiting for the spawned process to finish.
If it's the main thread of the spawned process, how do you know the call to SuspendThread is not successful?

Radu
0
 
LVL 5

Author Comment

by:allmer
ID: 13892574
It is the main thread of the spawned process.
I know it is not successful because the Taskmanager displays 100% CPU time
for that process (java.exe).
class CAutoPeaksDlg {
   PROCESSING_INFO pi;
   RunPeaks() {
       AfxBeginThread(WorkThread,this,priority,0);
   };
   WorkThread() {
       CreateProcess( ... pDlg->pi);
       WaitForSingleObject(pi.hProcess,INFINITE);
       //The call to suspend thread kicks in when this is done.
       //After this point there is however not much to do.
   };
   OnButtonPause() {
      SuspendThread(pi.hThread);
   };
};
Highly simplified.
Any idea?
0
 
LVL 9

Expert Comment

by:rcarlan
ID: 13892818
What value does SuspendThread return? It should be 0.
If it returns -1, call GetLastError immediately after the failed call to SuspendThread. What's the error code?

Radu
0
 
LVL 9

Expert Comment

by:rcarlan
ID: 13892839
Does the launched process have a GUI? Does it output any kind of results as it processes its data, or does it process things silently until it finishes and then outputs the results?

Java.exe may be running multiple threads and its other threads continue to run after you've suspended the main thread - i.e. the process will still take CPU time.

Radu
0
 
LVL 14

Expert Comment

by:wayside
ID: 13894040
> Java.exe may be running multiple threads

You can see how many threads a process has via the task manager, via the "select columns" entry in the View menu.

I'd be shocked if java.exe didn't have 10 or 20 threads going.
0
 
LVL 5

Author Comment

by:allmer
ID: 13894336
Alright I'll try it now.
I was probably too blue-eyed assuming there would be only
one thread.
Jens
0
 
LVL 5

Author Comment

by:allmer
ID: 13895115
So I was just looking at waysides suggestion (at the beginning)
Daniel Turini implements a function:
BOOL PauseResumeThreadList(DWORD dwOwnerPID, bool bResumeThread)

If I can get dwOwnerPID, I guess I could just use that function to pause and
rerun the process.

What would dwOwnerPID correspond to in my environment?
Jens
0
 
LVL 9

Expert Comment

by:rcarlan
ID: 13898275
pi.dwProcessId - returned by CreateProcess together with hProcess, hThread, and dwThreadId
0
 
LVL 5

Author Comment

by:allmer
ID: 13900891
Tried,
but doesn't work, yet.
I will try some real debugging tomorrow.
Jens
0
 
LVL 5

Author Comment

by:allmer
ID: 13903490
OK Done!
I had to pass both pi.dwProcessId
and pi.dwThreadId.
After that it paused and resumed just fine.
Thanks alot!

Please check out my question on SettingThreadPriority:
http://www.experts-exchange.com/Programming/Programming_Languages/MFC/Q_21408570.html
0

Featured Post

Concerto Cloud for Software Providers & ISVs

Can Concerto Cloud Services help you focus on evolving your application offerings, while delivering the best cloud experience to your customers? From DevOps to revenue models and customer support, the answer is yes!

Learn how Concerto can help you.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Introduction: Finishing the grid – keyboard support for arrow keys to manoeuvre, entering the numbers.  The PreTranslateMessage function is to be used to intercept and respond to keyboard events. Continuing from the fourth article about sudoku. …
Introduction: Dialogs (2) modeless dialog and a worker thread.  Handling data shared between threads.  Recursive functions. Continuing from the tenth article about sudoku.   Last article we worked with a modal dialog to help maintain informat…
This video will show you how to get GIT to work in Eclipse.   It will walk you through how to install the EGit plugin in eclipse and how to checkout an existing repository.
Is your organization moving toward a cloud and mobile-first environment? In this transition, your IT department will encounter many challenges, such as navigating how to: Deploy new applications and services to a growing team Accommodate employee…
Suggested Courses

621 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question