Tag Archives: C++

Common Controls: Versions, Compatibility, WTL

An application appears to be not working in Windows XP in a weird way: rebar control appeared to fail showing up.

Where the application is expected to look much nicer with rebar control as a container for menu (implemented as command bar WTL control) and toolbar with buttons:

A WTL sample project generated by a Visual Studio wizard would never give such effect, and the bug was a combination of factors:

  1. An application built with a newer version of Windows SDK, which includes support for features (Windows Vista+ Common Controls) that are more recent than production environment (Windows XP); the application targets to Windows Vista+ environment too (_WIN32_WINNT >= 0×0600)
  2. Compatibility issues of Common Controls library
  3. WTL version (7.5), which did not yet include a workaround for the problem

The problem, which caused the bug directly was the REBARBANDINFO structure and its use as an argument with Common Controls API. As MSDN shows, the structure was amended twice with additional fields.

One of the way to support multiple versions of the structure definition, and to resolve compatibility issues, is to embed structure size into structure payload. In fact, REBARBANDINFO::cbSize member is there exactly for this reason.

The application is normally filling cbSize with the maximal known structure size and fills the rest of the fields respectively. The API is expected to be checking cbSize member and be detecting API version compatibility scenarios:

  1. cbSize holds exactly the value the API expects (that is, the maximal value known/defined to the API) – the simplest scenario where the API and the application are on the same page, both are using the same versions of the “protocol”/interface.
  2. cbSize is smaller than API can support – the API sees that it is dealing with a sort of legacy application which cannot utilize all available features, and the API acts respectively supporting the older part of the protocol, and keeping defaults or “old look” for the rest of implementation. This addresses backward compatibility: the newer API works with apps designed for older version of the API
  3. cbSize is greater then API can support – the API sees that the application is already aware of newer version API and is possibly requesting some of the missing features. The API might be ignoring the unsupported part in assumption that API evolution tool place keeping some compatibility in mind, and still do the best it can with the existing implementation. Or, the API might just fail to work.

The latter item #3 is the scenario here with rebar control. The application is using Windows Vista version of REBARBANDINFO structure and Windows XP implementation choses to completely fail.

While it does not seem to be directly a bug, this attitude is definitely not developer friendly: there is no reason for the control to not work in its best and default way. Having API acting this way, each developer using the API needs to take care of the situation explicitly: whenever Windows Vista enabled application needs to be able to run in Windows XP system, the code around REBARBANDINFO would look like this:

REBARBANDINFO BandInformation = { sizeof BandInformation, RBBIM_LPARAM };
#if _WIN32_WINNT >= 0x0600
if(GetOsVersion() < 0x00060000 || GetCommCtrlVersion() < 0x00060000) // pre-Vista, Common Controls pre-6.0
    BandInformation.cbSize = REBARBANDINFO_V6_SIZE;
#endif// _WIN32_WINNT >= 0x0600
const BOOL bGetBandInfoResult = Rebar.GetBandInfo(0, &BandInformation);

If the API was nicer to developers, the code would be plain and simple:

REBARBANDINFO BandInformation = { sizeof BandInformation, RBBIM_LPARAM };
const BOOL bGetBandInfoResult = Rebar.GetBandInfo(0, &BandInformation);

To address this problem, WTL 8.0 comes up with RunTimeHelper namespace and its SizeOf_REBARBANDINFO function. It takes care of details for the developer choosing the proper size of the structure on runtime. The code is being taken back to a simpler shape:

REBARBANDINFO BandInformation = { RunTimeHelper::SizeOf_REBARBANDINFO(), RBBIM_LPARAM };
const BOOL bGetBandInfoResult = Rebar.GetBandInfo(0, &BandInformation);

All in all:

  • be aware of compatibility issues (same scenario exists with other SDK structures: LVGROUP, LVTILEINFO, MCHITTESTINFO, NONCLIENTMETRICS and other).
  • use latest version of WTL to have things worked around for you where Microsoft developers were not kid enough to provide perfect API
  • be aware and take advantage of WTL’s RunTimeHelper class

