Alexm
Entra

Utiliza tu e-mail o nombre de usuario y la contraseña para identificarte

Regístrate

Recibirá un correo electrónico con un enlace de activación. El último campo es opcional


🠉

Obtener argumentos de la línea de comandos como vector de wstring en C++

Si necesita hacer esto, consulta este pequeño artículo que viene con código completamente probado y ejemplos de cómo usarlo

Acabo de comenzar este nuevo pequeño proyecto mío en C++, y como la aplicación necesita leer y analizar los argumentos que se le pasan en la línea de comandos, pensé que sería mejor tenerlos a todos almacenados dentro de un arreglo std::vector. En general suelo activar el soporte de caracteres anchos en mis aplicaciones, para asegurarme de que se pueden usar caracteres Unicode, por lo que el arreglo almacenará los argumentos como objetos std::wstring.

Probé la solución en Windows (consola y programas GUI Win32) y Linux (consola). Encontrarás más información en los comentarios. Espero que os sea de utilidad. Aquí está el código. Lo tengo en un fichero de cabecera que incluyo en mis fuentes CPP.


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

// APLICACIONES DE CONSOLA DE WINDOWS
#if _CONSOLE

/**
 * Obtén argumentos de línea de comandos como vector en la aplicación de consola de Windows
 *
 * Obtén el número de argumentos y el puntero del punto de entrada wmain
 */
std::vector<std::wstring> cmdArguments(int argc, wchar_t* argv[]) {
    return std::vector<std::wstring>(argv, argv + argc);
}

// APLICACIONES CON INTERFAZ GRÁFICA DE WINDOWS
#elif _WIN32

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

/**
 * Obtén argumentos de línea de comandos como vector en una aplicación de Windows
 *
 * Opcionalmente, puedes pasar un puntero a la línea de comandos
 */
std::vector<std::wstring> cmdArguments(LPWSTR lpCmdLine = NULL) {
    std::vector<std::wstring> arguments;
    LPWSTR* szArglist;
    int nArgs;

    // consigue un puntero a los argumentos de la línea de comandos mediante la API de Shell
    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

/**
 * Obtén argumentos de línea de comandos como vector en la aplicación de consola de Windows/Linux
 *
 * Obtén el número de argumentos y el puntero del punto de entrada principal
 */
std::vector<std::wstring> cmdArguments(int argc, char* argv[]) {
    std::vector<std::wstring> arguments;

    for (auto i = 0; i < argc; i++) {
        // número de caracteres multibyte en el origen y el resultado
        size_t mbslen = strlen(argv[i]) + 1;
        size_t mbolen;
        // puntero al argumento de caracteres anchos convertido
        wchar_t* wcs = new wchar_t[mbslen];
#ifdef _WIN32
        // en Windows podemos usar mbstowcs_s para convertir char a wchar_t antes de agregar como wstring al arreglo
        mbstowcs_s(&mbolen, wcs, mbslen, argv[i], mbslen - 1);
#else
        // en Linux, calcula la longitud necesaria para contener argv[i] convertido a una cadena de caracteres anchos
        mbslen = mbstowcs(NULL, argv[i], 0);
        if (mbslen == (size_t) - 1) {
            perror("mbstowcs");
            exit(EXIT_FAILURE);
        }

        // asigna una cadena de caracteres anchos del tamaño deseado + 1 para el carácter ancho nulo de final
        if (calloc(mbslen + 1, sizeof(*wcs)) == NULL) {
            perror("calloc");
            exit(EXIT_FAILURE);
        }

        // convierte la cadena de caracteres multibyte en argv[i] en una cadena de caracteres anchos
        if (mbstowcs(wcs, argv[i], mbslen + 1) == (size_t) - 1) {
            perror("mbstowcs");
            exit(EXIT_FAILURE);
        }
#endif
        arguments.push_back(std::wstring(wcs));
    }

    return arguments;
}

A continuación se muestra un ejemplo de cómo lo uso dentro de un proyecto Win32 (especificar un puntero a la línea de comandos es opcional)


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;
}

En Linux, también establezco la configuración regional para poder usar caracteres especiales


#include <iostream>

#include <locale.h>

#include "arguments.h"

int main(int argc, char* argv[])
{
    // establece configuración regional para permitir el uso de caracteres UNICODE en la línea de comandos
    if (setlocale(LC_ALL, "es_ES.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;
}

Dejadme saber si tenéis algún problema o si queréis sugerir algunas mejoras.