I have a function which takes two CHAR* as input viz. int _stdcall FileTrans(char* InFile, char* OutFile) in a DLL project.
In the function I'm just calling CopyFile(InFile, OutFile, false); after some process (not related to the files). But it says that it needs both inputs as LPCWSTR. I Googled it but couldn't find anything very interesting.
Like all Windows API functions that accept a string parameter, there are actually two variants of the CopyFile function:
CopyFileA is the ANSI version, which takes narrow (non-Unicode) strings in the system's default character set. Basically, it accepts parameters of type const char*, but the Windows headers use the typedef LPCSTR for this.
CopyFileW is the wide version, which takes Unicode strings. In order to do this, it accepts parameters of type w_char*, but the Windows headers use the typedef LPCWSTR for this (note the additional W in the typedef).
Then, depending on whether the UNICODE preprocessor macro is defined for your project (either in your code before you include the Windows headers, or in your project's properties in Visual Studio), the Windows headers define the unadorned CopyFile as either CopyFileA or CopyFileW. Naturally, if UNICODE is defined, CopyFile will be defined as the Unicode version CopyFileW. Otherwise, it will be defined as CopyFileA. The idea is that the call to the general CopyFile function is automatically resolved at compile time to the correct variant.
Of course, now that you understand all of that, you can mostly forget about it. In modern Windows programming, there is absolutely no reason to call the old ANSI versions of functions or to deal with narrow strings at all. Forget that char* can even be used as a string type—those strings are dead to you. The only strings you're going to be using from now on are Unicode strings, composed of wchar_t characters. Thus the UNICODE symbol should always be defined for your code, and you should only use the W version of Windows API functions.
Looking again at the prototype for the CopyFileW function (the same one you get when you call CopyFile with UNICODE defined), we see:
BOOL WINAPI CopyFile(LPCWSTR lpExistingFileName,
LPCWSTR lpNewFileName,
BOOL bFailIfExists);
Recall that you learned above that LPCWSTR is just a typedef synonym for const wchar_t*, a C-style string that is composed of wide characters. You already know why the parameters are marked const: because the function doesn't modify those values.
And because you also learned above that these are the only types of strings that you should be using anymore, the next step is to modify your FileTrans function to accept wide strings (and make them const if it's not going to modify them):
int _stdcall FileTrans(const wchar_t* InFile, const wchar_t* OutFile);
Now, from inside of FileTrans, you can call CopyFile without any problems because you have the right type of strings.
But a bit of free, extra advice: never use raw C-style strings in C++. Always use the C++ string class, defined in the std namespace by the <string> header.
There are two common variants of this class, std::string and std::wstring. As before, the w refers to wide strings, which are the only type you want to use in Windows. So std::wstring is your new replacement for CHAR* throughout your code base.
Change your declaration of the FileTrans function to look like this:
#include <string>
// ...some other stuff...
int __stdcall FileTrans(const std::wstring& InFile, const std::wstring& OutFile);
Note that I've changed your original CHAR* parameters to constant references to std::wstring objects. Constant references work well here, since you're not going to be changing either of those values inside of the function.
If you're unclear on what constant means, how to use references, or how class types generally work in C++, please consult your favorite C++ book)—this is required knowledge for all C++ programmers. Remember that C++ is not the same language as C and therefore the same idioms do not apply. In many cases, there is a better way to do things, and this is certainly an example of such a case.
Related
I'm learning to write and use DLLs and this is my first attempt at exporting a function from my dll. It works, but this line is what gave me trouble and what I've been able to find regarding the TEXT cast for UNICODE and ANSI I think I need some guidance. As far as I can find this question has not been asked elsewhere on the site so I apologize if anyone finds what I couldn't.
HINSTANCE hInstLibrary = LoadLibrary("MyDLL.dll");
My initial usage, from a short tutorial on explicit linking gives E0167 and C2664 errors regarding LPCWSTR type
HINSTANCE hInstLibrary = LoadLibrary(TEXT("MyDLL.dll"));
Casting the string to TEXT solves the problem, though I'm not sure why and would like to know
HINSTANCE hInstLibrary = LoadLibraryA("MyDLL.dll");
The line I decided to use in the working example. LoadLibraryA() expands LoadLibrary to accept ANSI rather than Wide, which may be the root of my misunderstanding. Why is this necessary when most examples I find show LoadLibrary("NameOfDLL.dll")?
Why does the string not satisfy the standard LoadLibrary() call?
LoadLibrary() is a preprocessor macro. It maps to either LoadLibraryW() or LoadLibraryA() depending on whether UNICODE is defined or not, respectively. LoadLibraryW() takes a const wchar_t* string as input, while LoadLibraryA() takes a const char * string instead.
The string literal "MyDLL.dll" is a const char[10], which decays into a const char *. If UNICODE is defined, LoadLibrary("MyDLL.dll") will fail to compile, as you cannot pass a const char * where a const wchar_t * is expected.
TEXT() is also a preprocessor macro. If UNICODE is defined, it appends an L prefix to the specified literal making the literal use wchar_t, otherwise no prefix is added and the literal uses char instead.
Thus, if UNICODE is defined, then LoadLibrary(TEXT("MyDLL.dll")) is compiled as LoadLibraryW(L"MyDLL.dll"), otherwise it is compiled as LoadLibraryA("MyDLL.dll") instead.
A majority of Win32 APIs that deal with textual data have similar A and W versions, and corresponding UNICODE-aware preprocessor macros. So, when using character/string literals with these APIs, you should always use the TEXT() macro. Otherwise, just use the A and W APIs directly as needed, depending on the type of textual data you are working with.
I'm learning to write and use DLLs and this is my first attempt at exporting a function from my dll. It works, but this line is what gave me trouble and what I've been able to find regarding the TEXT cast for UNICODE and ANSI I think I need some guidance. As far as I can find this question has not been asked elsewhere on the site so I apologize if anyone finds what I couldn't.
HINSTANCE hInstLibrary = LoadLibrary("MyDLL.dll");
My initial usage, from a short tutorial on explicit linking gives E0167 and C2664 errors regarding LPCWSTR type
HINSTANCE hInstLibrary = LoadLibrary(TEXT("MyDLL.dll"));
Casting the string to TEXT solves the problem, though I'm not sure why and would like to know
HINSTANCE hInstLibrary = LoadLibraryA("MyDLL.dll");
The line I decided to use in the working example. LoadLibraryA() expands LoadLibrary to accept ANSI rather than Wide, which may be the root of my misunderstanding. Why is this necessary when most examples I find show LoadLibrary("NameOfDLL.dll")?
Why does the string not satisfy the standard LoadLibrary() call?
LoadLibrary() is a preprocessor macro. It maps to either LoadLibraryW() or LoadLibraryA() depending on whether UNICODE is defined or not, respectively. LoadLibraryW() takes a const wchar_t* string as input, while LoadLibraryA() takes a const char * string instead.
The string literal "MyDLL.dll" is a const char[10], which decays into a const char *. If UNICODE is defined, LoadLibrary("MyDLL.dll") will fail to compile, as you cannot pass a const char * where a const wchar_t * is expected.
TEXT() is also a preprocessor macro. If UNICODE is defined, it appends an L prefix to the specified literal making the literal use wchar_t, otherwise no prefix is added and the literal uses char instead.
Thus, if UNICODE is defined, then LoadLibrary(TEXT("MyDLL.dll")) is compiled as LoadLibraryW(L"MyDLL.dll"), otherwise it is compiled as LoadLibraryA("MyDLL.dll") instead.
A majority of Win32 APIs that deal with textual data have similar A and W versions, and corresponding UNICODE-aware preprocessor macros. So, when using character/string literals with these APIs, you should always use the TEXT() macro. Otherwise, just use the A and W APIs directly as needed, depending on the type of textual data you are working with.
Unfortunately I have been tasked with compiling an old C++ DLL so that it will work on Win 7 64 Bit machines. I have zero C++ experience. I have muddled through other issues, but this one has stumped me and i have not found a solution elsewhere. The following code is throwing a C2440 compiler error.
The error: "error C2440: '' : cannot convert from 'std::_String_iterator<_Elem,_Traits,_Alloc>' to 'LPCTSTR'"
the code:
#include "StdAfx.h"
#include "Antenna.h"
#include "MyTypes.h"
#include "objbase.h"
const TCHAR* CAntenna::GetMdbPath()
{
if(m_MdbPath.size() <= 0) return NULL;
return LPCTSTR(m_MdbPath.begin());
}
"m_MdbPath" is defined in the Antenna.h file as
string m_MdbPath;
Any assistance or guidance someone could provide would be extremely helpful. Thank you in advance. I am happy to provide additional details on the code if needed.
std::string has a .c_str() member function that should accomplish what you're looking for. It will return a const char* (const wchar_t* with std::wstring). std::string also has an empty() member function and I recommend using nullptr instead of the NULL macro.
const TCHAR* CAntenna::GetMdbPath()
{
if(m_MdbPath.empty()) return nullptr;
return m_MdbPath.c_str();
}
The solution:
const TCHAR* CAntenna::GetMdbPath()
{
if(m_MdbPath.size() <= 0) return NULL;
return m_MdbPath.c_str();
}
will work if you can guarantee that your program is using the MBCS character set option (or if the Character Set option is set to Not Setif you're using Visual Studio), since a std::string character type will be the same as the TCHAR type.
However, if your build is UNICODE, then a std::string character type will not be the same as the TCHAR type, since TCHAR is defined as a wide character, not a single-byte character. Thus returning c_str() will give a compiler error. So your original code, plus the tentative fix of returning std::string::c_str() may work, but it is technically wrong with respect to the types being used.
You should be working directly with the types presented to your function -- if the type is TCHAR, then you should be using TCHAR based types, but again, a TCHAR is a type that changes definition depending on the build type.
As a matter of fact, the code will fail miserably at runtime if it were a UNICODE build and to fix the compiler error(s), you casted to an LPCTSTR to keep the compiler quiet. You cannot cast a string pointer type to another string pointer type. Casting is not converting, and merely casting will not convert a narrow string into a wide string and vice-versa. You will wind up with at the least, odd characters being used or displayed, and worse, a program with random weird behavior and crashes.
In addition to this you never know when you really will need to build a UNICODE version of the DLL, since MBCS builds are becoming more rare. Going by your original post, the DLL's usage of TCHAR indicates that yes, this DLL may (or even has) been built for UNICODE.
There are a few alternate solutions that you can use. Note that some of these solutions may require you to make additional coding changes beyond the string types. If you're using C++ stream objects, they may also need to be changed to match the character type (for example, std::ostream and std::wostream)
Solution 1. Use a typedef, where the string type to use depends on the build type.
For example:
#ifdef UNICODE
typedef std::wstring tchar_string;
#else
typedef std::string tchar_string;
#endif
and then use tchar_string instead of std::string. Switching between MBCS and UNICODE builds will work without having to cast strin types, but requires coding changes.
Solution 2. Use a typedef to use TCHAR as the underlying character type in the std::basic_string template.
For example:
typedef std::basic_string<TCHAR> tchar_string;
And use tchar_string in your application. You get the same public interface as std::(w)string and this allows you to switch between MBCS and UNICODE build seamlessly (with respect to the string types).
Solution 3. Go with UNICODE builds and drop MBCS builds altogether
If the application you're building is a new one, then UNICODE is the default option (at least in Visual Studio) when creating a new application. Then all you really need to do is use std::wstring.
This is sort of the opposite of the original solution given of making sure you use MBCS, but IMO this solution makes more sense if there is only going to be one build type. MBCS builds are becoming more rare, and basically should be used only for legacy apps.
since I can get hands on the new RAD Studio Xe4 I thought I'd give it a try.
Unfortunatly, I am not so experienced with C++ and therefore I was wondering why the Code that works perfectly fine in VC++ doesn't work at all in C++ Builder.
Most of the problems are converting different var-types.
For example :
std::string Test = " ";
GetFileAttributes(Test.c_str());
works in VC++ but in C++ Builder it won't compile, telling me "E2034 Cannot convert 'const char *' to 'wchar_t *'.
Am I missing something? What is the reason that doesn't work the same on all compilers the same?
Thanks
Welcome to Windows Unicode/ASCII hell.
The function
GetFileAttributes
is actually a macro defined to either GetFileAttributesA or GetFileAttributesW depending on if you have _UNICODE (or was it UNICODE, or both?) defined when you include the Windows headers. The *A variants take char* and related arguments, the *W functions take wchar_t* and related arguments.
I suggest calling only the wide *W variants directly in new code. This would mean switching to std::wstring for Windows only code and some well-thought out design choices for a cross-platform application.
Your C++ Builder config is set to use UNICODE character set, which means that Win32 APIs are resolved to their wide character versions. Therefore you need to use wide char strings in your C++ code. If you would set your VS config to use UNICODE, you would get the same error.
You can try this:
// wstring = basic_string<wchar_t>
// _T macro ensures that the specified literal is a wide char literal
std::wstring Test = _T(" ");
GetFileAttributes(Test.c_str()); // c_str now returns const wchar_t*, not const char*
See more details about _T/_TEXT macros here: http://docwiki.embarcadero.com/RADStudio/XE3/en/TCHAR_Mapping
You have defined _UNICODE and/or UNICODE in Builder and not defined it in VC.
Most Windows APIs come in 2 flavours the ANSI flavour and the UNICODE flavour.
For, when you call SetWindowText, there really is no SetWindowText functions. Instead there are 2 different functions
- SetWindowTextA which takes an ANSI string
and
- SetWindowTextW which takes a UNICODE string.
If your program is compiled with /DUNICODE /D_UNICODE, SetWindowText maps to SetWindowTextWwhich expects aconst wchar_t *`.
If your program is compiled without these macros defined, it maps to SetWindowTextA which takes a const char *.
The windows headers typically do something like this to make this happen.
#ifdef UNICODE
#define SetWindowText SetWindowTextW
#else
#define SetWindowText SetWindowTextA
#endif
Likewise, there are 2 GetFileAttributes.
DWORD WINAPI GetFileAttributesA(LPCSTR lpFileName);
DWORD WINAPI GetFileAttributesW(LPCWSTR lpFileName);
In VC, you haven't defined UNICODE/_UNICODE & hence you are able to pass string::c_str() which returns a char *.
In Builder, you probably have defined UNICODE/_UNICODE & it expects a wchar_t *.
You may not have done this UNICODE/_UNICODE thing explicitly - may be the IDE is doing it for you - so check the options in the IDE.
You have many ways of fixing this
find the UNICODE/_UNICODE option in the IDE and disable it.
or
use std::w_string - then c_str() will return a wchar_t *
or
Call GetFileAttributesA directly instead of GetFileAttributes - you will need to do this for every other Windows API which comes with these 2 variants.
I want to save file in temporary folder which can be easy found when type at run by putting "%temp%", but don't know how to navigate to them from c++.
I try using function like "GetTempPathA" or "GetTempFileNameA()" but they don't return char value. For my purpose I use "SaveToFile" method from "TResourceStream" and need UnicodeString, how to find those information?
No, neither GetTempPath nor GetTempFileName return a char value. In general, C functions do not ever return strings. Instead, you pass in a string buffer and the length of that string buffer, and the function fills in the string buffer with the requested string.
For example, to call GetTempPath, you would write the following code:
TCHAR szTempPathBuffer[MAX_PATH];
GetTempPath(MAX_PATH, // length of the buffer
szTempPathBuffer); // the buffer to fill
szTempPathBuffer will contain the path to the temporary directory.
Do note that you should probably not be explicitly calling the ANSI functions (those with an A suffix). Windows has been fully Unicode for over a decade now. Either use the macros defined by the Windows headers, which automatically resolve to the correct version of the function depending on whether _UNICODE is defined, or explicitly call the Unicode versions (those with a W suffix).
Since you're calling the ANSI versions, you're getting an ANSI string (composed of char values), rather than a Unicode string (composed of wchar_t values), which is presumably what the SaveToFile method that you're trying to call expects.
One can use also getenv(), but it's deprecated and consider as "non-safe":
#pragma warning(disable:4996) //disable getenv warning
char* p = getenv("TEMP");
std::string path(p);