Sample: Simultaneous Audio Playback via Waveform Audio (waveOut) API

The minimalistic sample demonstrates support of [deprecated] Waveform Audio API for multiple playback streams.

Depending on command line parameters, the application starts threads to open audio hardware using separate waveOutOpen call and stream one or more generated sine waves:

  • 1,000 Hz sine wave as 22,050 Hz, Mono, 16-bit PCM (command line parameter “a”)
  • 5,000 Hz sine wave as 32,000 Hz, Mono, 16-bit PCM (command line parameter “b”)
  • 15,000 Hz sine wave as 44,100 Hz, Mono, 16-bit PCM (command line parameter “c”)
Check(waveOutOpen(&hWaveOut, WAVE_MAPPER, &WaveFormatEx, NULL, NULL, CALLBACK_NULL));
ATLASSERT(hWaveOut);
WAVEHDR* pWaveHeader;
HGLOBAL hWaveHeader = (WAVEHDR*) GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, sizeof *pWaveHeader + WaveFormatEx.nAvgBytesPerSec * 10);
pWaveHeader = (WAVEHDR*) GlobalLock(hWaveHeader);
ATLENSURE_THROW(pWaveHeader, E_OUTOFMEMORY);
pWaveHeader->lpData = (LPSTR) (BYTE*) (pWaveHeader + 1);
pWaveHeader->dwBufferLength = WaveFormatEx.nAvgBytesPerSec * 10;
//pWaveHeader->dwUser = 
pWaveHeader->dwFlags = 0;
pWaveHeader->dwLoops = 0;
#pragma region Generate Actual Data
{
    SHORT* pnData = (SHORT*) pWaveHeader->lpData;
    SIZE_T nDataCount = pWaveHeader->dwBufferLength / sizeof *pnData;
    for(SIZE_T nIndex = 0; nIndex < nDataCount; nIndex++)
    pnData[nIndex] = (SHORT) (32000 * sin(1.0 * nIndex / WaveFormatEx.nSamplesPerSec * nFrequency * 2 * M_PI));
}
#pragma endregion 
Check(waveOutPrepareHeader(hWaveOut, pWaveHeader, sizeof *pWaveHeader));
Check(waveOutWrite(hWaveOut, pWaveHeader, sizeof *pWaveHeader));
GlobalUnlock(hWaveHeader);

The operating system is supposed to mix the waves, which can be easily perceived taking place. It is possible to run the application with multiple waveforms within a process, e.g. “abc” command line parameter, and/or start multiple instances of the application.

A binary [Win32] and partial Visual C++ .NET 2010 source code are available from SVN.

Utility Clearance: Generate PCM .WAV File

GeneratePcmWavFile tool is generating PCM .WAV files with requested parameters, silent or with a sine wave data. The utility uses WavDest SDK Sample as a multiplexer, so it is expected to be available.

It is possible to define the following audio data parameters:

  • sampling frequency, number of samples per second, such as 44100 or 48000
  • number of channels; the utility does not constrain this to be stereo or 5.1, it will be able to create 64 or 128 channel audio data as well
  • 8 or 16 bit audio samples
    • 16-bit PCM only: sine wave signal parameters, frequency in Hz and amplitude/loudness relative to full scale, that is 0 dB is maximal loudness, and an argument of 23 will result in -23 dB loud audio (such as -23 dbFS, also see EBU R128 Specification, the signal depending on frequency may be used a reference source for normalized -23 LUFS audio)
  • file duration in seconds
Microsoft Windows [Version 6.1.7601]
Copyright (c) 2009 Microsoft Corporation.  All rights reserved.

C:\>GeneratePcmWavFile.exe
Syntax: GeneratePcmWavFile <options> <output-path>
  /s:N: Sampling Rate N, Hz
  /c:N: Channel Count N
  /b:N: Sample Bit Count N, 8 or 16
  /t:N: Length N, seconds
  /f:N: Sine Signal Frequency N, Hz
  /l:N: Sine Signal Loudness N, dB below full scale
  /n:N: Noise Signal Loudness N, dB below full scale

