ciyo
ciyo

Reputation: 735

CMD Exception Handling C/C++

I will use some CMD commands in my program and these commands might throw some exceptions. And as you know, when an exception accours, CMD writes its own error message the screen. But, I want to write my own error message.

My question is this: Is there a way to block CMD messages and write only my own error messages?

P.S. This is not a complex program. It executes CMD commands using System().

Example:

Let's say, the user can rename and copy any files in the program. As you know, if the user does not enter file's path properly, an error message is showed on the screen. And I want that this error message never appears on the screen. Only my own error message is showed.

Thank you!

Upvotes: 1

Views: 2089

Answers (5)

Max Truxa
Max Truxa

Reputation: 3478

It depends on your platform and the commands you are going to use. The usage of system() for calling console commands is by the way strongly discouraged by most people (it's way to heavy for most purposes).
I would suggest to you using CreateProcess() with the CREATE_NO_WINDOW flag and waiting for the process to exit with a call to WaitForSingleObject() and GetExitCodeProcess().
This approach utilizes the fact, that most CMD command are executables, located somewhere in C:/Windows/....

/*
 * Executes a program and returns it's exit code.
 *
 * TODO: Error checking should be added for
 *   CreateProcess()
 *   WaitForSingleObject()
 *   GetExitCodeProcess()
 */
DWORD ExecCmdLine(TCHAR const* cmdline)
{
    STARTUPINFO si;
    memset(&si, 0, sizeof(si));
    si.cb = sizeof(si);
    PROCESS_INFORMATION pi;
    memset(&pi, 0, sizeof(pi));
    ::CreateProcess(NULL, cmdline, NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi);
    ::CloseHandle(pi.Thread);
    ::WaitForSingleObject(pi.hProcess, INFINITE);
    DWORD exitcode;
    ::GetExitCodeProcess(pi.hProcess, &exitcode);
    ::CloseHandle(pi.hProcess);
    return exitcode;
}

If you want to retrieve the output of the command you could also provide hStdOutput, hStdError in the STARTUPINFO structure and set STARTF_USESTDHANDLES in STARTUPINFO.dwFlags. You can even do other things in your own program while the command is executing (especially as you mentioned file copy). This one is done the C++ way:

/*
 * TODO: Error checking should be added for
 *   CreateProcess()
 *   WaitForSingleObject()
 *   GetExitCodeProcess()
 */
class AsyncCmd
{
public:
    AsyncCmd(std::string const& cmdline)
        : cmdline(cmdline),
          processHandle(NULL)
    {
    }
    ~AsyncCmd()
    {
        if (this->processHandle != NULL)
            ::CloseHandle(this->processHandle);
    }
    // Starts the execution of the commandline.
    void Start(HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE), HANDLE hErr = GetStdHandle(STD_ERROR_HANDLE))
    {
        STARTUPINFO si;
        memset(&si, 0, sizeof(si));
        si.cb = sizeof(si);
        si.dwFlags = STARTF_USESTDHANDLES;
        si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
        si.hStdOutput = hOut;
        si.hStdError = hErr;
        PROCESS_INFORMATION pi;
        memset(&pi, 0, sizeof(pi));
        ::CreateProcess(NULL, this->cmdline.c_str(), NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi);
        ::CloseHandle(pi.hThread);
        this->processHandle = pi.hProcess;
    }
    // Blocks until execution is finished and returns the exit code.
    DWORD ExitCode()
    {
        ::WaitForSingleObject(this->processHandle, INFINITE);
        DWORD exitcode;
        ::GetExitCodeProcess(this->processHandle, &exitcode);
        return exitcode;
    }
private:
    AsyncCmd(AsyncCmd const&);
    AsyncCmd& operator=(AsyncCmd const&);
    std::string cmdline;
    HANDLE processHandle;
}

Upvotes: 3

paulsm4
paulsm4

Reputation: 121881

To rephrase what's already been said:

Q: Can you somehow intercept an error thrown by a command you've invoked via "system()"?

A: No. For many reasons.

But you can redirect the textual error message that's written by the command line program:

  • Redirecting "stderr" is relatively easy. "GetStdHandle(STD_ERROR_HANDLE)" is one way. Redirecting to "> :err" is another.

  • Unfortunately, not all programs are nice enough to write error messages to "stderr".

    Many write everything to "stdout".

In the latter case, you'd have to figure out 1) that an error actually occurred, and 2) figure out how to separate the parts of the text input that are "standard output", vs those parts that are "error text".

PS:

An alternative API to "system()" is "popen()".

Upvotes: 1

0xC0000022L
0xC0000022L

Reputation: 21369

Adding to what yourmt wrote, I'd like to point out that exceptions won't bleed through across process boundaries. So what you are tackling here is the output (both stderr and stdout) of the executed program (and shell) plus its exit code (in case this provides any useful information). This is mostly so you understand that this is literally not about exception handling as your title implies.

That means you can set hStdOutput and hStdError when using CreateProcess/CreateProcessEx to use pipes that you control and where you "catch" the output and then replace it with your own before outputting it to the console. In case you want to only replace stderr you can also do that.

Upvotes: 0

Kane Anderson
Kane Anderson

Reputation: 563

You can use

#include "Exception.h"

class MyClass
{
public:
    class Error : public Exception { };
    MyClass();
    ~MyClass();
    void myFunction() throw(Error);
}

...

catch (MyClass::Error & error)
{
    cout << error.full_message << endl;

    return;
}

and

class Exception
{
public:
    std::string message;
    std::string full_message;

    virtual char * info() { return ""; };

    void setMessage(std::string msg)
    {
        message = msg;
        if (*info() == 0) { full_message = msg; }
        else { full_message = MakeString() << info() << " " << msg; }
    }
};

// template function ///////////////////////////////////////////

template <class errType>
void Throw(const std::string msg=std::string(""))
{
errType err;
err.setMessage(msg);

throw(err);
}

Upvotes: -1

Jonathan Henson
Jonathan Henson

Reputation: 8206

One brute force way to do it would be to pipe the output of the CMD i.e. yourCommand > file.txt to a file and then read the file contents to determine if there was an exception.

Upvotes: 0

Related Questions