Brxxn
Brxxn

Reputation: 110

How to run Windows Commands through C++ Program?

For some back info to help you guys out, I'm making a small program that will take an .SQL file and restore it to the users localhost phpMyAdmin using mysql.exe. Importing the file through the MyAdmin itself by hand takes forever as it has to unzip and parse the information, this method is quicker and it's for my coworkers as we're always pushing backups and restores to our WAMP to experiment with.

My one issue I'm facing is that I can't seem to get it to change directories no matter what. I understand that wherever the executable was launched is the path that the system will inherit, so why can't I simply tell it to change directories?

Here's my code so far:

system("cd c:\wamp64\bin\mysql\mysql5.7.14\bin");
system("PAUSE");
system("dir");
system("PAUSE");
system("mysql.exe -u root -p dataBase < tables.sql");

Note: The pauses and system("dir") are in there strictly for testing purposes, once the window spits back the correct contents of the directory, then I know that it works and I'll only have the first and last lines left in the program.

UPDATE: I've been notified that every call to system creates a new CMD process which makes perfect sense as to why the cd will not effect anything, as it's opening a new process, changing directories, then on the next line it's calling a new process and pausing that. What would be the workaround for this?

UPDATE 2: I decided to join multiple commands together using && but that limits me on the usability and "styling" side of things, but that's not a problem, the program is needed as a run & done type of deal.

Here's what I've come up with:

system("cd c:\\wamp64\\bin\\mysql\\mysql5.7.14\\bin && mysql.exe -u root -p dataBase < table.sql");
cout << "\n" << "The database 'dataBase' has been restored using 'table.sql' successfully!" << "\n\n";
return 0;

Are there any advantages to creating a .cmd file and running it using CreateProcess() or disadvantages to using the method that I've come up with?

Note: The database name and .sql files aren't pulled from anything, the names will be static so I don't have to worry about updating that.

Upvotes: 2

Views: 10037

Answers (2)

cmdLP
cmdLP

Reputation: 1856

Here is a way you can execute system with directory change:

// using AND_CMD like an operator to concat commands -- you can also just type it in one string: "edit & shutdown -h"...
// this works in c, too, but no dynamic variables are possible
//#define AND_CMD " & "

// c++
#define AND_CMD +" & "+

system(
    "cd c:\wamp64\bin\mysql\mysql5.7.14\bin" AND_CMD
    "PAUSE" AND_CMD
    "dir" AND_CMD
    "PAUSE" AND_CMD
    "mysql.exe -u root -p dataBase < tables.sql"
);

So you only need to join the commands with the string " & ". If you need to run the code on conditions to the program itsself/keep the directory by next time calling the system-function, you should look for alternatives or you pipe the commands into the commandprompt (eg. running your program by myprogram.exe | %comspec% and invoking the commands by outputting with eg. printf.

Upvotes: 4

Dai
Dai

Reputation: 154995

A better idea is to dynamically generate a batch file (.cmd or .bat, there are slight differences) and then invoke that with cmd.exe /c batchFile.cmd:

#include <cstdlib>
#include <cstdio>

ofstream batch_file;
batch_file.open( "commands.cmd", ios::trunc );
batch_file <<
    "cd c:\\wamp64\\bin\\mysql\\mysql5.7.14\\bin" << endl <<
    "PAUSE" << endl <<
    "dir" << endl <<
    "PAUSE" << endl <<
    "mysql.exe -u root -p dataBase < tables.sql" << endl;
batch_file.close();

int batch_exit_code = system( "cmd.exe /c commands.cmd" ); // blocks until the child process is terminated
if( batch_exit_code != 0 ) {
    cout << "Batch file exited with code " << batch_exit_code << endl;
}

remove( "commands.cmd" ); // delete the batch file

This has the benefit of allowing you to customize the text and commands, such as performing your own detection of the location of the MySQL binaries and your temporary files.

Note that if you're always running on Windows, you might want to use CreateProcess instead, as system has some overheads:

system() and CreateProcess() / CreateProcessW()

The system function passes command to the command interpreter, which executes the string as an operating-system command. system refers to the COMSPEC and PATH environment variables that locate the command-interpreter file (the file named CMD.EXE in Windows NT).

As you want to run cmd.exe directly, this would result in it opening twice.

You can use CreateProcess instead:

STARTUPINFO info={sizeof(info)};
PROCESS_INFORMATION processInfo;
if( CreateProcess( "cmd.exe", "/c commands.cmd", NULL, NULL, TRUE, 0, NULL, NULL, &info, &processInfo ) )
{
    WaitForSingleObject( processInfo.hProcess, INFINITE );
    CloseHandle( processInfo.hProcess );
    CloseHandle( processInfo.hThread );
}

Upvotes: 4

Related Questions