A binary [Win32] and partial Visual C++ .NET 2010 source code are available from SVN.

UPDATE: An extra /n parameter lets the application add random noise within provided loudness parameter.

Utility Clearance: Artificial CPU Load

The LoadCpu tool creates one or more threads to waste CPU cycles and emulate slower environment, such as for testing and troubleshooting. There has been a great deal of similar tools out there, and this one in particular is convenient/special as it takes process affinity mask as a parameter.

That is, /a:NN defines affinity mask, e.g. 5 for 1st and 3rd CPU (two threads), and /r:MM defines [roughly] CPU load time in percent.

Microsoft Windows [Version 6.1.7601]
Copyright (c) 2009 Microsoft Corporation.  All rights reserved.

C:\>loadcpu /a:5 /r:25
System Affinity Mask: 0xff
Process Affinity Mask: 0x05
Rate: 25%

A binary [Win32] and partial Visual C++ .NET 2010 source code are available from SVN.

ATLENSURE_SUCCEEDED double failure

A colleague pointed out that code snippet in previous post is misusing ATL’s ATLENSURE_SUCCEEDED macro making it [possibly] evaluate its argument twice in case of failure, that is evaluating into failure HRESULT code. As it is defined like this:

#define ATLENSURE_SUCCEEDED(hr) ATLENSURE_THROW(SUCCEEDED(hr), hr)

It does things in a straightforward way, for a code line

ATLENSURE_SUCCEEDED(pFilterGraph.CoCreateInstance(CLSID_FilterGraph));

It is doing “let’s CoCreateInstance the thing and if it fails, let’s CoCreateInstance it again to find out error code”. Disassembly shows this clearly:

This is exactly another spin of the story previously happened with HRESULT_FROM_WIN32 macro and possibly a number of others. With it being originally a macro, SDK offered an option to override the definition by pre-defining INLINE_HRESULT_FROM_WIN32. This way a user might be explicitly requesting a safer definition while still leaving legacy code live with macro. See more detailed story on this in Matthew’s blog.

A tricky thing is that with successful execution the problem does not come up. In case of failure, it depends on the functions called, some with just repeat the error code, some will return a different code on second run, some might create less desired and expected consequences. So you can find yourself having written quite some code before you even suspect a problem.

Having identified the issue, there are a few solutions.

1. First of all, the original ATLENSURE_SUCCEEDED macro can still be used, provided that you don’t put expressions as arguments.

This is going to do just fine:

const HRESULT nCoCreateInstanceResult = pFilterGraph.CoCreateInstance(CLSID_FilterGraph);
ATLENSURE_SUCCEEDED(nCoCreateInstanceResult);

2. Second straightforward way is to replace the original ATL definition in ATL code (boo, woodenly)

3. As ATL code is checking for the macros to be already defined, and skipping its own definition in such case, it is possible to inject a safer private definition before including ATL headers (which would typically need one to do the define in stdafx.h):

#define ATLENSURE_SUCCEEDED(x){ const HRESULT nResult =(x); ATLENSURE_THROW(SUCCEEDED(nResult), nResult); }

#include <atlbase.h>
#include <atlstr.h>

Pre-evaluating the argument into local variable is going to resolve the original multi-evaluation problem.

4. There might be a new inline function defined on top of the original macro, which will be used instead and which is free from the problem:

inline VOID ATLENSURE_INLINE_SUCCEEDED(HRESULT nResult)
{
    ATLENSURE_SUCCEEDED(nResult);
}

Either way, the correct code compiles into single argument evaluation and throws an exception with failure code immediately:

Also, vote for the suggestion on Microsoft Connect. The issue is marked as fixed in future version of Visual Studio.

LogProcessExceptions: Minidumps on User Request

An updated version of LogProcessExceptions utility is given an additional option to create minidump .DMP files for debugged process on user request. This is in particular useful in conjunction with flag choices (on the previous page of the wizard).

