Intel® Fortran Compiler
Build applications that can scale for the future with optimized code designed for Intel® Xeon® and compatible processors.

CreateProcess question

rahzan
Novice
2,457 Views
I am launching a "legacy" DOS app from another cosole app. The display from the old DOS app is extensive and for many repeated launches it comsumes a lot of time. So I have tried to run it using CreateProcess instead of RUNQQ. However, the child app still shows its dos screen. It seems to share the same the caller (i.e. no different from runqq).
I would like to know how to do this to prevent the callee from doing all the elaborate text output.
Here is the heart of the createProcess call:

!STARTUPINOF sets the properties of the thread
call ZeroMemory (LOC (si), SIZEOFSTARTUPINFO)
dwCreate = IOR(HIGH_PRIORITY_CLASS,dwCreate)! /h
dwCreate = IAND(NOT(CREATE_NEW_CONSOLE),dwCreate) !turn off this bit to supress the DOS screen

!set to hide the console window
si%dwFlags =STARTF_USESHOWWINDOW
si%wShowWindow =SW_HIDE
!other startup info
si%cb= SIZEOFSTARTUPINFO
sa%nLength = SIZESECURITYATTRIBUTES
sa%lpSecurityDescriptor = NULL
sa%bInheritHandle = .true.
sb%nLength = SIZESECURITYATTRIBUTES
sb%lpSecurityDescriptor = NULL
sb%bInheritHandle = .true.
!Get the Path info
call zeromemory (LOC(szPath), len(szPath))
res=GETDRIVEDIRQQ(szpath)+1
szPath(res:res)=char(0)

!Run the requested PROGRAM (can we check for return code?)
call zeromemory (LOC(szArgs), SIZEOFszArgs)
szArgs= trim(cmd)//char(0) !watch SIZEOFSzArgs
! szArgs= 'cmd.exe /c '//trim(cmd)//char(0) !watch SIZEOFSzArgs
! res=len_trim(szArgs)+1 ; szArgs(res:res)=char(0)

fSuccess = CreateProcess(null_character, & szArgs, & ! command line (including program name)NULL_security_attributes, & NULL_security_attributes, &
.FALSE., & ! new process inherits handles?
dwCreate, & ! creation flags
NULL, & ! environment
szPath, & ! new current DirName si, & ! STARTUPINFO structure
pi) ! PROCESSINFORMATION structure


Also I would like to know if this scheme can return the exit/return code of the callee the way runqq would. Would it be in fuccess?

Thanks in advance
Tim H
0 Kudos
15 Replies
Jugoslav_Dujic
Valued Contributor II
2,457 Views
See CREATE_NEW_CONSOLE or DETACHED_PROCESS flags in documentation on CreateProcess.

Also, take a look on GetExitCodeProcess API (it can return the exit code even if the process has finished, until you CloseHandle(hProcess)).

Jugoslav
0 Kudos
rahzan
Novice
2,457 Views
Thanks Duj,
I changed my dwCreate to:

dwCreate = IOR(HIGH_PRIORITY_CLASS,dwCreate)

dwCreate = IOR(DETACHED_PROCESS,dwCreate)

dwCreate = IAND(NOT(CREATE_NEW_CONSOLE),dwCreate)

But I get the error: ERROR_INVALID_PARAMETER
The same happens with the thrid line above, taken out. Any hints?

Thanks again, Tim
0 Kudos
Jugoslav_Dujic
Valued Contributor II
2,457 Views
DETACHED_PROCESS leaves the process without any console to write to so I'm not surprised it doesn't work -- but I wouldn't expect that kind of error (I'd expect a crash on first WRITE(*) statement). Nevertheless, I think the winning combination is HIGH_PRIORITY_CLASS + CREATE_NEW_CONSOLE.
0 Kudos
rahzan
Novice
2,457 Views
Sorry to be dense,
I am now thoroughly confused!
1. Are you saying that your initial idea of adding "Detached process" was not the way to go?

2. Are you saying that "Create new console" IS necessary?

3. But if I add (instead of exclude) "Create new console" I get a new console which is just what I want to avoid!

Your further help will be appreciated.

Tim H
0 Kudos
Jugoslav_Dujic
Valued Contributor II
2,457 Views
Well, my first suggestion was based just on reading the docs -- when you said that DETACHED_PROCESS doesn't work I thought about it and found that it probably crashes when first WRITE(* is encountered. It would probably work if output is redirected to a file or a pipe (SI%hStdOutput) but I think it's an unnecesary complication.

OTOH, what's wrong with CREATE_NEW_CONSOLE? You also have SI%wShowWindow=SW_HIDE so the new console will be invisible.
0 Kudos
rahzan
Novice
2,457 Views
Nothing is wrong with it.
I assumed that sw_HIDE simply _minimizes the window but I did not want ANY console created. Did I misunderstand?

I have used NOT(new_console) (and only HIGH_priority) with success in the context of a (com)DLL. SO the distinction seems to be from the fact that a console-app is calling createProcess.

Does this give any ideas?

One other related thing. As for the "exit code" you said to look at GetExitCodeProcess. Now Fortran pointers are uneccessaerily complicated (as compared to say C). I have no clue how to handle the DWord pointer args in GetExitCodeProcess. Do I just declare integer(dword) and use its value? Is there not a de-ref necessary??

