Problem passing a directory with a space in it as an argument



  • I was making a simple little tool as a front-end to PDF Creator to batch print a load of Publisher files...

    I know the code is not very good, in the sense that I'm hard-coding some file paths in there, but this is just to work on three computers in my office, so that doesn't matter. I just wanted to make this small job (batch printing Publisher -> PDF) easier for a couple of people in my office.

    You choose a directory and then the PDFs are made. After that, the directory where PDF Creator auto-saves files opens and the application closes. However, when the directory name has a space in it (and possibly also when there's a minus sign in it (I think)), the files are not processed. I've tried to escape the space somehow, but I haven't managed to do it.

    Here's my 'mainwindow.cpp' file.

    @#include "mainwindow.h"
    #include "ui_mainwindow.h"

    #include "QFileDialog"
    #include "QProcess"
    #include "QFile"

    MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
    {
    this->setWindowTitle("Batch Publisher to PDF");
    ui->setupUi(this);

    }

    MainWindow::~MainWindow()
    {
    delete ui;
    }

    void MainWindow::on_pushButton_clicked()
    {
    QString dir = QFileDialog::getExistingDirectory(this, tr("Open Directory"),
    "/home",
    QFileDialog::ShowDirsOnly
    | QFileDialog::DontResolveSymlinks);

    QString r;
    r = dir.replace("/", "\\");
    
    QString program;
    QString pA;
    QString pB;
    
    pA = "C:\\Program Files (x86)\\PDFCreator\\PDFCreator.exe";
    pB = "C:\\Program Files\\PDFCreator\\PDFCreator.exe";
    
    QFile dFile(pB);
    
    if (dFile.exists()) {
        program = pB;
    }
    else {
        program = pA;
    }
    QStringList arguments;
    arguments << "/NOSTART" << "/PF"+r+"\\*.pub";
    QProcess *myProcess = new QProcess(qApp);
    myProcess->start(program, arguments);
    myProcess->waitForFinished();
    
    
    QProcess *process = new QProcess(qApp);
    
    QString pdf;
    QString pdfA;
    QString pdfB;
    
    pdfA = "C:\\Users\\Steve\\Documents\\PDFs\\";
    pdfB = "E:\\pdfs\\";
    
    QDir dDir(pdfA);
    if (dDir.exists()) {
        pdf = pdfA;
    }
    else {
        pdf = pdfB;
    }
    
    process->start("explorer \""+pdf+"");
    process->waitForFinished();
    
    qApp->exit();
    

    }
    @

    I'm using Qt 4.7. I tried this with a new installation of Qt 5, but I had a nightmare with deployment which I didn't have on my old computer with Qt 4.7 installed.

    Thanks if anyone can see where I'm going wrong here.



  • Arguments with spaces need to be quoted when passed to the shell. Either do that or, preferably, use the version of QProcess::start() that takes the argument list as a QStringList.

    In line 77 you put a literal double quote in your string before the file name, but do not put one after the file name.



  • Ah! That might be it - what you said about line 77. I'll check that later. Thanks a lot. I'll let you know how it goes.



  • Hi. I realised that that wasn't the problem (about line 77). That's the bit that opens the PDF directory, and that's been working fine (although I did need to fix that too.) The problem seems to be in the arguments being passed to PDFCreator. When I do this straight from the command line, it works fine...

    Thanks for any more help.



  • Line 54 probably should read:
    @
    arguments << "/NOSTART" << "/PF" << r+"\*.pub";
    @



  • Chris,

    Unfortunately, that didn't do it - in fact, that broke it. Before, in a directory without a space in it, it worked OK, but now - with that line - it doesn't.

    Thanks for the input and any further ideas.



  • Hello again.

    I'm getting really frustrated. Any help would be great. The command line for batch printing PDFs normally looks something like this for me:

    @PDFCreator.exe /PF"C:\Users\Steve\Documents\arf*.pub"@

    This works fine. If I have a space in that, it still works fine....

    @PDFCreator.exe /PF"C:\Users\Steve\Documents\arf 2*.pub"@

    The PDF Creator page about the command line options stresses that there should be no space between "/PF" and the file path, so I'm passing that as one argument in my code up there. (And, as mentioned above, sending "/PF" and the file as separate arguments doesn't work.)

    In my Qt thing, when I try to escape the quotes so that I can keep that whitespace in the file name or path, PDF Creator gives me this when there's no space:

    bq. The file can not be found!
    "C:\Users\Steve\Documents\arf*.pub"

    When there is a space, it gives me nothing - it just doesn't work and my Qt thing goes to the next step.

    So... any idea what I can do?

    Thanks again.


  • Moderators

    Hi,

    QProcess has a strange requirement regarding quotation marks. Its "documentation":http://qt-project.org/doc/qt-5/qprocess.html#start-3 says, "quotes need to be both escaped and quoted" (see the documentation for an example).

    This is unintuitive behaviour, and the Qt devs are planning to change it.



  • Thanks for that.

    Now, I have this:

    @ QString dir = QFileDialog::getExistingDirectory(this, tr("Open Directory"),
    "/home",
    QFileDialog::ShowDirsOnly
    | QFileDialog::DontResolveSymlinks);
    QString r;
    r = dir.replace("/", "\");

    QStringList arguments;
    arguments << "/NOSTART" << "/PF""""+r+"\*.pub"""";
    QProcess *myProcess = new QProcess(qApp);
    myProcess->start(program, arguments);

    @

    ... and PDF Creator gives me this:

    bq. The file can not be found!
    ""\“C:\Users\Steve\Documents\arf*.pub\”""

    Any more help would be greatly appreciated. Alternatively, do you know another simple way to do this? All I want to do is select a directory and pass that directory as part of an argument to PDF Creator. That seems simple, and I thought this 'project' would be easy to do. If there's another simple way to do this, I'd love to hear it.

    Thanks.


  • Moderators

    I'm not able to test at the moment, but what do you get if you write:

    @
    myProcess->start("C:\Program Files\PDFCreator\PDFCreator.exe /NOSTART /PF"""C:\Users\Steve\Documents\arf*.pub"""");
    @

    As a last resort, you could also try...
    @
    system("C:\Program Files\PDFCreator\PDFCreator.exe /NOSTART /PF"C:\Users\Steve\Documents\arf*.pub"");
    @
    ...but the general consensus is to avoid system() if at all possible because it can be a security risk. system() might also upset your antivirus.



  • JKSH,

    With this:
    @ myProcess->start("C:\Program Files (x86)\PDFCreator\PDFCreator.exe /NOSTART /PF"""C:\Users\Steve\Documents\arf\*.pub"""");@

    I get nothing.

    With this:
    @system("C:\Program Files (x86)\PDFCreator\PDFCreator.exe /NOSTART /PF"C:\Users\Steve\Documents\arf\*.pub"");@

    I get this:

    @'C:\Program' は、内部コマンドまたは外部コマンド、
    操作可能なプログラムまたはバッチ ファイルとして認識されていません。
    @

    ... which means it's no good (my OS is Japanese).

    I tried playing around with your ideas - escaping quotes and stuff, but I got nowhere.

    Thanks for the interest and any further help.


  • Moderators

    Oops, you need to wrap the path to PDFCreator.exe in quotes too, because there are spaces in the "Program Files" part.

    Try these:

    @
    myProcess->start(""""C:\Program Files\PDFCreator\PDFCreator.exe""" /NOSTART /PF"""C:\Users\Steve\Documents\arf*.pub"""");
    @

    @
    system(""C:\Program Files\PDFCreator\PDFCreator.exe" /NOSTART /PF"C:\Users\Steve\Documents\arf*.pub"");
    @



  • Hello again.

    @ myProcess->start(""""C:\Program Files (x86)\PDFCreator\PDFCreator.exe""" /NOSTART /PF"""C:\Users\Steve\Documents\arf*.pub"""");
    @

    This gave me an error (unknown escape sequence '*'), so I added an extra backslash there and then I just got nothing.

    @system(""C:\Program Files (x86)\PDFCreator\PDFCreator.exe" /NOSTART /PF"C:\Users\Steve\Documents\arf\*.pub"");@

    This gave me the same error as above (the Japanese one).


  • Moderators

    Huh, it looks like system() is broken on Windows.

    @
    // This works
    system(""C:\Program Files\7-zip\7z.exe" a -tzip C:\Test\output.zip C:\Test\*.txt");
    @

    @
    // This gives me:
    // 'C:\Program' is not recognized as an internal or external command,
    // operable program or batch file.
    system(""C:\Program Files\7-zip\7z.exe" a -tzip "C:\Test\output.zip" C:\Test\*.txt");
    @

    @
    // This works, surprisingly!
    QProcess p;
    p.start(""C:\Program Files\7-zip\7z.exe" a -tzip "C:\Test\output.zip" C:\Test\*.txt");
    @

    So, perhaps you should try this?:
    @
    myProcess->start(""C:\Program Files (x86)\PDFCreator\PDFCreator.exe" /NOSTART /PF"C:\Users\Steve\Documents\arf\*.pub"");
    @

    (P.S. This is with Qt 5.3.0 beta. I don't know what it's like on Qt 4.7)



  • Thanks again, but that kind of takes me back to where I was: the line works when there's no space in the directory name, but not when there is a space. (And I tried it on Qt 5).


  • Moderators

    I'm out of ideas, I'm afraid.

    Try subscribing to the "Interest mailing list":http://lists.qt-project.org/mailman/listinfo/interest and asking there. Someone had a similar problem not long ago, which was resolved on that list: http://comments.gmane.org/gmane.comp.lib.qt.user/11719



  • Hi, just tested, when I changed your lines:
    @ QStringList arguments;
    arguments << "/NOSTART" << "/PF"+r+"\*.pub";@

    to
    @ QStringList arguments;
    QString sQuote = """;
    arguments << "/NOSTART" << "/PF"+ sQuote + r + "\*.pub" + sQuote;
    @

    I got a PDF file (but my system is English). Anyway I think it should work, just a matter of fixing those d*mn quotes..



  • hskoglund,

    Thanks for trying, but with that, with a directory that worked before, I get this from PDFCreator:

    bq. The file can not be found!
    \“C:\Users\Steve\Documents\arf*.pub\”

    I don't know if the language of the system is relevant, but interestingly, I often find in things like this, where you would normally see a '', I Windows shows a yen sign. I'm not sure why that is or if it means the code is treated any differently (maybe I should look this up) but I don't usually have a problem with it - I type a blackslash and I see a backslash in my code and it escapes things and whatever it needs to do, but the little message windows that give errors like the one I've shown above actually show yen signs. This is not specific to what I'm doing here - it often happens, and it's never been a problem - I just see it as a quirk of the Japanese OS.

    [edit]
    Here's something related to the backslash/yen thing: http://www.pcreview.co.uk/forums/japanese-fonts-changing-backslash-yen-symbol-t452657.html
    [/edit]



  • [quote author="BonRouge" date="1397489044"]Hello again.

    @ myProcess->start(""""C:\Program Files (x86)\PDFCreator\PDFCreator.exe""" /NOSTART /PF"""C:\Users\Steve\Documents\arf*.pub"""");
    @

    This gave me an error (unknown escape sequence '*'), so I added an extra backslash there and then I just got nothing.

    @system(""C:\Program Files (x86)\PDFCreator\PDFCreator.exe" /NOSTART /PF"C:\Users\Steve\Documents\arf\*.pub"");@

    This gave me the same error as above (the Japanese one).
    [/quote]

    I really know nothing about how the Qt Process object works as I am new to Qt. However, the system option is formatted incorrectly. You must have a space between /PF and "C:\Users\Steve\Documents\arf\*.pub""

    It should read:
    @system(""C:\Program Files (x86)\PDFCreator\PDFCreator.exe" /NOSTART /PF "C:\Users\Steve\Documents\arf\*.pub"");@



  • prImem0ver,

    Thanks for the input, but this is from "the PDF Creator site":http://www.pdfforge.org/content/command-line-parameters:

    /PF<filename>
    Print a file with the standard program linking with the extension of the file. In general, this option is useful in connection with Auto-Save mode. It is not possible to use this parameter in conjunction with the /OF parameter. There is NO space between the parameter and the file name.

    (The emphasis is on the site, not added by me.)



  • You will not be able to use quotes then. At least not around that directory. Try it without the quotes since there is no space there.



  • The problem is that I want this to work with directories that have spaces - not just that one that I was testing with there - I have a couple of directories and files that I'm testing with - with and without spaces.



  • nevermind i was wrong about that anyway. apparently quotes in the middle of a text block is still interpreted as one argument even if spaces exist.



  • Hi everyone. Bad news and good news... The bad news is that I've given up trying to do this with Qt because it's driving me crazy. The good news is that someone's already done this in VBScript. :)

    http://www.developpez.net/forums/d1431043/autres-langages/general-visual-basic-6-vbscript/vbscript/script-pdf-creator/#post7772759



  • Hi again, couldn't resist another attempt at this problem :-)

    first: about those yen signs, it's because of the japanese codepage used by windows (where \ is replaced by the yen sign)

    then: I reread your problem description, and it's not about backslashes or yen signs but spaces in directories/paths. And as you say, invoking PDF Creator from the command line. in that case when you quote the path a space works fine and dandy. But not from Qt :-(

    So I thought, why not go lowtech on this problem and launch PDF Creator using a .cmd file (for 64-bit Win7 it's better to use .cmd instead of .bat files).

    I tested with a cmd file (C:\Temp\StartPF.cmd) that contains two lines:
    @"C:\Program Files (x86)\PDFCreator\PDFCreator.exe" /NOSTART /PF"%1 %2 %3 %4 %5 %6 %7 %8 %9
    "C:\Program Files\PDFCreator\PDFCreator.exe" /NOSTART /PF"%1 %2 %3 %4 %5 %6 %7 %8 %9@

    (One of the two lines should work regardless if you're on 32 bit or 64 bits Win7.)

    Then I modified your program again:
    @ program = "cmd.exe";

    QStringList arguments;
    arguments << "/c C:\\temp\\StartPF.cmd " + r + "\\*.pub";@
    

    Note in the .cmd file there's no trailing quote, it's because QProcess inserts quotes anyway around each separate argument. (Also note I add everything together into just one argument in the string list, to minimize the extra cooking by QProcess.)

    Anyway, the low-tech idea here is to use that sequence of %1 %2 %3 etc. to bake paths together to it's full glory again even with spaces inside. As long as the # of different spaces are 8 or less :-)



  • hskoglund,

    You're a star! It works perfectly. :-)

    Now, can you tell me what you did there? You said you've gone low-tech there, but unfortunately, I've only ever cobbled together some simple .bat files in the past and I've never even used a .cmd file before.

    Thanks a lot. :-)



  • Good! (when I saw you posted couple of seconds before me, I hoped my post would save you from the brink of insanity.)

    About .bat/.cmd files, that's one advantage of being old age, they're primitive tools for sure but not unknown territory. Anyway a .cmd file is nothing more than a renamed .bat file, because sometimes .bat files don't start in 64-bit Windows, but .cmd works fine.

    The low-tech? It's just that sequence %1 %2 %3 %4 %5 ... that works as a "catch-all", i.e. when QProcess launches the .cmd file, an argument with spaces inside gets split up into separate arguments, but %1 %2 %3 %4 %5 ... just pastes them together again.



  • Great! Thanks for being old! And thanks for your time and help. :-)


  • Moderators

    Great work, hskoglund! :)



  • Hello again,

    I'm not sure if I should start a new post here, but I have another small problem with this script.

    I've posted the whole file below. The problem is with opening a directory in Explorer after the work (PDF printing) is done. I didn't have this problem before because I'm building and testing on two Windows 7 machines. Now I've come to deploy this on an XP machine, I've found that the simple code I had to do this isn't working. I tried it in a cmd window and it didn't work. I found that this works:
    @start C:\temp@
    ...but this doesn't:
    @start E:\pdfs@
    With a bit more investigation, I found that this works:
    @%SystemRoot%\explorer.exe /e, E:\pdfs@
    ... but when I put that into my Qt code, it doesn't work. Please take a look near the end of the code below and see if you can see where I might be going wrong.

    Thanks a lot. I appreciate any help.

    @#include "mainwindow.h"
    #include "ui_mainwindow.h"

    #include "QFileDialog"
    #include "QProcess"
    #include "QFile"
    #include "QDebug"

    MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
    {
    this->setWindowTitle("Batch Publisher to PDF");
    ui->setupUi(this);

    }

    MainWindow::~MainWindow()
    {
    delete ui;
    }

    void MainWindow::on_pushButton_clicked()
    {
    QString dir = QFileDialog::getExistingDirectory(this, tr("Open Directory"),
    "/home",
    QFileDialog::ShowDirsOnly
    | QFileDialog::DontResolveSymlinks);

    QString r;
    r = dir.replace("/", "\\");
    
    QString program;
    program = "cmd.exe";
    QStringList arguments;
        arguments << "/c C:\\sb\\StartPF.cmd " + r + "\\*.pub";
    
    
    QProcess *myProcess = new QProcess(qApp);
    
    
    myProcess->start(program,arguments);
    qDebug() << arguments;
    myProcess->waitForFinished();
    
    
    QProcess *process = new QProcess(qApp);
    
    QString pdf;
    QString pdfA;
    QString pdfB;
    QString pdfC;
    
    pdfA = "C:\\Users\\Steve\\Documents\\PDFs\\";
    pdfB = "E:\\pdfs"; // This is the one in the XP machine
    pdfC = "C:\\Users\\Steve\\Dropbox\\pdfs\\";
    
    QDir dDirA(pdfA);
    QDir dDirB(pdfB);
    if (dDirA.exists()) {
        pdf = pdfA;
    }
    else if (dDirB.exists()) {
        pdf = pdfB;
    }
    else {
        pdf = pdfC;
    }
    
    if (pdf==pdfB) { // if XP
    process->start("%SystemRoot%\\explorer.exe /e, "+pdf);
    process->waitForFinished();
    }
    

    else {
    process->start("explorer ""+pdf+"");
    process->waitForFinished();
    }
    qApp->exit();

    }
    @



  • Hi, re. that XP problem, probably process->start() does not interpret/expand that environment variable SystemRoot into C:\Windows. So when fiddling with Explorer, start etc. it's usually better to launch them via "cmd.exe /c" (as used to run StartPF.cmd).

    So try changing to
    @process->start("cmd.exe","/c %SystemRoot%\explorer.exe /e, " + pdf);@



  • Thanks.

    That didn't actually work as you've posted - I got compile errors - but it worked when I changed it to this:

    @QStringList args;
    args << "/c %SystemRoot%\explorer.exe /e, " + pdf;
    process->start("cmd.exe",args);@

    Thanks again.



  • Good! And sorry about the compile error


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.