Partial Visual C++ .NET 2010 source code is available from SVN, release binary included (Win32, x64).

Using IP Video Source programmatically as a standalone DirectShow Video Source Filter for JPEG and M-JPEG IP Cameras

Recent IP Video Source class/module is not limited to use via registration as a video input/capture/source device. The filter is also registered as a regular filter and can be used programmatically in a regular way: CoCreateInstance, AddFilter etc.

A C++ code snippet below shows how to import interface definition, create an instance of the filter, set it up and start video from a camera:

#include <dshow.h>
#pragma comment(lib, "strmiids.lib")

#import "libid:BDCE8B49-8895-4605-8278-E9A1FBC889AC" no_namespace raw_interfaces_only raw_dispinterfaces named_guids // IpVideoSource

// [...]

inline VOID ATLENSURE_INLINE_SUCCEEDED(HRESULT nResult)
{
    ATLENSURE_SUCCEEDED(nResult);
}

// [...]

CComPtr<IBaseFilter> pBaseFilter;
ATLENSURE_INLINE_SUCCEEDED(pBaseFilter.CoCreateInstance(CLSID_JpegVideoSourceFilter));
CComQIPtr<IJpegVideoSourceFilter> pJpegVideoSourceFilter = pBaseFilter;
ATLENSURE_THROW(pJpegVideoSourceFilter, E_NOINTERFACE);
ATLENSURE_INLINE_SUCCEEDED(pFilterGraph->AddFilter(pBaseFilter, L"Source"));

ATLENSURE_INLINE_SUCCEEDED(pJpegVideoSourceFilter->put_Location(CComBSTR(_T("http://p.viewnetcam.com:60001/nphMotionJpeg?Resolution=640x480&Quality=Standard"))));
ATLENSURE_INLINE_SUCCEEDED(pJpegVideoSourceFilter->put_Width(640));
ATLENSURE_INLINE_SUCCEEDED(pJpegVideoSourceFilter->put_Height(480));

#pragma region Render Output Pin
CComPtr<IEnumPins> pEnumPins;
ATLENSURE_INLINE_SUCCEEDED(pBaseFilter->EnumPins(&pEnumPins));
CComPtr<IPin> pPin;
ATLENSURE_THROW(pEnumPins->Next(1, &pPin, NULL) == S_OK, E_FAIL);
ATLASSERT(pPin);
ATLENSURE_INLINE_SUCCEEDED(pFilterGraph->Render(pPin));
#pragma endregion

This starts M-JPEG video from Panasonic BB-HCM381A camera available to public online at http://p.viewnetcam.com:60001/nphMotionJpeg?Resolution=640×480&Quality=Standard

UPDATE: ATLENSURE_SUCCEEDED macro in the snippet replaced with ATLENSURE_INLINE_SUCCEEDED – see additional post on this.

C#.NET code snippet will look like this (using DirectShowLib):

using DirectShowLib;
using AlaxInfoIpVideoSource;

// [...]

FilterGraph filterGraph = new FilterGraph();
IJpegVideoSourceFilter sourceFilter = Activator.CreateInstance(Type.GetTypeFromCLSID(new Guid("{A8DA2ECB-DEF6-414D-8CE2-E651640DBA4F}"))) as IJpegVideoSourceFilter;
IBaseFilter sourceBaseFilter = sourceFilter as IBaseFilter;
(filterGraph as IFilterGraph2).AddFilter(sourceBaseFilter, "Source");
sourceFilter.Location = @"http://p.viewnetcam.com:60001/nphMotionJpeg?Resolution=640x480&Quality=Standard";
sourceFilter.Width = 640;
sourceFilter.Height = 480;
IPin pin = DsFindPin.ByDirection(sourceBaseFilter, PinDirection.Output, 0);
(filterGraph as IFilterGraph2).Render(pin);
(filterGraph as IMediaControl).Run();
EventCode eventCode;
(filterGraph as IMediaEvent).WaitForCompletion(-1, out eventCode);