Rss Feed
Tweeter button
Facebook button
Technorati button
Reddit button
Myspace button
Linkedin button
Webonews button
Delicious button
Digg button
Flickr button
Stumbleupon button
Newsvine button

Getting code coverage for a child process?

By , May 31, 2017 5:43 pm

In this blog post I’m going to explain how to collect code coverage for a process that is launched by another process. We’ll be using C++ Coverage Validator to collect the code coverage.

For example you may have a control process that launches helper programs to do specific jobs and you wish to collect code coverage data for one of the helper programs. I’m first going to show how you do this with the GUI, then I’ll show you how to do this with the command line.

For the purposes of this blog post I’m going to use a test program called testAppFromOtherProcess.exe as the child program and testAppOtherProcessCpp.exe as the parent process. Once I’ve explained this for C++, I’ll also provide examples for programs launched from Java and for programs launched from Python.

The test program

The test program is simple. It takes two numbers and calculates the sum of all the products. If less than two arguments are supplied they default to 10.

int _tmain(int argc, _TCHAR* argv[])
{
	int	nx, ny;
	int	x, y;
	int	v;

	nx = 10;
	ny = 10;
	v = 0;

	if (argc == 2)
	{
		nx = _tcstol(argv[1], NULL, 10);
	}
	else if (argc >= 3)
	{
		nx = _tcstol(argv[1], NULL, 10);
		ny = _tcstol(argv[2], NULL, 10);
	}

	for(y = 0; y < ny; y++)
	{
		for(x = 0; x < nx; x++)
		{
			v += (x + 1) * (y + 1);
		}
	}

	return v;
}

The parent C++ program

The parent C++ program is a simple MFC dialog that collects two values and launches the test program. The code for launching the child process looks like this:

void CtestAppOtherProcessCppDlg::OnBnClickedOk()
{
	// get data values

	CString	str1, str2;
	DWORD	v = 0;

	GetDlgItemText(IDC_EDIT_COUNT1, str1);
	GetDlgItemText(IDC_EDIT_COUNT2, str2);

	// create command line

	CString	commandline;

	commandline += _T("testAppFromOtherProcess.exe");
	commandline += _T(" ");
	commandline += str1;
	commandline += _T(" ");
	commandline += str2;

	// run child process

	STARTUPINFO         stStartInfo;
	PROCESS_INFORMATION stProcessInfo;

	memset(&stStartInfo, 0, sizeof(STARTUPINFO));
	memset(&stProcessInfo, 0, sizeof(PROCESS_INFORMATION));

	stStartInfo.cb = sizeof(STARTUPINFO);
	stStartInfo.dwFlags = STARTF_USESHOWWINDOW;
	stStartInfo.wShowWindow = SW_HIDE;

	int	bRet;

	bRet = CreateProcess(NULL,
			(TCHAR *)(const TCHAR *)commandline,
			NULL,
			NULL,
			FALSE,
			0,
			NULL,
			NULL,
			&stStartInfo,
			&stProcessInfo);
	if (bRet)
	{
		// wait until complete then get exit code

		WaitForSingleObject(stProcessInfo.hProcess, INFINITE);

		GetExitCodeProcess(stProcessInfo.hProcess, &v);

		// tidy up

		CloseHandle(stProcessInfo.hProcess);
		CloseHandle(stProcessInfo.hThread);
	}

	// display result

	SetDlgItemInt(IDC_STATIC_VALUE, v, FALSE);
}

Configuring the target C++ program

Before we can collect code coverage we need to tell C++ Coverage Validator about the target program and the program that is going to launch it. We do this from the launch dialog (or launch wizard). From the launch dialog, select the program to launch using the Browse... button and selecting the file with the File dialog. Once a file has been chosen a default value will be selected for the Application to Monitor. This is the same program as you just selected with the File dialog.

CVLaunchDialogApplicationToMonitor

To allow us to monitor other programs we need to edit the list of applications we can monitor. Click the Edit... button to the right of the Application to monitor combo box. The Applications To Monitor dialog is displayed.

CVApplicationsToMonitorDialog

We need to add our target program to the list of programs to monitor. Click Add.... The Application To Monitor dialog is displayed. Choose our launch program testAppOtherProcessCpp.exe using Browse.... C++ Coverage Validator will identify any other executables in the same folder and add these to the list of target programs you may want to monitor. You can remove any programs you don't want to monitor with the Remove and Remove All buttons. Your dialog should look like the one shown below.

CVApplicationToMonitorDialog

Click OK to close the Application To Monitor dialog.

Click OK to close the Applications To Monitor dialog.

The Application to monitor combo will now have additional entries in it. Select testAppFromOtherProcess.exe in the Application to monitor combo. Leave the launch count set to 1. The first time testAppFromOtherProcess.exe is launched it will be monitored. Click Go! to start the parent process.

CVApplicationToMonitorParentProcess

You will notice that C++ Coverage Validator is not collecting data. Now click on the Launch Child Process button. The child process is launched, C++ Coverage Validator recognises the parent process is launching a child process that is configured to be monitored and has the correct launch count (this is the first time it is being launched and the launch count is set to "1") - the child process is instrumented for code coverage. You can see the instrumentation progress in the title bar and pretty soon code coverage statistics are being displayed by C++ Coverage Validator.

CVCodeCoverageResults

Command Line, example for C++

OK, that's wonderful, we can collect code coverage using the GUI to launch one program and collect data from a child process. All without any coding. Super. So how do we do that from the command line? Glad you asked!

"c:\C++ Coverage Validator\coverageValidator.exe" 
-program "e:\test\release\testAppOtherProcessCpp.exe"
-directory "e:\test\release" 
-programToMonitor "e:\test\release\testAppFromOtherProcess.exe" 

How does this work?

  • -directory. Specify the startup directory.
  • -program. Specify the program to launch.
  • -programToMonitor. Specify the program to that will be monitored for code coverage.

Very straightforward and simple. Paths must have quotes if they contain spaces. If in doubt always use quotes. Note also that where you've installed C++ Coverage Validator will be different, most likely in C:\Program Files (x86)\Software Verification. We shortened it for the example to make it fit the page.

Java

The parent program in Java is very simple. It takes any arguments passed to it and passes them to the target program.

import java.io.IOException;
import java.lang.ProcessBuilder;
import java.util.ArrayList;

