Whats wrong with TCHAR?
-
I rly can't understand that obsolete old complicated stuff, so can anybody please clarify me.
I have a TCHAR declaration in my code, and as i understand from google, it is string type for microsoft programming:
TCHAR szProcessName[MAX_PATH] = TEXT("<unknown>");
And all i want - simply compare that initial szProcessName with that what it now, like this:
TCHAR initial[MAX_PATH] = TEXT("<unknown>"); if(szProcessName != initial) { //Do stuff }
It compiles, but comparison dont work! And i can't understand why and it freak me out. Such stuff perfectly always worked with strings, QStrings, chars etc. but what wrong with that damn type...
P.S. full code here if someone think he need it:
int main() { EnumWindows(EnumWindowsProc, NULL); cin.get(); return 0; } BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam) { TCHAR szProcessName[MAX_PATH] = TEXT("<unknown>"); TCHAR szProcessNameBefore[MAX_PATH] = TEXT("<unknown>"); DWORD objID; GetWindowThreadProcessId(hwnd, &objID); HANDLE someHandle = OpenProcess(PROCESS_ALL_ACCESS, false, objID); if(NULL != someHandle) { HMODULE hMod; DWORD cbNeeded; if(EnumProcessModules(someHandle, &hMod, sizeof(hMod), &cbNeeded)) { GetModuleBaseName(someHandle, hMod, szProcessName, sizeof(szProcessName) / sizeof(TCHAR)); } } if(szProcessName != szProcessNameBefore) { _tprintf(TEXT("%s (PID: %u)\n"), szProcessName, objID); } return TRUE; }
P.P.S how can i use spoilers on this forum?)
-
szProcessName
andinitial
are pointers, or rather arrays that decay to pointers. So you're comparing addresses, not what they point to. To compare contents of the strings you need for example strcmp() if you're working with ANSI or _tcscmp() since you're using the "universal" TCHAR type..About the TCHAR and WinAPI stuff - see this old thread where I try to explain it .
-
@Engelard
In addition tom what @Chris-Kawa has (correctly) said.I don't know whether this will help or not, but if you do not want to start using
strcmp
/_tcscmp()
--- and other Windowsy things like_tprintf()
&TEXT()
--- you could create (temporary) QtQString
s out of yourTCHAR []
/LPTSTR
s. Then you can useQString
stuff, likeQString
's==
which does compare content:if (QString::fromWCharArray(szProcessName) != QString::fromWCharArray(szProcessNameBefore))
It will be marginally slower than
strcmp
/_tcscmp()
, so I wouldn't do it a billion times, but for your case you might prefer it so that you deal withQString
which you understand better. -
@JonB The thing is you can't just arbitrarily use
QString::fromWCharArray
withTCHAR
, as it's either a WCHAR or CHAR, depending on theUNICODE
define, so to make it correct you would have to do something like#ifdef UNICODE if (QString::fromWCharArray(szProcessName) != QString::fromWCharArray(szProcessNameBefore)) #else if (QString::fromLocal8Bit(szProcessName) != QString::fromLocal8Bit(szProcessNameBefore)) #endif { ... }
But this is both slower and uglier than simply using
_tcscmp()
. -
@Chris-Kawa said in Whats wrong with TCHAR?:
szProcessName
andinitial
are pointers, or rather arrays that decay to pointers. So you're comparing addresses, not what they point to. To compare contents of the strings you need for example strcmp() if you're working with ANSI or _tcscmp() since you're using the "universal" TCHAR type..None of them worked, but i found on their pages correct, which is wcscmp
-
@Chris-Kawa
Could you tell me whenUNICODE
is not defined, when compiling in 2018, please? If I am wrong about that I stand corrected. If it is always defined you don't have to define 5 lines if you don't want to and consider it neater without.I wanted to illustrate to the OP how he can to a
QString
in general so that he might feel more comfortable with that, keeping it simple. Once he has passed aTSTR
to/from a Windows call, he does not need to stick with that representation to do further manipulations in his Qt code. He might have loads of further string stuff he wants to do on the returned result. I did say I wasn't sure whether it would help or not...! -
@Engelard
wcscmp
is not the right one. It is meant for WCHAR. TCHAR is a macro that expands to either WCHAR or CHAR, depending on the UNICODE define. The fact that it works means you have UNICODE defined. If you undefine it your code won't compile. The correct one is_tcscmp
, as it is also a macro that expands to eitherstrcmp
orwcscmp
depending on the UNICODE define.I'll put it simply. There are 3 variants of types and functions in WinAPI and your code would look like this:
- ANSI:
CHAR szProcessName[MAX_PATH] = "<unknown>"; ... GetModuleBaseNameA(someHandle, hMod, szProcessName, sizeof(szProcessName) / sizeof(CHAR)); //Note the A postfix in function name ... if(strcmp(szProcessName, szProcessNameBefore) == 0) //Note strcmp
- UNICODE:
WCHAR szProcessName[MAX_PATH] = L"<unknown>"; //Note the W type and L prefix for string ... GetModuleBaseNameW(someHandle, hMod, szProcessName, sizeof(szProcessName) / sizeof(WCHAR)); //Note the W postfix in function name ... if(wcscmp(szProcessName, szProcessNameBefore) == 0) //Note wcscmp
- Universal:
TCHAR szProcessName[MAX_PATH] = TEXT("<unknown>"); //Note the T type and TEXT macro ... GetModuleBaseName(someHandle, hMod, szProcessName, sizeof(szProcessName) / sizeof(TCHAR)); //Note no postfix in function name ... if(_tcscmp(szProcessName, szProcessNameBefore) == 0) //Note _tcscmp
-
@JonB said in Whats wrong with TCHAR?:
Could you tell me when UNICODE is not defined, when compiling in 2018, please?
In about gazillion lines of code that were created before 2018 or when porting from a platform that doesn't need such shenanigans.
Obviously if you create a new Windows project today the defaults in most build systems define UNICODE, but if your code makes an effort to use the T types it should do that all the way IMO. If you don't want to bother that is perfectly fine, but make it clear and use only W variants of types and functions.
I wanted to illustrate to the OP how he can to a QString in general so that he might feel more comfortable with that, keeping it simple.
That's cool. I just wanted to give a little background on what's really going on under the hood.
-
@Chris-Kawa
Sorry, just let me clarify one thing. The guy is usingTCHAR
because he's callingGetModuleBaseName()
. That is a Windows function. It must pass the right thing to the Windows kernel. And that meansUNICODE
for any Windows since like 2000. This guy is only to compile his code once (there won't be two versions to release), and he's going to compile withUNICODE
defined. Is that right or am I still not understanding this? -
@JonB said in Whats wrong with TCHAR?:
The guy is using TCHAR because he's calling GetModuleBaseName()
I don't know the OP. For all I know he might be using it because he saw it somewhere on them internets ;) That's why I like to explain how things work instead of saying "just do X".
That is a Windows function. It must pass the right thing to the Windows kernel. And that means UNICODE for any Windows since like 2000
It has nothing to do with Windows kernel or version. Windows API are just dlls that expose both A and W functions. That is the case for everything from Win95 up. The "no postfix" functions are not exported by these libraries. They are just macros in Windows SDK headers that redirect to one or the other depending on the existence of
UNICODE
define. TheUNICODE
define is not something embedded in the system. It's a macro you define in your project. In case of qmake projects it is defined in the mkspec of your kit (look for amsvc-desktop.conf
file in your Qt/mkspecs/common directory). If you try to compile this without qmake (e.g. with naked cl.exe) it will not have that define and will bug out.This guy is only to compile his code once
I don't know that. Do you know that? I'd rather give him a solution that works more than once and still works when he shares it with his colleagues ;) Maybe he copy/pastes it into a non-Qt dll project without UNICODE defined, maybe in a year he switches to another build system that doesn't have that defined and now he has random crashes because he's treating char arrays with fromWCharArray.... I don't know, but the T solution works in any case.
-
Obviously if you create a new Windows project today the defaults in most build systems define UNICODE, but if your code makes an effort to use the T types it should do that all the way IMO. If you don't want to bother that is perfectly fine, but make it clear and use only W variants of types and functions.
Chris is correct and the proper way is to interface to TCHAR. However as I am lazy, and dislike that macro magic I often do:
#ifndef UNICODE #error "This code compiles only with unicode support" #endif
-
@Chris-Kawa
I do understand what you're saying. Thanks.And what @kshegunov has just posted is kind of how I was thinking of it.
I do have one last (honest!) question: can (either of) you give me a concrete example or two of why I would not want to define
UNICODE
when I compile my Qt program with the above code in it which callsGetModuleBaseName()
tomorrow? I'm not trying to point-score or anything, I'm just struggling to understand when I would or would not want to haveUNICODE
defined if I write code to be compiled now? That's what I don't get. -
It could be legacy of some sort, you know how that goes. Or another reason may be that you have mixing of ASCII and unicode calls. Then you'd not care about that macro, but instead would use the API directly, e.g.
GetModuleNameA
when working with ASCII andGetModuleNameW
when working with unicode. -
@JonB said in Whats wrong with TCHAR?:
give me a concrete example or two of why I would not want to define UNICODE when I compile my Qt program with the above code in it tomorrow?
With the above code I don't know. There could be no reason.
I can give you an example that I lived through though. TL;DR: Legacy code.In one of the companies I worked for we used this 3rd party library that shipped with just a dll and a header. This was fine, but at some point that library was discontinued and it just happened at the time we were switching to all static linking of our app. So we bought a license for the source code and decided to maintain it ourselves. It turned out that some parts of that lib were pretty much ancient, making silent assumption that UNICODE is not defined and all the strings are CHAR based. They used the "universal" functions though. Our code had UNICODE defined so it obviously didn't mix at all. Basically we had a choice - go through thousands of lines of that lib and try to fix it or undefine UNICODE in the whole app. Thankfully someone that designed our app (not me, mind you) had the foresight to explicitly use the W variants in our code and not depend on the define at all, which turned a pretty horrible situation into a simple one line undef.
-
@Chris-Kawa
OK, I believe then that my failure to understand is because we were talking at cross-purposes. I was thinking just of the user writing a standalone Qt program needing to call Windows SDKGetModuleName()
(or other Windows SDK) and so I cannot imagine why you could not assumeUNICODE
would always be defined. You are presenting a solution which you might need if you were interacting with some other, third-party legacy code headers/library. My assumption allows one to write "simpler" code with no#ifndef UNICODE
alternative code, yours allows third-party support but with more "messy" macros/defines. That's all. Assuming that's right, at least I now understand! -
@JonB said in Whats wrong with TCHAR?:
I cannot imagine why you could not assume UNICODE would always be defined
Because you may be writing a library and your users may not have that macro defined. Either you should handle it through the preprocessor, or even better use directly the relevant functions, i.e.
SomeAPIFunctionNameW
.
For example these now obsoleted by converting the library to a Qt module lines:
https://bitbucket.org/kshegunov/qtdaemon/src/f8c744b54a6a878ca612ccff429364450a5cdb87/src/daemon/private/controllerbackend_win.h#lines-47 -
Because you may be writing a library and your users may not have that macro defined.
I have said I understand that. In this case I suspect the OP is not doing that, and is writing a standalone Qt application of his own. Hence my original confusion. That's all.
-
@JonB said in Whats wrong with TCHAR?:
I have said I understand that. In this case I suspect the OP is not doing that, and is writing a standalone Qt application of his own. Hence my original confusion. That's all.
Okay, let say your code is your own and you always compile with
UNICODE
. You could assume that it's always defined then, but isn't it cleaner just to useGetModuleNameW()
and not care about any macros at all?