0

I use code from example in this answer. It works.

But I need to redirect standart input/output into files. Process startup info struct has fields:

public IntPtr hStdInput;
public IntPtr hStdOutput;
public IntPtr hStdError;

I tried something like this:

StartupInfo startupInfo = new StartupInfo();
startupInfo.cb = Marshal.SizeOf((object)startupInfo);
startupInfo.dwFlags = 128;

FileStream fs = new FileStream(filePath, FileMode.Open);
startupInfo.hStdInput = fs.Handle;

And it does not work.

How can I pass file as standart input/output?

UPD1.

How do I call CreateProccess:

StartupInfo startupInfo = new StartupInfo();
startupInfo.cb = Marshal.SizeOf((object)startupInfo);
startupInfo.dwFlags = 128;

FileStream fs = new FileStream(filePath, FileMode.Open);
startupInfo.hStdInput = fs.Handle;

Pinvoke.SetErrorMode(ErrorModes.SEM_FAILCRITICALERRORS | ErrorModes.SEM_NOALIGNMENTFAULTEXCEPT | ErrorModes.SEM_NOGPFAULTERRORBOX | ErrorModes.SEM_NOOPENFILEERRORBOX);
CreationFlags dwCreationFlags = CreationFlags.CREATE_BREAKAWAY_FROM_JOB | CreationFlags.CREATE_SUSPENDED | CreationFlags.CREATE_SEPARATE_WOW_VDM;
SecurityAttributes securityAttributes = new SecurityAttributes();
securityAttributes.bInheritHandle = 1;
ProcessInformation pi;

if (!Pinvoke.CreateProcess(null, configuration.RunString, ref securityAttributes, ref securityAttributes, boolInheritHandles: true, dwCreationFlags: dwCreationFlags, lpEnvironment: IntPtr.Zero, lpszCurrentDir: configuration.Directory, startupInfo: ref startupInfo, pi: out pi))
    throw new Win32Exception(Marshal.GetLastWin32Error());

UPD2. I added SetHandleInformation call, but it does not help:

FileStream fs = new FileStream(@"C:\input.txt", FileMode.Open, FileAccess.Read);
Pinvoke.SetHandleInformation(fs.Handle, 0x00000001, 0x00000001);
startupInfo.hStdInput = fs.Handle;
Backs
  • 24,430
  • 5
  • 58
  • 85
  • file handles must be *inheritable* and process must be created with *bInheritHandles == true* – RbMm Jun 18 '17 at 14:52
  • @RbMm thank you for your comment. I added true in `CreateProcess` call and in `securityAttributes`, but it does not help – Backs Jun 18 '17 at 15:01
  • but you need use securityAttributes in create/open file handle - not process – RbMm Jun 18 '17 at 15:03
  • `FileStream fs = new FileStream(filePath, FileMode.Open);` - this is `fs.Handle;` not inheritable handle – RbMm Jun 18 '17 at 15:04
  • @RbMm ok, I see... It's difficult for me :) thank you for comments, I'll try! – Backs Jun 18 '17 at 15:07
  • You are going to need to create the file with CreateFile. – David Heffernan Jun 18 '17 at 16:26
  • @DavidHeffernan or use [`SetHandleInformation()`](https://msdn.microsoft.com/en-us/library/windows/desktop/ms724935.aspx) to turn on the `HANDLE_FLAG_INHERIT` flag. – Remy Lebeau Jun 18 '17 at 19:38
  • @RemyLebeau I added this call, but it does not help. Maybe, I do something wrong – Backs Jun 19 '17 at 02:17
  • 1
    It's pointless to wrap the handle in a file stream. Not least because there is buffering at play. Call CreateFile. A classic mistake is to do this all from C# without having once learnt how to do it from C++ code. Easier to experiment in C++ code because all prototypes, constants etc. are available. Once you know how to do it, translate to pinvoke. – David Heffernan Jun 19 '17 at 06:27
  • 1
    `#define STARTF_USESTDHANDLES 0x00000100` - this flag you must use in `STARTUPINFO` but you use `startupInfo.dwFlags = 128;` - `128==0x80!=0x100` – RbMm Jun 19 '17 at 08:06
  • and better of course just create file as inheritable in single api call (via createfile) than do 2 system calls - first create file handle not inheritable and than change this – RbMm Jun 19 '17 at 08:09
  • @RbMm yes, that's was is. You can turn this comment to answer, I'll accept it. – Backs Jun 19 '17 at 08:25
  • @DavidHeffernan @RbMm thank you both for help. I'm really bad at wiapi and C++, but your comments give me right direction for search. I'll try to do it as you described: work with files with `CreateFile`. – Backs Jun 19 '17 at 08:27

1 Answers1

0

Based on comments.

  1. Set startupInfo.dwFlags to 0x00000100
  2. Add new function CreateFile:

    [DllImport("kernel32.dll", SetLastError = true)]
    public static extern IntPtr CreateFile(string lpFileName, DesiredAccess dwDesiredAccess, uint dwShareMode, ref SecurityAttributes lpSecurityAttributes, CreationDisposition dwCreationDisposition, uint dwFlagsAndAttributes, IntPtr hTemplateFile);
    
  3. Create input/output handlers:

    var input = Pinvoke.CreateFile(inputFile, DesiredAccess.GENERIC_READ, 0, ref securityAttributes,
                CreationDisposition.OPEN_EXISTING, 0, IntPtr.Zero);
    
    var output = Pinvoke.CreateFile(outputFile, DesiredAccess.GENERIC_WRITE, 0, ref securityAttributes,
                 CreationDisposition.CREATE_NEW, 0, IntPtr.Zero);
    
    startupInfo.hStdInput = input;
    startupInfo.hStdOutput = output;
    
    ...
    
    public enum DesiredAccess : uint
    {
        GENERIC_WRITE = 30,
        GENERIC_READ = 31
    }
    
    public enum CreationDisposition : uint
    {
        CREATE_NEW = 1,
        CREATE_ALWAYS = 2,
        OPEN_EXISTING = 3,
        OPEN_ALWAYS = 4,
        TRUNCATE_EXISTING = 5
    }
    
Backs
  • 24,430
  • 5
  • 58
  • 85