public class testAppFromOtherProcessJava 
{
    public static void main(String[] args) throws IOException, InterruptedException
	{
		String			target = "e:\\om\\c\\testApps\\testAppFromOtherProcess\\Release\\testAppFromOtherProcess.exe";
        	ProcessBuilder	p = new ProcessBuilder();

		// add the args to be passed to the target program, unlike C/C++, args[0] is not the program name

		ArrayList	targetArgs;

		targetArgs = new ArrayList();
		targetArgs.add(target);
		for(int i = 0; i < args.length; i++)
		{
			targetArgs.add(args[i]);
		}

		p.command(targetArgs);

		// run the process, wait for it to complete and report the value calculated

		Process			proc;

	        proc = p.start();
		proc.waitFor();

		System.out.println("Result: " + proc.exitValue()); 
    }
}

You can compile this program with this simple command line. This assumes you have a Java Development Kit installed and javac.exe on the command line.

javac testAppFromOtherProcessJava.java

Configuring the target Java program

As with the C++ target program we need to tell C++ Coverage Validator about the target program and the program that is going to launch it. We're running a Java program so the executable to launch is the Java runtime. Click the Browse... button and select the Java runtime you are using.

CVLaunchDialogJava

The launch directory is automatically configured to be the same as the launch program. In the case of a Java program, that is almost certainly incorrect. We're going to choose the directory where our Java class is located. Click the Dir... button and choose that directory.

CVLaunchDialogJavaDirectory

We also need to tell the Java runtime what class to execute. This is provided as an argument to the program being run (the Java rutnime). In the arguments field, type the name of the class. In this case testAppFromOtherProcessJava (without the .class extension).

CVLaunchDialogJavaArguments

To allow us to monitor other programs we need to edit the list of applications we can monitor. Click the Edit... button to the right of the Application to monitor combo box. The Applications To Monitor dialog is displayed.

CVApplicationsToMonitorDialog

We need to add our target program to the list of programs to monitor. Click Add.... The Application To Monitor dialog is displayed. Choose the Java runtime java.exe using Browse.... C++ Coverage Validator will identify any other executables in the same folder and add these to the list of target programs you may want to monitor. You can remove any programs you don't want to monitor with the Remove and Remove All buttons. We now need to add the target program to the list of programs we want to monitor. Click Add... and select testAppFromOtherProcess.exe. Your dialog should look like the one shown below.

CVApplicationToMonitorDialogJava

Select testAppFromOtherProcess.exe in the Application to monitor combo. Leave the launch count set to 1. The first time testAppFromOtherProcess.exe is launched it will be monitored. Click Go! to start the parent process.

CVLaunchDialogApplicationToMonitorJava

The Java process launches testAppFromOtherProcess.exe immediately. As such you will notice that C++ Coverage Validator starts collecting code coverage almost instantly because it has recognised the Java process is launching a child process that is configured to be monitored and has the correct launch count.

CVCodeCoverageResultsJava

Command Line, example for Java

As you can see, it's slightly more complicated for Java than for C++, but only because the Java runtime is located in a different folder than the test executable and because we also have to specify a Java class to execute. We still managed to collect code coverage for a child process of a just in time compiled language without any coding.

Of course, you now want to know how to do this for the command line. Is this any more complicated than for the C++ example? No! Just as easy. Here's how you do it:

"c:\C++ Coverage Validator\coverageValidator.exe" 
-program "c:\program files\java\jdk1.8.0_121\bin\java.exe"
-directory "e:\test\release" 
-arg testAppFromOtherProcessJava
-programToMonitor "e:\test\release\testAppFromOtherProcess.exe"

How does this work?

  • -arg. Specify an argument to the program to launch. In this example this specifies the Java class to execute.
  • -directory. Specify the startup directory.
  • -program. Specify the program to launch. In this example this specifies the Java runtime.
  • -programToMonitor. Specify the program to that will be monitored for code coverage.

Use as many -arg options as you need. We only used one because that's all we need for the example.

Python

The parent program in Python is very simple.

import sys
import subprocess

cmdLine = r"E:\om\c\testApps\testAppFromOtherProcess\Release\testAppFromOtherProcess.exe"
for arg in range(1, len(sys.argv)):
  cmdLine += " "
  cmdLine += sys.argv[arg]
  
subprocess.call(cmdLine, stdin=None, stdout=None, stderr=None, shell=False)

Configuring the target Python program

As with the C++ target program we need to tell C++ Coverage Validator about the target program and the program that is going to launch it. We're running a Python program so the executable to launch is the Python interpreter. Click the Browse... button and select the Python interpreter you are using.

CVLaunchDialogPython

The launch directory is automatically configured to be the same as the launch program. In the case of a Python program, that is almost certainly incorrect. We're going to choose the directory where our Python script is located. Click the Dir... button and choose that directory.

CVLaunchDialogPythonDirectory

We also need to tell Python what script to launch. This is provided as an argument to the program being run (the Python interpreter). In the arguments field, type the name of the script. In this case testAppFromOtherProcess.py.

CVLaunchDialogPythonArguments

To allow us to monitor other programs we need to edit the list of applications we can monitor. Click the Edit... button to the right of the Application to monitor combo box. The Applications To Monitor dialog is displayed.

CVApplicationsToMonitorDialog

We need to add our target program to the list of programs to monitor. Click Add.... The Application To Monitor dialog is displayed. Choose the Python interpreter python.exe using Browse.... C++ Coverage Validator will identify any other executables in the same folder and add these to the list of target programs you may want to monitor. You can remove any programs you don't want to monitor with the Remove and Remove All buttons. We now need to add the target program to the list of programs we want to monitor. Click Add... and select testAppFromOtherProcess.exe. Your dialog should look like the one shown below.

CVApplicationToMonitorDialogPython

Select testAppFromOtherProcess.exe in the Application to monitor combo. Leave the launch count set to 1. The first time testAppFromOtherProcess.exe is launched it will be monitored. Click Go! to start the parent process.

CVLaunchDialogPythonAppToMonitor

The Python process launches testAppFromOtherProcess.exe immediately. As such you will notice that C++ Coverage Validator starts collecting code coverage almost instantly because it has recognised the Python process is launching a child process that is configured to be monitored and has the correct launch count.

