0

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)

anastaciu
  • 23,467
  • 7
  • 28
  • 53
  • 3
    Unless you need to support Windows 9x there is absolutely, positively no reason to use the generic-text types. Just use `wchar_t` and the Unicode variants of the API and you won't run into an issue that doesn't exist in practice. – IInspectable Mar 03 '21 at 14:37
  • 1
    What I do in my projects, I define a TSTRING which is either `std::string` or `std::wstring`. Then the usage is the same as with `TCHAR`. – Devolus Mar 03 '21 at 14:39
  • 2
    It's pretty opaque what is and isn't negotiable here. You say that you cannot use C++' string types, for some odd reason. Now could you then use `using tstring = std::basic_string;`? – IInspectable Mar 03 '21 at 14:43
  • 2
    Given a `std::basic_string`, `c_str()` or `data()` return a pointer to a `TCHAR` array. It's getting ever more opaque, what *problem* you are **really** trying to solve. – IInspectable Mar 03 '21 at 14:49
  • Just define tstring as either string or wstring depending on whether you're compiling in unicode mode... that's how TCHAR and LPTSTR work – user253751 Mar 03 '21 at 14:52
  • 1
    I have not ever had an issue with passing the return values of `c_str()` or `data()` respectively into **any** Windows API. If you are running into issues, you are clearly doing it wrong. – IInspectable Mar 03 '21 at 14:55
  • You can use something like: `(tcslen(argv[1]) + 1) * sizeof(TCHAR)`. But I think the `tstring` class approach suggested by IInspectable is far better. – Adrian Mole Mar 03 '21 at 15:02
  • By not using a tool that's not meant to be used. `std::copy` is for times when you know the size of the sequence. This is not a case where you do. Use `std::strncpy` instead. Or, alternatively, `tstring arg { argv[0] };`. But then, you have already given up on using C++, so the clean solution isn't. – IInspectable Mar 03 '21 at 15:02
  • @IInspectable, you were right, sorry about that, I was just too invested in my solution to really truly analyze your comments, anyway, thaks. – anastaciu Mar 03 '21 at 15:53

1 Answers1

2

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;
}
Aykhan Hagverdili
  • 28,141
  • 6
  • 41
  • 93
  • 1
    It seems to be working allright with all my functions, passing it as `&filename[0]`, thanks man, this winAPI thing is killing me. – anastaciu Mar 03 '21 at 15:23
  • 2
    @ana This has nothing to do with the Windows API. It is strictly related to the C Runtime, and the arbitrary choice to support a use case that no one uses anymore. – IInspectable Mar 03 '21 at 19:39