Alexm
Log in

Use your e-mail or user name and the password to identify yourself

Sign up

You will receive an e-mail with an activation link. Last field is optional


🠉

Get command line arguments as a vector of wstring in C++

If you need to do this, then check this little article that comes with fully tested code and examples of how to use it

I just started this new little project of mine in C++, and because the application needs to read and parse the arguments passed to it in the command line, I thought it would be better to have all of them stored inside a std::vector. I tend to enable wide char support in the applications I create, to make sure they can use Unicode characters, so the vector will store the arguments as std::wstring objects.

I tested the solution in Windows (console and Win32 GUI programs) and Linux (console). You will get more information in the comments. I hope you will find it useful. Here is the code. I have it in a header file that I include in my CPP source.


#include <cstring>
#include <string>
#include <vector>

// WINDOWS CONSOLE APPS
#if _CONSOLE

/**
 * Get command line arguments as vector in Windows console application
 *
 * Get arguments count and pointer from wmain entry point
 */
std::vector<std::wstring> cmdArguments(int argc, wchar_t* argv[]) {
    return std::vector<std::wstring>(argv, argv + argc);
}

// WINDOWS GUI APPS
#elif _WIN32

#include <shellapi.h>
#include <windows.h>

/**
 * Get command line arguments as vector in Windows application
 *
 * Optionally, you can pass a pointer to the command line
 */
std::vector<std::wstring> cmdArguments(LPWSTR lpCmdLine = NULL) {
    std::vector<std::wstring> arguments;
    LPWSTR* szArglist;
    int nArgs;

    // retrieve a pointer to the command line arguments using the Shell API
    szArglist = CommandLineToArgvW(lpCmdLine != NULL ? lpCmdLine : GetCommandLineW(), &nArgs);
    if (szArglist != NULL) {
        for (auto i = 0; i < nArgs; i++) arguments.push_back(szArglist[i]);
    }

    LocalFree(szArglist);

    return arguments;
}

#endif

/**
 * Get command line arguments as vector in Windows/Linux console application
 *
 * Get arguments count and pointer from main entry point
 */
std::vector<std::wstring> cmdArguments(int argc, char* argv[]) {
    std::vector<std::wstring> arguments;

    for (auto i = 0; i < argc; i++) {
        // number of multibyte characters in source and result
        size_t mbslen = strlen(argv[i]) + 1;
        size_t mbolen;
        // pointer to converted wide character argument
        wchar_t* wcs = new wchar_t[mbslen];
#ifdef _WIN32
        // in Windows we can use mbstowcs_s to convert char to wchar_t before adding as wstring to array
        mbstowcs_s(&mbolen, wcs, mbslen, argv[i], mbslen - 1);
#else
        // in Linux, calculate the length required to hold argv[i] converted to a wide character string
        mbslen = mbstowcs(NULL, argv[i], 0);
        if (mbslen == (size_t) - 1) {
            perror("mbstowcs");
            exit(EXIT_FAILURE);
        }

        // allocate wide character string of the desired size + 1 for terminating null wide character
        if (calloc(mbslen + 1, sizeof(*wcs)) == NULL) {
            perror("calloc");
            exit(EXIT_FAILURE);
        }

        // convert the multibyte character string in argv[i] to a wide character string
        if (mbstowcs(wcs, argv[i], mbslen + 1) == (size_t) - 1) {
            perror("mbstowcs");
            exit(EXIT_FAILURE);
        }
#endif
        arguments.push_back(std::wstring(wcs));
    }

    return arguments;
}

The following is an example of how I use it inside a Win32 project (specifying a pointer to the command line is optional)


int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
                     _In_opt_ HINSTANCE hPrevInstance,
                     _In_ LPWSTR    lpCmdLine,
                     _In_ int       nCmdShow)
{
    UNREFERENCED_PARAMETER(hPrevInstance);
    UNREFERENCED_PARAMETER(lpCmdLine);

    // std::vector<std::wstring> arguments = cmdArguments();
    std::vector<std::wstring> arguments = cmdArguments(lpCmdLine);

    for (const std::wstring& i : arguments) {
        MessageBox(
            NULL,
            (LPCWSTR)i.c_str(),
            (LPCWSTR)L"Argument",
            MB_ICONINFORMATION | MB_OK
        );
        wprintf(_T("%ls"), i.c_str());
    }

    return 0;
}

In Linux, I also set the locale, in order to be able to use special characters


#include <iostream>

#include <locale.h>

#include "arguments.h"

int main(int argc, char* argv[])
{
    // set locale to allow usage of UNICODE characters in command-line
    if (setlocale(LC_ALL, "en_US.UTF-8") == NULL) {
        perror("setlocale");
        exit(EXIT_FAILURE);
    }

    std::vector<std::wstring> arguments = cmdArguments(argc, argv);

    for (const std::wstring& wArg : arguments) {
        std::wcout << wArg.c_str() << "\n";
    }

    return 0;
}

Let me know if you have any problems or if you have some improvements to suggest.