CVCodeCoverageResultsPython

Command Line, example for Python

As you can see, it's slightly more complicated for Python than for C++, but only because the Python interpreter is located in a different folder than the test executable and because we also have to specify a Python script. We still managed to collect code coverage for a child process of a scripted language without any coding.

Of course, you now want to know how to do this for the command line. Is this any more complicated than for the C++ example? No! Just as easy. Here's how you do it:

"c:\C++ Coverage Validator\coverageValidator.exe" 
-program "c:\python36-32\python.exe"
-directory "e:\test\release" 
-arg testAppFromOtherProcess.py
-programToMonitor "e:\test\release\testAppFromOtherProcess.exe"

How does this work?

  • -arg. Specify an argument to the program to launch. In this example this specifies the Python script to run.
  • -directory. Specify the startup directory.
  • -program. Specify the program to launch. In this example this specifies the Python interpreter.
  • -programToMonitor. Specify the program to that will be monitored for code coverage.

Use as many -arg options as you need. We only used one because that's all we need for the example.

Conclusion

We've demonstrated how to monitor code coverage in a target program launched from C++, Java and Python, using both the GUI and the command line. Each example is slightly different, showing you the changes required for each situation. If you have any questions please email support@softwareverify.com

You can download the C++, Java and Python code used in these examples here.

Share

Updated error codes for all Validator tools

By , May 12, 2017 12:01 pm

We’ve just updated our documentation for all our Validator tools to include an up to date list of Exit return codes. You may find these useful if you’re running these tools from the command line.

These error codes apply to C++ Bug Validator, C++ Coverage Validator, C++ Memory Validator, C++ Performance Validator, C++ Thread Validator, .Net Coverage Validator, .Net Memory Validator, .Net Performance Validator and VM Validator.

0 All ok
-1 Unknown error. An unexpected error occurred starting the runtime
-2 Application started ok. You should not see this code returned
-3 Application failed to start. E.g. runtime not present, not an executable or injection dll not present
-4 Target application is not an application
-5 Don’t know what format the executable is, cannot process it
-6 Not a 32 bit application
-7 Not a 64 bit application
-8 Using incorrect MSVCR(8|9).DLL that links to CoreDLL.dll (incorrect DLL is from WinCE)
-9 Win16 app cannot start these because we can’t inject into them
-10 Win32 app – not used
-11 Win64 app – not used
-12 .Net application
-13 User bailed out because app not linked to MSVCRT dynamically
-14 Not found in launch history
-15 DLL to inject was not found
-16 Startup directory does not exist
-17 Symbol server directory does not exist
-18 Could not build a command line
-19 No runtime specified, cannot execute script (or Java) (obsolete)
-20 Java arguments are OK – not an error (obsolete)
-21 Java agentlib supplied that is not allowed because Java Bug Validator uses it (obsolete)
-22 Java xrun supplied that is not allowed because Java Bug Validator uses it (obsolete)
-23 Java cp supplied that is not allowed because Java Bug Validator uses it (obsolete)
-24 Java classpath supplied that is not allowed because Java Bug Validator uses it (obsolete)
-25 Firefox is already running, please close it (obsolete)
-26 Lua runtime DLL version is not known (obsolete)
-27 Not compatible software
-28 InjectUsingCreateProcess, no DLL name supplied
-29 InjectUsingCreateProcess, Unable to open PE File when inspecting DLL
-30 InjectUsingCreateProcess, Invalid PE File when inspecting DLL
-31 InjectUsingCreateProcess, No Kernel32 DLL
-32 InjectUsingCreateProcess, NULL VirtualFree() from GetProcAddress
-33 InjectUsingCreateProcess, NULL GetModuleHandleW() from GetModuleHandleW
-34 InjectUsingCreateProcess, NULL LoadLibraryW() from LoadLibraryW
-35 InjectUsingCreateProcess, NULL FreeLibrary() from FreeLibrary
-36 InjectUsingCreateProcess, NULL VirtualProtect() from GetProcAddress
-37 InjectUsingCreateProcess, NULL VirtualFree() from GetProcAddress
-38 InjectUsingCreateProcess, unable to find DLL load address
-39 InjectUsingCreateProcess, unable to write to remote process’s memory
-40 InjectUsingCreateProcess, unable to read remote process’s memory
-41 InjectUsingCreateProcess, unable to resume a thread
-42 UPX compressed – cannot process such executables
-43 Java class not found in CLASSPATH
-44 Failed to launch the 32 bit svlGetProcAddressHelperUtil.exe
-45 Uknown error with svlGetProcAddressHelperUtil.exe
-46 Couldn’t load specified DLL into svlGetProcAddressHelperUtil.exe
-47 Couldn’t find function in the DLL svlGetProcAddressHelperUtil.exe
-48 Missing DLL argument svlGetProcAddressHelperUtil.exe
-49 Missing function argument svlGetProcAddressHelperUtil.exe
-50 Missing svlGetProcAddressHelperUtil.exe
-51 Target process has a manifest that requires elevation
-52 svlInjectIntoProcessHelper_x64.exe not found
-53 svlInjectIntoProcessHelper_x64.exe failed to start
-54 svlInjectIntoProcessHelper_x64.exe failed to return error code
-55 getImageBase() worked ok
-56 ReadFile() failed in getImageBase()
-57 NULL pointer when trying to allocate memory
-58 CreateFile() failed in getImageBase()
-59 ReadProcessMemory() failed in getImageBase()
-60 VirtualQueryEx() failed in getImageBase()
-61 Bad /appName argument in svlInjectIntoProcessHelper_x64.exe
-62 Bad /dllName argument in svlInjectIntoProcessHelper_x64.exe
-63 Bad /procId argument in svlInjectIntoProcessHelper_x64.exe
-64 Failed to OpenProcess in svlInjectIntoProcessHelper_x64.exe
-65 A DLL that the .exe depends upon cannot be found
Share

How to output to stdout from an MFC program

By , January 31, 2017 1:00 pm

If you’ve ever developed an MFC program with a graphical user interface and then later thought that it would be really nice to also provide a command line version with output to stdout you’ve probably bumped into the problem that there is no stdout for these programs. The program isn’t attached to a console.

So how do you do it?

