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?)


  • Moderators

    szProcessName and initial 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) Qt QStrings out of your TCHAR []/LPTSTRs. Then you can use QString stuff, like QString'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 with QString which you understand better.


  • Moderators

    @JonB The thing is you can't just arbitrarily use QString::fromWCharArray with TCHAR, as it's either a WCHAR or CHAR, depending on the UNICODE 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 and initial 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 when UNICODE 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 a TSTR 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...!


  • Moderators

    @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 either strcmp or wcscmp 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:

    1. 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
    
    1. 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
    
    1. 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
    

  • Moderators

    @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 using TCHAR because he's calling GetModuleBaseName(). 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. This guy is only to compile his code once (there won't be two versions to release), and he's going to compile with UNICODE defined. Is that right or am I still not understanding this?


  • Moderators

    @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. The UNICODE 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 a msvc-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.


  • Qt Champions 2017

    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 calls GetModuleBaseName() tomorrow? I'm not trying to point-score or anything, I'm just struggling to understand when I would or would not want to have UNICODE defined if I write code to be compiled now? That's what I don't get.


  • Qt Champions 2017

    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 and GetModuleNameW when working with unicode.


  • Moderators

    @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 SDK GetModuleName() (or other Windows SDK) and so I cannot imagine why you could not assume UNICODE 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!


  • Qt Champions 2017

    @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/nye/qtdaemon/src/f8c744b54a6a878ca612ccff429364450a5cdb87/src/daemon/private/controllerbackend_win.h#lines-47



  • @kshegunov

    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.


  • Qt Champions 2017

    @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 use GetModuleNameW() and not care about any macros at all?



  • @kshegunov Yep, that would be fine.


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.