dart-lang / sdk

The Dart SDK, including the VM, JS and Wasm compilers, analysis, core libraries, and more.
https://dart.dev
BSD 3-Clause "New" or "Revised" License
10.25k stars 1.58k forks source link

CreateProcessW failed 2 #41833

Open SaadArdati opened 4 years ago

SaadArdati commented 4 years ago

Dart VM version: 2.9.0-7.0.dev (dev) (Thu May 7 09:17:54 2020 +0200) on "windows_x64" Windows 10

  static void segmentFile(String inputFile) async {
    await Process.run('ls', []).then((result) => print(result.stdout));

    print('2');
    await Process.start('ffmpeg',
            ['-i', '$inputFile', '-f', 'segment', '-segment_time', '300', '-c', 'copy', '${dirname(inputFile)}/%03d.mp3'],
            workingDirectory: 'assets/ffmpeg-${Platform.isWindows ? 'windows/bin' : 'linux'}')
        .then((Process process) {
      process.stdout.transform(utf8.decoder).listen((data) {
        print(data);
      });
      process.stdin.writeln('Hello, world!');
      process.stdin.writeln('Hello, galaxy!');
      process.stdin.writeln('Hello, universe!');
    }).then((Process process) {
      // Get the exit code from the new process.
      process.exitCode.then((exitCode) {
        print('exit code: $exitCode');
      });
    });
  }

Both of these processes fail with CreateProcessW failed 2 No other messages are provided. Neither the first runs, nor the second. Not even a clue what's going on. I can't get any command to run.

lrhn commented 4 years ago

If you are running on Windows 10, I wouldn't expect ls to be available by default. Do you have anything installed which would provide the ls and ffmpeg commands, and are you sure they are findable by Process.run?

Try referring to the executables with an absolute path.

In particular, the ffmpeg is running with a new working directory, and the executable path is interpreted as relative to the new working directory, not the original one.

SaadArdati commented 4 years ago

@lrhn I just checked. Windows powershell supports ls, windows CMD does not.

I changed the command to a cmd compatible one which is cd, same error

    print('process 1');
    await Process.run('cd', []).then((result) => print(result.stdout));

    print('process 2');

process 2 is never printed.

image

SaadArdati commented 4 years ago

Tried:

    print('process 1');
    await Process.run('ping', ['google.com']).then((result) => print(result.stdout));

    print('process 2');

This is weird, because it didn't error. It just stopped and did nothing.

image

CMD: image

lrhn commented 4 years ago

That is interesting because running the same code on stable 2.8.1:

Dart VM version: 2.8.1 (stable) (Thu Apr 30 09:25:21 2020 +0200) on "windows_x64"

does print the output of ping.

The cd probably failed because it's a built-in command in cmd, not a program you can execute. It has no path, so you can't point to it. To run that, you'd have to do it by executing "cmd" with a "/c" parameter:

print((await Process.run("cmd", ["/c", r"cd \ & dir"])).stdout);
SaadArdati commented 4 years ago

Should i downgrade? Although this means this is a legitamate issue on the dev branch,

Another example: image

    print('process 2');
    await Process.start('ffmpeg',
            ['-i', '$inputFile', '-f', 'segment', '-segment_time', '300', '-c', 'copy', '${dirname(inputFile)}/%03d.mp3'],
            workingDirectory: 'C:/Users/<...>/ffmpeg-windows/bin')
        .then((Process process) {
      process.stdout.transform(utf8.decoder).listen((data) {
        print(data);
      });
      process.stdin.writeln('Hello, world!');
      process.stdin.writeln('Hello, galaxy!');
      process.stdin.writeln('Hello, universe!');
    }).then((Process process) {
      // Get the exit code from the new process.
      process.exitCode.then((exitCode) {
        print('exit code: $exitCode');
      });
    });
process 2
CreateProcessW failed 2

'ffmpeg', ['-h'] is the same btw

SaadArdati commented 4 years ago

I tried with the beta version of dart, same problem.

Dart VM version: 2.8.0-20.11.beta (beta) (Mon Apr 20 14:33:01 2020 +0200) on "windows_x64"

SaadArdati commented 4 years ago

Same story with stable

Dart VM version: 2.8.1 (stable) (Thu Apr 30 09:25:21 2020 +0200) on "windows_x64"

zichangg commented 4 years ago

I can't reproduce with my Windows 10 machine. Probably something went wrong with your Windows machine.

SaadArdati commented 4 years ago

@zichangg I highly doubt it's my windows. What diagnostic data can I provide? Could it be a permission issue with the dart VM and windows? Something to do with debug mode? The error message should really provide more information.

What diagnostics would you like for me to provide?

zichangg commented 4 years ago

I recommend you to try running CreateProcessW locally. Our Process.run()/start() will invoke the CreateProcessW. The environment will be content from Platform.environment.

If it succeeds, it is likely the arguments or permission is wrongly set on your machine. Otherwise, CreateProcessW is the one to be investigated.

SaadArdati commented 4 years ago

@zichangg Sorry, I'm not familiar with windows Apis. I don't have a means to make an app that uses CreateProcessW. Is there an existing script I can try to run?