The secret to this is a Win32 API “AttachConsole” which is available from Windows XP onwards.
AttachConsole takes one argument, a DWORD identifying the process to which to attach. In our case we want to attach to the parent process, so we pass ATTACH_PARENT_PROCESS which is defined as (DWORD)-1.

AllocConsole(ATTACH_PARENT_PROCESS);

When you need to print to this console use _cprintf(), which is defined in conio.h.

But we’re still working with legacy systems!

If you need your code to work on old systems as well as modern systems you’ll need to use GetProcAddress() as shown below.

#ifndef ATTACH_PARENT_PROCESS
#define ATTACH_PARENT_PROCESS	((DWORD)-1)
#endif	//ATTACH_PARENT_PROCESS

typedef BOOL (WINAPI *AttachConsole_FUNC)(DWORD);

//-NAME---------------------------------
//.DESCRIPTION..........................
//.PARAMETERS...........................
//.RETURN.CODES.........................
//--------------------------------------

BOOL attachToProcessConsole(DWORD	processId)
{
	HMODULE				hMod;

	hMod = GetModuleHandle(_T("Kernel32.dll"));
	if (hMod != NULL)
	{
		AttachConsole_FUNC	func;

		func = (AttachConsole_FUNC)GetProcAddress(hMod, "AttachConsole");
		if (func != NULL)
		{
			// valid for Windows XP onwards

			return (*func)(processId);
		}
	}

	return FALSE;
}
Share

More Effective Line Timing with C++ Performance Validator

By , December 20, 2016 5:06 pm

C++ Performance Validator has always had the ability to provide timing information for each line executed. However it has always been a bit clumsy to use – you could only enable line timing for all the methods that were being timed. This could result in slow execution due to methods you didn’t want line timed being line timed. You could filter them by file using the “Hooked Source Files” filter or the “Hooked File Types” filter, but that was about as granular as it got.

We’ve just introduced the capability to specify which classes, class methods and functions get line timed. So now, if you want to time just one function, or one method in a class, or time just the methods in a class, you can tell C++ Performance Validator to do that.

To introduce this capability we’ve had to add an additional filters setting in the Settings dialog, we’ve updated the line timing warning dialog to allow you to edit line timing filters right from the warning dialog and we’ve had to update the context menus on all the displays to include new options for line timing filters. We’ve also had to update the display of filtering information on the line timing display.

Updated Settings Dialog

We’ve added a new “Line Timing Filters” section to the settings dialog.

pvlinetimingfilters

The image above shows three filters have been selected and that only the classes, methods and functions identified by the filters will be line timed. All other class, methods and functions will be ignored for line timing. All methods in the class seoPage will be filtered for line timing, as well as the method CSpellCheckTabDlg::addWord and the function strncasecmp.

Updated Line Timing Warnings

The Line Timing Warning dialog has been updated to allow you to edit line timing filters directly from the warning dialog. The Edit Line Timing Filters… button displays the Line Timing Filters dialog previously described.

C++ Performance Validator Line Timing Warning Dialog

Updated Context Menus

We’ve added a new submenu to the context menus on all displays and renamed the existing “Instrumentation” submenu to “Function Filter (instrumentation)”. The new submenu added is “Line Timing Filter (instrumentation)”. These new context menu entries allow you to easily specify that a class, a class method or a function should be selected for line timing.

pvlinetimingmenu

How we expect these filters to be used

There are two main methods for using the line timing filters that come to mind.

  • Specify the filters ahead of time.
  • Specify the filters in response to function timing data you are looking at.

Specify the filters ahead of time

This method relies on you knowing your software code and having a very good idea which methods you think need line timing. Open the settings dialog, go to “Line Timing Filters” and enter your filters as appropriate. Be sure to set the filters to include or exclude methods for the use case you think is useful. For line timing, most of the time we expect you to be using the include setting and specifying just a few methods to line time.

pvlinetimingfilters

Specify filters based on data.

The alternative way of using line timing is in response to some profiling data that you already have. Ideally you’ll have recorded this profiling data with line timing turned off (you can turn this on/off from the launch application dialog). Once you have the profiling data, search for functions that you think will benefit from additional tuning. When you have a candidate function, right click to get the context menu, then select if you want to profile the whole class, just that class method or just that function for line timing.

pvlinetimingfilterexample

At the point you add line timing filters, if you have no line timing filters or the line timing filters are set to profile all classes, methods and functions, then the line timing filters will be set to profile just the classes, methods and functions identified by the filters. However, if there are pre-existing filters, the include/exclude setting of the line timing filter will not be changed – you will need to visit the Line Timing Filters settings dialog to change that setting if you want to switch from include to exclude or exclude to include. This design is deliberate as the main use case is to profile one or two methods (or all the methods in one class) – that use case is “include”, which is why this is the default behaviour.

If you have already recorded a session with line timing data and you specify some line timing filters, C++ Performance Validator will update the line timing display to grey out the classes, class methods and functions that will not be instrumented on the next run.

pvlinetimingresultsfiltered

Line Timing Results

Once you have the filter(s) you need setup you can run a new profiling session to get the line timing data you need.

pvlinetimingresults

Share

The Software Updates Menu

By , December 20, 2016 12:35 pm

We introduced the Software Updates menu in 2012. This coincided with the introduction of automatic software updates. Various bug fixes have been applied to the software update software since then. But we’ve done nothing with the software updates menu at all. Until recently.

In response to some unusual problems a few of our customers have had we thought we could improve the experience of using the software updates function.

Problems with authentication

One customer reported that although he’d entered his login credentials, the login and download were failing. But strangely our software wasn’t prompting him for a correct set of login credentials (which is what should happen). After some investigation we found that some failures on our server were being amalgamated into one global error code sent back to the client. The global error code was then interpreted correctly according to the error code, but that response wasn’t correct with respect to the specific error on the server. We broke all the failure points on the server into discrete error codes and now handle all of these individually. This allowed the problem the customer had to come to the surface – their credentials were in fact incorrect – he’d made a typo while entering his details.

In addition to this we’ve now made changes to the email entry fields that validate only correct characters can be entered in an email address – enter any incorrect ones and the field turns red. Not enough @ characters – red, too many @ characters – red, whitespace in the user name which isn’t quoted – red, whitespace in the domain – red. Etc.

softwareupdatesbademailaddress

