📜 ⬆️ ⬇️

Tales of incompatible applications

These are excerpts from the free chapter from the paper book The Old New Thing. They are not trying to teach anything; these are just short funny episodes from everyday fighters for compatibility of new versions of Windows with old applications.

Change the version number of Windows

Changing the version number that Windows communicates to programs is not as easy as it sounds. For example, some programs check the version number like this:
 UINT Ver = GetVersion ();
 UINT MajorVersion = LOBYTE (uVer);
 UINT MinorVersion = HIBYTE (uVer);
 if (MajorVersion <3 || MinorVersion <10) {
     Error ("This program requires Windows 3.1");

Imagine how this code will work on Windows 95, which has a version number of 4.0. The second check is triggered because 0 is less than 10.
And okay, the program would just display an error message and complete. Many collapsed: it was obvious that their behavior on "unsupported" versions of Windows was never tested.

There were so many such programs that we stopped correcting them one by one, and simply changed the version number returned from 4.0 to 3.95.
Programs for MS-DOS, too, not all responded smoothly to changing OS versions. This is surprising because by that time, MS-DOS already had dozens of released versions and sub-versions, and it would be time for developers to learn to check the version number. But for example, one software package used the DOS version number as an index in the function table: by its own function for each version released. There were five functions in the table: from MS-DOS 1.x to 5.x. When the program was run under MS-DOS 6.0, it caused an address outside the table, and crashed.

Changing the version number that Windows reports to programs is a necessary, but very difficult step. A couple of keystrokes, and fall hundreds of applications that have worked well so far; now the compatibility department will have to spend an extra thousand man-hours on catching other people's bugs.

We inform the developers about the bug in their program

Specifically, this conversation is fictional, but similar in fact conversations happened more than once.

—Hello, this is such and such, from the Microsoft Windows compatibility department. We found a bug in your program that prevented it from working. We had to add code in Windows 95 to bypass this bug in order for your program to continue working.
—Greatly, thank you so much! Goodbye. ( Short beeps. )
(Dial the number again.)
“Hello, uh ... you don't even want to know what the bug was?”
-What's the difference? You fixed it all. Thank! What would we do without you!
—But, uh, the code around the bug will only work with the current version of your program. When you release the next version, it will not work.
“That's how it is? .. Well, wait, I will connect you with our programmers.”

The developers absolutely do not pay attention to you until you mention that their program will stop working.

When I was engaged in backward compatibility with MS-DOS programs (mostly they were games), it often happened that I called to inform the developers that their program did not work under Windows 95. Many developers simply answered “That's right, we don’t work with Windows. "

We remind users to act strictly according to the instructions.

A neighbor of one of our developers bought a program that crashed immediately upon launch. The developer and his colleagues started with a standard procedure - they changed drivers, started them on another computer - but nothing helped. Desperate to cope with the problem on their own, they called the developers.

In technical support, they were told that if during installation all three fields — name, organization and serial number — were not filled in, then the installation is successful, but the program will crash on launch. And this is not a sophisticated copy protection, but a documented bug.

Put the cart before the horse

Sometimes such terrible bugs are found in programs that it is not clear how they work at all.

The installer of one popular program tries to replace the system file with its own version. It replaces the file, even if this own version is older than the one already installed in the system: the installer does not bother checking versions.
Great, the Windows file protection system is closely monitoring such programs, and it will restore the correct version as soon as the installation is complete. This is not the problem.

If the system file that the installer is trying to replace is busy, the installer overwrites it with the MOVEFILE_DELAY_UNTIL_REBOOT option - so that the busy file is replaced when the system is rebooted:
 // Code simplified for clarity
 MoveFileEx ("sysfile.new", "sysfile.dll", MOVEFILE_DELAY_UNTIL_REBOOT);
 CopyFile ("D: \\ CDROM \\ INSTALL \\ sysfile.dll", "sysfile.new");

So it is - the program is trying to copy a file that does not exist yet!
This code actually worked on Windows NT, because there the function MoveFileEx , when it was called with MOVEFILE_DELAY_UNTIL_REBOOT , did not check if the file to be copied exists. In the beta version of Windows 2000, we added a more rigorous parameter check; now MoveFileEx returned an error. The installer considered this error fatal, and stopped the installation.

In Windows 2000, I had to allow copying of nonexistent files with the MOVEFILE_DELAY_UNTIL_REBOOT option. MoveFileEx reports on success - in the hope that by the time of the reload the file being copied will actually appear.

The strangest way to check the success of a call

I have no idea what the developers of a multimedia program thought, which, checking the success of calling multimedia functions, did not compare MMRESULT returned with MMSYSERR_NOERROR , but received a text description for the error number, and then compared this line with “The specified command complete successfully. "
In fact, she compared only the first sixteen characters with “The specified co” - probably, someone was looking through the code and decided to optimize this place.
In one of the versions of Windows, when we rephrased this message a little, the program stopped working. Needless to say, it never worked on non-English versions of Windows.

A stranger than the strangest way to check the success of a call

What would you think? There is an even stranger way. At a minimum, the past program worked with the return error code. Another program that used MCI to play videos completely ignored the value returned by a call to MCIWndOpen . Instead, she read the title of the MCI window, and compared it with the “No Device” line to determine if the file was opened successfully.

In Windows 95, the MCI window header when opening a file was installed with a slight delay. Now that program could not open the videos: she checked the window title too early.

How to make a mistake in the function from one line

One day an employee came to my office and asked: “Hey, do you want a sound card?” I did not have my sound card, so I agreed.

It turned out that he gave me this card for a reason: he could not make it work under Windows 95. Now it was not he, but I was a loser with a broken sound. Soon I understood why he so wanted to get rid of her: from time to time she destroyed the entire system. A debug version of Windows was installed on my machine, so I was able to figure out the cause of the crashes.

The developers of the driver for this sound card took a function from a single line, a sample of which is given in the DDK, and they managed to make a mistake in it.
Here is a sample of the function in the DDK:
 void FAR PASCAL midiCallback (NPPORTALLOC pPortAlloc, WORD msg,
                              DWORD dwParam1, DWORD dwParm2) {
   if (pPostAlloc-> dwCallback)
     DriverCallBack (pPortalloc-> dwCallback, HIWORD (pPortalloc-> dwFlags),
                    pPortalloc-> hMidi, msg, dwParam1, dwParam2);
It is called by the system during a hardware interrupt.

This is how this function looked in that driver:
 void FAR PASCAL midiCallback (NPPORTALLOC pPortAlloc, WORD msg,
                              DWORD dwParam1, DWORD dwParm2) {
   char szBuf [80];
   if (pPostAlloc-> dwCallback) {
     wsprintf (szBuf, "Dc (hMidi =% X, wMsg =% X)", pPortalloc-> hMidi, msg);
 #ifdef DEBUG
     OutputDebugString (szBuf);
     DriverCallBack (pPortalloc-> dwCallback, HIWORD (pPortalloc-> dwFlags),
                    pPortalloc-> hMidi, msg, dwParam1, dwParam2);

They not only left in the final version of the driver remnants of the debugging code; during a hardware interrupt, they call a function that cannot be called at that moment. If wsprintf unloaded from memory, then the system receives a new “missing segment” interrupt during a hardware interrupt, and that’s the end. Even if lucky, and wsprintf is in memory - this function changes the upper word of 32-bit registers, and the hardware interrupt handler stores only the lower word. This means that the interrupted code is waiting for a very rude awakening when the interrupt processing is complete.

Despite all this, the sound card won an award from a popular computer magazine.
But injustice was not limited to this: the card manufacturer wanted a new version of their driver to be included in Windows 95. And what did we see in the new version? The same bug that destroys the system is the same one that we showed them to the programmers several months ago. As you can guess, the new version of the driver in Windows 95 is not included.

Sound editor and annoying parasite

We caught one tutorial that parasitized on the standard Sound Editor, and grabbed it so deeply that the further we studied it, the more amazed.

The first problem was banal: the program started the Sound Editor by the name of its 16-bit version, SOUNDREC.EXE . In Windows 95, the sound editor was 32-bit, and was called SNDREC32.EXE - the same as in Windows NT. We renamed the file back to a 16-bit name, and believed that the program would work now.

But she did not earn. Successfully launching the editor, the program searches for its window by title. First she tries to find an English headline, then Italian (the program was developed for Italians). Not so bad: many developers have not thought at all about supporting localized releases of Windows. True, Italians living, say, in France, will not be able to use this program; but this is not our concern.
In Windows 95, the header of the Sound Editor has changed: we added the name of the open file there. Now the program could not find the window of the editor that launched.

Then we changed the title of the 16-bit Sound Editor to match exactly the title used in Windows 3.1. Finally, the program has started. We followed her, and found that she never uses a running editor! Why did she run it?

It turned out that under certain conditions she really enjoys it. Usually it plays sounds with a special component ( apparently, it was VBX ), which supports asynchronous sound card drivers, but does not support the SPEAKER.DRV synchronous driver, which allowed to play sounds through SPEAKER.DRV without a sound card.
The developers were looking for something that could work with this synchronous driver - and found the Sound Editor. Imitate keystrokes in his window - and a cheap replacement for the player component is ready.

But if you pay attention at the time of launching the program, you will notice how the Sound Editor window flashes and immediately disappears, leaving only a strip on the left of the screen. Programmers did not want the editor window to be visible. But they did not know how to make the window invisible, so they did everything they could: they “dragged” the window off the screen. Well, almost out of bounds. They could not even move the window programmatically, so they simulated clicks and mouse movements, as if dragging a window by the title. When only a narrow strip remains on the screen, which the mouse can no longer drag further left, programmers consider their task accomplished: “We have removed the window behind the screen — probably no one will notice it.”

I advise you to look at my past translation at the same time - Favorite "iron" bug

Source: https://habr.com/ru/post/103598/

All Articles