lrhn commented 4 years ago

I can reproduce it, but it appears that the problem is that the executable is neither in the path, nor in the current working directory.

The workingDirectory parameter documentation states that:

Use workingDirectory to set the working directory for the process. Note that the change of directory occurs before executing the process on some platforms, which may have impact when using relative paths for the executable and the arguments.

Apparently Windows 10 is not one of the platforms where the change happens prior resolving the executable. If I provide a full path for the executable, then it runs successfully.

import "dart:io";
import "dart:convert";
main() async {
    print('process 1');

    // Works and does set working directory.
    var p = await Process.start(r"cmd", ["/c", "dir"], workingDirectory: r"d:\tools\ffmpeg\bin\");
    print('process 2');
    await p.stdout.transform(utf8.decoder).forEach(print);
    print('process 3');
    // Works and does set working directory.

    // Does not work, gives the same error.   
    var q = await Process.start(r"ffmpeg.exe", ["-L"], workingDirectory: r"d:\tools\ffmpeg\bin\");
    print('process 4');
    await q.stderr.transform(utf8.decoder).forEach(print);
    print('process 5');
}

If I add

    Directory.current = r"d:\tools\ffmpeg\bin\";

before the Process.start then it does work. If I change it to

    var q = await Process.start(r"d:\tools\ffmpeg\bin\ffmpeg.exe", ["-L"], 
        workingDirectory: r"d:\tools\ffmpeg\bin\");

then it also works. If I put d:\tools\ffmpeg\bin\ into my path, it also works.

So, the problem seems to be that the ffmpeg executable cannot be found in the path or the current working directory.

SaadArdati commented 4 years ago

@lrhn Interesting observations. I tried the following:

    Directory.current = File(DependencyManager.ffmpegDir).absolute.path;
    await Process.start(
            'ffmpeg.exe',
            [
              '-i',
              '${File(inputFile).absolute.path}',
              '-f',
              'segment',
              '-segment_time',
              '300',
              '-c',
              'copy',
              '${dirname(inputFile)}/%03d.mp3'
            ],
            workingDirectory: Directory.current.path)
        .then((Process process) {
      process.stdout.transform(utf8.decoder).listen((data) {
        print(data);
      });
      process.stdin.writeln('Hello, world!');
      process.stdin.writeln('Hello, galaxy!');
      process.stdin.writeln('Hello, universe!');
    }).then((Process process) {
      // Get the exit code from the new process.
      process.exitCode.then((exitCode) {
        print('exit code: $exitCode');
      });
    });

When I run this, there is no output, no error, and no result (ffmpeg did nothing). Running the ffmpeg command in a powershell runs it properly.

Interestingly, when I add Directory.current to 'ffmpeg.exe', it gives me the Processw 2 error.

lrhn commented 4 years ago

I had the same issue. Listening on process.stderr instead of process.stdout helped. Not sure why stdout isn't closed.

SaadArdati commented 4 years ago

@lrhn

      process.stderr.transform(utf8.decoder).listen((data) {
        print(data);
      });

still prints nothing

SaadArdati commented 4 years ago

@lrhn I was missing an await where I call this method. My bad.

  static void segmentFile(String inputFile) async {
    print('1');
    Directory.current = File(DependencyManager.ffmpegDir).absolute.path;
    print('2');
    await Process.start(
            'ffmpeg.exe',
            [
              '-i',
              '${File(inputFile).absolute.path}',
              '-f',
              'segment',
              '-segment_time',
              '300',
              '-c',
              'copy',
              '${dirname(inputFile)}/%03d.mp3'
            ],
            workingDirectory: Directory.current.path)
        .then((Process process) {
      process.stderr.transform(utf8.decoder).listen((data) {
        print('error: $data');
      });
      process.stdout.transform(utf8.decoder).listen((data) {
        print('out: $data');
      });
    });
    print('3');
  }

this is my current setup. I tried currentDir before the command name as well as correct the \ to /.

Thing's I've observed:

console output:

1
2
3

.then() after this to get the exit code returns an error. The entire Process object is null after a then() call where the exitcode used to be. Not sure why this happens but i've ommitted it here.

there is no other console output, no error messages, and the command did nothing on the file.

SaadArdati commented 4 years ago

I got it to work!

Directory.current messed up the second File object in the code. '${File(inputFile).absolute.path}'. The path here turned into Directory.current + its own absolute path. Resulting in a file not found.

This is the final code

  static void segmentFile(String inputFile) async {
    print('1');
    await Process.start(
      '${File(DependencyManager.ffmpegDir).absolute.path}/ffmpeg.exe',
      [
        '-i',
        '${File(inputFile).absolute.path}',
        '-f',
        'segment',
        '-segment_time',
        '300',
        '-c',
        'copy',
        '${dirname(inputFile)}/%03d.mp3'
      ],
    ).then((Process process) {
      process.stderr.transform(utf8.decoder).listen((data) {
        print('error: $data');
      });
      process.stdout.transform(utf8.decoder).listen((data) {
        print('out: $data');
      });
    });
    print('2');
  }

I would like to note that I still do not have any console output from the command. I'm leaving this issue open so we can resolve that problem