Trying to CreateDirectory, getting char* to LPCWSTR error, willing to try another function
Asked Answered
T

3

9

I've tried Googling this, and there are so many answers based on various specific situations that frankly I'm more stuck than I was when I started.

The facts are these:

  • Language: C/C++
  • OS: Windows
  • IDE: Visual Studio 2005
  • I'm trying to create a directory from a function in my program, using CreateDirectory (after a #include of windows.h).
  • Supposedly, the first parameter (a path) should be a char*. However, when I try to compile, I get the following error: error C2664: 'CreateDirectoryW' : cannot convert parameter 1 from 'char *' to 'LPCWSTR'
  • What I've read is that I have some sort of issue between UNICODE and ANSI. The solutions vary wildly and I'm afraid of breaking something important, or doing something very stupid.
  • I am perfectly willing to use any other method of creating a new directory, if one exists without me having to find some other library.
  • I only minored in comp sci, and frankly I have no idea why it's so easy to open, close, edit, and otherwise access files through stdio, but doing anything with directories (specifically making them and finding out if they exist) is a wild goose chase through the streets of the Internet.

Please help me, either to fix the current attempt at CreateDirectory or to use something else to create a directory.

Thank you!

Transplant answered 10/8, 2011 at 18:57 Comment(0)
S
12

This is completely crazy. Microsoft have mechanisms to support both ANSI (as they call it) and UNICODE from the same code, because Windows 95/8/Me were ANSI operating systems and NT/XP/Vista etc are UNICODE operating systems. So if you really want to you can write one set of code that supports both systems and just recompile for each system.

But who is interested in Windows 95/98/Me any more? Yet this stuff carries on confusing newbies.

Here's the beef, there is no function called CreateDirectory in Windows, there a UNICODE function called CreateDirectoryW and an ANSI function called CreateDirectoryA. The macro CreateDirectory is converted to CreateDirectoryW or CreateDirectoryA depending on which compiler options you have defined. If you end up using CreateDirectoryW (as you evidentally did) the you must pass a Unicode string to it, if you use CreateDirectoryA then you pass a normal ASCII string.

The simplest thing in your case would be to forget about all this and just call CreateDirectoryA directly.

CreateDirectoryA("c:\\some\\directory", NULL);

Why it is so hard to create a directory in C++? I would guess that because back in the 70s when C was new, not every operating system had directories so this stuff never made it into the language standard.

Sublimity answered 10/8, 2011 at 19:12 Comment(2)
Will it break anything to use CreateDirectoryA on Windows XP? Or is that acceptable practice?Transplant
Behind the scenes XP will convert your ANSI strings to Unicode, because it only knows Unicode. If you start using unusual characters there might be a few surprises but even then probably not. For the full story you'd probably need to talk to a real expert (which I'm not).Sublimity
E
2

VS2005 is Unicode by default, and you should better keep it that way. Might save you a lot of issues in the future. In Unicode builds CreateDirectory (and other Win32 functions) expect wchar_t strings, and not regular char. Making string literals wchar_t's is simple -

L"Some string" to make it always Unicode, or _T("Some string") to make it configuration dependent.

I don't know how exactly are you calling CreateDirectory, but if converting the string to Unicode is too much trouble, you can use the ANSI version directly - CreateDirectoryA. Post some code if you want a more detailed answer.

Enthusiasm answered 10/8, 2011 at 19:4 Comment(7)
char* path = ("%s%s",TOP_EXPORT_PATH,TOP_EXPORT_FOLDER); CreateDirectory(path,NULL); //this is what I was tryingTransplant
That code doesn't look right, but in any case you have an ASCII string so just call CreateDirectoryA. Simple.Sublimity
@Rae, use wchar_t* path = <the path>; CreateDirectory(path, NULL);. I've used <the path> because the way you're creating the string is wrong. You might just want to use std::wstring for that, or springtf if you're limited to C, but that's a different issue than the original question...Enthusiasm
Uh oh. Right how? The TOP_EXPORT_PATH and TOP_EXPORT_FOLDER are #defines at the beginning of the program. I'll try CreateDirectoryA.Transplant
Well it looks like you think it's going to concatenate the TOP_EXPORT_PATH and TOP_EXPORT_FOLDER strings. I promise you it's not going to do that. Print out the value of path to check what you've got.Sublimity
@Rae, you still have to take care of the way you create the string, but if they're defined as simple string literals (#define PATH "somepath"), you can use char* path = TOP_EXPORT_PATH TOP_EXPORT_FOLDER; CreateDirectoryA(path);. Not the prettiest code, but it will work. But to be honest, hardcoding a path doesn't smell right...Enthusiasm
Alright, it's looking like CreateDirectoryA is functional for the moment. Thanks guys!Transplant
M
1

Try using #undef UNICODE before including windows.h. Unless you are really coding Unicode, but that's another story.

With the new Visual Studio's, windows.h defaults to Unicode version, so #undef UNICODE will give you back the ANSI version. It will show in the stack trace -- you will call CreateDirectoryA rather than CreateDirectoryW.

Or, just call CreateDirectoryA directly, but somehow I feel that is not a best practice.

Matrimonial answered 10/8, 2011 at 18:59 Comment(7)
How will I know if I really am using Unicode? Or is that highly unusual/deliberately applied?Transplant
You are Unicode by default. The question is did you want to use Unicode? That is entirely up to you :-) Also, eran's suggestion to use _T() macro is also good.Matrimonial
@Andrew, I wouldn't #undef UNICODE. Firstly, you might get mismatches with other compilation units that don't see that undef. Secondly, there's also a _UNICODE define (can't remember which does what at the moment...). Lastly, these kind of features should better be set using the regular configuration.Enthusiasm
The purpose of the _T macro is to enable you to support both unicode and ansi. Unless you want to do that what is the point of using it? Just say what you mean and use the right kind of strings.Sublimity
@eran, you bring up some very good points and I agree with you. I think simplest solution then is a toss up between using CreateDirectoryA versus just using wchar_t all across the board.Matrimonial
@john, I really see no good reason to create ANSI builds these days, nor to make the code portable in case one day you'd like to do so. The _T() is extremely common is VS-based code, so it's worth at least being familiar with it. But you're right that new code can just go ahead and use Unicode explicitly.Enthusiasm
@eran: Agreed, but so much code is out there using _T and _tcscpy etc, and it gets copied without the coder really understanding what they're doing.Sublimity

© 2022 - 2024 — McMap. All rights reserved.