Gunnar B.
Gunnar B.

Reputation: 2989

QProcess start process (blender.exe) with multiple arguments

I try to start blender.exe from inside my program (FaceModifier.exe) using QProcess (on Windows). The command follows this structure:

'path-to-blender' --background 'path-to-blend-file' --python 'path-to-python-script' -- 'additional-arg-for-python-script'

A full example (that works if I type it into cmd.exe) would be

"C:\Program Files\Blender Foundation\Blender\blender.exe" --background "C:\Program Files(x86)\FaceModifier\Resources\GenericHeadMesh.blend" --python "C:\Program Files(x86)\FaceModifier\python\local.py" -- "C:\Users\Gunnar\Documents\FaceModifier\Output\"

Now, inside my program I escape the paths and surround them with quotes so I have something like this

std::string blenderPath := "\"C:\\Program Files\\Blender Foundation\\Blender\\blender.exe\""

For QProcess I feed all my arguments into a QStringList with a leading /c so it is treated as a single command and pass it to cmd.exe.

My problem is that I can't get this to be executed. If I type the command (that I pass to QProcess, not the one from above) into cmd by hand neither.

My function that starts the process looks like this:

void PythonConnector::createSubprocess(const QStringList &args)
{
    QProcess *process = new Process();
    process->start("cmd.exe", args);
    process->waitForFinished(-1);

    QString result(process->readAll());
    qDebug() << "result: " << result;    // this gives just an empty string

    process->close();
}

I call it like this:

// for the documents path
CoInitialize(NULL);
TCHAR *myDocuments = 0;
SHGetKnownFolderPath(FOLDERID_Documents, 0, NULL, &myDocuments);
CString temp(myDocuments);
CT2CA tempConv(temp);
std::string outputFolderPath(tempConv);
outputFolderPath += "\\FaceModifier\\Output\\";
outputFolderPath = pH.ExcapeString(outputFolderPath);
CoTaskMemFree(myDocuments);

blenderPath = pH.EscapeString(blenderPath);

std::string meshGlobalPath = "\"" + pH.GetResourcesPath() + "GenericHeadMesh.blend" + "\"";
std::string pythonGlobalPath = "\"" + pH.GetPythonPath() + "global.py" + "\"";

QStringList args;
args << "/c" << QString::fromStdString(blenderPath) << "--background" << QString::fromStdString(meshGlobalPath) << "--python" << QString::fromStdString(pythonGlobalPath) << "--" << QString::fromStdString("\"" + outputFolderPath "\"");
pC.createSubprocess(args);

blenderPath is passed to this function and read from registry in another function.
These are my other helper functions:

std::string PathHandler::GetResourcesPath()
{
    if(resourcesPath.empty())
        resourcesPath = GetFullPath("Resources\\");
    return resourcesPath;
}

std::string PathHandler::GetPythonPath()
{
    if(pythonPath.empty())
        pythonPath = GetFullPath("python\\");
    return pythonPath;
}

std::string PathHandler::GetFullPath(const std::string &relPath)
{
    char full[_MAX_PATH];
    _fullpath(full, relPath.c_str(), _MAX_PATH);
    return EscapeString(std::string(full));
}

std::string PathHandler::EscapeString(const std::string &input)
{
    std::regex toReplace("\\\\");
    std::string output(input.begin(), input.end());
    output = std::regex_replace(output, toReplace, "\\\\");
    return output;
}

Debugging the args list with qDebug results in these outputs (these are actually from debugging in Visual Studio 2013, therefore the different paths):

"/c"  
""C:\\Program Files\\Blender Foundation\\Blender\\blender.exe""  
"--background"  
""C:\\Users\\Gunnar\\documents\\visual studio 2013\\Projects\\FaceModifier\\x64\\Release\\Resources\\GenericHeadMesh.blend""  
"--python"  
""C:\\Users\\Gunnar\\documents\\visual studio 2013\\Projects\\FaceModifier\\x64\\Release\\python\\global.py""  
"--"  
""C:\\Users\\Gunnar\\Documents\\FaceModifier\\Output\\""

and the result debug from createSubprocess just gives "".

If I type this command into cmd by hand like this:

cmd /c "C:\Program Files\Blender Foundation\Blender\blender.exe" --background "C:\Users\Gunnar\documents\visual studio 2013\Projects\FaceModifier\x64\Release\Resources\GenericHeadMesh.blend" --python "C:\Users\Gunnar\documents\visual studio 2013\Projects\FaceModifier\x64\Release\python\global.py" -- "C:\Users\Gunnar\Documents\FaceModifier\Output\"

I get The command "C:\\Program" is either missspelled or couldn't be found. Same for various different quotings with and without escaping like this

cmd "/c \"C:\Program Files\Blender Foundation\Blender\blender.exe\" --background \"C:\Users\Gunnar\documents\visual studio 2013\Projects\FaceModifier\x64\Release\Resources\GenericHeadMesh.blend\" --python \"C:\Users\Gunnar\documents\visual studio 2013\Projects\FaceModifier\x64\Release\python\global.py\" -- \"C:\Users\Gunnar\Documents\FaceModifier\Output\\""

Just typing

cmd /c "C:\Program Files\Blender Foundation\Blender\blender.exe"

works fine, but that is obviously not what I need.

I can't wrap my head around how I have to build this so it works. Can somebody tell me what my mistake is?

UPDATE:
Ok, in theory it works now (thanks to ahmed), it really depands on the paths though. If I have the .blend and the .py in ...\Documents\FaceModifier it works, if they are in C:\Program Files (x86)\FaceModifier\ (where my program is installed) it doesn't work. How can I achieve that? I would prefer that so I don't have to copy those files over there.

Upvotes: 0

Views: 575

Answers (1)

ahmed
ahmed

Reputation: 5590

You don't need to quote or double quote file paths, QProccess already handle that, change this part of your code to:

std::string meshGlobalPath = pH.GetResourcesPath() + "GenericHeadMesh.blend" ;
std::string pythonGlobalPath = pH.GetPythonPath() + "global.py" ;

QStringList args;
args << "/c" << QString::fromStdString(blenderPath) << "--background" 
     << QString::fromStdString(meshGlobalPath) << "--python" 
     << QString::fromStdString(pythonGlobalPath) << "--"
     << QString::fromStdString(outputFolderPath);

Upvotes: 2

Related Questions