Tim
p.s. Thanks for your time.
0 Kudos
james1
Beginner
2,457 Views
Is the "legacy" app also in Fortran or is this just some executable for which you have no source? Does it accept any input from the user, or does it just list a bunch of stuff to the console for which you have no use?

Also, you don't need any pointers to use GetExitCodeProcess. Just pass the location of the integer.

James
0 Kudos
Jugoslav_Dujic
Valued Contributor II
2,457 Views
> Nothing is wrong with it.
> I assumed that sw_HIDE simply _minimizes the window
> but I did not want ANY console created. Did I
> misunderstand?

Yes you did -- SW_MINIMIZE minimizes it and SW_HIDE hides it completely.

> I have used NOT(new_console) (and only HIGH_priority)
> with success in the context of a (com)DLL. SO the
> distinction seems to be from the fact that a
> console-app is calling createProcess.
>
> Does this give any ideas?

Well, in this case I'd prefer approach "make it work and let it stay that way" :-). It's possible also that the actual "callee" makes the difference. CVF console applications crash during WRITE(* when there's no console; I think that MSVC ones don't (I'm not sure).

> One other related thing. As for the "exit code" you
> said to look at GetExitCodeProcess. Now Fortran
> pointers are uneccessaerily complicated (as compared
> to say C). I have no clue how to handle the DWord
> pointer args in GetExitCodeProcess. Do I just declare
> integer(dword) and use its value? Is there not a
> de-ref necessary??

Just a general hint for "translating" API arguments: (to be on the safe side, always take a look at actual INTERFACE in ...DFInclude):
1) LPCTSTR, LPTSTR arguments usually translate to CHARACTER(*)
2) LPSomeStructure usually translates to SomeStructure
3) LPWORD, LPDWORD require LOC(iVariable)
4) LPVOID or LPBYTE is tricky; it is commonly used when the argument can be of more types. Usually, it requires LOC(Something).

For example:
1) GetCurrentDirectory(LEN(sDir), sDir)
2) LPSTARTUPINFO in CreateProcess
3) GetExitCodeProcess(hProcess, LOC(iExitCode))
4) CHARACTER(100) szFile
   INTEGER nFiles
   ...
   RegQueryValueEx(hKey, "nFiles"C, 0, LOC(iType), LOC(szFile), LOC(iSize))
   RegQueryValueEx(hKey, "MRUList"C, 0, LOC(iType), LOC(nFiles), LOC(iSize))


Jugoslav
0 Kudos
rahzan
Novice
2,457 Views
The "legacy" code is not in fortran and its source is in another language for which I do not have a compiler besides its age. Reworking it in cvf will takes way more time than I'd like to spend.

It prints a huge amount of junk to the screen which slows things down on multiple calls.

tim
0 Kudos
rahzan
Novice
2,457 Views
Thanks for the detailed answer Duj.

I undestand your point about twriting to a non-existent console, but the error as I've stated is the funny "invalid parameter".

I will try the hiding setup. Mymain need to eliminate the the consuming excessive screen writes to shorten long run times. One way or another makes no difference,

thanks again,
Tim

0 Kudos
james1
Beginner
2,457 Views
I asked because if it wasn't written in Fortran, you may be able to redirect standard output elsewhere by creating a suitable file handle (the null file seems ideal) and when you do your CreateProcess just have it inherit handles.

James
0 Kudos
rahzan
Novice
2,457 Views
Jim, I looked up CreateProcess again, but have no idea how the handle inheriting scheme would work.

Care to give a sketch?

Thanks, Tim
0 Kudos
james1
Beginner
2,457 Views
A simple example. This runs a program that both does console output and has a side effect (to prove that it ran :-). Replace the command in the CreateProcess call as needed.
program spawn_quiet_child
use kernel32
implicit none
type (T_STARTUPINFO) si
type (T_PROCESS_INFORMATION) pi
integer(HANDLE) hNl
integer(DWORD) status
hNl = CreateFile('nul'c,GENERIC_ALL,0,NULL,OPEN_EXISTING,0,NULL)
si%cb = SIZEOF (si)
si%dwFlags = STARTF_USESTDHANDLES
si%hStdInput = hNl
si%hStdOutput = hNl
si%hStdError = hNl
if (CreateProcess(NULL,'msg /v * Hello'c,NULL,NULL,TRUE,0,NULL,NULL,si,pi) /= 0) then
  type *, 'Process created', pi%dwProcessId
else
  type *, 'CreateProcess error', GetLastError()
end if
pause
if (GetExitCodeProcess(pi%hProcess,LOC(status)) /= 0) then
  type '(a,z8.8)', 'exit code was ', status
end if
end

James
0 Kudos
Steven_L_Intel1
Employee
2,457 Views
I'd be more comfortable if all the fields in si were initialized.

Steve
0 Kudos
james1
Beginner
2,457 Views
Fortunately the compilers give us a zero'd structure. :-) Seriously, initializing variables, checking return status and closing/deallocating resources should always be done for production code, this is just something to illustrate a concept while saving some forum real estate, so is just enough to "work".

James
0 Kudos
Reply