Some time ago I’ve ported some library from the 32-bit version to the 64-bit one (С++, MSVS 2008, Windows). I felt a temptation to write about it immediately after that but I had no time then. That is why I am writing about it now and I think it is not too bad since I will write about those things that really stuck in my memory.
What does any programmer do first when he deals with porting? Of course, he just takes the product and simply builds it in a new configuration (on spec)! Strange as it may be, my project was successfully built and launched and even seemed to work. But later it turned out that it had not worked at all, and here I must give some explanations of the project’s specifics.
The point of the program is that once launched it integrates into all the processes, and when new processes are launched the program integrates into them as well. It is done through dll injection. This dll doesn’t do anything special – it just checks what is written in the active program window at the moment. If there is something saying that an error has occurred in the program, an additional button appears on this window that contains its own menu from which, among other variants, you can get to the site of this program where you can find forums and helps – perhaps this problem has been chewed over and you just have to read what is written there to solve it. If there is no solution yet, a corresponding topic will be created on the forum automatically. That is, it is a kind of a context help to solve software issues. You may see the pictures and description on the program site:
Of everything said above, the key words are "dll injection." While investigating why the FaultWire button appears in 32-bit applications, but fails to do this in 64-bit ones (or still appears but leads to a crash right after), I also made out what is WOW64 and learned other specifics of coexistence of programs with different capacities.
Now I briefly describe the porting issues that are memorize to me. First of all this post is intended for developers of utilities and systems that provide aid in porting. If your products miss means that help solve these problems, maybe you should to include them? It won’t be worse anyway. I personally didn’t use additional tools and therefore spent more time than I had expected.
Firstly, I had to replace all the obsolete versions of functions with corresponding new ones. I’ve even encountered one 16-bit function version. These are examples that I just managed to recall:
SetWindowLong –> SetWindowLongPtr
GetWindowLong –> GetWindowLongPtr
SetClassLong –> SetClassLongPtr
GetClassLong –> GetClassLongPtr
Secondly, I’ve encountered the problem of discrepancy of alignments in structures. It occurs when data are transferred between processes with different capacities. It was just my case: the main 32-bit program exchanges (chiefly receives) data with libraries injected with different processes – 32-bit and 64-bit. In particular, the message WM_COPYDATA was used to pass the data structure. The problem was that the structure contained fields of the HWND type – such fields are 32-bit in 32-bit applications and 64-bit in 64-bit ones. That is why everything that was received from the 64-bit process was spoiled when this structure was being parsed in the 32-bit process. I solved the problem by making all such fields 64-bit (in 32-bit processes, the handle was put into the 32 less significant bits of such a field) replacing them by this union:
union _hwnd3264 // for placing 32 or 64-bit HWNDs on 64-bit
And finally, here is the last error I struggled with: when the FaultWire button appeared, the application crashed showing an incomprehensible message and addresses specified in this message looked rather strange, to put it mildly, since both the disassembler and debugger showed some rubbish there (or maybe I started to slowly go mad). The reason was the following. Since a new control element (a button) appeared in the program which had not been provided for, I had to implement processing of all its events since the native window function didn’t know anything about it. It was done in an obvious way – using subclassing. The native window procedure was replaced with the own procedure that processed all the messages of this button and sent all the rest messages to the native procedure. The error lied in the line responsible for the replacement:
SetWindowLongPtr(hwnd, GWLP_WNDPROC, (DWORD)(LONG_PTR)SubClassMenuProc);
Because of the type conversion to the DWORD type, only a half of the procedure’s address was saved and therefore the processor started executing instructions from a false place when the control was passed to the procedure. And now consider how I found this error. There is the following flag in the project settings in MSVS:
Smaller Type Check (/RTCc).
It enables runtime-checks of variable conversion to types of smaller sizes. When I found and enabled it, the studio halted at this line at the very first launch and showed a warning message. The rest was pie. Since this flag is disabled by default, I think it would be good if utilities like PVS-Studio had some warning about conversion to types of smaller sizes (however, perhaps there is such a warning).
There is also a similar flag Basic Runtime Checks. Maybe I did enable them from time to time but the point about them is that they work only in the debug mode. And what modes and parameters haven’t I tried!.. But a static analyzer, I think, could easily find an error in that line. You even wouldn’t have to launch the program…
Most often I launched the program in the release version. In general, in the beginning (when I just settled to the project), it worked well only in the release mode. There were several reasons for it. One of them was an error occurring when working with XML. It doesn’t matter how I fixed it but I discovered an interesting thing meanwhile. I had a license for Parallel Studio at that time and I decided to perform some launches of the program with Parallel Inspector to search for the error. I tried to link with various versions of the MSXML library beginning with the third version hoping that the error would disappear. The error remained there, but Parallel Inspector detected a whole lot of errors inside these libraries!..