Search code examples
c++constantscreatefilelpwstr

C++ #define LPWSTR?


Ok, i have a problem in my program. I made a question about it before, but no one really understood my problem, so im trying a new approach to it this time.

If you're curious this is my problem:

My program takes arguments in the form of char* argv[] , and im having trouble making a pointer to whatever is on argv[1] using LPWSTR, since it only point to const wchar_t* objects.

This is a new thing im trying to solve my problem (i've tried multiple things but i need to know how to do what im thinking, or if its possible)

Basicly, my idea is to #define some sort of function that take whatever is on argv[1] and defines a const wchar_t* with that value.

Something like this:

#define path       ((const wchar_t*)argv[1])

Im not sure that is the correct way (or even if this is possible) to do what i want to do...

If you have anny better way of solving me problem, please (please) tell me how to and help me out, i've been thinking about this so a really long time!

Explanation of my program:

Im making a program that recieves arguments. The arguments are the name of the drives, for example "F:". It then uses the function CreateFile with the drive letter. If you go here , and see the first parameter of the function, i think you will see what i mean.... the problem is that for me to make a LPWSTR, i would need a const wchat_t* object.... I hope my question is clear this time, last time people didnt really understand what i was trying to do.

Regardless, thanks! EDIT 1: here are solve lines of my program (this is how i have to do for it to work, without arguments) (i used a fixed value in here)

int main()
{

HANDLE device;

device = CreateFile(L"\\\\.\\F:",    // Drive to open
    GENERIC_READ | GENERIC_WRITE,       // Access mode
    FILE_SHARE_READ | FILE_SHARE_WRITE, // Share Mode
    NULL,                   // Security Descriptor
    OPEN_EXISTING,          // How to create
    0,                      // File attributes
    NULL);  
}

This is with arguments (doesn't work)

int main(int argc, char* argv[])
{

HANDLE device;

device = CreateFile(argv[1],    // Drive to open
    GENERIC_READ | GENERIC_WRITE,       // Access mode
    FILE_SHARE_READ | FILE_SHARE_WRITE, // Share Mode
    NULL,                   // Security Descriptor
    OPEN_EXISTING,          // How to create
    0,                      // File attributes
    NULL);                  // Handle to template
}

^ this shows what im trying to do

EDIT 2: i changed the CreateFile to CreateFileA , and this is the eroor codes it gives me (the drive D is a usb, its not a hard drive) erros

So unless im typing the wrong way to type a path, it always gives me erros. I think ill try another way to solve the problem, or if someone knows why thoes errors are happening, please tell!


Solution

  • EDIT 2: i changed the CreateFile to CreateFileA , and this is the eroor codes it gives me (the drive D is a usb, its not a hard drive)

    This is a completely different question, and has nothing to do with wchar_t.

    In your first snipped you passed "\\\\.\\F:" (AKA \\.\F: once we remove the C escaping); in all your tries from the command line you never provided this path, but respectively:

    • D - so it tried to open a file named D in the current directory, and it didn't find it (error 2, aka ERROR_FILE_NOT_FOUND);
    • D:\ - the root directory, which cannot be opened with CreateFile (error 3, aka ERROR_PATH_NOT_FOUND),
    • D: - the current directory on the drive D:, which again cannot be opened with CreateFile (error 5, aka ERROR_ACCESS_DENIED);
    • \D: - a file named "D:" in the root of the current drive, which cannot be created given that D: is not a valid file name (error 123, aka ERROR_INVALID_NAME).

    To open a drive as a device, you must use the \\.\X: path (where X is the drive letter); you cannot just throw whatever floats in your mind and hope that it'll work. Call your program from the command line passing "\\.\D:" and it'll work fine.

    Of course if you want to keep it simpler for the user you can accept just the drive letter on the command line and write some code to create the string required by CreateFile based on it.

    if(argc<1) {
        printf("Not enough arguments\n");
        return 1;
    }
    const char *drive = argv[1];
    char d = drive[0];
    // accept both `d` and `d:` as argument for the drive
    if(!((d>='a' && d<='z') || (d>='A' && d<='Z')) ||
         (drive[1]!=0 && drive[1]!=':') ||
          drive[2]!=0) {
        printf("Invalid drive specifier: `%s`\n", drive);
        return 2;
    }
    char path[]="\\\\.\\X:";
    path[4] = d;
    // now you can use path as argument to CreateFileA
    

    What follows was the original answer, which is still valid but it addresses a completely different problem, unrelated to the actual problem OP is experiencing

    You cannot make LPWSTR point to a char *, especially not by brutally casting the pointer - casting a pointer just makes the compiler shut up, it doesn't change the fact that what you are pointing at is not a wchar_t string. If you want to pass a char * to a function expecting a wchar_t * you have to perform an actual conversion of the pointed data.

    Now, you have several possible solutions:

    • you can use _wmain and receive your command line arguments directly as wide characters;
    • you can convert your local-encoding strings to UTF-16 strings by using a function such as the MultiByteToWideChar; this can be encapsulated in a function returning a std::wstring;
    • you can just invoke the ANSI version of the API and let it deal with it; almost all Win32 APIs have both an ANSI and Unicode version, suffixed with A and W (CreateFile is just a macro that expands to CreateFileA or CreateFileW depending on the _UNICODE macro). So, you can use CreateFileA and pass it your string as-is.

    The last two solutions are not great because using local-encoding strings as command line arguments precludes your program from opening files using arbitrary Unicode characters. OTOH, using wchar_t almost everywhere is quite a dread, since they "infect" virtually every string-processing corner of your application. The correct (IMHO) way out is to use UTF-8 everywhere, and convert on the fly when talking to the operating systems; see here for details.