There is also the error use case where a customer enters their login details for, say, C++ Performance Validator but the tool they are using when they enter these details is C++ Memory Validator. The login details are valid, but not for this software tool. The image below shows the error message when using the Test Login Details… button.

softwareupdatesbadlogin

We also added two new menu entries for resetting the user credentials and also for setting the user credentials. If the user credentials are reset, no software updates will occur. If the user credentials are set (correctly) software updates will occur.

softwareupdatessetcredentials

Problems with TMP security

When a software update for one of our tools downloads it’s downloaded by default to the directory defined by the TMP environment variable. On a Windows 10 machine this most likely points somewhere like c:\users\stephen\AppData\Local\Temp.

The TMP environment variable is used by the _ttempnam() function to provide a temporary filename for use by the software that calls it. _ttempnam() uses the TMP environment variable to do it’s job. We wrote the software updater code, tested it, and didn’t really think much more about it until we recently received an email from a customer. I’m going to quote a bit of it below.

I am an IT manager for a software house that uses your Performance
Validator and Memory Validator. With the new threats from ransomware
we have locked down developers machines so files cannot be executed
under the users Appdata folders which contains the users temp folder.

He wanted to know what our filename policy was so that he could whitelist our software updater to run inside the directory that he’d locked down. _ttempnam() returns names that are different each time. There is a pattern to the names we use. I explained the rules but then suggested that providing a dedicated download directory removes the need for whitelisting and provides a better security environment. He agreed. So that’s what I’m going to discuss next.

Specifying a directory

The first thing we had to do is replace the use of _ttempnam() with a user specified directory.

The user specified directory defaults to the same location that _ttempnam() would have used. Consult the _ttempnam() documentation and follow the rules for generating the default value. This is basically using GetEnvironmentVariable() to query the TMP environment variable.

Provide a means for the user to specify the download directory.

softwareupdatedirectorydefault

The directory needs to exist. If the directory doesn’t exist, it should be obvious as the directory name is entered.

softwareupdatedirectoryerror

The directory needs to have execute privileges and write privileges. If either of these privileges does not exist for the specified directory the user should be alerted to the fact.

softwareupdatedirectorynowrite

softwareupdatedirectorynoexecute

The Reset button allows the directory to be set to the default value.

Add an entry to the Software Updates menu to enable the user to access this dialog. Update the Startup Wizard to allow the software update directory to be specified.

softwareupdatedirectory

We’ve also updated the software update code to handle the use cases where a valid software update directory is supplied but is then deleted, or it’s permissions altered to deny write or deny execute privileges. This also accommodates the case where nothing changes with the directory but the settings get damaged or corrupted somehow (editing the registry, a machine crash…).

Conclusion

We’re always trying to improve your experience with our software. Whether it’s making the use of Software Updates so easy you don’t need to talk to us about it, or improving your security environment. If you have an issue that you think will improve the software for everyone please do get in touch.

Share

The Startup Wizard

By , December 20, 2016 11:33 am

We’ve just added a new Startup Wizard to all our C++ tools.

The purpose of the startup wizard is to unify the various different dialogs that would be shown the first time the software ran. These would configure different aspects of the software. Placing them all in one place, a wizard, provides a better first run experience. We’ve also taken this opportunity to configure software updates for non-evaluation users of the software. As a final touch, we’ve included an overview video for the software.

Introduction

The first panel explains what’s going to happen next.

startupwizardintroduction

Symbol Environment Variables

The second panel allows you to configure which environment variables (if any) you wish to use to control the symbol search process for symbols contained in PDB files.

startupwizardenvvars

IDE / Compiler

The third panel allows you to configure which IDE / Compiler / Linker you are using. This is important as it affects how symbol lookup is performed (Visual Studio has various quirks in its history of symbol handling, we have to work around that).

startupwizardidecompiler

Software Updates

The next panel is not shown to people evaluating the software. Only purchasing customers see this panel. The panel allows you to configure your software update information and also where the software updates are downloaded to.

It is important to be able to specify where they are downloaded to because of potential security risks that arise from allowing the TMP directory (c:\users\[username]\AppData\Local\Temp) to be executable. We use the TMP directory as a default, but if you think that’s not a good idea you can specify your own download directory and set permissions for TMP to deny execute privileges.

startupwizardsoftwareupdates

Video Overview

The final panel is a video overview of the software. The purpose of this video is to quickly show many areas of the software to encourage you to explore the capabilities of the software.

startupwizardvideooverview

Share

Working with Dev C++

By , December 14, 2016 5:57 pm

We’ve had a few people asking how to configure C++ Memory Validator to work with programs built using Dev C++. Dev C++ is an IDE used for developing programs with the MingW compiler.

We tested using this download of Dev C++.

Debug information

Any program built using the default Dev C++ settings will generate a binary image that contains debugging information that is in a format that our tools cannot read. The MingW compiler can create debug information in many formats, including COFF and STABS, both of which our tools support. You can turn these debugging formats on using the -gCoff and -gstabs flags. We recommend using STABS symbols.

Configuring Dev C++

Open the Project Options… dialog from the Project menu.
devcppprojectmenu

Choose the Parameters tab. Add the option -gstabs to all three columns. Click OK.
devcpp_debugoptions

Now that you have configured the debug options all you need to do is to rebuild your project to ensure the debug information is present.

Share

The correct way to determine if a file is a directory.

By , November 30, 2016 1:41 pm

After writing for Microsoft’s Windows platform for 20 years I thought I knew all I could know about GetFileAttributes(). Until I found a rather odd and subtle bug in some code that interacted with data supplied by the user of the software. A call would succeed that I expected to fail. Naturally this meant the software didn’t make the right choices and instead of being presented with an helpful dialog explaining what had failed, the software sat silently in a corner humming to itself waiting for the user to work out what had happened. The failure was that I was presenting incorrect data to GetFileAttributes() assuming that it would always fail for bad input. How wrong I was!

I thought I’d write up what can go wrong with GetFileAttributes().

It’s tempting to test if a file is a directory by writing code like this:

if ((GetFileAttributes(fileName) & FILE_ATTRIBUTE_DIRECTORY) != 0)
{
    // file is a directory
}

The above looks logically correct. But there are problems with it.

First, a refresher on file attribute values…

File Attributes

