Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to call CreateProcess in Delphi? [duplicate]

Following the documentation of CreateProcess, and the example from MSDN, i'm trying to call CreateProcess:

var
   commandLine: string;
   si: TStartupInfo;
   pi: TProcessInformation;
begin
   commandLine := 'C:\Windows\System32\cmd.exe';

   si := Default(TStartupInfo);
   si.cb := sizeof(si);

   CreateProcess(
        PChar(nil),         //no module name (use command line)
        PChar(commandLine), //Command Line
        nil,                //Process handle not inheritable
        nil,                //Thread handle not inheritable
        False,              //Don't inherit handles
        0,                  //No creation flags
        nil,                //Use parent's environment block
        PChar(nil),         //Use parent's starting directory
        si,                 //Startup Info
        pi                  //Process Info
   );

The call crashes with an access violation:

Exception EAccessViolation in module kernel32.dll at 0003B77B.
Access violation at address 7671B77B in module 'kernel32.dll'. Write of address 00B47EA6.

Now i understand why it's crashing for me, but i don't understand why it isn't crashing for the sample code on MSDN, nor do i understand why it wasn't failing for you David.

The documentation for CreateProcess says that if the first parameter is null (as it is in my example, the MSDN example, and the other example) then CreateProcess will modify the commandLine argument:

lpCommandLine [in, out, optional]

...

The Unicode version of this function, CreateProcessW, can modify the contents of this string. Therefore, this parameter cannot be a pointer to read-only memory (such as a const variable or a literal string). If this parameter is a constant string, the function may cause an access violation.

When i look at the access violation, it's trying to write to address 0x00B47EA6:

enter image description here

So CreateProcess is trying to scribble over null terminator of my unicode string. There is some debate in the CreateProcess page comments if CreateProcess will try to modify the command line rather than making it longer.

It is entirely possible that my string

C:\Windows\System32\cmd.exe

is sitting in a read-only data section. The string itself has a reference count of -1:

enter image description here

which happens when constant strings come from constants.

I can test this by copying the string into a buffer:

var
    commandLine: string;
    si: TStartupInfo;
    pi: TProcessInformation;
    l: Integer;
    buffer: TByteDynArray;

commandLine := 'C:\Windows\System32\cmd.exe';

//Copy to writable buffer (including null terminator)    
l := (Length(commandLine)+1)*sizeof(Char);
SetLength(buffer, l);
Move(commandLine[1], buffer[0], l);

si := Default(TStartupInfo);
si.cb := sizeof(si);

if not CreateProcess(
            PChar(nil),               //no module name (use command line)
//          PChar(commandLine), //Command Line
            @buffer[0],
            nil,                //Process handle not inheritable
            nil,                //Thread handle not inheritable
            False,              //Don't inherit handles
            0,                  //No creation flags
            nil,                //Use parent's environment block
            PChar(nil),         //Use parent's starting directory
            si,                 //Startup Info
            {var}pi             //Process Info
);

And that works successfully.

So after authoring my question, and researching it, i have answered my own problem. But i'd still like to know what the decent way to handle this is

How to call CreateProcess in Delphi?

How does everyone else call it? Is everyone else copying the string to a byte buffer?

Bonus ShellExecute

He's how you use ShellExecute:

var
   shi: TShellExecuteInfo;

shi := Default(TShellExecuteInfo);
shi.cbSize := SizeOf(TShellExecuteInfo);
shi.lpFile := PChar(commandLine); 
shi.nShow := SW_SHOWNORMAL;

ShellExecuteEx(@shi);
like image 594
Ian Boyd Avatar asked Oct 20 '22 23:10

Ian Boyd


1 Answers

You can replace PChar(commandLine) with PChar(WideString(commandLine)). This worked for me in Delphi XE6.

I suppose they have broken something in string casts as my old code in Delphi XE works without such strict conversion.

like image 86
Denis Grinyuk Avatar answered Oct 23 '22 00:10

Denis Grinyuk