Search code examples
c++winapitcharlptstrstdcopy

Using std::copy with LPTSTR type argument


I'm using Win32 API and I need to copy arguments captured with argv, the problem is, the code has to be compliant with both ASCII and UNICODE and that's a problem with C/C++ in Windows.

Beyond that I must use, when possible, C++ instead of C, so I'm using std::copy to copy arguments of type LPTSTR (or TCHAR*), I could use _tcscpy_s but as I said, it must be C++ whenever possible.

Note: I can't use std::wstring or std::string because these must be passed to CreateProcess() and the argument must be of type TCHAR* so it can convert it to LPTSTR or LPWSTR deppending on the encoding.

Here is a console executable minimal reproducible example:

#include <windows.h>
#include <tchar.h>
#include <corecrt_io.h>
#include <fcntl.h>
#include <iostream>

#ifdef UNICODE
    #define tcout wcout
    #define tcin wcin
#else
    #define tcout cout
    #define tcin cin
#endif
int _tmain(int argc, LPTSTR argv[])
{
    using std::copy;
    using std::tcout;
    using std::tcin;

    constexpr int size = 1024;
    TCHAR fileName[size];

#ifdef UNICODE
    _setmode(_fileno(stdin), _O_WTEXT);
    _setmode(_fileno(stdout), _O_WTEXT);
#endif

    if (argc > 1)
    {
        copy(&argv[1][0], &argv[1][1023], fileName); //is this copy ok?
    }
    else
    {
        tcout << "Program name: ";
        tcin >> fileName;
    }
    tcout << fileName;
}

My question is:

Is the code safe, and/or is there a better alternative (preferably with C++) to this?

(Not only the copy part, but whole idea)


Solution

  • You should use std::basic_string:

    using tstring = std::basic_string<TCHAR>;
    

    It handles all the copying itself. Whenever you need to talk to some C API, use str.c_str() for a const pointer and str.data() (after C++17) or &str[0] (pre C++17) for a non-const pointer.

    #include <windows.h>
    #include <tchar.h>
    #include <corecrt_io.h>
    #include <fcntl.h>
    #include <iostream>
    #include <string>
    
    using tstring = std::basic_string<TCHAR>;
    
    #ifdef UNICODE
    static auto& tcout = std::wcout;
    static auto& tcin = std::wcin;
    #else
    static auto& tcout = std::cout;
    static auto& tcin = std::cin;
    #endif
    
    int _tmain(int argc, LPTSTR argv[])
    {
        tstring fileName;
    
        if (argc > 1)
        {
            fileName = argv[1];
        }
        else
        {
            tcout << _T("Program name: ");
            tcin >> fileName;
        }
        tcout << fileName;
    
        return 0;
    }