The list of defined file attributes is in WinNT.h. The values are shown below.

#define FILE_ATTRIBUTE_READONLY             0x00000001  
#define FILE_ATTRIBUTE_HIDDEN               0x00000002  
#define FILE_ATTRIBUTE_SYSTEM               0x00000004  
#define FILE_ATTRIBUTE_DIRECTORY            0x00000010  
#define FILE_ATTRIBUTE_ARCHIVE              0x00000020  
#define FILE_ATTRIBUTE_DEVICE               0x00000040  
#define FILE_ATTRIBUTE_NORMAL               0x00000080  
#define FILE_ATTRIBUTE_TEMPORARY            0x00000100  
#define FILE_ATTRIBUTE_SPARSE_FILE          0x00000200  
#define FILE_ATTRIBUTE_REPARSE_POINT        0x00000400  
#define FILE_ATTRIBUTE_COMPRESSED           0x00000800  
#define FILE_ATTRIBUTE_OFFLINE              0x00001000  
#define FILE_ATTRIBUTE_NOT_CONTENT_INDEXED  0x00002000  
#define FILE_ATTRIBUTE_ENCRYPTED            0x00004000  
#define FILE_ATTRIBUTE_VIRTUAL              0x00010000  

Rather strangely, the invalid attributes flag is defined in a different file, WinBase.h.

#define INVALID_FILE_ATTRIBUTES ((DWORD)-1)

Problem 1

What if GetFileAttributes() fails? If the file doesn’t exist, the call fails. If the filename specifies a computer name, the call fails. See GetFileAttributes() documentation for more informtion. When GetFileAttributes() fails it returns INVALID_FILE_ATTRIBUTES. This error status passes the above test. OK, so add an additional check and the code becomes

DWORD attribs;

attribs = GetFileAttributes(fileName);
if ((attribs != INVALID_FILE_ATTRIBUTES) &&
    ((attribs & FILE_ATTRIBUTE_DIRECTORY) != 0))
{
    // file is a directory
}

Problem 2

Even with the above file-does-not-exist problem solved there is another problem. The file could be a directory, but it could be a directory that you don’t want. For example what if you’ve allowed the user to specify the directory name and they typed _T(“/”), or what if your filename creation code has a bug in it that fails when passed an empty name, resulting in a calculated filename of _T(“\”). What then?

In these cases the following calls all return 0x16.

GetFileAttributes(_T("\\")); 
GetFileAttributes(_T("/"));

0x16 means hidden (0x02), system (0x04), directory (0x10).

It’s a reasonable bet that in your code, any code looking for a directory to use is probably not looking for a hidden directory and almost certainly not intending to use a system directory. OK, time for a new implementation.

DWORD attribs;

attribs = GetFileAttributes(fileName);
if ((attribs != INVALID_FILE_ATTRIBUTES) &&         // check if a valid file
    ((attribs & FILE_ATTRIBUTE_DIRECTORY) != 0) &&  // file is a directory
    ((attribs & FILE_ATTRIBUTE_HIDDEN) == 0) &&     // file is not hidden
    ((attribs & FILE_ATTRIBUTE_SYSTEM) == 0))       // file is not system
{
    // file is a directory that isn't hidden and isn't system
}

What about files, rather than directories?

It’s natural to think about implementing checks for if a filename identifies a file rather than a directory. You test for this in exactly the same way but looking for different attributes. You’ll want to exclude FILE_ATTRIBUTE_DIRECTORY and then depending on the job your code is doing you’ll want to consider excluding files depending upon the following attributes:

FILE_ATTRIBUTE_DEVICE
FILE_ATTRIBUTE_INTEGRITY_STREAM
FILE_ATTRIBUTE_OFFLINE
FILE_ATTRIBUTE_REPARSE_POINT
FILE_ATTRIBUTE_SPARSE_FILE
FILE_ATTRIBUTE_TEMPORARY
FILE_ATTRIBUTE_VIRTUAL

and of course, you might also want to consider FILE_ATTRIBUTE_HIDDEN and FILE_ATTRIBUTE_SYSTEM.

Additional reading

Microsoft documentation on GetFileAttributes().

Why is GetFileAttributes the way old-timers test file existence? Old New Thing.

Share

How to make your MFC (or non MFC) program support high DPI monitors. The easy way.

By , November 29, 2016 10:35 am

A few years ago Microsoft introduced support for very high resolution monitors. Such monitors might have nearly 4000 pixels horizontally and over 2000 pixels vertically. This provides wonderful scope for editing photographs and providing great graphics. But at your normal scaling the text on your displays starts looking a bit small. This is typical if you’ve specified your text height in pixels. Which from what I’ve seen is how a lot of people (most?) do it. “Oh, 14 look looks OK, I’ll go with that”. Looks fab for a 21 year. No so good for your customers over 40 though, most of them are wearing glasses, and so will you be when you get there (or contacts, you know what I mean).

This approach isn’t going to work with high DPI displays. You need to think in terms of point sizes for text, like you would if you are printing a document. Once you have an appropriate point size for your normal non-scaled (non high-DPI) display you can then just get the correct font height in pixels (based on the point size) and multiply by a scale factor to determine the correct column width for any columns in grids, or any graphics that need scaling.

Getting the scale factor

Non scaled monitors are deemed to be monitors that display at 96 DPI. From this the following table follows:

DPI scaling
96 100%
120 125%
144 150%
192 200%

You can calculate a scale factor for the whole system, or a scale factor per monitor – if you have monitors with different display facilities. The ability to calculate a scale factor per monitor only works on Windows 8.1 and Windows 10 onwards. You can find out more about this using the GetDpiForMonitor() function. For the purposes of this discussion we will only cover getting the system scale factor.

If you are thinking of doing per-monitor scaling, you might be well served by first doing system wide scaling, because it is simpler and will introduce you to most of the problems you will face, but in an easier to manage problem space than per-monitor scaling. Stepping from a working system wide scaling to per monitor scaling should be a lot easier than going from non-scaling to per-monitor scaling in one go.

