
Application Verifier for the programmer: testing Windows applications
Perhaps they don’t write
Then you are lucky, you can proceed to the next topic.
But sometimes, it would seem, strange things happen. The program “crashes” out of the blue, the memory is leaking somewhere, and one more time they called you complaining about the strange behavior of the program running on the server 24/7, but of course you “wrapped” their problem, convincing that it is hardware-dependent and okay. Still, the development of programs for Windows is often tricky, and no one is safe from errors of carelessness or due to ignorance of the architecture. I won’t learn how to avoid these mistakes - I don’t know myself. But here I can advise one tool for effective debugging.
It will be about Microsoft Application Verifier. But this is not a debugger. On the contrary, without a debugger, in itself, a thing is relatively useless. But in conjunction with it allows you to detect a number of important platform-specific problems. In addition, it will not be possible to obtain a “Compatible with windows 7” certificate without passing testing using the AppVerifier (actually for “Vista Certified” as well, but apparently this is not accepted). And this certificate is for the user some guarantee that the program that received it may not do better, but at least it will not hurt . Okay, the "water" is over, let's get down to business.
Download and install AppVerifier for Habrocheloveka, I am sure, not complexity. Let's run (from under the real-administrator, under Vista + it will not work in another way) its graphical shell:

On the left is a list of applications for testing; on the right is a list of sections to check for the selected application. MSDN claims that AppVerifier is designed to test C ++ programs, but is generally applicable to any native code.
The graphical shell does not produce any tests, only makes it possible to select the desired items. The checks themselves are implemented thanks to the so-called “layers”, dynamically connected libraries
There is an important feature here : despite the fact that when adding, we select the file of the application under test, the checks are attached only to its name without a path. On the one hand, you don’t have to worry in what configuration (and in which folder) to collect the project (usually the folders for Debug and Release are different), but on the other hand, you can forget about the installed checks, and when you start the program from the desktop, you’ll be surprised that it “ does not work".
We’ll talk about the meaning of the test items a little later, but now we’ll add, for example, notepad.exe and install all the daws. Run the notebook, add a couple of lines, try to save. O-pa, failure:

Not the only outcome of the situation, perhaps you will get another warning window, or even do without it. What happened? Let's look again at the AppVerifier graphic add-in. This time, select the Logs item from the main menu, see a list of log files associated with the tested applications. By the launch log.

Physically, these log files are located in a folder

For those who have carefully watched: the error shown in this screenshot does not correspond to the error message (which is the normal behavior of the program) from the previous screenshot, but occurs a bit later.
Here is a brief description of the problem, and stack trace. And from me a hint how to look for errors, not warnings. By the way, if errors are present, then the program does not receive certification for compatibility with Vista / Win7. Wait, but this is a notebook ?! Well, yes, only shhh.
Now run the debugger. Let it be a debugger built into the studio, or a free WinDbg from Debugging Tools for Windows (it is certainly more sophisticated, but now it does not matter).
And here is our patient:
The potential danger of this fragment is easy to assess if the lines with
Now we add the program for testing the Basics group in the Application Verifier. And run it from under the debugger (from the studio on F5, for example). AppVerifier spoke to us in the voice of the studio:

And in Debug Output, the corresponding structural exception is shown :
It tells you what the exception is (00000013), with what memory address (02B59FF8) and at what code address (0082142F) it happened. The lucky ones who downloaded Windows Debug Symbols will also be shown the place in the source code where the problem occurred and Stack Trace, which led to the exception.
Well, we found this problem, which means we fixed it. For other error classes, the operation algorithm is preserved, but the correction procedure may not be so trivial.
Let's now figure out what problems AppVerifier will allow us to identify. All test options are divided into groups. With the exception of the Low Resource Simulation group and the TimeRollOver and HighVersionLie tests, the checks do not change the behavior of the application (in case no errors are detected).
Here it is the reason for the fall of the notebook. Tests of this group allow you to simulate the behavior of the system with a lack of resources. An application can easily be denied (using a random number sensor) memory allocation, file creation, Event'a, windows, entries in the registry. Usually there is some “calm” time of about 2-5 seconds, when the application is allowed to use the resources at full strength; this is done so that the application can even start up (it was invented not so long ago, it used to be sadder). The normal behavior of a program is stability; display warning dialogs, but not “crashes”. So in the code it would be necessary to provide these situations.
Consider the following code example that executes an action
The catch is visible to the naked eye; if it is
And this is not the only case that you say about the next fragment?
To diagnose such problems, the TimeRollOver check “runs” the value of the GetTickCount () function faster. A full cycle before zeroing takes 5 minutes.
If you suddenly use the function
There is a clear mistake in this fragment; In order to cut off Windows 2000 (5.0), an additional check is introduced for minor version XP (5.1), but the code also discards Windows Vista (6.0). On Windows 7 (6.1) it will work. Is this really the reason for poor compatibility with Windows Vista? Microsoft claims that 70% of incompatible with Vista programs do not work, including because of this problem .
But diagnosing this situation on the developer's computer is difficult - he has one, fixed version of the OS. You can use a virtual machine with a different version of the OS, or you can just poke the daw HighVersionLie. Then the value
Validation of calls
Monitors the correctness of calls to the Thread Local Storage API.
It monitors the appropriateness of catching exceptions, in particular, attempts to “drown out” Access Violation exceptions, and “unmasks” exceptions in view stubs
It monitors the admissibility of operations on handles, the correctness of handles and their lifetime. A little more in English .
Checks the correct use of critical sections, does not allow the critical section to be dropped from another stream relative to the critical section setting.
Periodically fills the unused part of the stack with the 0xCD pattern, which allows detecting uninitialized variables or function parameters.
Alerts about the use and unwanted potentially dangerous functions of an API like
Limited-user-account privileges test. Checks if the program needs administrative privileges, does the program perform actions that are valid only for the real-administrator.
It consists of two parts: predictive (lists all program actions that can be performed only by the administrator) and diagnostic (refuses the program administrative actions with an error
AppVerifier is an interesting tool that allows you to identify and solve a number of "floating" and "hidden" (and sometimes specially hidden) problems. Using it as a whole is not difficult, with certain skills it is convenient. And if you want to receive the certificate "Windows compatible", then acquaintance with it cannot be avoided. I personally helped already on two projects, I hope it will be useful to you too.
* All source code was highlighted with Source Code Highlighter .
try { /* code */ } catch(...) { }
to your project in order to avoid exceptions when working with memory, they can close handles and know about Windows Vista virtualization, and programs never crash for unknown or rarely repeated reasons. Then you are lucky, you can proceed to the next topic.
But sometimes, it would seem, strange things happen. The program “crashes” out of the blue, the memory is leaking somewhere, and one more time they called you complaining about the strange behavior of the program running on the server 24/7, but of course you “wrapped” their problem, convincing that it is hardware-dependent and okay. Still, the development of programs for Windows is often tricky, and no one is safe from errors of carelessness or due to ignorance of the architecture. I won’t learn how to avoid these mistakes - I don’t know myself. But here I can advise one tool for effective debugging.
It will be about Microsoft Application Verifier. But this is not a debugger. On the contrary, without a debugger, in itself, a thing is relatively useless. But in conjunction with it allows you to detect a number of important platform-specific problems. In addition, it will not be possible to obtain a “Compatible with windows 7” certificate without passing testing using the AppVerifier (actually for “Vista Certified” as well, but apparently this is not accepted). And this certificate is for the user some guarantee that the program that received it may not do better, but at least it will not hurt . Okay, the "water" is over, let's get down to business.
Mode of application
Download and install AppVerifier for Habrocheloveka, I am sure, not complexity. Let's run (from under the real-administrator, under Vista + it will not work in another way) its graphical shell:

On the left is a list of applications for testing; on the right is a list of sections to check for the selected application. MSDN claims that AppVerifier is designed to test C ++ programs, but is generally applicable to any native code.
The graphical shell does not produce any tests, only makes it possible to select the desired items. The checks themselves are implemented thanks to the so-called “layers”, dynamically connected libraries
vfbasics, vfcompat, vfLuaPriv, vfprint
(you can admire them in the folder system32
). When the application under test is launched, they connect to it and intercept a call to system functions, such asHeapAlloc, GetTickCount, CloseHandle
and many others. The interceptor performs a number of additional checks, then calls the original function, and therefore, with the exception of a few cases considered below , this will not affect the operation of the application under test. Unless some performance loss will be noticeable. Subjectively, in the worst case scenario, the program will “slow down” five times, and whether any specific numbers are needed or not, I will leave it up to you. There is an important feature here : despite the fact that when adding, we select the file of the application under test, the checks are attached only to its name without a path. On the one hand, you don’t have to worry in what configuration (and in which folder) to collect the project (usually the folders for Debug and Release are different), but on the other hand, you can forget about the installed checks, and when you start the program from the desktop, you’ll be surprised that it “ does not work".
We’ll talk about the meaning of the test items a little later, but now we’ll add, for example, notepad.exe and install all the daws. Run the notebook, add a couple of lines, try to save. O-pa, failure:

Not the only outcome of the situation, perhaps you will get another warning window, or even do without it. What happened? Let's look again at the AppVerifier graphic add-in. This time, select the Logs item from the main menu, see a list of log files associated with the tested applications. By the launch log.

Physically, these log files are located in a folder
AppVerifierLogs
in the root of the user profile. It will be difficult to read them with your bare hands (binary format), so we poke the “View” button for the corresponding log. It will dump in xml and open the default viewer for xml: 
For those who have carefully watched: the error shown in this screenshot does not correspond to the error message (which is the normal behavior of the program) from the previous screenshot, but occurs a bit later.
Here is a brief description of the problem, and stack trace. And from me a hint how to look for errors, not warnings. By the way, if errors are present, then the program does not receive certification for compatibility with Vista / Win7. Wait, but this is a notebook ?! Well, yes, only shhh.
Patient treatment
Now run the debugger. Let it be a debugger built into the studio, or a free WinDbg from Debugging Tools for Windows (it is certainly more sophisticated, but now it does not matter).
And here is our patient:
int _tmain(int argc, _TCHAR* argv[])
{
int *p = new int();
delete p;
*p = 0; // p = 0 will be OK, but *p = 0 is error!
}
The potential danger of this fragment is easy to assess if the lines with
delete
and overwriting the memory were stretched in time. But neither in the release nor in the release assembly is such a problem detected (Visual Studio, default configuration). Now we add the program for testing the Basics group in the Application Verifier. And run it from under the debugger (from the studio on F5, for example). AppVerifier spoke to us in the voice of the studio:

And in Debug Output, the corresponding structural exception is shown :
=======================================
VERIFIER STOP 00000013: pid 0xB54: First chance access violation for current stack trace.
02B59FF8 : Invalid address causing the exception.
0082142F : Code address executing the invalid access.
0013F670 : Exception record.
0013F68C : Context record.
=======================================
It tells you what the exception is (00000013), with what memory address (02B59FF8) and at what code address (0082142F) it happened. The lucky ones who downloaded Windows Debug Symbols will also be shown the place in the source code where the problem occurred and Stack Trace, which led to the exception.
Well, we found this problem, which means we fixed it. For other error classes, the operation algorithm is preserved, but the correction procedure may not be so trivial.
Detectable problems
Let's now figure out what problems AppVerifier will allow us to identify. All test options are divided into groups. With the exception of the Low Resource Simulation group and the TimeRollOver and HighVersionLie tests, the checks do not change the behavior of the application (in case no errors are detected).
1. Distorting checks
1.1. Low resource simulation
Here it is the reason for the fall of the notebook. Tests of this group allow you to simulate the behavior of the system with a lack of resources. An application can easily be denied (using a random number sensor) memory allocation, file creation, Event'a, windows, entries in the registry. Usually there is some “calm” time of about 2-5 seconds, when the application is allowed to use the resources at full strength; this is done so that the application can even start up (it was invented not so long ago, it used to be sadder). The normal behavior of a program is stability; display warning dialogs, but not “crashes”. So in the code it would be necessary to provide these situations.
1.2. TimeRollOver in the group Misc
Consider the following code example that executes an action
action
several times, but no more than one second:DWORD time_end = GetTickCount() + 1000; // 1s timelimit
do { action(); } while (GetTickCount() < time_end);
The catch is visible to the naked eye; if it is
time_end
very close to DWORD_MAX
, but less than DWORD_MAX-1000
, and action()
sometimes it takes more than a second, then the cycle will work a little longer than we would like. Namely, 50 days (DWORD_MAX / (1000 * 60 * 24)). And this is not the only case that you say about the next fragment?
char buf[8];
sprintf(buf, "%i", GetTickCount());
To diagnose such problems, the TimeRollOver check “runs” the value of the GetTickCount () function faster. A full cycle before zeroing takes 5 minutes.
1.3. HighVersionLie in the Compatibility group
If you suddenly use the function
GetVersionEx
, then this test will help you detect code branches with incorrect verification of a valid OS version.OSVERSIONINFO osvi;
ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
GetVersionEx(&osvi);
BOOL bIsWindowsXP_or_Later = (osvi.dwMajorVersion >= 5) && (osvi.dwMinorVersion >= 1);
if (!bIsWindowsXP_or_Later)
printf("Windows XP or later required.\n");
There is a clear mistake in this fragment; In order to cut off Windows 2000 (5.0), an additional check is introduced for minor version XP (5.1), but the code also discards Windows Vista (6.0). On Windows 7 (6.1) it will work. Is this really the reason for poor compatibility with Windows Vista? Microsoft claims that 70% of incompatible with Vista programs do not work, including because of this problem .
But diagnosing this situation on the developer's computer is difficult - he has one, fixed version of the OS. You can use a virtual machine with a different version of the OS, or you can just poke the daw HighVersionLie. Then the value
GetVersionEx
will be modified (usually by the rule dwMajorVersion += 3; dwMinorVersion = 0
).2. Non-modifying checks
2.1. Memory in the Basics group
Validation of calls
HeapAlloc, GlobalAlloc
and other Windows Heap Manager APIs. He does not monitor memory leaks, but this can be solved in other ways .2.2. TLS in the Basics group
Monitors the correctness of calls to the Thread Local Storage API.
2.3. Exceptions in the Basics Group
It monitors the appropriateness of catching exceptions, in particular, attempts to “drown out” Access Violation exceptions, and “unmasks” exceptions in view stubs
try { } catch(...) { }
.2.4. Handles in the Basics group
It monitors the admissibility of operations on handles, the correctness of handles and their lifetime. A little more in English .
2.5. Locks in the group Basics
Checks the correct use of critical sections, does not allow the critical section to be dropped from another stream relative to the critical section setting.
2.6. DirtyStacks in the Misc group
Periodically fills the unused part of the stack with the 0xCD pattern, which allows detecting uninitialized variables or function parameters.
2.7. DangerousAPIs in the Misc group
Alerts about the use and unwanted potentially dangerous functions of an API like
TerminateThread
.2.8. Luapriv
Limited-user-account privileges test. Checks if the program needs administrative privileges, does the program perform actions that are valid only for the real-administrator.
It consists of two parts: predictive (lists all program actions that can be performed only by the administrator) and diagnostic (refuses the program administrative actions with an error
ACCESS_DENIED
). Thus, the programmer does not have to test the program separately by logging in as a guest. It also checks a number of features related to virtualization under Windows Vista and later.Conclusion
AppVerifier is an interesting tool that allows you to identify and solve a number of "floating" and "hidden" (and sometimes specially hidden) problems. Using it as a whole is not difficult, with certain skills it is convenient. And if you want to receive the certificate "Windows compatible", then acquaintance with it cannot be avoided. I personally helped already on two projects, I hope it will be useful to you too.
* All source code was highlighted with Source Code Highlighter .