double getSystemScaleFactor()				// returns 1.0 for no scaling
{
	// some simple caching to speed things up

	static int		calculatedScale = FALSE;
	static double	scale = 0;

	if (calculatedScale)
		return scale;

	// get DPI for the system

	HDC		hdc;

	hdc = ::GetDC(NULL);

	UINT	dpiX = 96;		// default DPI

	dpiX = GetDeviceCaps(hdc, LOGPIXELSX);

	scale = (double)dpiX / 96.0;
	if (scale < 1.0)
		scale = 1.0;		// prevent funny business with super large monitors returning very low DPI values

	::ReleaseDC(NULL, hdc);

	calculatedScale = TRUE;
	return scale;
}

We also need some supporting functions that will assist us during the following phases.


  • We need to be able to calculate a font point size based on a font pixel height.

  • We need to be able to calculate a font pixel height based on a font point size.

Getting a font point size

Win32

double getFontPointSize(HDC		hDC,
			DWORD	pixelSize)
{
	LONG	pixPerInch = GetDeviceCaps(hDC, LOGPIXELSY);
	double	points = (pixelSize * 72.0) / pixPerInch;

	return points;
}

MFC

double getFontPointSize(CDC		*dc,
			DWORD	pixelSize)
{
	if (dc == NULL)
	{
		return getFontPointSizeForDefaultDC(pixelSize);
	}
	else
	{
		return getFontPointSize(dc->GetSafeHdc(), pixelSize);
	}
}

Getting a font pixel height

LONG getFontHeightForDefaultDC(double	pointSize)		// returns font height for the appropriate screen
{
	// this function can be optimized by using a simple cache on the point size (worth it in some situations!)

	HDC		hdc;

	hdc = ::GetDC(NULL);
	height = getFontHeight(hdc, pointSize);
	::ReleaseDC(NULL, hdc);
}
LONG getFontHeight(HDC		hDC,
		   double	pointSize)		// returns font height for the appropriate screen
{
	LONG	pixPerInch = GetDeviceCaps(hDC, LOGPIXELSY);
	LONG	lfHeight = (LONG)((pointSize * (double)pixPerInch) / 72.0);

	return lfHeight;
}
LONG getFontHeight(CDC		*dc,			// can be NULL for default screen
		   double	pointSize)		// returns font height for the appropriate screen
{
	if (dc == NULL)
	{
		return getFontHeightForDefaultDC(pointSize);
	}
	else
	{
		return getFontHeight(dc->GetSafeHdc(), pointSize);
	}
}

Updating your code

Versions of MFC since Visual Studio 2010 support MFC scaling, although nothing happens by default. You still have to supply the scale factor for the fonts in use. These fonts will mainly be for any text drawn on graphics displays and for any owner drawn controls such as lists and grid controls which sit on top of huge amounts of data that can't be reliably stored inside the control. You need to find the places where you create fonts and set the font height. Often this will be when you create a font or when you set the lfHeight member of a LOGFONT structure. Once we have these locations we can update the code.

The sequence of operations to update your code is as follows:


  • You need to find each location where a font height is set.

  • For each location you need to calculate the point height and replace the integer height with a call to calculate a height based on a point height.

  • For each location verify that the calculated height is correct for a non-scaled display.

  • For each location verify that the font now scales with the display.

Because the new height calculated by getFontHeight() is based on a point size we don't need to worry about scaling the returned value with getSystemScaleFactor(), the point size is relative to how we view the display, so is automatically the correct scale.

CreateFont

Search for uses of the MFC method CFont::CreateFont(), or the Win32 function ::CreateFont(). The first parameter is the height of the font.

#define FONT_SIZE		16

...

font.CreateFont(FONT_SIZE,		// default height
		0,			// default width
		0,
		0,
		FW_NORMAL,
		FALSE,
		FALSE,
		FALSE,
		DEFAULT_CHARSET,
		OUT_TT_PRECIS,
		CLIP_DEFAULT_PRECIS,
		PROOF_QUALITY,
		FF_DONTCARE,
		_T("Arial"));

We need to know what point size of the specified font equates to our chosen (above) font height of 16 pixels. The font height may change according to the font face, italics, weight etc. So you do need to check this for each one and not just rely on it being what you think it is. This is why I haven't provided a table of values for you to plug in.

We can calculate the font point size using the following code, passing in the device context of the appropriate display.

double testPointSize;

testPointSize = getFontPointSize(hDC, FONT_SIZE);

Just add the code before the call to CreateFont, build it, put a breakpoint there and when it stops in the debugger make a note of the value testPointSize. In our tests, a point size of 11.0 gave us a pixel height of 16 (FONT_SIZE).

Once we know testPointSize we can update the code, as shown below. We notated our code with info about how it was tested to help anyone that ends up at this code wondering why these sizes are used. People won't always search the version history and disk space is cheap. Comments help. Use them.

LONG	lfHeight;

lfHeight = getFontHeight(GetDC(), 11.0);		// aiming for lfHeight == 16 (at "small" 1920x1080);

// set font for tab control

font.CreateFont(lfHeight,		// default height (used to be hard coded at 16)
		0,			// default width
		0,
		0,
		FW_NORMAL,
		FALSE,
		FALSE,
		FALSE,
		DEFAULT_CHARSET,
		OUT_TT_PRECIS,
		CLIP_DEFAULT_PRECIS,
		PROOF_QUALITY,
		FF_DONTCARE,
		_T("Arial"));

If the above font is going to be created many times, caching the value of getSystemScaleFactor() and/or caching the fonts created will be a good idea.

CreateFontIndirect

Search for uses of the MFC method CFont::CreateFontIndirect(), or the Win32 function ::CreateFontIndirect(). With this usage you are interested in the lfHeight value of the LOGFONT structure passed to CreateFontIndirect. The procedure is identical as for CreateFont() described above.

Before scaling:

pDispInfo->item.lfFont.lfWeight = FW_NORMAL;
pDispInfo->item.lfFont.lfHeight = -11;

After scaling:

pDispInfo->item.lfFont.lfWeight = FW_NORMAL;
pDispInfo->item.lfFont.lfHeight = -getFontHeightForDefaultDC(8.5);		// aiming for lfHeight == -11 (at "small" 1920x1080);

Alternative implementation

Ignore point sizes and just multiply your existing font pixel heights by getSystemScaleFactor(). This is less work to implement, but results in lots of calls to getSystemScaleFactor() throughout your code base. If you eventually decide to implement per-monitor scaling I think you will regret choosing this implementation method.

pDispInfo->item.lfFont.lfWeight = FW_NORMAL;
pDispInfo->item.lfFont.lfHeight = -11 * getSystemScaleFactor();

Setting column widths

If you are setting column widths in a grid control you'll need to scale these appropriately.

Before scaling:

m_Grid.SetColumnWidth(0, 200);
m_Grid.SetColumnWidth(1, 400);

After scaling:

 double scaleFactor;

scaleFactor = getSystemScaleFactor();
m_Grid.SetColumnWidth(0, (UINT)(200 * scaleFactor));
m_Grid.SetColumnWidth(1, (UINT)(400 * scaleFactor));

Implementation note: For per-monitor scaling you'll need to update these calls to get the scale factor for the appropriate monitor.

How to test

To test you don't need to purchase a high DPI monitor. Although if you do have a high DPI monitor you will be more aware of the issues your high DPI using users have.


  • Change the text scaling size from 100% to another value.

  • Right click on the Windows desktop.

  • Choose Screen Resolution from the context menu.
    dpicontextmenu


  • Click the link Make text and other items larger or smaller.
    dpiscreenresolution


  • Change the text size slider larger or smaller as appropriate. Notice that as you change it a warning notice is displayed about different scaling results. Click Apply. Now close the dialog box.
    dpichangetextsize


When you click Apply you should notice the text and graphics scale up or down in relation to the choice you made. "Aha! I've done it!" you think. Not so fast. This is just Windows pretending everything is OK. You'll notice some of the items that have been scaled up don't look so perfect, a bit blurry. This is because this is Windows virtualising the scaling. This also means that many of the fonts you need to change will have been automatically changed for you - for this session only! Don't let this fool you into thinking your job is done. If you want to see what they'll look like for your customers you need to log out and then log back in. You don't need to shutdown the computer or reboot it. Just log out and then log in (not lock the screen and log in!).

When you log in, start your application. Now look at the text you are updating to be responsive to scaling. Has it scaled? Yes? Great! No? You must have missed something. Failed to update it to work from point sizes? Columns too narrow? Forgot to add the multiply by scaling factor? Debug as necessary. Just add developer.

When you've got this working, you're going to have to go back to normal font size (same procedure as before, but just move the text slider all the way to the left), then log out and log in. Now do your tests for the next font. Rinse and repeat. You are going to be logging out and logging back in a lot during this work. Or purchase a modern laptop with a QHD screen or a nice QHD monitor (or better!).

Optimisations and improvements

Namespaces.
We've placed all the helper functions I described in their own namespace to separate them from the rest of the code.

Caching.
We've also used some caches to improve the query time of some requests. We didn't find this bothersome until we had a grid with a lot of items (about a million) that wanted to do a specific query. We thought the delay was unacceptable. Adding a cache fixed that quite simply.

Per Monitor DPI.
You could look into supporting different DPI values for each monitor. See the GetDpiForMonitor() link we mentioned earlier in the article.

Icons.
You may also need to scale icons (in toolbars etc) accordingly. In practice we haven't found this to be a huge problem so long as your non-scaled icons are reasonably large. Ideally you could scale your icons, but we've found scaling icons to be the least important thing for users of our software. What they really want is legible text, hence our focus on that in this article.

X and Y scaling.
If you've been paying attention you've noticed that getSystemScaleFactor() returns the horizontal scale factor. We've used this everywhere, for both scaling of font heights (vertical) and scaling of column widths (horizontal). You could choose to calculate a vertical scale factor and use that for the scaling of font heights.

The vertical scale factor should be the same (barring any hardware restrictions) to the horizontal scale factor in order to preserve image integrity - so we haven't bothered to calculate a vertical scaling factor. In practice we've been happy with the results returned from using one scale factor.

Conclusion

Now you know everything you need to know to get your Win32 or MFC program displaying well on high DPI displays. There's a lot of great information about this on the web, but reading it can make it seem quite daunting, when in actual fact it's very straight forward. We recommend changing one font at a time, then test, then check with a scaled display, then move on to the next font. Don't try to do them all at once.

If you're testing without a high DPI display, once you think it's all good to go, show it to some of your customers that have high DPI displays that have been bending your ear about supporting high DPI displays. They'll be pleased to tell you what isn't quite right. Across all our applications, we missed on thing that a customer spotted.

I'd also like to extend a big Thank You to Anna Jayne Metcalfe at Riverblade for some helpful pointers when we started this work. Riverblade make the excellent Visual Lint static analysis tool analyser, an excellent complement to our dynamic analysis tools.

Further Reading

Writing Win32 applications and high DPI.

MFC applications default to being DPI aware.

Getting the DPI per Monitor.

Getting the Logical Monitor Width.

High DPI Tutorial.

Share

We’ve been quiet for a while, sorry about that.

By , November 28, 2016 3:29 pm

Hi,

It’s been a while since we posted anything on the blog. If you weren’t a customer, regularly receiving our software update emails you might think we weren’t going anything.

That’s an oversight on our part. We’re hoping to rectify this over the next few months, posting more useful information both here and in the library.

_tempnam and friends

Our most recent update has been to update C++ Memory Validator provide memory tracking for the _tempnam group of functions. These are _tempnam, _tempnam_dbg, _wtempnam, _wtempnam_dbg.

This support is for all supported compilers, from Visual Studio 2015, Visual Studio 2013, Visual Studio 2012, Visual Studio 2010, Visual Studio 2008, Visual Studio 2005, Visual Studio 2003, Visual Studio 2002, Visual Studio 6. Delphi and C++ Builder, Metrowerks compiler, MingW compiler.

.Net support, for the future

Internal versions of C++ Coverage Validator can provide code coverage statistics for .Net (C#, VB.Net, J#, F#, etc) as well as native languages (C++, C, Delphi, Fortran 95, etc).

Internal versions of C++ Performance Validator can provide performance profiling statistics for .Net (C#, VB.Net, J#, F#, etc) as well as native languages (C++, C, Delphi, Fortran 95, etc).

UX improvements

All tools, free and paid, have had the UX for filename and directory editing improved so that if a filename doesn’t exist it is displayed in red and if it does exist it is displayed in it’s normal colour (typically black). See screenshots (from Windows 8.1).

Non existent filename:
filenamebad

Existing filename:
filenamegood

Share

Panorama Theme by Themocracy