/*==========================================================================;
 *
 *  Copyright (C) 1999 Microsoft Corporation.  All Rights Reserved.
 *
 *  File:       supervis.cpp
 *  Content:    Implements a process that oversees the full duplex
 *				testing proceedure, watching for nasty conditions in
 *				the two child processes that are used to implement the
 *				actual tests. This is required because on some older 
 *				VXD drivers, attempting full duplex can hang the process
 *				or even (grumble) lock the whole system.
 *  History:
 *	Date   By  Reason
 *	============
 *	08/19/99	pnewson		created
 *  10/27/99	pnewson		change guid members from pointers to structs
 *  10/28/99	pnewson 	Bug #114176 updated DVSOUNDDEVICECONFIG struct
 *  11/04/99	pnewson 	Bug #115279 - fixed cancel processing
 *										- fixed crash detection
 *										- fixed multiple instance detection
 *										- added HWND to check audio setup
 *										- automatically advance after full duplex test
 *  11/30/99	pnewson 	parameter validation and default device mapping
 *  12/16/99	rodtoll		Bug #119584 - Error code cleanup (Renamed runsetup error) 
 *  01/21/2000	pnewson		Update for UI revisions
 *  01/23/2000	pnewson		Improvded feedback for fatal errors (millen bug 114508)
 *  01/24/2000  pnewson 	Prefix detected bug fix
 *  01/25/2000  pnewson 	Added support for DVFLAGS_WAVEIDS
 *  01/27/2000	rodtoll	Updated with API changes 
 * 	02/08/2000	rodtoll	Bug #131496 - Selecting DVTHRESHOLD_DEFAULT results in voice
 *						never being detected 
 *  03/03/2000	rodtoll	Updated to handle alternative gamevoice build.   
 *  03/23/2000  rodtoll   Win64 updates
 *  04/04/2000	pnewson		Added support for DVFLAGS_ALLOWBACK
 *							Removed "Already Running" dialog box
 *  04/19/2000  rodtoll Bug #31106 - Grey out recording sliders when no vol control avail
 *  04/19/2000	pnewson	    Error handling cleanup  
 *							Clicking Volume button brings volume window to foreground
 *  04/21/2000  rodtoll Bug #32889 - Does not run on Win2k on non-admin account
 *                      Bug #32952 Does not run on Windows 95 w/o IE4 installed
 *  05/03/2000  pnewson Bug #33878 - Wizard locks up when clicking Next/Cancel during speaker test
 *  05/17/2000  rodtoll Bug #34045 - Parameter validation error while running TestNet.
 *				rodtoll Bug #34764 - Verifies capture device before render device 
 *  06/28/2000	rodtoll	Prefix Bug #38022
 *  07/12/2000	rodtoll		Bug #31468 - Add diagnostic spew to logfile to show what is failing the HW Wizard
 *  07/31/2000	rodtoll	Bug #39590 - SB16 class soundcards are passing when they should fail
 *						Half duplex code was being ignored in mic test portion.
 *  08/06/2000  RichGr  IA64: Use %p format specifier in DPFs for 32/64-bit pointers and handles.
 *  08/28/2000	masonb  Voice Merge: Changed ccomutil.h to ccomutil.h
 *  08/31/2000 	rodtoll	Bug #43804 - DVOICE: dwSensitivity structure member is confusing - should be dwThreshold 
 *  11/29/2000	rodtoll	Bug #48348 - DPVOICE: Modify wizard to make use of DirectPlay8 as the transport.
 *  02/04/2001	simonpow	Bug #354859 - Fixes for PREfast spotted problems (Unitialised variables)
 ***************************************************************************/

#include "dxvtlibpch.h"


#ifndef WIN95
#define PROPSHEETPAGE_STRUCT_SIZE 	sizeof( PROPSHEETPAGE )
#define PROPSHEETHEAD_STRUCT_SIZE	sizeof( PROPSHEETHEADER )
#else
#define PROPSHEETPAGE_STRUCT_SIZE 	PROPSHEETPAGE_V1_SIZE
#define PROPSHEETHEAD_STRUCT_SIZE	PROPSHEETHEADER_V1_SIZE
#endif


#undef DPF_SUBCOMP
#define DPF_SUBCOMP DN_SUBCOMP_VOICE


// logical defines for registry values
#define REGVAL_NOTRUN 	0
#define REGVAL_CRASHED 	1
#define REGVAL_FAILED 	2
#define REGVAL_PASSED 	3

// local typedefs
typedef HRESULT (WINAPI *TDirectSoundEnumFnc)(LPDSENUMCALLBACK, LPVOID);

// local static functions
static HRESULT SupervisorQueryAudioSetup(CSupervisorInfo* psinfo);
static HRESULT RunFullDuplexTest(CSupervisorInfo* lpsinfo);
static HRESULT DoTests(CSupervisorInfo* lpsinfo);
static HRESULT IssueCommands(CSupervisorInfo* lpsinfo);
static HRESULT IssueShutdownCommand(CSupervisorIPC* lpipcSupervisor);

// callback functions
INT_PTR CALLBACK WelcomeProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
INT_PTR CALLBACK AlreadyRunningProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
INT_PTR CALLBACK PreviousCrashProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
INT_PTR CALLBACK FullDuplexProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
INT_PTR CALLBACK MicTestProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
INT_PTR CALLBACK MicTestFailedProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
INT_PTR CALLBACK SpeakerTestProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
INT_PTR CALLBACK CompleteProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
INT_PTR CALLBACK FullDuplexFailedProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
INT_PTR CALLBACK HalfDuplexFailedProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);

// Thread functions
DWORD WINAPI FullDuplexTestThreadProc(LPVOID lpvParam);
DWORD WINAPI LoopbackTestThreadProc(LPVOID lpvParam);

// Message handlers
BOOL WelcomeInitDialogHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, CSupervisorInfo* psinfo);
BOOL WelcomeSetActiveHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, CSupervisorInfo* psinfo);
BOOL WelcomeBackHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, CSupervisorInfo* psinfo);
BOOL WelcomeNextHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, CSupervisorInfo* psinfo);
BOOL WelcomeResetHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, CSupervisorInfo* psinfo);
BOOL FullDuplexInitDialogHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, CSupervisorInfo* psinfo);
BOOL FullDuplexSetActiveHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, CSupervisorInfo* psinfo);
BOOL FullDuplexBackHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, CSupervisorInfo* psinfo);
BOOL FullDuplexNextHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, CSupervisorInfo* psinfo);
BOOL FullDuplexCompleteHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, CSupervisorInfo* psinfo);
BOOL FullDuplexResetHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, CSupervisorInfo* psinfo);
BOOL MicTestInitDialogHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, CSupervisorInfo* psinfo);
BOOL MicTestSetActiveHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, CSupervisorInfo* psinfo);
BOOL MicTestNextHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, CSupervisorInfo* psinfo);
BOOL MicTestBackHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, CSupervisorInfo* psinfo);
BOOL MicTestLoopbackRunningHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, CSupervisorInfo* psinfo);
BOOL MicTestRecordStartHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, CSupervisorInfo* psinfo);
BOOL MicTestRecordStopHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, CSupervisorInfo* psinfo);
BOOL MicTestResetHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, CSupervisorInfo* psinfo);
BOOL MicTestVScrollHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, CSupervisorInfo* psinfo);
BOOL MicTestRecAdvancedHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, CSupervisorInfo* psinfo);
BOOL MicTestFailedInitDialogHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, CSupervisorInfo* psinfo);
BOOL MicTestFailedSetActiveHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, CSupervisorInfo* psinfo);
BOOL MicTestFailedRecordStopHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, CSupervisorInfo* psinfo);
BOOL MicTestFailedBackHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, CSupervisorInfo* psinfo);
BOOL MicTestFailedResetHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, CSupervisorInfo* psinfo);
BOOL MicTestFailedFinishHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, CSupervisorInfo* psinfo);
BOOL SpeakerTestInitDialogHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, CSupervisorInfo* psinfo);
BOOL SpeakerTestSetActiveHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, CSupervisorInfo* psinfo);
BOOL SpeakerTestNextHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, CSupervisorInfo* psinfo);
BOOL SpeakerTestBackHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, CSupervisorInfo* psinfo);
BOOL SpeakerTestResetHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, CSupervisorInfo* psinfo);
BOOL SpeakerTestVScrollHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, CSupervisorInfo* psinfo);
BOOL SpeakerTestRecAdvancedHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, CSupervisorInfo* psinfo);
BOOL SpeakerTestOutAdvancedHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, CSupervisorInfo* psinfo);
BOOL CompleteInitDialogHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, CSupervisorInfo* psinfo);
BOOL CompleteSetActiveHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, CSupervisorInfo* psinfo);
BOOL CompleteLoopbackEndedHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, CSupervisorInfo* psinfo);
BOOL CompleteFinishHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, CSupervisorInfo* psinfo);
BOOL CompleteResetHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, CSupervisorInfo* psinfo);
BOOL FullDuplexFailedInitDialogHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, CSupervisorInfo* psinfo);
BOOL FullDuplexFailedSetActiveHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, CSupervisorInfo* psinfo);
BOOL FullDuplexFailedFinishHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, CSupervisorInfo* psinfo);
BOOL FullDuplexFailedResetHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, CSupervisorInfo* psinfo);
BOOL FullDuplexFailedBackHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, CSupervisorInfo* psinfo);
BOOL HalfDuplexFailedInitDialogHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, CSupervisorInfo* psinfo);
BOOL HalfDuplexFailedSetActiveHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, CSupervisorInfo* psinfo);
BOOL HalfDuplexFailedFinishHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, CSupervisorInfo* psinfo);
BOOL HalfDuplexFailedResetHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, CSupervisorInfo* psinfo);
BOOL HalfDuplexFailedBackHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, CSupervisorInfo* psinfo);

// globals
HINSTANCE g_hResDLLInstance;

#undef DPF_MODNAME
#define DPF_MODNAME "CSupervisorInfo::CSupervisorInfo"
CSupervisorInfo::CSupervisorInfo()
	: m_hfTitle(NULL)
	, m_guidCaptureDevice(GUID_NULL)
	, m_guidRenderDevice(GUID_NULL)
	, m_hFullDuplexThread(NULL)
	, m_hLoopbackThreadExitEvent(NULL)
	, m_fAbortFullDuplex(FALSE)
	, m_hLoopbackThread(NULL)
	, m_hLoopbackShutdownEvent(NULL)
	, m_fVoiceDetected(FALSE)
	, m_hwndWizard(NULL)
	, m_hwndDialog(NULL)
	, m_hwndProgress(NULL)
	, m_hwndInputPeak(NULL)
	, m_hwndOutputPeak(NULL)
	, m_hwndInputVolumeSlider(NULL)
	, m_hwndOutputVolumeSlider(NULL)
	, m_lpdpvc(NULL)
	, m_hMutex(NULL)
	, m_uiWaveInDeviceId(0)
	, m_uiWaveOutDeviceId(0)
	, m_dwDeviceFlags(0)
	, m_fLoopbackRunning(FALSE)
	, m_fCritSecInited(FALSE)
{
	DPF_ENTER();

	ZeroMemory(&m_piSndVol32Record, sizeof(PROCESS_INFORMATION));
	ZeroMemory(&m_piSndVol32Playback, sizeof(PROCESS_INFORMATION));
	ZeroMemory(&m_woCaps, sizeof(WAVEOUTCAPS));
	
	DPF_EXIT();
	return;
}

#undef DPF_MODNAME
#define DPF_MODNAME "CSupervisorInfo::~CSupervisorInfo"
CSupervisorInfo::~CSupervisorInfo()
{
	if (m_fCritSecInited)
	{
		DNDeleteCriticalSection(&m_csLock);
	}
}

// this structure is only used by the next two functions, hence
// it is declared here for convenince.
struct SMoveWindowEnumProcParam
{
	PROCESS_INFORMATION* lppi;
	int x;
	int y;
	BOOL fMoved;
};

struct SBringToForegroundEnumProcParam
{
	PROCESS_INFORMATION* lppi;
	BOOL fFound;
};

#undef DPF_MODNAME
#define DPF_MODNAME "BringToForegroundWindowProc"
BOOL CALLBACK BringToForegroundWindowProc(HWND hwnd, LPARAM lParam)
{
	SBringToForegroundEnumProcParam* param;
	param = (SBringToForegroundEnumProcParam*)lParam;
	DPFX(DPFPREP, DVF_INFOLEVEL, "looking for main window for pid: %i", param->lppi->dwProcessId);

	DWORD dwProcessId;
	GetWindowThreadProcessId(hwnd, &dwProcessId);
	DPFX(DPFPREP, DVF_INFOLEVEL, "window: 0x%p has pid: 0x%08x", hwnd, dwProcessId);
	if (dwProcessId == param->lppi->dwProcessId)
	{
		TCHAR rgtchClassName[64];
		GetClassName(hwnd, rgtchClassName, 64);
		if (_tcsnicmp(rgtchClassName, _T("Volume Control"), 64) == 0)
		{
		    
			if (!SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE))
			{
				Diagnostics_Write(DVF_ERRORLEVEL, "SetWindowPos failed, code: 0x%x", GetLastError());
			}
			else
			{
				param->fFound = TRUE;
			}
		}
		return FALSE;
	}
	return TRUE;
}


#undef DPF_MODNAME
#define DPF_MODNAME "BringToForegroundWindowEnumProc"
BOOL CALLBACK BringToForegroundWindowEnumProc(HWND hwnd, LPARAM lParam)
{
	SMoveWindowEnumProcParam* param;
	param = (SMoveWindowEnumProcParam*)lParam;
	DPFX(DPFPREP, DVF_INFOLEVEL, "looking for main window for pid: 0x%x", param->lppi->dwProcessId);

	DWORD dwProcessId;
	GetWindowThreadProcessId(hwnd, &dwProcessId);
	DPFX(DPFPREP, DVF_INFOLEVEL, "window: 0x%p has pid: 0x%08x", hwnd, dwProcessId);
	if (dwProcessId == param->lppi->dwProcessId)
	{
		TCHAR rgtchClassName[64];
		GetClassName(hwnd, rgtchClassName, 64);
		if (_tcsnicmp(rgtchClassName, _T("Volume Control"), 64) == 0)
		{
			if (!SetForegroundWindow(hwnd))
			{
				Diagnostics_Write(DVF_ERRORLEVEL, "SetForegroundWindow failed, code: 0x%x", GetLastError());
			}
		}
		return FALSE;
	}
	return TRUE;
}


#undef DPF_MODNAME
#define DPF_MODNAME "MoveWindowEnumProc"
BOOL CALLBACK MoveWindowEnumProc(HWND hwnd, LPARAM lParam)
{
	SMoveWindowEnumProcParam* param;
	param = (SMoveWindowEnumProcParam*)lParam;
	DPFX(DPFPREP, DVF_INFOLEVEL, "looking for main window for pid: %i", param->lppi->dwProcessId);

	DWORD dwProcessId;
	GetWindowThreadProcessId(hwnd, &dwProcessId);
	DPFX(DPFPREP, DVF_INFOLEVEL, "window: 0x%p has pid: 0x%08x", hwnd, dwProcessId);
	if (dwProcessId == param->lppi->dwProcessId)
	{
		TCHAR rgtchClassName[64];
		GetClassName(hwnd, rgtchClassName, 64);
		if (_tcsnicmp(rgtchClassName, _T("Volume Control"), 64) == 0)
		{
			if (!SetWindowPos(hwnd, HWND_TOP, param->x, param->y, 0, 0, SWP_NOSIZE))
			{
				Diagnostics_Write(DVF_ERRORLEVEL, "SetWindowPos failed, code: 0x%x", GetLastError());
			}
			else
			{
				param->fMoved = TRUE;
			}
		}
		return FALSE;
	}
	return TRUE;
}

#undef DPF_MODNAME
#define DPF_MODNAME "CSupervisorInfo::SetDeviceFlags"
void CSupervisorInfo::SetDeviceFlags( DWORD dwFlags )
{
    m_dwDeviceFlags = dwFlags;
}

#undef DPF_MODNAME
#define DPF_MODNAME "CSupervisorInfo::GetDeviceFlags"
void CSupervisorInfo::GetDeviceFlags( DWORD *pdwFlags )
{
    *pdwFlags = m_dwDeviceFlags;
}

#undef DPF_MODNAME
#define DPF_MODNAME "CSupervisorInfo::LaunchWindowsVolumeControl"
HRESULT CSupervisorInfo::LaunchWindowsVolumeControl(HWND hwndWizard, BOOL fRecord)
{
	DPF_ENTER();

	STARTUPINFO si;
	ZeroMemory(&si, sizeof(STARTUPINFO));
	si.cb = sizeof(STARTUPINFO);

	TCHAR ptcharCommand[64];
	PROCESS_INFORMATION* lppi;
	UINT uiPlayMixerID = 0;
	UINT uiCaptureMixerID = 0;
	MMRESULT mmr = MMSYSERR_NOERROR;
	
	mmr = mixerGetID( (HMIXEROBJ) (UINT_PTR) m_uiWaveInDeviceId, &uiCaptureMixerID, MIXER_OBJECTF_WAVEIN );

	if( mmr != MMSYSERR_NOERROR )
	{
		DPFX(DPFPREP, DVF_ERRORLEVEL, "Failed to get capture mixerline mmr=0x%x", mmr );
		return DV_OK;
	}
	
	mmr = mixerGetID( (HMIXEROBJ) (UINT_PTR) m_uiWaveOutDeviceId, &uiPlayMixerID, MIXER_OBJECTF_WAVEOUT );

	if( mmr != MMSYSERR_NOERROR )
	{
		DPFX(DPFPREP, DVF_ERRORLEVEL, "Failed to get playback mixerline mmr=0x%x", mmr );
		return DV_OK;		
	}	

	if (fRecord)
	{
		_stprintf(ptcharCommand, _T("sndvol32 /R /D %i"), uiCaptureMixerID);
		lppi = &m_piSndVol32Record;
	}
	else
	{
		_stprintf(ptcharCommand, _T("sndvol32 /D %i"), uiPlayMixerID);
		lppi = &m_piSndVol32Playback;
	}

	if (lppi->hProcess != NULL)
	{
		DWORD dwExitCode;
		if (GetExitCodeProcess(lppi->hProcess, &dwExitCode) != 0)
		{
			if (dwExitCode == STILL_ACTIVE)
			{
				// not dead yet! Don't start another copy,
				// but do bring it to the foreground.
				SMoveWindowEnumProcParam param;
				param.lppi = lppi;
				param.fMoved = FALSE;
				param.x = 0;
				param.y = 0;
				EnumWindows(BringToForegroundWindowEnumProc, (LPARAM)&param);
				DPF_EXIT();
				return DV_OK;
			}
			
			// The user terminated the process. Close our handles, 
			// and zero the process information structure
			CloseHandle(lppi->hProcess);
			CloseHandle(lppi->hThread);
			ZeroMemory(lppi, sizeof(PROCESS_INFORMATION));
		}
		else
		{
			// If GetExitCodeProcess fails, assume the handle
			// is bad for some reason. Log it, then behave as
			// if the process was shut down. At worst, we'll
			// have multiple copies of SndVol32 running around.
			Diagnostics_Write(DVF_ERRORLEVEL, "GetExitCodeProcess failed, code:0x%x", GetLastError());
			// Don't close the handles, they may be bad. If they're
			// not, the OS will clean 'em up when the wizard exits.
			ZeroMemory(lppi, sizeof(PROCESS_INFORMATION));
		}
	}

	if (lppi->hProcess == NULL)
	{
		CreateProcess(
			NULL, 
			ptcharCommand, 
			NULL, 
			NULL, 
			FALSE, 
			0, 
			NULL, 
			NULL, 
			&si, 
			lppi);

		DPFX(DPFPREP, DVF_INFOLEVEL, "Launched volume control, pid:%i", lppi->dwProcessId);
		
		// Find main window for the process we just created and
		// move it manually.
		//
		// Note the race condition - I have no reliable way to wait until
		// the main window has been displayed. So, the work around
		// (a.k.a. HACK) is to keep looking for it for a while.
		// If it hasn't been found by then, we just give up. It's not
		// tragic if we don't find it, it just won't be as neat and
		// tidy, since the window will come up wherever it was last time.
		//
		// Note that sndvol32.exe does not accept the STARTF_USEPOSITION
		// flag on the PROCESS_INFORMATION structure, so I have to do 
		// this hack.
		//
		// In an attempt to let the sndvol32.exe get up and running,
		// I call Sleep to relinquish my timeslice.
		Sleep(0);

		RECT rect;
		if (GetWindowRect(hwndWizard, &rect))
		{
			SMoveWindowEnumProcParam param;
			param.lppi = lppi;
			param.fMoved = FALSE;

			param.x = rect.left;
			param.y = rect.top;

			int iOffset = GetSystemMetrics(SM_CYCAPTION) + GetSystemMetrics(SM_CYBORDER);
			
			if (m_piSndVol32Record.hProcess == lppi->hProcess)
			{
				// This is the recording control. Cascade it
				// one level down from the wizard main window.
				param.x += iOffset;
				param.y += iOffset;
			}
			else
			{
				// This is the playback control. Cascade it
				// two levels down from the wizard main window.
				param.x += iOffset*2;
				param.y += iOffset*2;
			}

			// Make twenty attempts to move the window.
			// Combined with Sleep(25), this will not
			// wait for more than 1/2 second before giving up.
			for (int i = 0; i < 20; ++i)
			{
				EnumWindows(MoveWindowEnumProc, (LPARAM)&param);
				if (param.fMoved)
				{
					// window was moved, break out.
					break;
				}

				// Window was not moved. Wait 25 milliseconds (plus change)
				// and try again.
				DPFX(DPFPREP, DVF_WARNINGLEVEL, "Attempt to move sndvol32 window failed");
				Sleep(25);
			}
		}
		else
		{
			Diagnostics_Write(DVF_ERRORLEVEL, "GetWindowRect failed");
		}
		
	}
		
	DPF_EXIT();
	return DV_OK;
}

#undef DPF_MODNAME
#define DPF_MODNAME "CloseWindowEnumProc"
BOOL CALLBACK CloseWindowEnumProc(HWND hwnd, LPARAM lParam)
{
	DWORD dwProcessId;
	DPFX(DPFPREP, DVF_INFOLEVEL, "looking for pid: 0x%p to shutdown", lParam);
	GetWindowThreadProcessId(hwnd, &dwProcessId);
	DPFX(DPFPREP, DVF_INFOLEVEL, "window: 0x%p has pid: 0x%08x", hwnd, dwProcessId);
	if (dwProcessId == (DWORD)lParam)
	{
		// found it, ask it to close.
		DPFX(DPFPREP, DVF_INFOLEVEL, "Sending WM_CLOSE to 0x%p", hwnd);
		SendMessage(hwnd, WM_CLOSE, 0, 0);
		return FALSE;
	}
	return TRUE;
}

#undef DPF_MODNAME
#define DPF_MODNAME "CSupervisorInfo::CloseWindowsVolumeControl"
HRESULT CSupervisorInfo::CloseWindowsVolumeControl(BOOL fRecord)
{
	DPF_ENTER();

	PROCESS_INFORMATION* lppi;

	if (fRecord)
	{
		lppi = &m_piSndVol32Record;
	}
	else
	{
		lppi = &m_piSndVol32Playback;
	}

	if (lppi->hProcess != NULL)
	{
		DWORD dwExitCode;
		if (GetExitCodeProcess(lppi->hProcess, &dwExitCode) != 0)
		{
			if (dwExitCode == STILL_ACTIVE)
			{
				DPFX(DPFPREP, DVF_INFOLEVEL, "Attempting to close volume control with pid: %i", lppi->dwProcessId);

				// there is currently a volume control showing, find it and 
				// ask it to close
				EnumWindows(CloseWindowEnumProc, lppi->dwProcessId);

				// Zero out the process information struct
				ZeroMemory(lppi, sizeof(PROCESS_INFORMATION));
			}
		}
	}
		
	DPF_EXIT();
	return DV_OK;
}

#undef DPF_MODNAME
#define DPF_MODNAME "CSupervisorInfo::GetWaveOutVolume"
HRESULT CSupervisorInfo::GetWaveOutVolume(DWORD* lpdwVolume)
{
	DPF_ENTER();

	if (!(m_woCaps.dwSupport & WAVECAPS_VOLUME|WAVECAPS_LRVOLUME))
	{
		// doesn't support wave out
		Diagnostics_Write(DVF_ERRORLEVEL, "Wave device %i does not support volume control", m_uiWaveOutDeviceId);
		DPF_EXIT();
		return E_FAIL;
	}
	
	MMRESULT mmr = waveOutGetVolume((HWAVEOUT) ((UINT_PTR) m_uiWaveOutDeviceId ), lpdwVolume);
	if (mmr != MMSYSERR_NOERROR)
	{
		Diagnostics_Write(DVF_ERRORLEVEL, "waveOutGetVolume failed, code: %i", mmr);
		DPF_EXIT();
		return E_FAIL;
	}

	if (m_woCaps.dwSupport & WAVECAPS_LRVOLUME)
	{
		// has a left and right volume control - average them
		*lpdwVolume = (HIWORD(*lpdwVolume) + LOWORD(*lpdwVolume))/2;
	}
	else
	{
		// just a mono control - only the low word is significant
		*lpdwVolume = LOWORD(*lpdwVolume);
	}

	DPF_EXIT();
	return DV_OK;
}

#undef DPF_MODNAME
#define DPF_MODNAME "CSupervisorInfo::SetWaveOutVolume"
HRESULT CSupervisorInfo::SetWaveOutVolume(DWORD dwVolume)
{
	DPF_ENTER();

	MMRESULT mmr = waveOutSetVolume((HWAVEOUT) ((UINT_PTR) m_uiWaveOutDeviceId), LOWORD(dwVolume)<<16|LOWORD(dwVolume));
	if (mmr != MMSYSERR_NOERROR)
	{
		DPF_EXIT();
		return E_FAIL;
	}
		
	DPF_EXIT();
	return DV_OK;
}

#undef DPF_MODNAME
#define DPF_MODNAME "CSupervisorInfo::SetRecordVolume"
HRESULT CSupervisorInfo::SetRecordVolume(LONG lVolume)
{
	HRESULT hr;
	
	DVCLIENTCONFIG dvcc;
	dvcc.dwSize = sizeof(dvcc);
	
	if (m_lpdpvc != NULL)
	{
		hr = m_lpdpvc->GetClientConfig(&dvcc);
		if (FAILED(hr))
		{
			Diagnostics_Write(DVF_ERRORLEVEL, "GetClientConfig failed, hr:%i", hr);
			return hr;
		}
		
		dvcc.lRecordVolume = lVolume;
		hr = m_lpdpvc->SetClientConfig(&dvcc);
		if (FAILED(hr))
		{
			Diagnostics_Write(DVF_ERRORLEVEL, "SetClientConfig failed, hr:%i", hr);
			return hr;
		}
	}
	else
	{
		return DVERR_INVALIDPOINTER;
	}

	return DV_OK;
}

#undef DPF_MODNAME
#define DPF_MODNAME "CSupervisorInfo::CancelFullDuplexTest"
HRESULT CSupervisorInfo::CancelFullDuplexTest()
{
	HRESULT hrFnc;
	HRESULT hr;
	DWORD dwRet;
	LONG lRet;	

	hrFnc = DV_OK;

	Diagnostics_Write(DVF_ERRORLEVEL,"User cancelled wizard during full duplex test." );

	DNEnterCriticalSection(&m_csLock);
	
	// Are we currently in the middle of a full duplex test?
	if (m_hFullDuplexThread != NULL)
	{
		// This flag is periodically checked by the full duplex test 
		// while it works.
		m_fAbortFullDuplex = TRUE;

		// wait for the full duplex thread to exit gracefully
		DNLeaveCriticalSection(&m_csLock);
		dwRet = WaitForMultipleObjects( 1, &m_hFullDuplexThread, FALSE, gc_dwLoopbackTestThreadTimeout);
		switch (dwRet)
		{
		case WAIT_OBJECT_0:
			// the full duplex thread is now done, so continue...
			DNEnterCriticalSection(&m_csLock);
			break;

		case WAIT_TIMEOUT:
			// The full duplex thread is not cooperating - get tough with it.
			DNEnterCriticalSection(&m_csLock);
			hr = m_sipc.TerminateChildProcesses();
			if (FAILED(hr))
			{
				Diagnostics_Write(DVF_ERRORLEVEL, "TerminateChildProcesses failed, code: 0x%x", hr);
				hrFnc = hr;
			}
			if (!TerminateThread(m_hFullDuplexThread, hr))
			{
				lRet = GetLastError();
				Diagnostics_Write(DVF_ERRORLEVEL, "TerminateThread failed, code: 0x%x", lRet);
				hrFnc = DVERR_GENERIC;
			}
			break;

		default:
			// this is not supposed to happen. Note it, terminate everything,
			// and continue.
			DNEnterCriticalSection(&m_csLock);
			if (dwRet == WAIT_ABANDONED)
			{
				Diagnostics_Write(DVF_ERRORLEVEL, "WaitForSingleObject abandoned unexpectedly");
				hrFnc = DVERR_GENERIC;
			}
			else
			{
				lRet = GetLastError();
				Diagnostics_Write(DVF_ERRORLEVEL, "WaitForSingleObject failed, code: 0x%x", lRet);
				hrFnc = DVERR_GENERIC;
			}
			hr = m_sipc.TerminateChildProcesses();
			if (FAILED(hr))
			{
				Diagnostics_Write(DVF_ERRORLEVEL, "TerminateChildProcesses failed, code: 0x%x", hr);
				hrFnc = hr;
			}
			if (!TerminateThread(m_hFullDuplexThread, hr))
			{
				lRet = GetLastError();
				Diagnostics_Write(DVF_ERRORLEVEL, "TerminateThread failed, code: 0x%x", lRet);
				hrFnc = DVERR_GENERIC;
			}
			break;		
		}

		// Close the full duplex thread handle
		hr = WaitForFullDuplexThreadExitCode();
		if (FAILED(hr))
		{
			Diagnostics_Write(DVF_ERRORLEVEL, "WaitForFullDuplexThreadExitCode failed, code: 0x%x", hr);
			hrFnc = hr;
		}

		// cleanup the IPC objects
		hr = DeinitIPC();
		if (FAILED(hr))
		{
			Diagnostics_Write(DVF_ERRORLEVEL, "DeinitIPC failed, code: 0x%x", hr);
			hrFnc = hr;
		}
	}

	DNLeaveCriticalSection(&m_csLock);
	return hrFnc;
}

#undef DPF_MODNAME
#define DPF_MODNAME "CSupervisorInfo::CancelLoopbackTest"
HRESULT CSupervisorInfo::CancelLoopbackTest()
{
	HRESULT hr = DV_OK;

    Diagnostics_Write(DVF_ERRORLEVEL,"User cancelled wizard during loopback test" );
	
	DNEnterCriticalSection(&m_csLock);
	
	// Are we currently in the middle of a loopback test?
	if (m_hLoopbackThread != NULL)
	{
		// If the loopback thread handle is not null, the mic test may still
		// be going on, in which case we want to set that flag to
		// REGVAL_NOTRUN, since the test was not completed.
		DWORD dwRegVal;
		GetMicDetected(&dwRegVal);
		if (dwRegVal == REGVAL_CRASHED)
		{
			SetMicDetected(REGVAL_NOTRUN);
		}
		
		DNLeaveCriticalSection(&m_csLock);	// ShutdownLoopbackThread has its own guard
		hr = ShutdownLoopbackThread();
		DNEnterCriticalSection(&m_csLock);
		if (FAILED(hr))
		{
			Diagnostics_Write(DVF_ERRORLEVEL, "ShutdownLoopbackThread failed, code: %i", hr);
		}
	}

	DNLeaveCriticalSection(&m_csLock);

	return hr;
}


#undef DPF_MODNAME
#define DPF_MODNAME "CSupervisorInfo::Cancel"
HRESULT CSupervisorInfo::Cancel()
{
	DPF_ENTER();
	
	DWORD dwRet;
	HRESULT hr;
	LONG lRet;
	HRESULT hrFnc;
	BOOL fDone;
	BOOL fGotMsg;
	BOOL fWelcomeNext;
	MSG msg;

	DNEnterCriticalSection(&m_csLock);

	hrFnc = DV_OK;

	// close any open volume controls
	CloseWindowsVolumeControl(TRUE);
	CloseWindowsVolumeControl(FALSE);

	// The cancel button can be hit at any time. 
	// We are not in a known state. This function	
	// has to figure it out from the member variables.
	// How fun.
	DNLeaveCriticalSection(&m_csLock);  // CancelFullDuplexTest has it's own guard
	hrFnc = CancelFullDuplexTest();
	DNEnterCriticalSection(&m_csLock);
	
	// Are we currently in the middle of a loopback test?
	if (m_hLoopbackThread != NULL)
	{
		DNLeaveCriticalSection(&m_csLock);	// ShutdownLoopbackThread has its own guard
		hr = ShutdownLoopbackThread();
		DNEnterCriticalSection(&m_csLock);
		if (FAILED(hr))
		{
			Diagnostics_Write(DVF_ERRORLEVEL, "ShutdownLoopbackThread failed, code: %i", hr);
			hrFnc = hr;
		}
	}

	// Reset the registry to the "test not yet run" state
	// but only if the user moved past the welcome page
	GetWelcomeNext(&fWelcomeNext);
	if (fWelcomeNext)
	{
		hr = SetHalfDuplex(REGVAL_NOTRUN);
		if (FAILED(hr))
		{
			Diagnostics_Write(DVF_ERRORLEVEL, "SetHalfDuplex failed, code: %i", hr);
			hrFnc = hr;
		}

		hr = SetFullDuplex(REGVAL_NOTRUN);
		if (FAILED(hr))
		{
			Diagnostics_Write(DVF_ERRORLEVEL, "SetFullDuplex failed, code: %i", hr);
			hrFnc = hr;
		}

		hr = SetMicDetected(REGVAL_NOTRUN);
		if (FAILED(hr))
		{
			Diagnostics_Write(DVF_ERRORLEVEL, "SetMicDetected failed, code: %i", hr);
			hrFnc = hr;
		}
	}

	// record the fact that the wizard was bailed out of
	SetUserCancel();
	
	DNLeaveCriticalSection(&m_csLock);
	DPF_EXIT();
	return hrFnc;
}

#undef DPF_MODNAME
#define DPF_MODNAME "CSupervisorInfo::Abort"
HRESULT CSupervisorInfo::Abort(HWND hDlg, HRESULT hrDlg)
{
	// This is the function called whenever a fatal error is hit while in the wizard
	
	DPF_ENTER();
	
	DWORD dwRet;
	HRESULT hr;
	LONG lRet;
	HRESULT hrFnc;
	BOOL fDone;
	BOOL fGotMsg;
	BOOL fWelcomeNext;
	MSG msg;
	HWND hwndParent = NULL;


	hrFnc = DV_OK;

	// close any open volume controls
	DNEnterCriticalSection(&m_csLock);
	CloseWindowsVolumeControl(TRUE);
	CloseWindowsVolumeControl(FALSE);
	DNLeaveCriticalSection(&m_csLock);

	// The cancel button can be hit at any time. 
	// We are not in a known state. This function	
	// has to figure it out from the member variables.
	// How fun.
	// CancelFullDuplexTest has it's own guard
	hrFnc = CancelFullDuplexTest();
	
	// Are we currently in the middle of a loopback test?
	DNEnterCriticalSection(&m_csLock);
	if (m_hLoopbackThread != NULL)
	{
		DNLeaveCriticalSection(&m_csLock);	// ShutdownLoopbackThread has its own guard
		hr = ShutdownLoopbackThread();
		if (FAILED(hr))
		{
			Diagnostics_Write(DVF_ERRORLEVEL, "ShutdownLoopbackThread failed, code: %i", hr);
			hrFnc = hr;
		}
	}

	// Leave the registry alone - this will signal that there was
	// an error to the next wizard run, assuming we're still in 
	// the middle of a test. If we've actually gotten far enough
	// along to record a pass in the registry, so be it.

	// Call EndDialog to forcibly bail out of the wizard.
	Diagnostics_Write(DVF_ERRORLEVEL, "Attempting to abort wizard, hr: %i", hrDlg);
	hwndParent = GetParent(hDlg);
	if (IsWindow(hwndParent))
	{
		PostMessage(hwndParent, WM_CLOSE, (WPARAM)NULL, (LPARAM)NULL);
		/*
		// EndDialog does not work because we are in a property sheet
		if (!EndDialog(hwndParent, hrDlg))
		{
			Diagnostics_Write(DVF_ERRORLEVEL, "EndDialog failed, code: %i:", GetLastError());
		}
		*/
	}
	else
	{
		Diagnostics_Write(DVF_ERRORLEVEL, "Unable to get a handle to the wizard!");
	}
	
	DPF_EXIT();
	return hrFnc;
}


#undef DPF_MODNAME
#define DPF_MODNAME "CSupervisorInfo::Finish"
HRESULT CSupervisorInfo::Finish()
{
	DPF_ENTER();

	DWORD dwRet;
	HRESULT hr;
	LONG lRet;
	HRESULT hrFnc;
	DWORD dwValue;
	
	DNEnterCriticalSection(&m_csLock);

	hrFnc = DV_OK;

	// close any open volume controls
	CloseWindowsVolumeControl(TRUE);
	CloseWindowsVolumeControl(FALSE);

	DNLeaveCriticalSection(&m_csLock);
	DPF_EXIT();
	return hrFnc;
}

#undef DPF_MODNAME
#define DPF_MODNAME "CSupervisorInfo::CreateFullDuplexThread"
HRESULT CSupervisorInfo::CreateFullDuplexThread()
{
	DPF_ENTER();
	
	HRESULT hr;
	LONG lRet;
	DWORD dwThreadId;
	
	DNEnterCriticalSection(&m_csLock);

	// Null out the interface pointer
	m_lpdpvc = NULL;	

	if (m_hFullDuplexThread != NULL)
	{
		Diagnostics_Write(DVF_ERRORLEVEL, "m_hFullDuplexThread not NULL");
		hr = DVERR_GENERIC;
		goto error_cleanup;
	}
	
	m_hFullDuplexThread = CreateThread(NULL, 0, FullDuplexTestThreadProc, (LPVOID)this, NULL, &dwThreadId);
	if (m_hFullDuplexThread == NULL)
	{
		// error, log it and bail
		lRet = GetLastError();
		Diagnostics_Write(DVF_ERRORLEVEL, "CreateThread failed, code: %i", lRet);
		hr = DVERR_GENERIC;
		goto error_cleanup;
	}

	DNLeaveCriticalSection(&m_csLock);
	DPF_EXIT();
	return DV_OK;

error_cleanup:
	DNLeaveCriticalSection(&m_csLock);
	DPF_EXIT();
	return hr;
}

#undef DPF_MODNAME
#define DPF_MODNAME "CSupervisorInfo::WaitForFullDuplexThreadExitCode"
HRESULT CSupervisorInfo::WaitForFullDuplexThreadExitCode()
{
	DPF_ENTER();
	
	HRESULT hr;
	HRESULT hrFnc;
	LONG lRet;
	DWORD dwThreadId;
	DWORD dwRet;
	
	DNEnterCriticalSection(&m_csLock);

	if (m_hFullDuplexThread == NULL)
	{
		Diagnostics_Write(DVF_ERRORLEVEL, "m_hFullDuplexThread is NULL");
		hr = DVERR_GENERIC;
		goto error_cleanup;
	}

	dwRet = WaitForSingleObject(m_hFullDuplexThread, gc_dwChildWaitTimeout);
	switch(dwRet)
	{
	case WAIT_OBJECT_0:
		break;

	case WAIT_TIMEOUT:
		Diagnostics_Write(DVF_ERRORLEVEL, "Timed out waiting for full duplex test thread to exit - terminating forcibly");
		TerminateThread(m_hFullDuplexThread, DVERR_GENERIC);
		hr = DVERR_GENERIC;
		goto error_cleanup;

	default:
		Diagnostics_Write(DVF_ERRORLEVEL, "Unknown error waiting for full duplex test thread to exit - terminating forcibly");
		TerminateThread(m_hFullDuplexThread, DVERR_GENERIC);
		hr = DVERR_GENERIC;
		goto error_cleanup;
	}

	if (!GetExitCodeThread(m_hFullDuplexThread, (DWORD*)&hrFnc))
	{
		Diagnostics_Write(DVF_ERRORLEVEL, "Error retrieving full duplex test thread's exit code");
		hr = DVERR_GENERIC;
		goto error_cleanup;
	}
	
	if (!CloseHandle(m_hFullDuplexThread))
	{
		// error, log it and bail
		lRet = GetLastError();
		Diagnostics_Write(DVF_ERRORLEVEL, "CloseHandle failed, code: %i", lRet);
		hr = DVERR_GENERIC;
		goto error_cleanup;
	}
	m_hFullDuplexThread = NULL;

	DNLeaveCriticalSection(&m_csLock);
	DPF_EXIT();
	return hrFnc;

error_cleanup:
	DNLeaveCriticalSection(&m_csLock);
	DPF_EXIT();
	return hr;
}

#undef DPF_MODNAME
#define DPF_MODNAME "CSupervisorInfo::CreateLoopbackThread"
HRESULT CSupervisorInfo::CreateLoopbackThread()
{
	DPF_ENTER();
	
	HRESULT hr;
	LONG lRet;
	DWORD dwThreadId;
	
	DNEnterCriticalSection(&m_csLock);
	m_hLoopbackShutdownEvent = NULL;

	if (m_hLoopbackThread != NULL)
	{
		// The loopback test is already running. 
		// Just dump a warning and return pending.
		DPFX(DPFPREP, DVF_WARNINGLEVEL, "m_hLoopbackThread not NULL");
		hr = DVERR_PENDING;
		goto error_cleanup;
	}

	// create an event the loopback thread signals just before it exits
	m_hLoopbackThreadExitEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
	if (m_hLoopbackThreadExitEvent == NULL)
	{
		// error, log it and bail
		lRet = GetLastError();
		Diagnostics_Write(DVF_ERRORLEVEL, "CreateEvent failed, code: %i", lRet);
		hr = DVERR_GENERIC;
		goto error_cleanup;
	}

	// create an event the loopback thread listens for to shutdown
	m_hLoopbackShutdownEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
	if (m_hLoopbackShutdownEvent == NULL)
	{
		// error, log it and bail
		lRet = GetLastError();
		Diagnostics_Write(DVF_ERRORLEVEL, "CreateEvent failed, code: %i", lRet);
		hr = DVERR_GENERIC;
		goto error_cleanup;
	}

	m_hLoopbackThread = CreateThread(NULL, 0, LoopbackTestThreadProc, (LPVOID)this, NULL, &dwThreadId);
	if (m_hLoopbackThread == NULL)
	{
		// error, log it and bail
		lRet = GetLastError();
		Diagnostics_Write(DVF_ERRORLEVEL, "CreateThread failed, code: %i", lRet);
		hr = DVERR_GENERIC;
		goto error_cleanup;
	}

	DNLeaveCriticalSection(&m_csLock);
	DPF_EXIT();
	return DV_OK;

error_cleanup:
	if (m_hLoopbackThreadExitEvent != NULL)
	{
		CloseHandle(m_hLoopbackThreadExitEvent);
		m_hLoopbackThreadExitEvent = NULL;
	}
	if (m_hLoopbackShutdownEvent != NULL)
	{
		CloseHandle(m_hLoopbackShutdownEvent);
		m_hLoopbackShutdownEvent = NULL;
	}
	DNLeaveCriticalSection(&m_csLock);
	DPF_EXIT();
	return hr;
}

#undef DPF_MODNAME
#define DPF_MODNAME "CSupervisorInfo::WaitForLoopbackShutdownEvent"
HRESULT CSupervisorInfo::WaitForLoopbackShutdownEvent()
{
	DPF_ENTER();
	
	HRESULT hr;
	LONG lRet;
	DWORD dwThreadId;
	DWORD dwRet;
	HANDLE hEvent;
	
	DNEnterCriticalSection(&m_csLock);
	hEvent = m_hLoopbackShutdownEvent;
	DNLeaveCriticalSection(&m_csLock);
	dwRet = WaitForSingleObject(hEvent, INFINITE);
	DNEnterCriticalSection(&m_csLock);
	switch (dwRet)
	{
	case WAIT_OBJECT_0:
		// expected behavior, continue
		break;
	case WAIT_TIMEOUT:
		Diagnostics_Write(DVF_ERRORLEVEL, "WaitForSingleObject timed out unexpectedly");
		hr = DVERR_GENERIC;
		goto error_cleanup;
		
	case WAIT_ABANDONED:
		Diagnostics_Write(DVF_ERRORLEVEL, "WaitForSingleObject abandoned unexpectedly");
		hr = DVERR_GENERIC;
		goto error_cleanup;

	default:
		lRet = GetLastError();
		Diagnostics_Write(DVF_ERRORLEVEL, "WaitForSingleObject returned unknown code, GetLastError(): %i", lRet);
		hr = DVERR_GENERIC;
		goto error_cleanup;
	}
	
	DNLeaveCriticalSection(&m_csLock);
	DPF_EXIT();
	return DV_OK;

error_cleanup:
	DNLeaveCriticalSection(&m_csLock);
	DPF_EXIT();
	return hr;
}

#undef DPF_MODNAME
#define DPF_MODNAME "CSupervisorInfo::SignalLoopbackThreadDone"
HRESULT CSupervisorInfo::SignalLoopbackThreadDone()
{
	DPF_ENTER();
	
	HRESULT hr;
	LONG lRet;
	HANDLE hEvent;

	DNEnterCriticalSection(&m_csLock);
	hEvent = m_hLoopbackThreadExitEvent;
	DNLeaveCriticalSection(&m_csLock);
	if (!SetEvent(hEvent))
	{
		lRet = GetLastError();
		Diagnostics_Write(DVF_ERRORLEVEL, "SetEvent failed, code: %i", lRet);
		hr = DVERR_GENERIC;
		goto error_cleanup;
	}
	
	DPF_EXIT();
	return DV_OK;

error_cleanup:
	DPF_EXIT();
	return hr;
}

#undef DPF_MODNAME
#define DPF_MODNAME "CSupervisorInfo::ShutdownLoopbackThread"
HRESULT CSupervisorInfo::ShutdownLoopbackThread()
{
	DPF_ENTER();
	
	HRESULT hr;
	LONG lRet;
	DWORD dwThreadId;
	DWORD dwRet;
	BOOL fDone;
	BOOL fGotMsg;
	MSG msg;
	HANDLE rghHandle[2];
	HANDLE hEvent;

	DNEnterCriticalSection(&m_csLock);

	if (m_hLoopbackThread == NULL)
	{
		Diagnostics_Write(DVF_ERRORLEVEL, "m_hLoopbackThread is NULL");
		hr = DVERR_GENERIC;
		goto error_cleanup;
	}

	if (!SetEvent(m_hLoopbackShutdownEvent))
	{
		lRet = GetLastError();
		Diagnostics_Write(DVF_ERRORLEVEL, "SetEvent failed, code: %i", lRet);
		hr = DVERR_GENERIC;
		goto error_cleanup;
	}

	/*
	rghHandle[0] = m_hLoopbackThreadExitEvent;
	rghHandle[1] = m_hLoopbackThread;
	*/
	hEvent = m_hLoopbackThreadExitEvent;

	DNLeaveCriticalSection(&m_csLock);
	fDone = FALSE;
	while (!fDone)
	{
		dwRet = WaitForSingleObject(hEvent, gc_dwLoopbackTestThreadTimeout);
		switch(dwRet)
		{
		case WAIT_OBJECT_0:
			DNEnterCriticalSection(&m_csLock);
			fDone = TRUE;
			break;

		case WAIT_ABANDONED:
		default:
			// not supposed to be possible, but treat both like a timeout.

		case WAIT_TIMEOUT:
			DNEnterCriticalSection(&m_csLock);
			hr = DVERR_TIMEOUT;
			goto error_cleanup;
			break;
		}
		/*
		dwRet = MsgWaitForMultipleObjects(2, rghHandle, FALSE, gc_dwLoopbackTestThreadTimeout, QS_ALLINPUT);
		switch (dwRet)
		{
		case WAIT_OBJECT_0:
		case WAIT_OBJECT_0 + 1:
			// expected result, continue...
			DNEnterCriticalSection(&m_csLock);
			fDone = TRUE;
			break;
			
		case WAIT_TIMEOUT:
			// loopback thread is not behaving, jump to
			// the error block where it will be terminated forcibly
			DNEnterCriticalSection(&m_csLock);
			hr = DVERR_TIMEOUT;
			goto error_cleanup;
			break;

		case WAIT_OBJECT_0 + 2:
			// One or more windows messages have been posted to this thread's
			// message queue. Deal with 'em.
			fGotMsg = TRUE;
			
			while( fGotMsg )
			{
				fGotMsg = PeekMessage( &msg, NULL, 0U, 0U, PM_REMOVE );

				if( fGotMsg )
				{
					TranslateMessage( &msg );
					DispatchMessage( &msg );
				}
			}
			break;

		default:
			if (dwRet == WAIT_ABANDONED)
			{
				Diagnostics_Write(DVF_ERRORLEVEL, "MsgWaitForMultipleObjects abandoned unexpectedly");
			}
			else
			{
				lRet = GetLastError();
				Diagnostics_Write(DVF_ERRORLEVEL, "MsgWaitForMultipleObjects failed, code: %i");
			}
			DNEnterCriticalSection(&m_csLock);
			hr = DVERR_TIMEOUT;
			goto error_cleanup;
			break;
		}
		*/
	}

	if (!CloseHandle(m_hLoopbackThread))
	{
		// error, log it and bail
		lRet = GetLastError();
		Diagnostics_Write(DVF_ERRORLEVEL, "CloseHandle failed, code: %i", lRet);
		m_hLoopbackThread = NULL;
		hr = DVERR_GENERIC;
		goto error_cleanup;
	}
	m_hLoopbackThread = NULL;

	if (!CloseHandle(m_hLoopbackThreadExitEvent))
	{
		// error, log it and bail
		lRet = GetLastError();
		Diagnostics_Write(DVF_ERRORLEVEL, "CloseHandle failed, code: %i", lRet);
		m_hLoopbackThreadExitEvent = NULL;
		hr = DVERR_GENERIC;
		goto error_cleanup;
	}
	m_hLoopbackThreadExitEvent = NULL;

	if (!CloseHandle(m_hLoopbackShutdownEvent))
	{
		// error, log it and bail
		lRet = GetLastError();
		Diagnostics_Write(DVF_ERRORLEVEL, "CloseHandle failed, code: %i", lRet);
		m_hLoopbackShutdownEvent = NULL;
		hr = DVERR_GENERIC;
		goto error_cleanup;
	}
	m_hLoopbackShutdownEvent = NULL;
	
	DNLeaveCriticalSection(&m_csLock);
	DPF_EXIT();
	return DV_OK;

error_cleanup:
	if (m_hLoopbackThread != NULL)
	{
		TerminateThread(m_hLoopbackThread, DVERR_GENERIC);
		CloseHandle(m_hLoopbackThread);
		m_hLoopbackThread = NULL;
	}

	if (m_hLoopbackThreadExitEvent != NULL)
	{
		CloseHandle(m_hLoopbackThreadExitEvent);
		m_hLoopbackThreadExitEvent = NULL;
	}
	
	if (m_hLoopbackShutdownEvent != NULL)
	{
		CloseHandle(m_hLoopbackShutdownEvent);
		m_hLoopbackShutdownEvent = NULL;
	}
	
	DNLeaveCriticalSection(&m_csLock);
	DPF_EXIT();
	return hr;
}

#undef DPF_MODNAME
#define DPF_MODNAME "CSupervisorInfo::SetFullDuplex"
HRESULT CSupervisorInfo::SetFullDuplex(DWORD dwFullDuplex)
{
	DPF_ENTER();
	
	HRESULT hr = DV_OK;
	HKEY hk;
	LONG lRet;
	
	DNEnterCriticalSection(&m_csLock);

	if (!m_creg.WriteDWORD(gc_wszValueName_FullDuplex, dwFullDuplex))
	{
		Diagnostics_Write(DVF_ERRORLEVEL, "CRegistry::WriteDWORD failed");
		hr = DVERR_GENERIC;
	}
	else
	{
		// Flush the registry operations to ensure they
		// are written. Otherwise we may not detect a crash!
		hk = m_creg.GetHandle();
		lRet = RegFlushKey(hk);
		if (lRet != ERROR_SUCCESS)
		{
			Diagnostics_Write(DVF_ERRORLEVEL, "RegFlushKey failed");
			hr = DVERR_GENERIC;
		}
	}

	DNLeaveCriticalSection(&m_csLock);
	DPF_EXIT();
	return hr;	
}

#undef DPF_MODNAME
#define DPF_MODNAME "CSupervisorInfo::GetFullDuplex"
HRESULT CSupervisorInfo::GetFullDuplex(DWORD* pdwFullDuplex)
{
	DPF_ENTER();
	
	HRESULT hr = DV_OK;
	
	DNEnterCriticalSection(&m_csLock);

	if (!m_creg.ReadDWORD(gc_wszValueName_FullDuplex, *pdwFullDuplex))
	{
		// registry key is not present
		*pdwFullDuplex = 0;
		hr = DVERR_GENERIC;		
	}

	DNLeaveCriticalSection(&m_csLock);
	DPF_EXIT();
	return hr;	
}

#undef DPF_MODNAME
#define DPF_MODNAME "CSupervisorInfo::SetHalfDuplex"
HRESULT CSupervisorInfo::SetHalfDuplex(DWORD dwHalfDuplex)
{
	DPF_ENTER();
	
	HRESULT hr = DV_OK;
	LONG lRet;
	HKEY hk;
	
	DNEnterCriticalSection(&m_csLock);

	if (!m_creg.WriteDWORD(gc_wszValueName_HalfDuplex, dwHalfDuplex))
	{
		Diagnostics_Write(DVF_ERRORLEVEL, "CRegistry::WriteDWORD failed");
		hr = DVERR_GENERIC;
	}
	else
	{
		// Flush the registry operations to ensure they
		// are written. Otherwise we may not detect a crash!
		hk = m_creg.GetHandle();
		lRet = RegFlushKey(hk);
		if (lRet != ERROR_SUCCESS)
		{
			Diagnostics_Write(DVF_ERRORLEVEL, "RegFlushKey failed");
			hr = DVERR_GENERIC;
		}
	}

	DNLeaveCriticalSection(&m_csLock);
	DPF_EXIT();
	return hr;	
}

#undef DPF_MODNAME
#define DPF_MODNAME "CSupervisorInfo::GetHalfDuplex"
HRESULT CSupervisorInfo::GetHalfDuplex(DWORD* pdwHalfDuplex)
{
	DPF_ENTER();
	
	HRESULT hr = DV_OK;
	
	DNEnterCriticalSection(&m_csLock);

	if (!m_creg.ReadDWORD(gc_wszValueName_HalfDuplex, *pdwHalfDuplex))
	{
		// registry key is not present
		*pdwHalfDuplex = 0;
		hr = DVERR_GENERIC;		
	}

	DNLeaveCriticalSection(&m_csLock);
	DPF_EXIT();
	return hr;	
}

#undef DPF_MODNAME
#define DPF_MODNAME "CSupervisorInfo::SetMicDetected"
HRESULT CSupervisorInfo::SetMicDetected(DWORD dwMicDetected)
{
	DPF_ENTER();
	
	HRESULT hr = DV_OK;
	LONG lRet;
	HKEY hk;

	DNEnterCriticalSection(&m_csLock);

	if (!m_creg.WriteDWORD(gc_wszValueName_MicDetected, dwMicDetected))
	{
		Diagnostics_Write(DVF_ERRORLEVEL, "CRegistry::WriteDWORD failed");
		hr = DVERR_GENERIC;
	}
	else
	{
		// Flush the registry operations to ensure they
		// are written. Otherwise we may not detect a crash!
		hk = m_creg.GetHandle();
		lRet = RegFlushKey(hk);
		if (lRet != ERROR_SUCCESS)
		{
			Diagnostics_Write(DVF_ERRORLEVEL, "RegFlushKey failed");
			hr = DVERR_GENERIC;
		}
	}

	DNLeaveCriticalSection(&m_csLock);
	DPF_EXIT();
	return hr;	
}

#undef DPF_MODNAME
#define DPF_MODNAME "CSupervisorInfo::GetMicDetected"
HRESULT CSupervisorInfo::GetMicDetected(DWORD* pdwMicDetected)
{
	DPF_ENTER();
	
	HRESULT hr = DV_OK;
	
	DNEnterCriticalSection(&m_csLock);

	if (!m_creg.ReadDWORD(gc_wszValueName_MicDetected, *pdwMicDetected))
	{
		// registry key is not present
		*pdwMicDetected = 0;
		hr = DVERR_GENERIC;		
	}

	DNLeaveCriticalSection(&m_csLock);
	DPF_EXIT();
	return hr;	
}

#undef DPF_MODNAME
#define DPF_MODNAME "CSupervisorInfo::QueryFullDuplex"
HRESULT CSupervisorInfo::QueryFullDuplex()
{
	DPF_ENTER();
	
	HRESULT hr;
	DWORD dwFullDuplex;
	DWORD dwHalfDuplex;
	DWORD dwMicDetected;
	
	DNEnterCriticalSection(&m_csLock);

	if (!m_creg.ReadDWORD(gc_wszValueName_HalfDuplex, dwHalfDuplex))
	{
		// registry key is not present - setup has not run
		DPFX(DPFPREP, DVF_WARNINGLEVEL, "HalfDuplex key not found in registry");
		hr = DVERR_RUNSETUP;
		goto error_cleanup;
	}
	switch (dwHalfDuplex)
	{
	case REGVAL_NOTRUN:
		// test not run
		DPFX(DPFPREP, DVF_WARNINGLEVEL, "HalfDuplex = 0; test not run");
		hr = DVERR_RUNSETUP;
		goto error_cleanup;
		
	case REGVAL_CRASHED:
		// test crashed out!
		DPFX(DPFPREP, DVF_WARNINGLEVEL, "HalfDuplex = 1; test crashed");
		hr = DVERR_SOUNDINITFAILURE;
		goto error_cleanup;

	case REGVAL_FAILED:
		// test failed gracefully
		DPFX(DPFPREP, DVF_WARNINGLEVEL, "HalfDuplex = 2; test failed gracefully");
		hr = DVERR_SOUNDINITFAILURE;
		goto error_cleanup;
		
	case REGVAL_PASSED:
		// test passed
		DPFX(DPFPREP, DVF_INFOLEVEL, "HalfDuplex = 3; test passed");
		break;

	default:
		// bad key value - return run setup
		DPFX(DPFPREP, DVF_WARNINGLEVEL, "HalfDuplex = %i; bad key value!", dwHalfDuplex);
		hr = DVERR_RUNSETUP;
		goto error_cleanup;
	}

	if (!m_creg.ReadDWORD(gc_wszValueName_FullDuplex, dwFullDuplex))
	{
		// registry key is not present - very odd.
		// however, since we at least passed half duplex to get
		// here, we'll return half duplex
		DPFX(DPFPREP, DVF_WARNINGLEVEL, "FullDuplex key not found in registry");
		hr = DV_HALFDUPLEX;
		goto error_cleanup;
	}
	switch (dwFullDuplex)
	{
	case REGVAL_NOTRUN:
		// Test not run - this is very odd, considering that
		// in order to get here, the half duplex test must have
		// been run and passed. Return half duplex.
		DPFX(DPFPREP, DVF_WARNINGLEVEL, "FullDuplex = 0; test not run");
		hr = DV_HALFDUPLEX;
		goto error_cleanup;
		
	case REGVAL_CRASHED:
		// test crashed out! - They tried, and going further
		// wouldn't help, so certify them for half duplex
		DPFX(DPFPREP, DVF_WARNINGLEVEL, "FullDuplex = 1; test crashed");
		hr = DV_HALFDUPLEX;
		goto error_cleanup;

	case REGVAL_FAILED:
		// test failed gracefully - mic test would not have been
		// run, certify them for half duplex.
		DPFX(DPFPREP, DVF_WARNINGLEVEL, "FullDuplex = 2; test failed gracefully");
		hr = DV_HALFDUPLEX;
		goto error_cleanup;
		
	case REGVAL_PASSED:
		// test passed
		DPFX(DPFPREP, DVF_INFOLEVEL, "FullDuplex = 3; test passed");
		break;

	default:
		// bad key value - but the system can at least do
		// half duplex, so certify them for half duplex
		DPFX(DPFPREP, DVF_WARNINGLEVEL, "FullDuplex = %i; bad key value!", dwFullDuplex);
		hr = DV_HALFDUPLEX;
		goto error_cleanup;
	}

	// From this point on, we know the full duplex test was ok.
	// However, in order to get a full duplex pass, the mic must
	// also have been detected.

	if (!m_creg.ReadDWORD(gc_wszValueName_MicDetected, dwMicDetected))
	{
		// registry key is not present - very odd
		DPFX(DPFPREP, DVF_WARNINGLEVEL, "MicDetected key not found in registry");
		hr = DV_HALFDUPLEX;
		goto error_cleanup;
	}
	switch (dwMicDetected)
	{
	case REGVAL_NOTRUN:
		// Test not run - odd, but pass them for half duplex anyway
		DPFX(DPFPREP, DVF_WARNINGLEVEL, "MicDetected = 0; test not run");
		hr = DV_HALFDUPLEX;
		goto error_cleanup;
		
	case REGVAL_CRASHED:
		// test crashed out! This shouldn't happen, since it
		// should have been caught in the full duplex test,
		// but either way, they tried thier best and half
		// duplex worked, so certify them for half duplex.
		DPFX(DPFPREP, DVF_WARNINGLEVEL, "MicDetected = 1; test crashed");
		hr = DV_HALFDUPLEX;
		goto error_cleanup;

	case REGVAL_FAILED:
		// test failed gracefully - certify for half duplex
		DPFX(DPFPREP, DVF_WARNINGLEVEL, "MicDetected = 2; test failed gracefully");
		hr = DV_HALFDUPLEX;
		goto error_cleanup;
		
	case REGVAL_PASSED:
		// test passed
		DPFX(DPFPREP, DVF_INFOLEVEL, "MicDetected = 3; test passed");
		break;

	default:
		// bad key value - odd, but pass them for half duplex anyway.
		DPFX(DPFPREP, DVF_WARNINGLEVEL, "MicDetected = %i; bad key value!", dwMicDetected);
		hr = DV_HALFDUPLEX;
		goto error_cleanup;
	}

	// If we get here, all keys were a clean pass, so return full duplex
	hr = DV_FULLDUPLEX;	

error_cleanup:
	DNLeaveCriticalSection(&m_csLock);
	DPF_EXIT();
	return hr;
}

#undef DPF_MODNAME
#define DPF_MODNAME "CSupervisorInfo::OpenRegKey"
HRESULT CSupervisorInfo::OpenRegKey(BOOL fCreate)
{
	DPF_ENTER();
	
	LONG lRet;
	HRESULT hr;
	CRegistry cregAudioConfig;
	HKEY hkAudioConfig;
	CRegistry cregRender;
	HKEY hkRender;
	WCHAR wszRenderGuidString[GUID_STRING_LEN];
	WCHAR wszCaptureGuidString[GUID_STRING_LEN];
	BOOL bAudioKeyOpen = FALSE;
	BOOL bRenderKeyOpen = FALSE;
		
	DNEnterCriticalSection(&m_csLock);

	if (!cregAudioConfig.Open(HKEY_CURRENT_USER, gc_wszKeyName_AudioConfig, FALSE, fCreate))
	{
		Diagnostics_Write(DVF_ERRORLEVEL, "CRegistry::Open failed");
		hr = DVERR_GENERIC;
		goto error_cleanup;
	}
	bAudioKeyOpen = TRUE;

	hkAudioConfig = cregAudioConfig.GetHandle();
	
	if (!cregRender.Open(hkAudioConfig, &m_guidRenderDevice, FALSE, fCreate))
	{
		Diagnostics_Write(DVF_ERRORLEVEL, "CRegistry::Open failed");
		hr = DVERR_GENERIC;
		goto error_cleanup;
	}
	bRenderKeyOpen = TRUE;

	hkRender = cregRender.GetHandle();
	
	if (!m_creg.Open(hkRender, &m_guidCaptureDevice, FALSE, fCreate))
	{
		Diagnostics_Write(DVF_ERRORLEVEL, "CRegistry::Open failed");
		hr = DVERR_GENERIC;
		goto error_cleanup;
	}
	
	DNLeaveCriticalSection(&m_csLock);
	DPF_EXIT();
	return S_OK;

error_cleanup:
	if (bRenderKeyOpen)
	{
		cregRender.Close();
	}

	if (bAudioKeyOpen)
	{
		cregAudioConfig.Close();
	}

	DNLeaveCriticalSection(&m_csLock);
	DPF_EXIT();
	return hr;
}

#undef DPF_MODNAME
#define DPF_MODNAME "CSupervisorInfo::ThereCanBeOnlyOne"
HRESULT CSupervisorInfo::ThereCanBeOnlyOne()
{
	DPF_ENTER();
	
	LONG lRet;
	HANDLE hMutex;
	HRESULT hr;
	
	hr = DV_OK;
	hMutex = CreateMutex(NULL, FALSE, gc_szMutexName);
	lRet = GetLastError();
	if (hMutex == NULL)
	{
		// something went very wrong
		Diagnostics_Write(DVF_ERRORLEVEL, "CreateMutex failed, code: %i", lRet);
		return DVERR_GENERIC;
	}
	
	// see if the mutex already existed
	if (lRet == ERROR_ALREADY_EXISTS)
	{
		Diagnostics_Write(DVF_ERRORLEVEL, "Detected another instance of test running");
		if (!CloseHandle(hMutex))
		{
			lRet = GetLastError();
			Diagnostics_Write(DVF_ERRORLEVEL, "CloseHandle failed, code: %i", lRet);
		}
		return DVERR_ALREADYPENDING;
	}

	DNEnterCriticalSection(&m_csLock);
	if (m_hMutex != NULL)
	{
		DNLeaveCriticalSection(&m_csLock);
		Diagnostics_Write(DVF_ERRORLEVEL, "m_hMutex not null");
		if (!CloseHandle(hMutex))
		{
			lRet = GetLastError();
			Diagnostics_Write(DVF_ERRORLEVEL, "CloseHandle failed, code: %i", lRet);
		}
		return DVERR_GENERIC;
	}

	m_hMutex = hMutex;

	DNLeaveCriticalSection(&m_csLock);
	DPF_EXIT();
	return DV_OK;
}


#undef DPF_MODNAME
#define DPF_MODNAME "CSupervisorInfo::CloseMutex"
void CSupervisorInfo::CloseMutex()
{
	DPF_ENTER();

	DNEnterCriticalSection(&m_csLock);

	LONG lRet;
	
	// close the mutex
	if (!CloseHandle(m_hMutex))
	{
		lRet = GetLastError();
		Diagnostics_Write(DVF_ERRORLEVEL, "CloseHandle failed, code: %i", lRet);
	}
	m_hMutex = NULL;

	DNLeaveCriticalSection(&m_csLock);
	DPF_EXIT();
	return;
}

#undef DPF_MODNAME
#define DPF_MODNAME "CSupervisorInfo::SetVoiceDetected"
void CSupervisorInfo::SetVoiceDetected()
{
	DPF_ENTER();
	DNEnterCriticalSection(&m_csLock);
	m_fVoiceDetected = TRUE;
	DNLeaveCriticalSection(&m_csLock);
	DPF_EXIT();
	return;
}

#undef DPF_MODNAME
#define DPF_MODNAME "CSupervisorInfo::ClearVoiceDetected"
void CSupervisorInfo::ClearVoiceDetected()
{
	DPF_ENTER();
	DNEnterCriticalSection(&m_csLock);
	m_fVoiceDetected = FALSE;
	DNLeaveCriticalSection(&m_csLock);
	DPF_EXIT();
	return;
}

#undef DPF_MODNAME
#define DPF_MODNAME "CSupervisorInfo::GetVoiceDetected"
void CSupervisorInfo::GetVoiceDetected(BOOL* lpfPreviousCrash)
{
	DPF_ENTER();
	DNEnterCriticalSection(&m_csLock);
	*lpfPreviousCrash = m_fVoiceDetected;
	DNLeaveCriticalSection(&m_csLock);
	DPF_EXIT();
	return;
}

#undef DPF_MODNAME
#define DPF_MODNAME "CSupervisorInfo::SetUserBack"
void CSupervisorInfo::SetUserBack()
{
	DPF_ENTER();
	DNEnterCriticalSection(&m_csLock);
	m_fUserBack = TRUE;
	DNLeaveCriticalSection(&m_csLock);
	DPF_EXIT();
	return;
}

#undef DPF_MODNAME
#define DPF_MODNAME "CSupervisorInfo::ClearUserBack"
void CSupervisorInfo::ClearUserBack()
{
	DPF_ENTER();
	DNEnterCriticalSection(&m_csLock);
	m_fUserBack = FALSE;
	DNLeaveCriticalSection(&m_csLock);
	DPF_EXIT();
	return;
}

#undef DPF_MODNAME
#define DPF_MODNAME "CSupervisorInfo::GetUserBack"
void CSupervisorInfo::GetUserBack(BOOL* lpfUserBack)
{
	DPF_ENTER();
	DNEnterCriticalSection(&m_csLock);
	*lpfUserBack = m_fUserBack;
	DNLeaveCriticalSection(&m_csLock);
	DPF_EXIT();
	return;
}

#undef DPF_MODNAME
#define DPF_MODNAME "CSupervisorInfo::SetUserCancel"
void CSupervisorInfo::SetUserCancel()
{
	DPF_ENTER();
	DNEnterCriticalSection(&m_csLock);
	m_fUserCancel = TRUE;
	DNLeaveCriticalSection(&m_csLock);
	DPF_EXIT();
	return;
}

#undef DPF_MODNAME
#define DPF_MODNAME "CSupervisorInfo::ClearUserCancel"
void CSupervisorInfo::ClearUserCancel()
{
	DPF_ENTER();
	DNEnterCriticalSection(&m_csLock);
	m_fUserCancel = FALSE;
	DNLeaveCriticalSection(&m_csLock);
	DPF_EXIT();
	return;
}

#undef DPF_MODNAME
#define DPF_MODNAME "CSupervisorInfo::GetUserCancel"
void CSupervisorInfo::GetUserCancel(BOOL* lpfUserCancel)
{
	DPF_ENTER();
	DNEnterCriticalSection(&m_csLock);
	*lpfUserCancel = m_fUserCancel;
	DNLeaveCriticalSection(&m_csLock);
	DPF_EXIT();
	return;
}

#undef DPF_MODNAME
#define DPF_MODNAME "CSupervisorInfo::SetWelcomeNext"
void CSupervisorInfo::SetWelcomeNext()
{
	DPF_ENTER();
	DNEnterCriticalSection(&m_csLock);
	m_fWelcomeNext = TRUE;
	DNLeaveCriticalSection(&m_csLock);
	DPF_EXIT();
	return;
}

#undef DPF_MODNAME
#define DPF_MODNAME "CSupervisorInfo::ClearWelcomeNext"
void CSupervisorInfo::ClearWelcomeNext()
{
	DPF_ENTER();
	DNEnterCriticalSection(&m_csLock);
	m_fWelcomeNext = FALSE;
	DNLeaveCriticalSection(&m_csLock);
	DPF_EXIT();
	return;
}

#undef DPF_MODNAME
#define DPF_MODNAME "CSupervisorInfo::GetWelcomeNext"
void CSupervisorInfo::GetWelcomeNext(BOOL* lpfWelcomeNext)
{
	DPF_ENTER();
	DNEnterCriticalSection(&m_csLock);
	*lpfWelcomeNext = m_fWelcomeNext;
	DNLeaveCriticalSection(&m_csLock);
	DPF_EXIT();
	return;
}

#undef DPF_MODNAME
#define DPF_MODNAME "CSupervisorInfo::GetError"
void CSupervisorInfo::GetError(HRESULT* hr)
{
	DPF_ENTER();
	DNEnterCriticalSection(&m_csLock);
	*hr = m_hrError;
	DNLeaveCriticalSection(&m_csLock);
	DPF_EXIT();
	return;
}

#undef DPF_MODNAME
#define DPF_MODNAME "CSupervisorInfo::SetError"
void CSupervisorInfo::SetError(HRESULT hr)
{
	DPF_ENTER();
	DNEnterCriticalSection(&m_csLock);
	m_hrError = hr;
	DNLeaveCriticalSection(&m_csLock);
	DPF_EXIT();
	return;
}

#undef DPF_MODNAME
#define DPF_MODNAME "CSupervisorInfo::GetTitleFont"
void CSupervisorInfo::GetTitleFont(HFONT* lphfTitle)
{
	DPF_ENTER();
	DNEnterCriticalSection(&m_csLock);
	*lphfTitle = m_hfTitle;
	DNLeaveCriticalSection(&m_csLock);
	DPF_EXIT();
	return;
}

#undef DPF_MODNAME
#define DPF_MODNAME "CSupervisorInfo::GetBoldFont"
void CSupervisorInfo::GetBoldFont(HFONT* lphfBold)
{
	DPF_ENTER();
	DNEnterCriticalSection(&m_csLock);
	*lphfBold = m_hfBold;
	DNLeaveCriticalSection(&m_csLock);
	DPF_EXIT();
	return;
}

#undef DPF_MODNAME
#define DPF_MODNAME "CSupervisorInfo::GetCaptureDevice"
void CSupervisorInfo::GetCaptureDevice(GUID* lpguidCaptureDevice)
{
	DPFX(DPFPREP, DVF_ENTRYLEVEL, "Enter");
	DNEnterCriticalSection(&m_csLock);
	*lpguidCaptureDevice = m_guidCaptureDevice;
	DNLeaveCriticalSection(&m_csLock);
	DPFX(DPFPREP, DVF_ENTRYLEVEL, "Exit");
	return;
}

#undef DPF_MODNAME
#define DPF_MODNAME "CSupervisorInfo::SetCaptureDevice"
void CSupervisorInfo::SetCaptureDevice(GUID guidCaptureDevice)
{
	DPF_ENTER();
	DNEnterCriticalSection(&m_csLock);
	m_guidCaptureDevice = guidCaptureDevice;
	DNLeaveCriticalSection(&m_csLock);
	DPF_EXIT();
	return;
}

#undef DPF_MODNAME
#define DPF_MODNAME "CSupervisorInfo::GetRenderDevice"
void CSupervisorInfo::GetRenderDevice(GUID* lpguidRenderDevice)
{
	DPFX(DPFPREP, DVF_ENTRYLEVEL, "Enter");
	DNEnterCriticalSection(&m_csLock);
	*lpguidRenderDevice = m_guidRenderDevice;
	DNLeaveCriticalSection(&m_csLock);
	DPFX(DPFPREP, DVF_ENTRYLEVEL, "Exit");
	return;
}

#undef DPF_MODNAME
#define DPF_MODNAME "CSupervisorInfo::SetRenderDevice"
void CSupervisorInfo::SetRenderDevice(GUID guidRenderDevice)
{
	DPF_ENTER();
	DNEnterCriticalSection(&m_csLock);
	m_guidRenderDevice = guidRenderDevice;
	DNLeaveCriticalSection(&m_csLock);
	DPF_EXIT();
	return;
}

#undef DPF_MODNAME
#define DPF_MODNAME "CSupervisorInfo::SetWaveOutId"
HRESULT CSupervisorInfo::SetWaveOutId(UINT ui)
{
	HRESULT hr = DV_OK;
	
	DPF_ENTER();
	DNEnterCriticalSection(&m_csLock);
	m_uiWaveOutDeviceId = ui;

	ZeroMemory(&m_woCaps, sizeof(WAVEOUTCAPS));
	MMRESULT mmr = waveOutGetDevCaps(ui, &m_woCaps, sizeof(WAVEOUTCAPS));
	if (mmr != MMSYSERR_NOERROR)
	{
		ZeroMemory(&m_woCaps, sizeof(WAVEOUTCAPS));
		hr = DVERR_INVALIDPARAM;
	}

//error_cleanup:
	DNLeaveCriticalSection(&m_csLock);
	DPF_EXIT();
	return hr;
}

#undef DPF_MODNAME
#define DPF_MODNAME "CSupervisorInfo::SetWaveInId"
void CSupervisorInfo::SetWaveInId(UINT ui)
{
	DPF_ENTER();
	DNEnterCriticalSection(&m_csLock);
	m_uiWaveInDeviceId = ui;
	DNLeaveCriticalSection(&m_csLock);
	DPF_EXIT();
	return;
}

#undef DPF_MODNAME
#define DPF_MODNAME "CSupervisorInfo::GetLoopbackFlags"
void CSupervisorInfo::GetLoopbackFlags(DWORD* pdwFlags)
{
	DPF_ENTER();
	DNEnterCriticalSection(&m_csLock);
	*pdwFlags = m_dwLoopbackFlags;
	DNLeaveCriticalSection(&m_csLock);
	DPF_EXIT();
	return;
}

#undef DPF_MODNAME
#define DPF_MODNAME "CSupervisorInfo::SetLoopbackFlags"
void CSupervisorInfo::SetLoopbackFlags(DWORD dwFlags)
{
	DPF_ENTER();
	DNEnterCriticalSection(&m_csLock);
	m_dwLoopbackFlags = dwFlags;
	DNLeaveCriticalSection(&m_csLock);
	DPF_EXIT();
	return;
}

#undef DPF_MODNAME
#define DPF_MODNAME "CSupervisorInfo::GetCheckAudioSetupFlags"
void CSupervisorInfo::GetCheckAudioSetupFlags(DWORD* pdwFlags)
{
	DPF_ENTER();
	DNEnterCriticalSection(&m_csLock);
	*pdwFlags = m_dwCheckAudioSetupFlags;
	DNLeaveCriticalSection(&m_csLock);
	DPF_EXIT();
	return;
}

#undef DPF_MODNAME
#define DPF_MODNAME "CSupervisorInfo::SetCheckAudioSetupFlags"
void CSupervisorInfo::SetCheckAudioSetupFlags(DWORD dwFlags)
{
	DPF_ENTER();
	DNEnterCriticalSection(&m_csLock);
	m_dwCheckAudioSetupFlags = dwFlags;
	DNLeaveCriticalSection(&m_csLock);
	DPF_EXIT();
	return;
}

#undef DPF_MODNAME
#define DPF_MODNAME "CSupervisorInfo::GetFullDuplexResults"
void CSupervisorInfo::GetFullDuplexResults(HRESULT* phr)
{
	DPF_ENTER();
	DNEnterCriticalSection(&m_csLock);
	*phr = m_hrFullDuplexResults;
	DNLeaveCriticalSection(&m_csLock);
	DPF_EXIT();
	return;
}

#undef DPF_MODNAME
#define DPF_MODNAME "CSupervisorInfo::SetFullDuplexResults"
void CSupervisorInfo::SetFullDuplexResults(HRESULT hr)
{
	DPF_ENTER();
	DNEnterCriticalSection(&m_csLock);
	m_hrFullDuplexResults = hr;
	DNLeaveCriticalSection(&m_csLock);
	DPF_EXIT();
	return;
}

#undef DPF_MODNAME
#define DPF_MODNAME "CSupervisorInfo::GetHWNDParent"
void CSupervisorInfo::GetHWNDParent(HWND* lphwnd)
{
	DPF_ENTER();
	DNEnterCriticalSection(&m_csLock);
	*lphwnd = m_hwndParent;
	DNLeaveCriticalSection(&m_csLock);
	DPF_EXIT();
	return;
}

#undef DPF_MODNAME
#define DPF_MODNAME "CSupervisorInfo::SetHWNDWizard"
void CSupervisorInfo::SetHWNDParent(HWND hwnd)
{
	DPF_ENTER();
	DNEnterCriticalSection(&m_csLock);
	m_hwndParent = hwnd;
	DNLeaveCriticalSection(&m_csLock);
	DPF_EXIT();
	return;
}

#undef DPF_MODNAME
#define DPF_MODNAME "CSupervisorInfo::GetHWNDWizard"
void CSupervisorInfo::GetHWNDWizard(HWND* lphwnd)
{
	DPF_ENTER();
	DNEnterCriticalSection(&m_csLock);
	*lphwnd = m_hwndWizard;
	DNLeaveCriticalSection(&m_csLock);
	DPF_EXIT();
	return;
}

#undef DPF_MODNAME
#define DPF_MODNAME "CSupervisorInfo::SetHWNDWizard"
void CSupervisorInfo::SetHWNDWizard(HWND hwnd)
{
	DPF_ENTER();
	DNEnterCriticalSection(&m_csLock);
	m_hwndWizard = hwnd;
	DNLeaveCriticalSection(&m_csLock);
	DPF_EXIT();
	return;
}

#undef DPF_MODNAME
#define DPF_MODNAME "CSupervisorInfo::GetHWNDDialog"
void CSupervisorInfo::GetHWNDDialog(HWND* lphwnd)
{
	DPF_ENTER();
	DNEnterCriticalSection(&m_csLock);
	*lphwnd = m_hwndDialog;
	DNLeaveCriticalSection(&m_csLock);
	DPF_EXIT();
	return;
}

#undef DPF_MODNAME
#define DPF_MODNAME "CSupervisorInfo::SetHWNDDialog"
void CSupervisorInfo::SetHWNDDialog(HWND hwnd)
{
	DPF_ENTER();
	DNEnterCriticalSection(&m_csLock);
	m_hwndDialog = hwnd;
	DNLeaveCriticalSection(&m_csLock);
	DPF_EXIT();
	return;
}

#undef DPF_MODNAME
#define DPF_MODNAME "CSupervisorInfo::GetHWNDProgress"
void CSupervisorInfo::GetHWNDProgress(HWND* lphwnd)
{
	DPF_ENTER();
	DNEnterCriticalSection(&m_csLock);
	*lphwnd = m_hwndProgress;
	DNLeaveCriticalSection(&m_csLock);
	DPF_EXIT();
	return;
}

#undef DPF_MODNAME
#define DPF_MODNAME "CSupervisorInfo::SetHWNDProgress"
void CSupervisorInfo::SetHWNDProgress(HWND hwnd)
{
	DPF_ENTER();
	DNEnterCriticalSection(&m_csLock);
	m_hwndProgress = hwnd;
	DNLeaveCriticalSection(&m_csLock);
	DPF_EXIT();
	return;
}

#undef DPF_MODNAME
#define DPF_MODNAME "CSupervisorInfo::GetHWNDInputPeak"
void CSupervisorInfo::GetHWNDInputPeak(HWND* lphwnd)
{
	DPF_ENTER();
	DNEnterCriticalSection(&m_csLock);
	*lphwnd = m_hwndInputPeak;
	DNLeaveCriticalSection(&m_csLock);
	DPF_EXIT();
	return;
}

#undef DPF_MODNAME
#define DPF_MODNAME "CSupervisorInfo::SetHWNDInputPeak"
void CSupervisorInfo::SetHWNDInputPeak(HWND hwnd)
{
	DPF_ENTER();
	DNEnterCriticalSection(&m_csLock);
	m_hwndInputPeak = hwnd;
	DNLeaveCriticalSection(&m_csLock);
	DPF_EXIT();
	return;
}

#undef DPF_MODNAME
#define DPF_MODNAME "CSupervisorInfo::GetHWNDOutputPeak"
void CSupervisorInfo::GetHWNDOutputPeak(HWND* lphwnd)
{
	DPF_ENTER();
	DNEnterCriticalSection(&m_csLock);
	*lphwnd = m_hwndOutputPeak;
	DNLeaveCriticalSection(&m_csLock);
	DPF_EXIT();
	return;
}

#undef DPF_MODNAME
#define DPF_MODNAME "CSupervisorInfo::SetHWNDOutputPeak"
void CSupervisorInfo::SetHWNDOutputPeak(HWND hwnd)
{
	DPF_ENTER();
	DNEnterCriticalSection(&m_csLock);
	m_hwndOutputPeak = hwnd;
	DNLeaveCriticalSection(&m_csLock);
	DPF_EXIT();
	return;
}

#undef DPF_MODNAME
#define DPF_MODNAME "CSupervisorInfo::GetHWNDInputVolumeSlider"
void CSupervisorInfo::GetHWNDInputVolumeSlider(HWND* lphwnd)
{
	DPF_ENTER();
	DNEnterCriticalSection(&m_csLock);
	*lphwnd = m_hwndInputVolumeSlider;
	DNLeaveCriticalSection(&m_csLock);
	DPF_EXIT();
	return;
}

#undef DPF_MODNAME
#define DPF_MODNAME "CSupervisorInfo::SetHWNDInputVolumeSlider"
void CSupervisorInfo::SetHWNDInputVolumeSlider(HWND hwnd)
{
	DPF_ENTER();
	DNEnterCriticalSection(&m_csLock);
	m_hwndInputVolumeSlider = hwnd;
	DNLeaveCriticalSection(&m_csLock);
	DPF_EXIT();
	return;
}

#undef DPF_MODNAME
#define DPF_MODNAME "CSupervisorInfo::GetInputVolumeSliderPos"
void CSupervisorInfo::GetInputVolumeSliderPos(LONG* lpl)
{
	DPF_ENTER();
	DNEnterCriticalSection(&m_csLock);
	*lpl = m_lInputVolumeSliderPos;
	DNLeaveCriticalSection(&m_csLock);
	DPF_EXIT();
	return;
}

#undef DPF_MODNAME
#define DPF_MODNAME "CSupervisorInfo::SetInputVolumeSliderPos"
void CSupervisorInfo::SetInputVolumeSliderPos(LONG l)
{
	DPF_ENTER();
	DNEnterCriticalSection(&m_csLock);
	m_lInputVolumeSliderPos = l;
	DNLeaveCriticalSection(&m_csLock);
	DPF_EXIT();
	return;
}

#undef DPF_MODNAME
#define DPF_MODNAME "CSupervisorInfo::GetHWNDOutputVolumeSlider"
void CSupervisorInfo::GetHWNDOutputVolumeSlider(HWND* lphwnd)
{
	DPF_ENTER();
	DNEnterCriticalSection(&m_csLock);
	*lphwnd = m_hwndOutputVolumeSlider;
	DNLeaveCriticalSection(&m_csLock);
	DPF_EXIT();
	return;
}

#undef DPF_MODNAME
#define DPF_MODNAME "CSupervisorInfo::SetHWNDOutputVolumeSlider"
void CSupervisorInfo::SetHWNDOutputVolumeSlider(HWND hwnd)
{
	DPF_ENTER();
	DNEnterCriticalSection(&m_csLock);
	m_hwndOutputVolumeSlider = hwnd;
	DNLeaveCriticalSection(&m_csLock);
	DPF_EXIT();
	return;
}

#undef DPF_MODNAME
#define DPF_MODNAME "CSupervisorInfo::GetDPVC"
void CSupervisorInfo::GetDPVC(LPDIRECTPLAYVOICECLIENT* lplpdpvc)
{
	DPF_ENTER();
	DNEnterCriticalSection(&m_csLock);
	*lplpdpvc = m_lpdpvc;
	DNLeaveCriticalSection(&m_csLock);
	DPF_EXIT();
	return;
}

#undef DPF_MODNAME
#define DPF_MODNAME "CSupervisorInfo::SetDPVC"
void CSupervisorInfo::SetDPVC(LPDIRECTPLAYVOICECLIENT lpdpvc)
{
	DPF_ENTER();
	DNEnterCriticalSection(&m_csLock);
	m_lpdpvc = lpdpvc;
	DNLeaveCriticalSection(&m_csLock);
	DPF_EXIT();
	return;
}

#undef DPF_MODNAME
#define DPF_MODNAME "CSupervisorInfo::GetLoopbackShutdownEvent"
void CSupervisorInfo::GetLoopbackShutdownEvent(HANDLE* lphEvent)
{
	DPF_ENTER();
	DNEnterCriticalSection(&m_csLock);
	*lphEvent = m_hLoopbackShutdownEvent;
	DNLeaveCriticalSection(&m_csLock);
	DPF_EXIT();
	return;
}

#undef DPF_MODNAME
#define DPF_MODNAME "CSupervisorInfo::SetLoopbackShutdownEvent"
void CSupervisorInfo::SetLoopbackShutdownEvent(HANDLE hEvent)
{
	DPF_ENTER();
	DNEnterCriticalSection(&m_csLock);
	m_hLoopbackShutdownEvent = hEvent;
	DNLeaveCriticalSection(&m_csLock);
	DPF_EXIT();
	return;
}

#undef DPF_MODNAME
#define DPF_MODNAME "CSupervisorInfo::GetIPC"
void CSupervisorInfo::GetIPC(CSupervisorIPC** lplpsipc)
{
	DPF_ENTER();
	DNEnterCriticalSection(&m_csLock);
	*lplpsipc = &m_sipc;
	DNLeaveCriticalSection(&m_csLock);
	DPF_EXIT();
	return;
}

#undef DPF_MODNAME
#define DPF_MODNAME "CSupervisorInfo::GetAbortFullDuplex"
void CSupervisorInfo::GetAbortFullDuplex(BOOL* lpfAbort)
{
	DPF_ENTER();
	DNEnterCriticalSection(&m_csLock);
	*lpfAbort = m_fAbortFullDuplex;
	DNLeaveCriticalSection(&m_csLock);
	DPF_EXIT();
	return;
}

#undef DPF_MODNAME
#define DPF_MODNAME "CSupervisorInfo::ClearAbortFullDuplex"
void CSupervisorInfo::ClearAbortFullDuplex()
{
	DPF_ENTER();
	DNEnterCriticalSection(&m_csLock);
	m_fAbortFullDuplex = FALSE;
	DNLeaveCriticalSection(&m_csLock);
	DPF_EXIT();
	return;
}

#undef DPF_MODNAME
#define DPF_MODNAME "CSupervisorInfo::InitIPC"
HRESULT CSupervisorInfo::InitIPC()
{
	DPF_ENTER();
	
	HRESULT hr;

	// the IPC object has it's own guard...
	hr = m_sipc.Init();
	
	DPF_EXIT();
	return hr;
}

#undef DPF_MODNAME
#define DPF_MODNAME "CSupervisorInfo::DeinitIPC"
HRESULT CSupervisorInfo::DeinitIPC()
{
	DPF_ENTER();
	
	HRESULT hr;

	// The IPC object has it's own guard...
	// and this is a safe call even if Init
	// has not been called
	hr = m_sipc.Deinit();
	
	DPF_EXIT();
	return hr;
}

#undef DPF_MODNAME
#define DPF_MODNAME "CSupervisorInfo::InitClass"
BOOL CSupervisorInfo::InitClass()
{
	if (DNInitializeCriticalSection(&m_csLock))
	{
		m_fCritSecInited = TRUE;
		return TRUE;
	}	
	else
	{
		return FALSE;
	}
}

#undef DPF_MODNAME
#define DPF_MODNAME "SupervisorCheckAudioSetup"
HRESULT SupervisorCheckAudioSetup(
	const GUID* lpguidRenderDevice,
	const GUID* lpguidCaptureDevice,
	HWND hwndParent,
	DWORD dwFlags)
{
	DPF_ENTER();
	
	HRESULT hr;
	LRESULT lRet;
	HKEY hkDevice = NULL;
	int iRet;
	CSupervisorInfo sinfo;
	CPeakMeterWndClass pmwc;
	BOOL fFullDuplexPassed;
	BOOL fVoiceDetected;
	BOOL fUserCancel;
	BOOL fUserBack;
	GUID guidCaptureDevice;
	GUID guidRenderDevice;
	BOOL fRegKeyOpen = FALSE;
	BOOL fWndClassRegistered = FALSE;
	BOOL fTitleFontCreated = FALSE;
	BOOL fBoldFontCreated = FALSE;
	BOOL fMutexOpen = FALSE;

	if (!sinfo.InitClass())
	{
		return DVERR_OUTOFMEMORY;
	}

	// init the globals
	g_hResDLLInstance = NULL;

	// validate the HWND, if non null
	if (hwndParent != NULL && !IsWindow(hwndParent))
	{
		Diagnostics_Write(DVF_ERRORLEVEL, "Invalid (but non-null) Window Handle passed in CheckAudioSetup");
		hr = DVERR_INVALIDPARAM;
		goto error_cleanup;
	}

	// validate the flags
	if (dwFlags & ~(DVFLAGS_QUERYONLY|DVFLAGS_NOQUERY|DVFLAGS_WAVEIDS|DVFLAGS_ALLOWBACK))
	{
		Diagnostics_Write(DVF_ERRORLEVEL, "Invalid flags specified in CheckAudioSetup: %x", dwFlags);
		hr = DVERR_INVALIDFLAGS;
		goto error_cleanup;
	}
	if (dwFlags & DVFLAGS_QUERYONLY)
	{
		if (dwFlags & (DVFLAGS_NOQUERY|DVFLAGS_ALLOWBACK))
		{
			Diagnostics_Write(DVF_ERRORLEVEL, "Invalid flags specified in CheckAudioSetup: %x", dwFlags);
			hr = DVERR_INVALIDFLAGS;
			goto error_cleanup;
		}
	}

	// save the flags
	sinfo.SetCheckAudioSetupFlags(dwFlags);

	// if the waveid flag was specified, translate the waveid to a guid
	if (dwFlags & DVFLAGS_WAVEIDS)
	{
		hr = DV_MapWaveIDToGUID( FALSE, lpguidRenderDevice->Data1, guidRenderDevice );
		if (FAILED(hr))
		{
			Diagnostics_Write(DVF_ERRORLEVEL, "DV_MapWaveIDToGUID failed, code: %i", hr);
			goto error_cleanup;
		}
		
		hr = DV_MapWaveIDToGUID( TRUE, lpguidCaptureDevice->Data1, guidCaptureDevice );
		if (FAILED(hr))
		{
			Diagnostics_Write(DVF_ERRORLEVEL, "DV_MapWaveIDToGUID failed, code: %i", hr);
			goto error_cleanup;
		}
	}
	else
	{
		hr = DV_MapPlaybackDevice(lpguidRenderDevice, &guidRenderDevice);
		if (FAILED(hr))
		{
			Diagnostics_Write(DVF_ERRORLEVEL, "DV_MapPlaybackDevice failed, code: %i", hr);
			goto error_cleanup;
		}

		// map the devices
		hr = DV_MapCaptureDevice(lpguidCaptureDevice, &guidCaptureDevice);
		if (FAILED(hr))
		{
			Diagnostics_Write(DVF_ERRORLEVEL, "DV_MapCaptureDevice failed, code: %i", hr);
			goto error_cleanup;
		}
	}
	
	// the device guids have been mapped, if required, so save them
	sinfo.SetCaptureDevice(guidCaptureDevice);
	sinfo.SetRenderDevice(guidRenderDevice);

	// get the device descriptions, which also validates the GUIDs on
	// pre-millennium systems.
	hr = sinfo.GetDeviceDescriptions();
	if (FAILED(hr))
	{
		// error, log it and bail out of the wizard
		Diagnostics_Write(DVF_ERRORLEVEL, "Error getting device descriptions, code: %i", hr);
		goto error_cleanup;
	}

	// Open the registry key
	hr = sinfo.OpenRegKey(TRUE);
	if (FAILED(hr))
	{
		// error, log it and bail out of the wizard
		Diagnostics_Write(DVF_ERRORLEVEL, "Unable to open reg key, code: %i", hr);
		hr = DVERR_GENERIC;
		goto error_cleanup;
	}
	fRegKeyOpen = TRUE;
	
	if (dwFlags & DVFLAGS_QUERYONLY)
	{
		hr = SupervisorQueryAudioSetup(&sinfo);	
		sinfo.CloseRegKey();
		DPF_EXIT();
		return hr;
	}

	g_hResDLLInstance = LoadLibraryA(gc_szResDLLName);
	if (g_hResDLLInstance == NULL)
	{
		lRet = GetLastError();
		Diagnostics_Write(DVF_ERRORLEVEL, "Unable to get instance handle to resource dll: %s", gc_szResDLLName);
		Diagnostics_Write(DVF_ERRORLEVEL, "LoadLibrary error code: %i", lRet);
		hr = DVERR_GENERIC;
		goto error_cleanup;
	}

	// check for other instances of the wizard
	hr = sinfo.ThereCanBeOnlyOne();
	if (FAILED(hr))
	{
		if (hr == DVERR_ALREADYPENDING)
		{
			Diagnostics_Write(DVF_ERRORLEVEL, "DirectPlay Voice Setup Wizard already running");
		}
		else
		{
			Diagnostics_Write(DVF_ERRORLEVEL, "ThereCanBeOnlyOne failed, hr: %i", hr);
		}
		goto error_cleanup;
	}
	fMutexOpen = TRUE;
	
	// register the peak meter custom control window class
	hr = pmwc.Register();
	if (FAILED(hr))
	{
		Diagnostics_Write(DVF_ERRORLEVEL, "CPeakMeterWndClass::Init failed, code: %i", hr);
		goto error_cleanup;
	}
	fWndClassRegistered = TRUE;

	// create the wizard header fonts
	hr = sinfo.CreateTitleFont();
	if (FAILED(hr))
	{
		Diagnostics_Write(DVF_ERRORLEVEL, "CreateTitleFont failed");
		goto error_cleanup;
	}
	fTitleFontCreated = TRUE;
	hr = sinfo.CreateBoldFont();
	if (FAILED(hr))
	{
		Diagnostics_Write(DVF_ERRORLEVEL, "CreateBoldFont failed");
		goto error_cleanup;
	}
	fBoldFontCreated = TRUE;

	// prepare the wizard pages
	PROPSHEETPAGE psp;
	HPROPSHEETPAGE rghpsp[10];
	PROPSHEETHEADER psh;

	// Welcome page
	ZeroMemory(&psp, sizeof(psp));
//	psp.dwSize = sizeof(psp);
	psp.dwSize = PROPSHEETPAGE_STRUCT_SIZE;
	psp.dwFlags = PSP_DEFAULT; //|PSP_HIDEHEADER;
	psp.hInstance =	g_hResDLLInstance;
	psp.lParam = (LPARAM) &sinfo;
	psp.pfnDlgProc = WelcomeProc;
	psp.pszTemplate = MAKEINTRESOURCE(IDD_WELCOME);

	rghpsp[0] = CreatePropertySheetPage(&psp);
	if (rghpsp[0] == NULL)
	{
		lRet = GetLastError();
		Diagnostics_Write(DVF_ERRORLEVEL, "CreatePropertySheetPage failed, code: %i", lRet);
		hr = DVERR_GENERIC;
		goto error_cleanup;
	}

	// Full Duplex Test Page
	ZeroMemory(&psp, sizeof(psp));
//	psp.dwSize = sizeof(psp);
	psp.dwSize = PROPSHEETPAGE_STRUCT_SIZE;
	psp.dwFlags = PSP_DEFAULT;//|PSP_HIDEHEADER;
	psp.hInstance =	g_hResDLLInstance;
	psp.lParam = (LPARAM) &sinfo;
	psp.pfnDlgProc = FullDuplexProc;
	psp.pszTemplate = MAKEINTRESOURCE(IDD_FULLDUPLEXTEST);

	rghpsp[1] = CreatePropertySheetPage(&psp);
	if (rghpsp[1] == NULL)
	{
		lRet = GetLastError();
		Diagnostics_Write(DVF_ERRORLEVEL, "CreatePropertySheetPage failed, code: %i", lRet);
		hr = DVERR_GENERIC;
		goto error_cleanup;
	}

	// Microphone Test Page
	ZeroMemory(&psp, sizeof(psp));
//	psp.dwSize = sizeof(psp);
	psp.dwSize = PROPSHEETPAGE_STRUCT_SIZE;
	psp.dwFlags = PSP_DEFAULT; //|PSP_HIDEHEADER;
	psp.hInstance =	g_hResDLLInstance;
	psp.lParam = (LPARAM) &sinfo;
	psp.pfnDlgProc = MicTestProc;
	psp.pszTemplate = MAKEINTRESOURCE(IDD_MICTEST);

	rghpsp[2] = CreatePropertySheetPage(&psp);
	if (rghpsp[2] == NULL)
	{
		lRet = GetLastError();
		Diagnostics_Write(DVF_ERRORLEVEL, "CreatePropertySheetPage failed, code: %i", lRet);
		hr = DVERR_GENERIC;
		goto error_cleanup;
	}

	// Microphone Failed Page
	ZeroMemory(&psp, sizeof(psp));
//	psp.dwSize = sizeof(psp);
	psp.dwSize = PROPSHEETPAGE_STRUCT_SIZE;
	psp.dwFlags = PSP_DEFAULT;//|PSP_HIDEHEADER;
	psp.hInstance =	g_hResDLLInstance;
	psp.lParam = (LPARAM) &sinfo;
	psp.pfnDlgProc = MicTestFailedProc;
	psp.pszTemplate = MAKEINTRESOURCE(IDD_MICTEST_FAILED);

	rghpsp[3] = CreatePropertySheetPage(&psp);
	if (rghpsp[3] == NULL)
	{
		lRet = GetLastError();
		Diagnostics_Write(DVF_ERRORLEVEL, "CreatePropertySheetPage failed, code: %i", lRet);
		hr = DVERR_GENERIC;
		goto error_cleanup;
	}

	// Speaker Test Page
	ZeroMemory(&psp, sizeof(psp));
//	psp.dwSize = sizeof(psp);
	psp.dwSize = PROPSHEETPAGE_STRUCT_SIZE;
	psp.dwFlags = PSP_DEFAULT;//|PSP_HIDEHEADER;
	psp.hInstance =	g_hResDLLInstance;
	psp.lParam = (LPARAM) &sinfo;
	psp.pfnDlgProc = SpeakerTestProc;
	psp.pszTemplate = MAKEINTRESOURCE(IDD_SPEAKER_TEST);

	rghpsp[4] = CreatePropertySheetPage(&psp);
	if (rghpsp[4] == NULL)
	{
		lRet = GetLastError();
		Diagnostics_Write(DVF_ERRORLEVEL, "CreatePropertySheetPage failed, code: %i", lRet);
		hr = DVERR_GENERIC;
		goto error_cleanup;
	}

	// Wizard Complete Page
	ZeroMemory(&psp, sizeof(psp));
//	psp.dwSize = sizeof(psp);
	psp.dwSize = PROPSHEETPAGE_STRUCT_SIZE;
	psp.dwFlags = PSP_DEFAULT;//|PSP_HIDEHEADER;
	psp.hInstance =	g_hResDLLInstance;
	psp.lParam = (LPARAM) &sinfo;
	psp.pfnDlgProc = CompleteProc;
	psp.pszTemplate = MAKEINTRESOURCE(IDD_COMPLETE);

	rghpsp[5] = CreatePropertySheetPage(&psp);
	if (rghpsp[5] == NULL)
	{
		lRet = GetLastError();
		Diagnostics_Write(DVF_ERRORLEVEL, "CreatePropertySheetPage failed, code: %i", lRet);
		hr = DVERR_GENERIC;
		goto error_cleanup;
	}

	// half duplex failed page
	ZeroMemory(&psp, sizeof(psp));
//	psp.dwSize = sizeof(psp);
	psp.dwSize = PROPSHEETPAGE_STRUCT_SIZE;
    psp.dwFlags = PSP_DEFAULT;//|PSP_HIDEHEADER;
	psp.hInstance =	g_hResDLLInstance;
	psp.lParam = (LPARAM) &sinfo;
	psp.pfnDlgProc = HalfDuplexFailedProc;
	psp.pszTemplate = MAKEINTRESOURCE(IDD_HALFDUPLEXFAILED);

	rghpsp[6] = CreatePropertySheetPage(&psp);
	if (rghpsp[6] == NULL)
	{
		lRet = GetLastError();
		Diagnostics_Write(DVF_ERRORLEVEL, "CreatePropertySheetPage failed, code: %i", lRet);
		hr = DVERR_GENERIC;
		goto error_cleanup;
	}

	// Full duplex failed page
	ZeroMemory(&psp, sizeof(psp));
//	psp.dwSize = sizeof(psp);
	psp.dwSize = PROPSHEETPAGE_STRUCT_SIZE;
	psp.dwFlags = PSP_DEFAULT;//|PSP_HIDEHEADER;
	psp.hInstance =	g_hResDLLInstance;
	psp.lParam = (LPARAM) &sinfo;
	psp.pfnDlgProc = FullDuplexFailedProc;
	psp.pszTemplate = MAKEINTRESOURCE(IDD_FULLDUPLEXFAILED);

	rghpsp[7] = CreatePropertySheetPage(&psp);
	if (rghpsp[7] == NULL)
	{
		lRet = GetLastError();
		Diagnostics_Write(DVF_ERRORLEVEL, "CreatePropertySheetPage failed, code: %i", lRet);
		hr = DVERR_GENERIC;
		goto error_cleanup;
	}

	// Put it all together...
	ZeroMemory(&psh, sizeof(psh));
//	psh.dwSize = sizeof(psh);
    psh.dwSize = PROPSHEETHEAD_STRUCT_SIZE;
	psh.hInstance =	g_hResDLLInstance;
	psh.hwndParent = hwndParent;
	psh.phpage = rghpsp;
	psh.dwFlags = PSH_WIZARD;
	psh.nStartPage = 0;
	psh.nPages = 8;

	sinfo.SetError(DV_OK);
	iRet = (INT) PropertySheet(&psh);
	if (iRet == -1)
	{
		lRet = GetLastError();
		Diagnostics_Write(DVF_ERRORLEVEL, "PropertySheet failed, code: %i", lRet);
		hr = DVERR_GENERIC;
		goto error_cleanup;
	}
	
	hr = sinfo.DestroyBoldFont();
	if (FAILED(hr))
	{
		Diagnostics_Write(DVF_ERRORLEVEL, "DestroyBoldFont failed");
		goto error_cleanup;
	}
	fBoldFontCreated = FALSE;
	
	hr = sinfo.DestroyTitleFont();
	if (FAILED(hr))
	{
		Diagnostics_Write(DVF_ERRORLEVEL, "DestroyTitleFont failed");
		goto error_cleanup;
	}
	fTitleFontCreated = FALSE;

	// unregister the peak meter window class
	hr = pmwc.Unregister();
	if (FAILED(hr))
	{
		Diagnostics_Write(DVF_ERRORLEVEL, "CPeakMeterWndClass::Deinit failed, code: %i", hr);
		goto error_cleanup;
	}
	fWndClassRegistered = FALSE;

 	if (!FreeLibrary(g_hResDLLInstance))
	{
		lRet = GetLastError();
		Diagnostics_Write(DVF_ERRORLEVEL, "FreeLibrary failed, code: %i", lRet);
		hr = DVERR_WIN32;
		goto error_cleanup;
	}
	g_hResDLLInstance = NULL;

	// see if an error occured
	sinfo.GetError(&hr);
	if (hr == DV_OK)
	{
		// nothing out of the ordinary happened,
		// so we'll return a cancel if the user
		// hit cancel, a "user back" if the user exited
		// the wizard by hitting back from the welcome
		// page, or the results from the registry otherwise.
		sinfo.GetUserCancel(&fUserCancel);
		sinfo.GetUserBack(&fUserBack);
		if (fUserCancel & fUserBack)
		{
			hr = DVERR_USERBACK;
		}
		else if(fUserCancel)
		{
			hr = DVERR_USERCANCEL;
		}
		else
		{
			// look in the registry for the test results
			hr = sinfo.QueryFullDuplex();

			// map a run setup result to a total failure
			if (hr == DVERR_RUNSETUP)
			{
				hr = DVERR_SOUNDINITFAILURE;
			}
		}
	}

	// close the mutex
	sinfo.CloseMutex();
	
	// close the registry key
	sinfo.CloseRegKey();

	DPF_EXIT();
	return hr;
	
error_cleanup:
	if (fBoldFontCreated == TRUE)
	{
		sinfo.DestroyBoldFont();
	}
	fBoldFontCreated = FALSE;

	if (fTitleFontCreated == TRUE)
	{
		sinfo.DestroyTitleFont();
	}
	fTitleFontCreated = FALSE;

	if (fWndClassRegistered == TRUE)
	{
		pmwc.Unregister();
	}
	fWndClassRegistered = FALSE;

	if (g_hResDLLInstance != NULL)
	{
		FreeLibrary(g_hResDLLInstance);
	}
	g_hResDLLInstance = NULL;

	if (fMutexOpen == TRUE)
	{
		sinfo.CloseMutex();
	}
	fMutexOpen = FALSE;

	if (fRegKeyOpen == TRUE)
	{
		sinfo.CloseRegKey();
	}
	fRegKeyOpen = FALSE;

	DPF_EXIT();
	return hr;
}

#undef DPF_MODNAME
#define DPF_MODNAME "SupervisorQueryAudioSetup"
HRESULT SupervisorQueryAudioSetup(CSupervisorInfo* psinfo)
{
	DPF_ENTER();
	
	HRESULT hr;

	// will return DV_HALFDUPLEX, DV_FULLDUPLEX, DVERR_SOUNDINITFAILURE or a real error
	hr = psinfo->QueryFullDuplex();
	if (FAILED(hr) && hr != DVERR_SOUNDINITFAILURE && hr != DVERR_RUNSETUP)
	{
		Diagnostics_Write(DVF_ERRORLEVEL, "QueryFullDuplex failed, code: %i", hr);
	}
	DPF_EXIT();
	return hr;
}

#undef DPF_MODNAME
#define DPF_MODNAME "CSupervisorInfo::CreateTitleFont"
HRESULT CSupervisorInfo::CreateTitleFont()
{
	DPF_ENTER();
	
	LONG lRet;
	HRESULT hr;
	HFONT hfTitle = NULL;
	INT iFontSize;
	LOGFONT lfTitle;
	HDC hdc = NULL;
	NONCLIENTMETRICS ncm;

	DNEnterCriticalSection(&m_csLock);
	
	// Set up the font for the titles on the intro and ending pages
	ZeroMemory(&ncm, sizeof(ncm));
	ncm.cbSize = sizeof(ncm);
	if (!SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, &ncm, 0))
	{
		lRet = GetLastError();
		Diagnostics_Write(DVF_ERRORLEVEL, "SystemParametersInfo failed, code: %i", lRet);
		hr = DVERR_GENERIC;
		goto error_cleanup;
	}

	// Create the intro/end title font
	lfTitle = ncm.lfMessageFont;
	lfTitle.lfWeight = FW_BOLD;
	lstrcpy(lfTitle.lfFaceName, TEXT("MS Shell Dlg"));

	hdc = GetDC(NULL); //gets the screen DC
	if (hdc == NULL)
	{
		lRet = GetLastError();
		Diagnostics_Write(DVF_ERRORLEVEL, "GetDC failed, code: %i", lRet);
		hr = DVERR_GENERIC;
		goto error_cleanup;
	}
	
	iFontSize = 12;
	lfTitle.lfHeight = 0 - GetDeviceCaps(hdc, LOGPIXELSY) * iFontSize / 72;
	
	hfTitle = CreateFontIndirect(&lfTitle);
	if (hfTitle == NULL)
	{
		lRet = GetLastError();
		Diagnostics_Write(DVF_ERRORLEVEL, "CreateFontIndirect failed, code: %i", lRet);
		hr = DVERR_GENERIC;
		goto error_cleanup;
	}
	
	if (ReleaseDC(NULL, hdc) != 1)
	{
		hdc = NULL;
		lRet = GetLastError();
		Diagnostics_Write(DVF_ERRORLEVEL, "ReleaseDC failed, code: %i", lRet);
		hr = DVERR_GENERIC;
		goto error_cleanup;
	}

	// save the font
	m_hfTitle = hfTitle;

	DNLeaveCriticalSection(&m_csLock);
	
	DPF_EXIT();
	return DV_OK;

error_cleanup:

	if (hfTitle != NULL)
	{
		DeleteObject(hfTitle);
	}
	hfTitle = NULL;
	
	if (hdc != NULL)
	{
		ReleaseDC(NULL, hdc);
	}
	hdc = NULL;

	DNLeaveCriticalSection(&m_csLock);
	DPF_EXIT();
	return hr;
}

#undef DPF_MODNAME
#define DPF_MODNAME "CSupervisorInfo::DestroyTitleFont"
HRESULT CSupervisorInfo::DestroyTitleFont()
{
	DPF_ENTER();
	
	HFONT hTitleFont;
	LONG lRet;
	HRESULT hr;

	DNEnterCriticalSection(&m_csLock);

	if (m_hfTitle == NULL)
	{
		Diagnostics_Write(DVF_ERRORLEVEL, "m_hTitleFont is Null");
		hr = DVERR_GENERIC;
		goto error_cleanup;
	}

	if (!DeleteObject(m_hfTitle))
	{
		lRet = GetLastError();
		Diagnostics_Write(DVF_ERRORLEVEL, "DeleteObject failed, code: %i", lRet);
		hr = DVERR_GENERIC;
		goto error_cleanup;
	}

	DNLeaveCriticalSection(&m_csLock);
	DPF_EXIT();
	return DV_OK;

error_cleanup:
	DNLeaveCriticalSection(&m_csLock);
	DPF_EXIT();
	return hr;
}

#undef DPF_MODNAME
#define DPF_MODNAME "CSupervisorInfo::CreateBoldFont"
HRESULT CSupervisorInfo::CreateBoldFont()
{
	DPF_ENTER();
	
	LONG lRet;
	HRESULT hr;
	HFONT hfBold = NULL;
	INT iFontSize;
	LOGFONT lfBold;
	HDC hdc = NULL;
	NONCLIENTMETRICS ncm;

	DNEnterCriticalSection(&m_csLock);
	
	// Set up the font for the titles on the intro and ending pages
	ZeroMemory(&ncm, sizeof(ncm));
	ncm.cbSize = sizeof(ncm);
	if (!SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, &ncm, 0))
	{
		lRet = GetLastError();
		Diagnostics_Write(DVF_ERRORLEVEL, "SystemParametersInfo failed, code: %i", lRet);
		hr = DVERR_GENERIC;
		goto error_cleanup;
	}

	// Create the intro/end title font
	lfBold = ncm.lfMessageFont;
	lfBold.lfWeight = FW_BOLD;
	lstrcpy(lfBold.lfFaceName, TEXT("MS Shell Dlg"));

	hdc = GetDC(NULL); //gets the screen DC
	if (hdc == NULL)
	{
		lRet = GetLastError();
		Diagnostics_Write(DVF_ERRORLEVEL, "GetDC failed, code: %i", lRet);
		hr = DVERR_GENERIC;
		goto error_cleanup;
	}
	
	iFontSize = 8;
	lfBold.lfHeight = 0 - GetDeviceCaps(hdc, LOGPIXELSY) * iFontSize / 72;
	
	hfBold = CreateFontIndirect(&lfBold);
	if (hfBold == NULL)
	{
		lRet = GetLastError();
		Diagnostics_Write(DVF_ERRORLEVEL, "CreateFontIndirect failed, code: %i", lRet);
		hr = DVERR_GENERIC;
		goto error_cleanup;
	}
	
	if (ReleaseDC(NULL, hdc) != 1)
	{
		hdc = NULL;
		lRet = GetLastError();
		Diagnostics_Write(DVF_ERRORLEVEL, "ReleaseDC failed, code: %i", lRet);
		hr = DVERR_GENERIC;
		goto error_cleanup;
	}

	// save the font
	m_hfBold = hfBold;

	DNLeaveCriticalSection(&m_csLock);
	DPF_EXIT();
	return DV_OK;

error_cleanup:
	if (hdc != NULL)
	{
		ReleaseDC(NULL, hdc);
	}
	hdc = NULL;

	if (hfBold != NULL)
	{
		DeleteObject(hfBold);
	}
	hfBold = NULL;

	DNLeaveCriticalSection(&m_csLock);
	DPF_EXIT();
	return hr;
}

#undef DPF_MODNAME
#define DPF_MODNAME "CSupervisorInfo::DestroyBoldFont"
HRESULT CSupervisorInfo::DestroyBoldFont()
{
	DPF_ENTER();
	
	HFONT hTitleFont;
	LONG lRet;
	HRESULT hr;

	DNEnterCriticalSection(&m_csLock);

	if (m_hfBold == NULL)
	{
		Diagnostics_Write(DVF_ERRORLEVEL, "m_hTitleFont is Null");
		hr = DVERR_GENERIC;
		goto error_cleanup;
	}

	if (!DeleteObject(m_hfBold))
	{
		lRet = GetLastError();
		Diagnostics_Write(DVF_ERRORLEVEL, "DeleteObject failed, code: %i", lRet);
		hr = DVERR_GENERIC;
		goto error_cleanup;
	}
	m_hfBold = NULL;

	DNLeaveCriticalSection(&m_csLock);
	DPF_EXIT();
	return DV_OK;

error_cleanup:
	DNLeaveCriticalSection(&m_csLock);
	DPF_EXIT();
	return hr;
}


#undef DPF_MODNAME
#define DPF_MODNAME "CSupervisorInfo::Unmute"
HRESULT CSupervisorInfo::Unmute()
{
	DPF_ENTER();
	
	LONG lRet;
	HRESULT hr;
	DVCLIENTCONFIG dvcc;

	DNEnterCriticalSection(&m_csLock);

	if (m_lpdpvc == NULL)
	{
		Diagnostics_Write(DVF_ERRORLEVEL, "NULL IDirectPlayVoiceClient");
		hr = DVERR_INVALIDPARAM;
		goto error_cleanup;
	}

	dvcc.dwSize = sizeof(dvcc);
	hr = m_lpdpvc->GetClientConfig(&dvcc);
	if (FAILED(hr))
	{
		Diagnostics_Write(DVF_ERRORLEVEL, "IDirectPlayVoiceClient::GetClientConfig failed, hr: %i", hr);
		goto error_cleanup;
	}
	
	dvcc.dwFlags &= (~DVCLIENTCONFIG_PLAYBACKMUTE);
	
	m_lpdpvc->SetClientConfig(&dvcc);
	if (FAILED(hr))
	{
		Diagnostics_Write(DVF_ERRORLEVEL, "IDirectPlayVoiceClient::SetClientConfig failed, hr: %i", hr);
		goto error_cleanup;
	}

	DNLeaveCriticalSection(&m_csLock);
	DPF_EXIT();
	return DV_OK;

error_cleanup:
	DNLeaveCriticalSection(&m_csLock);
	DPF_EXIT();
	return hr;
}

#undef DPF_MODNAME
#define DPF_MODNAME "CSupervisorInfo::Mute"
HRESULT CSupervisorInfo::Mute()
{
	DPF_ENTER();
	
	LONG lRet;
	HRESULT hr;
	DVCLIENTCONFIG dvcc;

	DNEnterCriticalSection(&m_csLock);

	if (m_lpdpvc == NULL)
	{
		Diagnostics_Write(DVF_ERRORLEVEL, "NULL IDirectPlayVoiceClient");
		hr = DVERR_INVALIDPARAM;
		goto error_cleanup;
	}

	dvcc.dwSize = sizeof(dvcc);
	hr = m_lpdpvc->GetClientConfig(&dvcc);
	if (FAILED(hr))
	{
		Diagnostics_Write(DVF_ERRORLEVEL, "IDirectPlayVoiceClient::GetClientConfig failed, hr: %i", hr);
		goto error_cleanup;
	}
	
	dvcc.dwFlags |= DVCLIENTCONFIG_PLAYBACKMUTE;
	
	m_lpdpvc->SetClientConfig(&dvcc);
	if (FAILED(hr))
	{
		Diagnostics_Write(DVF_ERRORLEVEL, "IDirectPlayVoiceClient::SetClientConfig failed, hr: %i", hr);
		goto error_cleanup;
	}

	DNLeaveCriticalSection(&m_csLock);
	DPF_EXIT();
	return DV_OK;

error_cleanup:
	DNLeaveCriticalSection(&m_csLock);
	DPF_EXIT();
	return hr;
}

#undef DPF_MODNAME
#define DPF_MODNAME "CSupervisorInfo::DSEnumCallback"
BOOL CALLBACK CSupervisorInfo::DSEnumCallback(
	LPGUID lpGuid, 
	LPCTSTR lpcstrDescription, 
	LPCTSTR lpcstrModule,
	LPVOID lpContext)
{
	DNASSERT(lpContext);
	CSupervisorInfo* psinfo = (CSupervisorInfo*)lpContext;

	if (lpGuid)
	{
		if (psinfo->m_guidRenderDevice == *lpGuid)
		{
			// matching guid, copy the description
			_tcsncpy(psinfo->m_szRenderDeviceDesc, lpcstrDescription, MAX_DEVICE_DESC_LEN-1);

			// all done, stop enum
			return FALSE;
		}
	}
	
	return TRUE;
}

#undef DPF_MODNAME
#define DPF_MODNAME "CSupervisorInfo::DSCEnumCallback"
BOOL CALLBACK CSupervisorInfo::DSCEnumCallback(
	LPGUID lpGuid, 
	LPCTSTR lpcstrDescription, 
	LPCTSTR lpcstrModule,
	LPVOID lpContext)
{
	DNASSERT(lpContext);
	CSupervisorInfo* psinfo = (CSupervisorInfo*)lpContext;

	if (lpGuid)
	{
		if (psinfo->m_guidCaptureDevice == *lpGuid)
		{
			// matching guid, copy the description
			_tcsncpy(psinfo->m_szCaptureDeviceDesc, lpcstrDescription, MAX_DEVICE_DESC_LEN-1);

			// all done, stop enum
			return FALSE;
		}
	}
	
	return TRUE;
}

#undef DPF_MODNAME
#define DPF_MODNAME "CSupervisorInfo::GetDeviceDescriptions"
HRESULT CSupervisorInfo::GetDeviceDescriptions()
{
	DPF_ENTER();

	HRESULT hr;
	TDirectSoundEnumFnc fpDSEnum;
	TDirectSoundEnumFnc fpDSCEnum;
	
	DNEnterCriticalSection(&m_csLock);

	ZeroMemory(m_szRenderDeviceDesc, MAX_DEVICE_DESC_LEN);
	ZeroMemory(m_szCaptureDeviceDesc, MAX_DEVICE_DESC_LEN);

	HINSTANCE hDSound = LoadLibrary(_T("dsound.dll"));
	if (hDSound == NULL)
	{
		Diagnostics_Write(DVF_ERRORLEVEL, "Error loading dsound.dll");
		hr = DVERR_SOUNDINITFAILURE;
		goto error_cleanup;
	}

#ifdef UNICODE
	fpDSEnum = (TDirectSoundEnumFnc)GetProcAddress(hDSound, "DirectSoundEnumerateW");
#else
	fpDSEnum = (TDirectSoundEnumFnc)GetProcAddress(hDSound, "DirectSoundEnumerateA");
#endif
	if (fpDSEnum == NULL)
	{
		Diagnostics_Write(DVF_ERRORLEVEL, "GetProcAddress failed for DirectSoundEnumerateW");
		hr = DVERR_SOUNDINITFAILURE;
		goto error_cleanup;
	}

#ifdef UNICODE
	fpDSCEnum = (TDirectSoundEnumFnc)GetProcAddress(hDSound, "DirectSoundCaptureEnumerateW");
#else
	fpDSCEnum = (TDirectSoundEnumFnc)GetProcAddress(hDSound, "DirectSoundCaptureEnumerateA");
#endif

	if (fpDSCEnum == NULL)
	{
		Diagnostics_Write(DVF_ERRORLEVEL, "GetProcAddress failed for DirectSoundCaptureEnumerateW");
		hr = DVERR_SOUNDINITFAILURE;
		goto error_cleanup;
	}

	hr = fpDSEnum(DSEnumCallback, (LPVOID)this);
	if (FAILED(hr))
	{
		Diagnostics_Write(DVF_ERRORLEVEL, "DirectSoundEnumerate failed, code: %i, assuming bad guid", hr);
		hr = DVERR_INVALIDDEVICE;
		goto error_cleanup;
	}
	if (m_szRenderDeviceDesc[0] == NULL)
	{
		// the device wasn't found!
		Diagnostics_Write(DVF_ERRORLEVEL, "Render device not found");
		hr = DVERR_INVALIDDEVICE;
		goto error_cleanup;
	}

	hr = fpDSCEnum(DSCEnumCallback, (LPVOID)this);
	if (FAILED(hr))
	{
		Diagnostics_Write(DVF_ERRORLEVEL, "DirectSoundCaptureEnumerate failed, code: %i, assuming bad guid", hr);
		hr = DVERR_INVALIDDEVICE;
		goto error_cleanup;
	}
	if (m_szCaptureDeviceDesc[0] == NULL)
	{
		// the device wasn't found!
		Diagnostics_Write(DVF_ERRORLEVEL, "Capture device not found");
		hr = DVERR_INVALIDDEVICE;
		goto error_cleanup;
	}

	FreeLibrary( hDSound );
	
	DNLeaveCriticalSection(&m_csLock);
	DPF_EXIT();
	return DV_OK;

error_cleanup:

    if( hDSound != NULL )
    {
    	FreeLibrary( hDSound );        
    }
    
	DNLeaveCriticalSection(&m_csLock);
	DPF_EXIT();
	return hr;
}

#undef DPF_MODNAME
#define DPF_MODNAME "CSupervisorInfo::CloseRegKey"
HRESULT CSupervisorInfo::CloseRegKey()
{
	DPF_ENTER();
	
	LONG lRet;
	HRESULT hr;
	
	DNEnterCriticalSection(&m_csLock);

	if (!m_creg.Close())
	{
		Diagnostics_Write(DVF_ERRORLEVEL, "CRegistry::Close failed");
		hr = DVERR_GENERIC;
		goto error_cleanup;
	}
	
	DNLeaveCriticalSection(&m_csLock);
	DPF_EXIT();
	return S_OK;

error_cleanup:
	DNLeaveCriticalSection(&m_csLock);
	DPF_EXIT();
	return hr;
}

#undef DPF_MODNAME
#define DPF_MODNAME "FullDuplexTestThreadProc"
DWORD WINAPI FullDuplexTestThreadProc(LPVOID lpvParam)
{
	DPF_ENTER();
	
	CSupervisorInfo* lpsinfo;
	CSupervisorIPC* lpipcSupervisor;
	LPGUID lpguidRenderDevice;
	LPGUID lpguidCaptureDevice;
	HKEY hkDevice;
	HRESULT hr;
	LONG lRet;
	HWND hwnd;

	lpsinfo = (CSupervisorInfo*)lpvParam;

	lpsinfo->GetHWNDDialog(&hwnd);

	hr = RunFullDuplexTest(lpsinfo);

	// post a message to the wizard so it knows we're done, but
	// only if this was not a user cancel, since the wizard will
	// already be waiting on the thread object
	if (hr != DVERR_USERCANCEL)
	{
		if (!PostMessage(hwnd, WM_APP_FULLDUP_TEST_COMPLETE, 0, (LPARAM)hr))
		{
			lRet = GetLastError();
			Diagnostics_Write(DVF_ERRORLEVEL, "PostMessage failed, code: %i", lRet);
			hr = DVERR_GENERIC;
		}
	}

	DPF_EXIT();
	return hr;
}

#undef DPF_MODNAME
#define DPF_MODNAME "RunFullDuplexTest"
static HRESULT RunFullDuplexTest(CSupervisorInfo* lpsinfo)
{
	DPF_ENTER();
	
	HRESULT hr;
	HRESULT hrFnc;
	CSupervisorIPC* lpsipc;
	
	lpsinfo->GetIPC(&lpsipc);

	hr = lpsipc->StartPriorityProcess();
	if (FAILED(hr))
	{
		Diagnostics_Write(DVF_ERRORLEVEL, "StartPriorityProcess failed, hr: %i", hr);
		goto error_cleanup;
	}
	
	hr = lpsipc->StartFullDuplexProcess();
	if (FAILED(hr))
	{
		Diagnostics_Write(DVF_ERRORLEVEL, "StartFullDuplexProcess failed, hr: %i", hr);
		goto error_cleanup;
	}

	hrFnc = DoTests(lpsinfo);

	hr = lpsipc->WaitOnChildren();
	if (FAILED(hr))
	{
		Diagnostics_Write(DVF_ERRORLEVEL, "WaitOnChildren failed, code: %i", hr);
		goto error_cleanup;
	}
	
	DPF_EXIT();
	return hrFnc;

error_cleanup:
	// this function is safe to call, even if the child processes have not 
	// been created
	lpsipc->TerminateChildProcesses();
	DPF_EXIT();
	return hr;
}

#undef DPF_MODNAME
#define DPF_MODNAME "CSupervisorInfo::CrashCheckIn"
HRESULT CSupervisorInfo::CrashCheckIn()
{
	DPF_ENTER();

	LONG lRet;
	BOOL fRet;
	DWORD dwRegVal;
	HRESULT hrFnc;
	HRESULT hr;
	HKEY hk;

	// Each of the following three functions take the critical section,
	// so there is no need to take it here.

	// Check each of the test results to see if any tests
	// crashed.
	hr = GetHalfDuplex(&dwRegVal);
	if (!FAILED(hr) && dwRegVal == REGVAL_CRASHED)
	{
		// The half duplex test crashed.
		Diagnostics_Write(DVF_ERRORLEVEL, "Previous half duplex test crashed");
		hrFnc = DVERR_PREVIOUSCRASH;
		goto error_cleanup;
	}

	hr = GetFullDuplex(&dwRegVal);
	if (!FAILED(hr) && dwRegVal == REGVAL_CRASHED)
	{
		// The full duplex test crashed.
		Diagnostics_Write(DVF_ERRORLEVEL, "Previous full duplex test crashed");
		hrFnc = DVERR_PREVIOUSCRASH;
		goto error_cleanup;
	}

	hr = GetMicDetected(&dwRegVal);
	if (!FAILED(hr) && dwRegVal == REGVAL_CRASHED)
	{
		// The mic test crashed.
		Diagnostics_Write(DVF_ERRORLEVEL, "Previous mic test crashed");
		hrFnc = DVERR_PREVIOUSCRASH;
		goto error_cleanup;
	}

	DPF_EXIT();
	return DV_OK;

// error block
error_cleanup:
	DPF_EXIT();
	return hrFnc;		
}

#undef DPF_MODNAME
#define DPF_MODNAME "DoTests"
HRESULT DoTests(CSupervisorInfo* lpsinfo)
{

	DPF_ENTER();
	
	LONG lRet;
	DWORD dwRet;
	HANDLE rghEvents[2];
	HRESULT hr;
	HRESULT hrFnc;
	CSupervisorIPC* lpsipc;

	lpsinfo->GetIPC(&lpsipc);

	// wait until the child processes are ready to go...
	hr = lpsipc->WaitForStartupSignals();
	if (FAILED(hr))
	{
		Diagnostics_Write(DVF_ERRORLEVEL, "WaitForStartupSignals failed, hr: %i", hr);
		goto error_cleanup;
	}

	// child processes are all set, tell them what to do
	// Note: this function has only four expected return codes
	// DV_FULLDUPLEX - all tests passed
	// DV_HALFDUPLEX - all half duplex tests passed, full duplex tests failed
	// DVERR_SOUNDINITFAILURE - half duplex tests failed
	// DVERR_USERCANCEL - tests canceled by user
	hrFnc = IssueCommands(lpsinfo);
	if (FAILED(hrFnc) 
		&& hrFnc != DVERR_SOUNDINITFAILURE 
		&& hrFnc != DVERR_USERCANCEL)
	{
		Diagnostics_Write(DVF_ERRORLEVEL, "IssueCommands failed, hr: %i", hrFnc);
		hr = hrFnc;
		goto error_cleanup;
	}

	// now tell the child processes to shut down
	hr = IssueShutdownCommand(lpsipc);
	if (FAILED(hr))
	{
		Diagnostics_Write(DVF_ERRORLEVEL, "IssueShutdownCommand failed, code: %i", hr);
		// Note we're not bailing out. We have our test results, so this is
		// a problem somewhere in the wizard code. Return the result
		// from the actual test.
	}
	
	DPF_EXIT();
	return hrFnc;

error_cleanup:
	// attempt to gracefully shutdown child processes.
	IssueShutdownCommand(lpsipc);
	DPF_EXIT();
	return hr;
}

#undef DPF_MODNAME
#define DPF_MODNAME "IssueCommands"
HRESULT IssueCommands(CSupervisorInfo* lpsinfo)
{
	// Note: this function has only four possible return codes
	// DV_FULLDUPLEX - all tests passed
	// DV_HALFDUPLEX - all half duplex tests passed, full duplex tests failed
	// DVERR_SOUNDINITFAILURE - half duplex tests failed
	// DVERR_USERCANCEL - tests canceled by user
	DPF_ENTER();
	
	HRESULT hr;
	DWORD dwIndex1;
	DWORD dwIndex2;
	BOOL fAbort = FALSE;
	BOOL fPassed;

	// First do a pass testing that we can run in 
	// half duplex without error.	
	// Set the half duplex key to crash state
	lpsinfo->SetHalfDuplex(REGVAL_CRASHED);
	dwIndex1 = 0;
	fPassed = FALSE;
	while (1)
	{
		if (gc_rgwfxPrimaryFormats[dwIndex1].wFormatTag == 0
			&& gc_rgwfxPrimaryFormats[dwIndex1].nChannels == 0
			&& gc_rgwfxPrimaryFormats[dwIndex1].nSamplesPerSec == 0
			&& gc_rgwfxPrimaryFormats[dwIndex1].nAvgBytesPerSec == 0
			&& gc_rgwfxPrimaryFormats[dwIndex1].nBlockAlign == 0
			&& gc_rgwfxPrimaryFormats[dwIndex1].wBitsPerSample == 0
			&& gc_rgwfxPrimaryFormats[dwIndex1].cbSize == 0)
		{
			// we've found the last element of the array, break out.
			fPassed = TRUE;
			break;
		}

		lpsinfo->GetAbortFullDuplex(&fAbort);
		if (fAbort)
		{
			// abort the tests
			break;
		}

		hr = lpsinfo->TestCase(&gc_rgwfxPrimaryFormats[dwIndex1], DVSOUNDCONFIG_HALFDUPLEX|DVSOUNDCONFIG_TESTMODE);
		if (hr != DV_HALFDUPLEX)
		{
			Diagnostics_Write(DVF_ERRORLEVEL, "Half duplex test case not supported hr = 0x%x", hr);
			break;
		}
		++dwIndex1;

		// Why is this here?  Because DSOUND doesn't like you to open/close quickly.
		Sleep( 200 );
	}

	if (fAbort)
	{
		// The user aborted the tests, make it like they were never run.
		lpsinfo->SetHalfDuplex(REGVAL_NOTRUN);
		DPF_EXIT();
		return DVERR_USERCANCEL;
	}

	// Record the results of the half duplex test in the registry,
	// and decide what to do next.
	if (fPassed)
	{
		lpsinfo->SetHalfDuplex(REGVAL_PASSED);
		// continue on with the full duplex test.
	}
	else
	{
		lpsinfo->SetHalfDuplex(REGVAL_FAILED);
		// we failed the half duplex test, we're done.
		DPF_EXIT();
		// map all failures at this point to sound problems.
		return DVERR_SOUNDINITFAILURE;
	}

	// Now that we're finished testing in half duplex mode,
	// we can move on to the full duplex testing.
	lpsinfo->SetFullDuplex(REGVAL_CRASHED);
	fPassed = FALSE;
	dwIndex1 = 0;
	while (1)
	{
		if (gc_rgwfxPrimaryFormats[dwIndex1].wFormatTag == 0
			&& gc_rgwfxPrimaryFormats[dwIndex1].nChannels == 0
			&& gc_rgwfxPrimaryFormats[dwIndex1].nSamplesPerSec == 0
			&& gc_rgwfxPrimaryFormats[dwIndex1].nAvgBytesPerSec == 0
			&& gc_rgwfxPrimaryFormats[dwIndex1].nBlockAlign == 0
			&& gc_rgwfxPrimaryFormats[dwIndex1].wBitsPerSample == 0
			&& gc_rgwfxPrimaryFormats[dwIndex1].cbSize == 0)
		{
			// we've found the last element of the array, break out.
			fPassed = TRUE;
			break;
		}

		lpsinfo->GetAbortFullDuplex(&fAbort);
		if (fAbort)
		{
			// abort the tests
			break;
		}

		hr = lpsinfo->TestCase(&gc_rgwfxPrimaryFormats[dwIndex1], DVSOUNDCONFIG_TESTMODE);
		if (hr != DV_FULLDUPLEX)
		{
			Diagnostics_Write(DVF_ERRORLEVEL, "Full duplex test case not supported. hr = 0x%x", hr);
			break;
		}
		++dwIndex1;

		// Why is this here?  Because DSOUND doesn't like you to open/close quickly.
		Sleep( 200 );
	}

	if (fAbort)
	{
		// The user aborted the tests, make it like they were never run.
		lpsinfo->SetFullDuplex(REGVAL_NOTRUN);
		DPF_EXIT();
		return DVERR_USERCANCEL;
	}

	// Record the results of the full duplex test in the registry,
	// and return the appropriate code.
	if (fPassed)
	{
		lpsinfo->SetFullDuplex(REGVAL_PASSED);
		DPF_EXIT();
		return DV_FULLDUPLEX;
	}
	else
	{
		lpsinfo->SetFullDuplex(REGVAL_FAILED);
		DPF_EXIT();
		return DV_HALFDUPLEX;
	}
}

#undef DPF_MODNAME
#define DPF_MODNAME "CSupervisorInfo::TestCase"
HRESULT CSupervisorInfo::TestCase(const WAVEFORMATEX* lpwfxPrimary, DWORD dwFlags)
{
	DPF_ENTER();
	HRESULT hr = DV_OK;
	HRESULT hrFnc = DV_OK;
	SFDTestCommand fdtc;

	// tell the priority process to go
	ZeroMemory(&fdtc, sizeof(fdtc));
	fdtc.dwSize = sizeof(fdtc);
	fdtc.fdtcc = fdtccPriorityStart;
	fdtc.fdtu.fdtcPriorityStart.guidRenderDevice = m_guidRenderDevice;
	fdtc.fdtu.fdtcPriorityStart.wfxRenderFormat = *lpwfxPrimary;
	fdtc.fdtu.fdtcPriorityStart.wfxSecondaryFormat = gc_wfxSecondaryFormat;	
	fdtc.fdtu.fdtcPriorityStart.hwndWizard = m_hwndWizard;
	fdtc.fdtu.fdtcPriorityStart.hwndProgress = m_hwndProgress;
	hr = m_sipc.SendToPriority(&fdtc);
	if (FAILED(hr))
	{
		DPF_EXIT();
		return hr;
	}

	// tell the full duplex process to attempt full duplex
	ZeroMemory(&fdtc, sizeof(fdtc));
	fdtc.dwSize = sizeof(fdtc);
	fdtc.fdtcc = fdtccFullDuplexStart;
	fdtc.fdtu.fdtcFullDuplexStart.guidRenderDevice = m_guidRenderDevice;
	fdtc.fdtu.fdtcFullDuplexStart.guidCaptureDevice = m_guidCaptureDevice;
	fdtc.fdtu.fdtcFullDuplexStart.dwFlags = dwFlags;
	hrFnc = m_sipc.SendToFullDuplex(&fdtc);
	if (FAILED(hrFnc))
	{
		// The full duplex process was unable to do it.
		// tell the priority process to stop and get out.
		ZeroMemory(&fdtc, sizeof(fdtc));
		fdtc.dwSize = sizeof(fdtc);
		fdtc.fdtcc = fdtccPriorityStop;
		m_sipc.SendToPriority(&fdtc);
		DPF_EXIT();
		return hrFnc;
	}

	// Wait for a half second before we shut it down.
	// This gives it the time required for the sound system
	// to detect a lockup if it is going to.
	Sleep(1000);
	
	// The full duplex process was ok, till now. Try to 
	// shut it down.
	ZeroMemory(&fdtc, sizeof(fdtc));
	fdtc.dwSize = sizeof(fdtc);
	fdtc.fdtcc = fdtccFullDuplexStop;
	hr = m_sipc.SendToFullDuplex(&fdtc);
	if (FAILED(hr))
	{
		// It looks like the full duplex wasn't quite up to stuff
		// after all. Tell the priority process to shut down
		ZeroMemory(&fdtc, sizeof(fdtc));
		fdtc.dwSize = sizeof(fdtc);
		fdtc.fdtcc = fdtccPriorityStop;
		m_sipc.SendToPriority(&fdtc);
		DPF_EXIT();
		return hr;
	}

	// All is well, up to now, one last hurdle...
	ZeroMemory(&fdtc, sizeof(fdtc));
	fdtc.dwSize = sizeof(fdtc);
	fdtc.fdtcc = fdtccPriorityStop;
	hr = m_sipc.SendToPriority(&fdtc);
	if (FAILED(hr))
	{
		DPF_EXIT();
		return hr;
	}

	// you have graduated from full duplex class...
	DPF_EXIT();
	return hrFnc;
}

#undef DPF_MODNAME
#define DPF_MODNAME "IssueShutdownCommand"
HRESULT IssueShutdownCommand(CSupervisorIPC* lpipcSupervisor)
{
	SFDTestCommand fdtc;
	HRESULT hr;

	DPF_EXIT();

	fdtc.dwSize = sizeof(fdtc);
	fdtc.fdtcc = fdtccExit;

	hr = lpipcSupervisor->SendToFullDuplex(&fdtc);
	if (FAILED(hr))
	{
		lpipcSupervisor->SendToPriority(&fdtc);
		DPF_EXIT();
		return hr;
	}

	hr = lpipcSupervisor->SendToPriority(&fdtc);
	if (FAILED(hr))
	{
		DPF_EXIT();
		return hr;
	}

	DPF_EXIT();
	return DV_OK;
}

#undef DPF_MODNAME
#define DPF_MODNAME "WelcomeProc"
INT_PTR CALLBACK WelcomeProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
	DPF_ENTER();
	
	BOOL fRet;

	CSupervisorInfo* psinfo = (CSupervisorInfo*)GetWindowLongPtr(hDlg, GWLP_USERDATA);

	fRet = FALSE;
	switch (message)
	{
	case WM_INITDIALOG :
		fRet = WelcomeInitDialogHandler(hDlg, message, wParam, lParam, psinfo); 
		break;

	case WM_NOTIFY :
		{
		LPNMHDR lpnm = (LPNMHDR) lParam;

		switch (lpnm->code)
			{
			case PSN_SETACTIVE : 
				// Enable the Next button	 
				fRet = WelcomeSetActiveHandler(hDlg, message, wParam, lParam, psinfo);
				break;

			case PSN_WIZBACK :
				// Back button clicked
				fRet = WelcomeBackHandler(hDlg, message, wParam, lParam, psinfo);
				break;

			case PSN_WIZNEXT :
				// Next button clicked
				fRet = WelcomeNextHandler(hDlg, message, wParam, lParam, psinfo);
				break;

			case PSN_RESET :
				fRet = WelcomeResetHandler(hDlg, message, wParam, lParam, psinfo);
				break;

			default :
				break;
			}
		}
		break;

	default:
		break;
	}
	
	DPF_EXIT();
	return fRet;
}

#undef DPF_MODNAME
#define DPF_MODNAME "WelcomeInitDialogHandler"
BOOL WelcomeInitDialogHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, CSupervisorInfo* psinfo)
{
	DPF_ENTER();
	
	HWND hwndControl;
	HWND hwndWizard = NULL;
	LONG lRet;
	HFONT hfTitle;
	HICON hIcon;
	HRESULT hr = DV_OK;
	HWND hwndParent = NULL;
	
	// Get the shared data from PROPSHEETPAGE lParam value
	// and load it into GWLP_USERDATA
	psinfo = (CSupervisorInfo*)((LPPROPSHEETPAGE)lParam)->lParam;
	SetWindowLongPtr(hDlg, GWLP_USERDATA, (LONG_PTR)psinfo);

	// Get the parent window
	hwndWizard = GetParent(hDlg);
	if (hwndWizard == NULL)
	{
		lRet = GetLastError();
		Diagnostics_Write(DVF_ERRORLEVEL, "GetParent failed, code: %i", lRet);
		hr = DVERR_GENERIC;
		goto error_cleanup;
	}

	// It's an intro/end page, so get the title font
	// from the shared data and use it for the title control
	hwndControl = GetDlgItem(hDlg, IDC_TITLE);
	if (hwndControl == NULL)
	{
		// error, log it and bail
		lRet = GetLastError();
		Diagnostics_Write(DVF_ERRORLEVEL, "GetDlgItem failed, code: %i", lRet);
		hr = DVERR_GENERIC;
		goto error_cleanup;
	}
	psinfo->GetTitleFont(&hfTitle);
    (void)::SendMessage(hwndControl, WM_SETFONT, (WPARAM)hfTitle, (LPARAM)TRUE);
	
	// load the warning icon
	//hIcon = LoadIcon(NULL, IDI_INFORMATION);
	//SendDlgItemMessage(hDlg, IDC_WARNINGICON, STM_SETIMAGE, IMAGE_ICON, (LPARAM)hIcon);

	// set the device descriptions
	SendDlgItemMessage(hDlg, IDC_TEXT_PLAYBACK, WM_SETTEXT, 0, (LPARAM)psinfo->GetRenderDesc());
	SendDlgItemMessage(hDlg, IDC_TEXT_RECORDING, WM_SETTEXT, 0, (LPARAM)psinfo->GetCaptureDesc());

	// reset the welcome next flag
	psinfo->ClearWelcomeNext();

	DPF_EXIT();
	return FALSE;

error_cleanup:
	psinfo->GetHWNDParent(&hwndParent);
	DV_DisplayErrorBox(hr, hwndParent);
	psinfo->SetError(hr);
	psinfo->Abort(hDlg, hr);
	DPF_EXIT();
	return FALSE;
}

#undef DPF_MODNAME
#define DPF_MODNAME "WelcomeSetActiveHandler"
BOOL WelcomeSetActiveHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, CSupervisorInfo* psinfo)
{
	DPF_ENTER();
	HWND hwndParent;
	HWND hwndWizard;
	LONG lRet;
	DWORD dwFlags;
	HRESULT hr;

	// Get the parent window
	hwndWizard = GetParent(hDlg);
	if (hwndWizard == NULL)
	{
		// log it, and return, don't know how to terminate the wizard properly
		// without this handle!
		lRet = GetLastError();
		Diagnostics_Write(DVF_ERRORLEVEL, "GetParent failed, code: %i", lRet);
		hr = DVERR_GENERIC;
		goto error_cleanup;
	}

	// set the HWNDs
	psinfo->SetHWNDWizard(hwndWizard);
	psinfo->SetHWNDDialog(hDlg);
	psinfo->SetHWNDProgress(NULL);
	psinfo->SetHWNDInputPeak(NULL);
	psinfo->SetHWNDOutputPeak(NULL);
	psinfo->SetHWNDInputVolumeSlider(NULL);
	psinfo->SetHWNDOutputVolumeSlider(NULL);

	// set the appropriate wizard buttons as active.
	psinfo->GetCheckAudioSetupFlags(&dwFlags);
	if (dwFlags & DVFLAGS_ALLOWBACK)
	{
		PropSheet_SetWizButtons(hwndWizard, PSWIZB_NEXT|PSWIZB_BACK);
	}
	else
	{
		PropSheet_SetWizButtons(hwndWizard, PSWIZB_NEXT);
	}

	// reset the user cancel and user back flags
	psinfo->ClearUserCancel();
	psinfo->ClearUserBack();

	DPF_EXIT();
	return FALSE;

// error block
error_cleanup:
	psinfo->GetHWNDParent(&hwndParent);
	DV_DisplayErrorBox(hr, hwndParent);
	psinfo->SetError(hr);
	psinfo->Abort(hDlg, hr);
	DPF_EXIT();
	return FALSE;
}

#undef DPF_MODNAME
#define DPF_MODNAME "WelcomeBackHandler"
BOOL WelcomeBackHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, CSupervisorInfo* psinfo)
{
	DPF_ENTER();
	
	HWND hwndWizard = NULL;
	HRESULT hr = DV_OK;
	LPGUID lpguidCaptureDevice;
	LPGUID lpguidRenderDevice;
	HKEY hkey;
	LONG lRet;
	DWORD dwErr;
	DWORD dwRegVal;

	// Get the parent window
	psinfo->GetHWNDWizard(&hwndWizard);
	
	// The back button was hit on the welcome page. Exit the wizard with the appropriate error code.
	psinfo->SetUserBack();
	PropSheet_PressButton(hwndWizard, PSBTN_CANCEL);

	// no previous crashes (or the user is boldly charging ahead anyway), 
	// so go to the full duplex test page
	SetWindowLongPtr(hDlg, DWLP_MSGRESULT, IDD_FULLDUPLEXTEST);
	DPF_EXIT();
	return TRUE;
}

#undef DPF_MODNAME
#define DPF_MODNAME "WelcomeNextHandler"
BOOL WelcomeNextHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, CSupervisorInfo* psinfo)
{
	DPF_ENTER();
	
	HWND hwndWizard = NULL;
	HRESULT hr = DV_OK;
	LPGUID lpguidCaptureDevice;
	LPGUID lpguidRenderDevice;
	HKEY hkey;
	LONG lRet;
	DWORD dwErr;
	DWORD dwRegVal;
	HWND hwndParent = NULL;

	// Get the parent window
	psinfo->GetHWNDWizard(&hwndWizard);
	
	// The next button was hit on the welcome page. Do all the basic init tasks.

	// check for previous crashes
	hr = psinfo->CrashCheckIn();
	if (FAILED(hr))
	{
		if (hr == DVERR_PREVIOUSCRASH)
		{
			// the previous test crashed out, display the warning.
			Diagnostics_Write(DVF_ERRORLEVEL, "DirectPlay Voice Setup Wizard detected previous full duplex test crashed");
			int iRet = (INT) DialogBox(g_hResDLLInstance, MAKEINTRESOURCE(IDD_PREVIOUS_CRASH), hDlg, PreviousCrashProc);
			switch (iRet)
			{
			case IDOK:
				// the previous test crashed, but the user wants to continue
				// anyway, so move along...
				Diagnostics_Write(DVF_ERRORLEVEL, "User choosing to ignore previous failure");
				break;
				
			case IDCANCEL:
				// the previous test crashed, and the user is wisely choosing
				// to bail out. Go to either the full duplex failed page, or the
				// half duplex failed page, depending on the registry state.
				Diagnostics_Write(DVF_ERRORLEVEL, "User choosing not to run test");
				hr = psinfo->GetHalfDuplex(&dwRegVal);
				if (!FAILED(hr) && dwRegVal == REGVAL_PASSED)
				{
					SetWindowLongPtr(hDlg, DWLP_MSGRESULT, IDD_FULLDUPLEXFAILED);
				}
				else
				{
					SetWindowLongPtr(hDlg, DWLP_MSGRESULT, IDD_HALFDUPLEXFAILED);
				}
				return TRUE;
				break;
				
			default:
				// this is an error
				Diagnostics_Write(DVF_ERRORLEVEL, "DialogBox failed");
				hr = DVERR_GENERIC;
				goto error_cleanup;
			}
		}
		else 
		{
			Diagnostics_Write(DVF_ERRORLEVEL, "CrashCheckIn failed, code: %i", hr);
			goto error_cleanup;
		}
	}

	// no previous crashes (or the user is boldly charging ahead anyway), 
	// so go to the full duplex test page
	SetWindowLongPtr(hDlg, DWLP_MSGRESULT, IDD_FULLDUPLEXTEST);
	DPF_EXIT();
	return TRUE;

error_cleanup:
	psinfo->GetHWNDParent(&hwndParent);
	DV_DisplayErrorBox(hr, hwndParent);
	psinfo->SetError(hr);
	psinfo->Abort(hDlg, hr);
	DPF_EXIT();
	return FALSE;
}

#undef DPF_MODNAME
#define DPF_MODNAME "WelcomeResetHandler"
BOOL WelcomeResetHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, CSupervisorInfo* psinfo)
{
	DPF_ENTER();
	
	HRESULT hr;
	HWND hwndWizard;

	// disable all buttons
	psinfo->GetHWNDWizard(&hwndWizard);
	PropSheet_SetWizButtons(hwndWizard, 0);

	psinfo->Cancel();
	
	DPF_EXIT();
	return FALSE;
}

/*
#undef DPF_MODNAME
#define DPF_MODNAME "AlreadyRunningProc"
INT_PTR CALLBACK AlreadyRunningProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
	DPF_ENTER();
	
	HICON hIcon;

	switch (message)
	{
	case WM_INITDIALOG :
		hIcon = LoadIcon(NULL, IDI_ERROR);
		SendDlgItemMessage(hDlg, IDC_ICON_ERROR, STM_SETIMAGE, IMAGE_ICON, (LPARAM)hIcon);
		break;

	case WM_COMMAND:
		switch (LOWORD(wParam))
		{
		case IDOK:
		case IDCANCEL:
			EndDialog(hDlg, LOWORD(wParam));
			return TRUE;

		default:
			break;
		}
		break;


	default:
		break;
	}
	
	DPF_EXIT();
	return FALSE;
}
*/

#undef DPF_MODNAME
#define DPF_MODNAME "PreviousCrashProc"
INT_PTR CALLBACK PreviousCrashProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
	DPF_ENTER();
	
	LPNMHDR lpnm;
	HICON hIcon;

	Diagnostics_Write(DVF_ERRORLEVEL, "Previous run crashed");

	switch (message)
	{
	case WM_INITDIALOG :
		PlaySound( _T("SystemExclamation"), NULL, SND_ASYNC );					
		hIcon = LoadIcon(NULL, IDI_WARNING);
		SendDlgItemMessage(hDlg, IDC_ICON_WARNING, STM_SETIMAGE, IMAGE_ICON, (LPARAM)hIcon);
		break;

	case WM_COMMAND:
		switch (LOWORD(wParam))
		{
		case IDOK:
		case IDCANCEL:
			EndDialog(hDlg, LOWORD(wParam));
			return(TRUE);
			break;

		default:
			break;
		}
		break;
		
	default:
		break;
	}
	
	DPF_EXIT();
	return FALSE;
}

#undef DPF_MODNAME
#define DPF_MODNAME "FullDuplexProc"
INT_PTR CALLBACK FullDuplexProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
	DPF_ENTER();
	
	LONG lRet;
	BOOL fRet;
	LPNMHDR lpnm;
	
	CSupervisorInfo* psinfo = (CSupervisorInfo*)GetWindowLongPtr(hDlg, GWLP_USERDATA);

	fRet = FALSE;
	switch (message)
	{
	case WM_INITDIALOG :
		FullDuplexInitDialogHandler(hDlg, message, wParam, lParam, psinfo);
		break;

	case WM_NOTIFY :
		lpnm = (LPNMHDR) lParam;

		switch (lpnm->code)
		{
		case PSN_SETACTIVE : 
			fRet = FullDuplexSetActiveHandler(hDlg, message, wParam, lParam, psinfo);
			break;

		case PSN_WIZBACK :
			fRet = FullDuplexBackHandler(hDlg, message, wParam, lParam, psinfo);
			break;
			
		case PSN_WIZNEXT :
			fRet = FullDuplexNextHandler(hDlg, message, wParam, lParam, psinfo);
			break;

		case PSN_RESET :
			fRet = FullDuplexResetHandler(hDlg, message, wParam, lParam, psinfo);
			break;

		default :
			break;
		}
		break;

	case WM_APP_FULLDUP_TEST_COMPLETE:
		fRet = FullDuplexCompleteHandler(hDlg, message, wParam, lParam, psinfo);
		break;

	default:
		break;
	}
	
	DPF_EXIT();
	return fRet;
}

#undef DPF_MODNAME
#define DPF_MODNAME "FullDuplexInitDialogHandler"
BOOL FullDuplexInitDialogHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, CSupervisorInfo* psinfo)
{
	DPF_ENTER();
	
	LONG lRet;
	HWND hwndWizard = NULL;
	HWND hwndParent = NULL;
	HWND hwndControl;
	HFONT hfBold;
	HRESULT hr = DV_OK;
	
	// Get the shared data from PROPSHEETPAGE lParam value
	// and load it into GWLP_USERDATA
	psinfo = (CSupervisorInfo*)((LPPROPSHEETPAGE)lParam)->lParam;
	SetWindowLongPtr(hDlg, GWLP_USERDATA, (LONG_PTR)psinfo);
	
	// Get the parent window
	hwndWizard = GetParent(hDlg);
	if (hwndWizard == NULL)
	{
		lRet = GetLastError();
		Diagnostics_Write(DVF_ERRORLEVEL, "GetParent failed, code: %i", lRet);
		hr = DVERR_GENERIC;
		goto error_cleanup;
	}
	
	hwndControl = GetDlgItem(hDlg, IDC_TITLE);
	if (hwndControl == NULL)
	{
		// error, log it and bail
		lRet = GetLastError();
		Diagnostics_Write(DVF_ERRORLEVEL, "GetDlgItem failed, code: %i", lRet);
		hr = DVERR_GENERIC;
		goto error_cleanup;
	}
	psinfo->GetBoldFont(&hfBold);
    (void)::SendMessage(hwndControl, WM_SETFONT, (WPARAM)hfBold, (LPARAM)TRUE);

	DPF_EXIT();
	return FALSE;

error_cleanup:
	psinfo->GetHWNDParent(&hwndParent);
	DV_DisplayErrorBox(hr, hwndParent);
	psinfo->SetError(hr);
	psinfo->Abort(hDlg, hr);
	DPF_EXIT();
	return FALSE;
}

#undef DPF_MODNAME
#define DPF_MODNAME "FullDuplexSetActiveHandler"
BOOL FullDuplexSetActiveHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, CSupervisorInfo* psinfo)
{
	DPF_ENTER();
	
	LONG lRet;
	HWND hwndWizard = NULL;
	HWND hwndParent = NULL;
	HWND hwndProgress;
	HWND hwndCancelButton;
	HANDLE hThread;
	DWORD dwThreadId;
	WORD wCount;
	HRESULT hr = DV_OK;

	// Get the parent window
	hwndWizard = GetParent(hDlg);
	if (hwndWizard == NULL)
	{
		lRet = GetLastError();
		Diagnostics_Write(DVF_ERRORLEVEL, "GetParent failed, code: %i", lRet);
		hr = DVERR_GENERIC;
		goto error_cleanup;
	}

	// reset all the test registry bits
	psinfo->SetHalfDuplex(REGVAL_NOTRUN);
	psinfo->SetFullDuplex(REGVAL_NOTRUN);
	psinfo->SetMicDetected(REGVAL_NOTRUN);

	// remember that we've been here, so we'll know to reset the registry
	// if the user hits cancel from this point forward
	psinfo->SetWelcomeNext();

	// get the progress bar's HWND
	hwndProgress = GetDlgItem(hDlg, IDC_PROGRESSBAR);
	if (hwndProgress == NULL)
	{
		lRet = GetLastError();
		Diagnostics_Write(DVF_ERRORLEVEL, "GetDlgItem failed, code: %i", lRet);
		hr = DVERR_GENERIC;
		goto error_cleanup;
	}

	// Init the progress bar...
	
	// count the number of elements in the primary format array
	wCount = 0;
	while (1)
	{
		// Increment before we test. This means that if there
		// are four formats, wCount will equal five after this
		// loop.
		++wCount;
		if (gc_rgwfxPrimaryFormats[wCount].wFormatTag == 0
			&& gc_rgwfxPrimaryFormats[wCount].nChannels == 0
			&& gc_rgwfxPrimaryFormats[wCount].nSamplesPerSec == 0
			&& gc_rgwfxPrimaryFormats[wCount].nAvgBytesPerSec == 0
			&& gc_rgwfxPrimaryFormats[wCount].nBlockAlign == 0
			&& gc_rgwfxPrimaryFormats[wCount].wBitsPerSample == 0
			&& gc_rgwfxPrimaryFormats[wCount].cbSize == 0)
		{
			// we've found the last element of the array, break out.
			break;
		}
	}

	// set up the progress bar with one segment for each
	// primary format, times two, since each is tested in
	// both half and full duplex.
	SendMessage(hwndProgress, PBM_SETRANGE, 0, MAKELPARAM(0, wCount*2));
	SendMessage(hwndProgress, PBM_SETPOS, 0, 0);
	SendMessage(hwndProgress, PBM_SETSTEP, 1, 0);

	// set the HWNDs
	psinfo->SetHWNDWizard(hwndWizard);
	psinfo->SetHWNDDialog(hDlg);
	psinfo->SetHWNDProgress(hwndProgress);
	psinfo->SetHWNDInputPeak(NULL);
	psinfo->SetHWNDOutputPeak(NULL);
	psinfo->SetHWNDInputVolumeSlider(NULL);
	psinfo->SetHWNDOutputVolumeSlider(NULL);

	// clear the abort flag!
	psinfo->ClearAbortFullDuplex();

	// enable the Back button only
	PropSheet_SetWizButtons(hwndWizard, PSWIZB_BACK);

	// init IPC stuff
	hr = psinfo->InitIPC();
	if (FAILED(hr))
	{
		Diagnostics_Write(DVF_ERRORLEVEL, "Unable to Initialize IPC");
		goto error_cleanup;
	}

	// Fire up the full duplex test thread
	hr = psinfo->CreateFullDuplexThread();
	if (FAILED(hr))
	{
		Diagnostics_Write(DVF_ERRORLEVEL, "CreateFullDuplexThread failed, code: %i", hr);
		goto error_cleanup;
	}

	DPF_EXIT();
	return FALSE;

error_cleanup:
    
	psinfo->GetHWNDParent(&hwndParent);
	DV_DisplayErrorBox(hr, hwndParent);
	psinfo->SetError(hr);
	psinfo->Abort(hDlg, hr);
	DPF_EXIT();
	return FALSE;
}

#undef DPF_MODNAME
#define DPF_MODNAME "FullDuplexNextHandler"
BOOL FullDuplexNextHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, CSupervisorInfo* psinfo)
{
	DPF_ENTER();
	
	BOOL fPassed;
	HRESULT hr;
	HWND hwndWizard;
	HWND hwndParent = NULL;

	psinfo->GetFullDuplexResults(&hr);

	switch (hr)
	{
	case DV_HALFDUPLEX:
		SetWindowLongPtr(hDlg, DWLP_MSGRESULT, IDD_FULLDUPLEXFAILED);
		Diagnostics_Write(DVF_ERRORLEVEL, "Test resulted in full duplex");
		break;
		
	case DV_FULLDUPLEX:
		SetWindowLongPtr(hDlg, DWLP_MSGRESULT, IDD_MICTEST);
		Diagnostics_Write(DVF_ERRORLEVEL, "Test resulted in full duplex");
		break;

	case DVERR_SOUNDINITFAILURE:
		SetWindowLongPtr(hDlg, DWLP_MSGRESULT, IDD_HALFDUPLEXFAILED);
		Diagnostics_Write(DVF_ERRORLEVEL, "Test encountered unrecoverable error");
		break;

	default:
		// we should not get any other result
		Diagnostics_Write(DVF_ERRORLEVEL, "Unexpected full duplex results, hr: %i", hr);
		goto error_cleanup;
	}

	DPF_EXIT();
	return TRUE;

error_cleanup:
	psinfo->GetHWNDParent(&hwndParent);
	DV_DisplayErrorBox(hr, hwndParent);
	psinfo->SetError(hr);
	psinfo->Abort(hDlg, hr);
	DPF_EXIT();
	return FALSE;
}

#undef DPF_MODNAME
#define DPF_MODNAME "FullDuplexBackHandler"
BOOL FullDuplexBackHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, CSupervisorInfo* psinfo)
{
	DPF_ENTER();
	
	HRESULT hr;
	HWND hwndWizard;
	HWND hwndParent = NULL;

	// Get the parent window
	psinfo->GetHWNDWizard(&hwndWizard);

	// shut down the full duplex test
	hr = psinfo->CancelFullDuplexTest();
	if (FAILED(hr) && hr != DVERR_USERCANCEL)
	{
		Diagnostics_Write(DVF_ERRORLEVEL, "CancelFullDuplexTest failed, hr: %i", hr);
		goto error_cleanup;
	}

	// reset the registry to the "test not run" state
	hr = psinfo->SetHalfDuplex(REGVAL_NOTRUN);
	if (FAILED(hr))
	{
		Diagnostics_Write(DVF_ERRORLEVEL, "SetHalfDuplex failed, code: %i", hr);
	}

	hr = psinfo->SetFullDuplex(REGVAL_NOTRUN);
	if (FAILED(hr))
	{
		Diagnostics_Write(DVF_ERRORLEVEL, "SetFullDuplex failed, code: %i", hr);
	}

	hr = psinfo->SetMicDetected(REGVAL_NOTRUN);
	if (FAILED(hr))
	{
		Diagnostics_Write(DVF_ERRORLEVEL, "SetMicDetected failed, code: %i", hr);
	}

	// go back to the welcome page
	SetWindowLongPtr(hDlg, DWLP_MSGRESULT, IDD_WELCOME);

	DPF_EXIT();
	return TRUE;

error_cleanup:
	psinfo->GetHWNDParent(&hwndParent);
	DV_DisplayErrorBox(hr, hwndParent);
	psinfo->SetError(hr);
	psinfo->Abort(hDlg, hr);
	DPF_EXIT();
	return FALSE;
}


#undef DPF_MODNAME
#define DPF_MODNAME "FullDuplexCompleteHandler"
BOOL FullDuplexCompleteHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, CSupervisorInfo* psinfo)
{
	DPF_ENTER();
	
	HWND hwndWizard = NULL;
	HWND hwndParent = NULL;
	HWND hwndProgress;
	LONG lRet;
	HRESULT hr = DV_OK;
	UINT idsErrorMessage = 0;

	// Get the parent window
	psinfo->GetHWNDWizard(&hwndWizard);

	// Disable all the wizard buttons
	PropSheet_SetWizButtons(hwndWizard, 0);

	// Get the progress bar window
	psinfo->GetHWNDProgress(&hwndProgress);

	// Close the full duplex thread handle and get the test results via the exit code
	hr = psinfo->WaitForFullDuplexThreadExitCode();
	switch(hr)
	{
	case DVERR_SOUNDINITFAILURE:
	case DV_HALFDUPLEX:
	case DV_FULLDUPLEX:
		// These are the expected results from the full duplex test thread
		// this means no strange internal error occured, and it is safe
		// to move along to the next part of the wizard.
		// Record the test results
		psinfo->SetFullDuplexResults(hr);
		break;

	case DPNERR_INVALIDDEVICEADDRESS:
		// This can result from no tcp/ip stack on the machine
		// we want to dispaly a special error code and then fall
		// through with the rest of the return codes.
		idsErrorMessage = IDS_ERROR_NODEVICES;
		// fall through
	default:
		// any other error code is not expected and means we hit
		// some internal problem. Bail.
		Diagnostics_Write(DVF_ERRORLEVEL, "Full duplex test thread exited with unexpected error code, hr: %i", hr);
		psinfo->DeinitIPC();
		goto error_cleanup;
	}

	// Deinit the IPC stuff
	hr = psinfo->DeinitIPC();
	if (FAILED(hr))
	{
		Diagnostics_Write(DVF_ERRORLEVEL, "DeinitIPC failed, code: %i", hr);
		goto error_cleanup;
	}

	// Move the progress bar all the way to the end.
	SendMessage(hwndProgress, PBM_SETRANGE, 0, MAKELPARAM(0, 1));
	SendMessage(hwndProgress, PBM_SETPOS, 1, 0);

	// enable and press the next button to move 
	// to the next page automatically
	PropSheet_PressButton(hwndWizard, PSBTN_NEXT);
	
	DPF_EXIT();
	return FALSE;

error_cleanup:
	psinfo->GetHWNDParent(&hwndParent);
	DV_DisplayErrorBox(hr, hwndParent, idsErrorMessage);
	psinfo->SetError(hr);
	psinfo->Abort(hDlg, hr);
	DPF_EXIT();
	return FALSE;
}

#undef DPF_MODNAME
#define DPF_MODNAME "FullDuplexResetHandler"
BOOL FullDuplexResetHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, CSupervisorInfo* psinfo)
{
	DPF_ENTER();	
	DPF_EXIT();
	return FALSE;
}

#undef DPF_MODNAME
#define DPF_MODNAME "MicTestProc"
INT_PTR CALLBACK MicTestProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
	DPF_ENTER();
	
	BOOL fRet;	
	LPNMHDR lpnm;
	
	CSupervisorInfo* psinfo = (CSupervisorInfo*)GetWindowLongPtr(hDlg, GWLP_USERDATA);

	fRet = FALSE;
	switch (message)
	{
	case WM_INITDIALOG :
		fRet = MicTestInitDialogHandler(hDlg, message, wParam, lParam, psinfo);
		break;

	case WM_NOTIFY :
		lpnm = (LPNMHDR) lParam;

		switch (lpnm->code)
		{
		case PSN_SETACTIVE : 
			fRet = MicTestSetActiveHandler(hDlg, message, wParam, lParam, psinfo);
			break;

		case PSN_WIZNEXT :
			fRet = MicTestNextHandler(hDlg, message, wParam, lParam, psinfo);
			break;

		case PSN_WIZBACK :
			fRet = MicTestBackHandler(hDlg, message, wParam, lParam, psinfo);
			break;

		case PSN_RESET :
			fRet = MicTestResetHandler(hDlg, message, wParam, lParam, psinfo);
			break;

		default :
			break;
		}
		break;

	case WM_COMMAND:
		switch (LOWORD(wParam))
		{
		case IDC_RECADVANCED:
			fRet = MicTestRecAdvancedHandler(hDlg, message, wParam, lParam, psinfo);
			break;

		default:
			break;
		}
		break;

	case WM_APP_LOOPBACK_RUNNING:
		fRet = MicTestLoopbackRunningHandler(hDlg, message, wParam, lParam, psinfo);
		break;

	case WM_APP_RECORDSTART:
		fRet = MicTestRecordStartHandler(hDlg, message, wParam, lParam, psinfo);
		break;

	case WM_APP_RECORDSTOP:
		fRet = MicTestRecordStopHandler(hDlg, message, wParam, lParam, psinfo);
		break;

	case WM_VSCROLL:
		fRet = MicTestVScrollHandler(hDlg, message, wParam, lParam, psinfo);
		break;

	default:
		break;
	}

	DPF_EXIT();
	return fRet;
}

#undef DPF_MODNAME
#define DPF_MODNAME "MicTestInitDialogHandler"
BOOL MicTestInitDialogHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, CSupervisorInfo* psinfo)
{
	DPF_ENTER();
	
	LONG lRet;
	HWND hwndWizard = NULL;
	HWND hwndParent = NULL;
	HWND hwndControl;
	HFONT hfBold;
	HWND hwndRecPeak;
	HWND hwndOutPeak;
	HWND hwndRecSlider;
	HWND hwndOutSlider;
	HWND hwndOutAdvanced;
	HWND hwndOutGroup;
	HRESULT hr = DV_OK;
	
	// Get the shared data from PROPSHEETPAGE lParam value
	// and load it into GWLP_USERDATA
	psinfo = (CSupervisorInfo*)((LPPROPSHEETPAGE)lParam)->lParam;
	SetWindowLongPtr(hDlg, GWLP_USERDATA, (LONG_PTR)psinfo);
	
	// Get the parent window
	hwndWizard = GetParent(hDlg);
	if (hwndWizard == NULL)
	{
		lRet = GetLastError();
		Diagnostics_Write(DVF_ERRORLEVEL, "GetParent failed, code: %i", lRet);
		hr = DVERR_GENERIC;
		goto error_cleanup;
	}
	
	hwndControl = GetDlgItem(hDlg, IDC_TITLE);
	if (hwndControl == NULL)
	{
		// error, log it and bail
		lRet = GetLastError();
		Diagnostics_Write(DVF_ERRORLEVEL, "GetDlgItem failed, code: %i", lRet);
		hr = DVERR_GENERIC;
		goto error_cleanup;
	}
	psinfo->GetBoldFont(&hfBold);
    (void)::SendMessage(hwndControl, WM_SETFONT, (WPARAM)hfBold, (LPARAM)TRUE);

	// Get the peak meter
	hwndRecPeak = GetDlgItem(hDlg, IDC_RECPEAKMETER);
	if (hwndRecPeak == NULL)
	{
		lRet = GetLastError();
		Diagnostics_Write(DVF_ERRORLEVEL, "GetDlgItem failed, code: %i", lRet);
		hr = DVERR_GENERIC;
		goto error_cleanup;
	}

	// Init the recording peak meter
	SendMessage(hwndRecPeak, PM_SETMIN, 0, 0);
	SendMessage(hwndRecPeak, PM_SETMAX, 0, 99);
	SendMessage(hwndRecPeak, PM_SETCUR, 0, 0);

	// Get the recording volume slider
	hwndRecSlider = GetDlgItem(hDlg, IDC_RECVOL_SLIDER);
	if (hwndRecSlider == NULL)
	{
		lRet = GetLastError();
		Diagnostics_Write(DVF_ERRORLEVEL, "GetDlgItem failed, code: %i", lRet);
		hr = DVERR_GENERIC;
		goto error_cleanup;
	}

	// Init the recording volume slider
	SendMessage(hwndRecSlider, TBM_SETRANGEMIN, 0, DBToAmpFactor(DSBVOLUME_MAX) - DBToAmpFactor(DSBVOLUME_MAX));
	SendMessage(hwndRecSlider, TBM_SETRANGEMAX, 0, DBToAmpFactor(DSBVOLUME_MAX) - DBToAmpFactor(DSBVOLUME_MIN));
	SendMessage(hwndRecSlider, TBM_SETPOS, 0, DBToAmpFactor(DSBVOLUME_MAX) - DBToAmpFactor(DSBVOLUME_MAX));
	SendMessage(hwndRecSlider, TBM_SETTICFREQ,
		(DBToAmpFactor(DSBVOLUME_MAX) - DBToAmpFactor(DSBVOLUME_MIN))/10, 0);
	SendMessage(hwndRecSlider, TBM_SETLINESIZE, 0,
		(DBToAmpFactor(DSBVOLUME_MAX) - DBToAmpFactor(DSBVOLUME_MIN))/20);
	SendMessage(hwndRecSlider, TBM_SETPAGESIZE, 0,
		(DBToAmpFactor(DSBVOLUME_MAX) - DBToAmpFactor(DSBVOLUME_MIN))/5);

	// Get the playback peak meter
	hwndOutPeak = GetDlgItem(hDlg, IDC_OUTPEAKMETER);
	if (hwndOutPeak == NULL)
	{
		lRet = GetLastError();
		Diagnostics_Write(DVF_ERRORLEVEL, "GetDlgItem failed, code: %i", lRet);
		hr = DVERR_GENERIC;
		goto error_cleanup;
	}

	// Init the playback peak meter
	SendMessage(hwndOutPeak, PM_SETMIN, 0, 0);
	SendMessage(hwndOutPeak, PM_SETMAX, 0, 99);
	SendMessage(hwndOutPeak, PM_SETCUR, 0, 0);

	// Get the playback volume slider
	hwndOutSlider = GetDlgItem(hDlg, IDC_OUTVOL_SLIDER);
	if (hwndOutSlider == NULL)
	{
		lRet = GetLastError();
		Diagnostics_Write(DVF_ERRORLEVEL, "GetDlgItem failed, code: %i", lRet);
		hr = DVERR_GENERIC;
		goto error_cleanup;
	}

	// Init the playback volume slider
	SendMessage(hwndOutSlider, TBM_SETRANGEMIN, 0, DBToAmpFactor(DSBVOLUME_MAX) - DBToAmpFactor(DSBVOLUME_MAX));
	SendMessage(hwndOutSlider, TBM_SETRANGEMAX, 0, DBToAmpFactor(DSBVOLUME_MAX) - DBToAmpFactor(DSBVOLUME_MIN));
	SendMessage(hwndOutSlider, TBM_SETPOS, 0, DBToAmpFactor(DSBVOLUME_MAX) - DBToAmpFactor(DSBVOLUME_MAX));
	SendMessage(hwndOutSlider, TBM_SETTICFREQ, 
		(DBToAmpFactor(DSBVOLUME_MAX) - DBToAmpFactor(DSBVOLUME_MIN))/10, 0);
	SendMessage(hwndOutSlider, TBM_SETLINESIZE, 0,
		(DBToAmpFactor(DSBVOLUME_MAX) - DBToAmpFactor(DSBVOLUME_MIN))/20);
	SendMessage(hwndOutSlider, TBM_SETPAGESIZE, 0,
		(DBToAmpFactor(DSBVOLUME_MAX) - DBToAmpFactor(DSBVOLUME_MIN))/5);

	// grey out all the playback volume stuff
	EnableWindow(hwndOutSlider, FALSE);
	
	hwndOutAdvanced = GetDlgItem(hDlg, IDC_OUTADVANCED);
	if (hwndOutAdvanced == NULL)
	{
		lRet = GetLastError();
		Diagnostics_Write(DVF_ERRORLEVEL, "GetDlgItem failed, code: %i", lRet);
		hr = DVERR_GENERIC;
		goto error_cleanup;
	}
	EnableWindow(hwndOutAdvanced, FALSE);
	
	hwndOutGroup = GetDlgItem(hDlg, IDC_OUTGROUP);
	if (hwndOutGroup == NULL)
	{
		lRet = GetLastError();
		Diagnostics_Write(DVF_ERRORLEVEL, "GetDlgItem failed, code: %i", lRet);
		hr = DVERR_GENERIC;
		goto error_cleanup;
	}
	EnableWindow(hwndOutGroup, FALSE);

	DPF_EXIT();
	return FALSE;

error_cleanup:
	psinfo->GetHWNDParent(&hwndParent);
	DV_DisplayErrorBox(hr, hwndParent);
	psinfo->SetError(hr);
	psinfo->Abort(hDlg, hr);
	DPF_EXIT();
	return FALSE;
}

#undef DPF_MODNAME
#define DPF_MODNAME "MicTestSetActiveHandler"
BOOL MicTestSetActiveHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, CSupervisorInfo* psinfo)
{
	DPF_ENTER();
	
	LONG lRet;
	HWND hwndWizard = NULL;
	HWND hwndParent = NULL;
	HWND hwndRecPeak;
	HWND hwndOutPeak;
	HWND hwndRecSlider;
	HWND hwndOutSlider;
	HANDLE hThread;
	HANDLE hEvent;
	DWORD dwThreadId;
	HRESULT hr = DV_OK;
	DWORD dwVolume;

	// Get the parent window
	hwndWizard = GetParent(hDlg);
	if (hwndWizard == NULL)
	{
		lRet = GetLastError();
		Diagnostics_Write(DVF_ERRORLEVEL, "GetParent failed, code: %i", lRet);
		hr = DVERR_GENERIC;
		goto error_cleanup;
	}

	// Set the recording peak meter to zero
	hwndRecPeak = GetDlgItem(hDlg, IDC_RECPEAKMETER);
	if (hwndRecPeak == NULL)
	{
		lRet = GetLastError();
		Diagnostics_Write(DVF_ERRORLEVEL, "GetDlgItem failed, code: %i", lRet);
		hr = DVERR_GENERIC;
		goto error_cleanup;
	}
	SendMessage(hwndRecPeak, PM_SETCUR, 0, 0);

	// Get the recording volume control hwnd
	hwndRecSlider = GetDlgItem(hDlg, IDC_RECVOL_SLIDER);
	if (hwndRecSlider == NULL)
	{
		lRet = GetLastError();
		Diagnostics_Write(DVF_ERRORLEVEL, "GetDlgItem failed, code: %i", lRet);
		hr = DVERR_GENERIC;
		goto error_cleanup;
	}
	// Set the slider to max
	SendMessage(hwndRecSlider, TBM_SETPOS, 1, SendMessage(hwndRecSlider, TBM_GETRANGEMIN, 0, 0));

	// Set the playback peak meter to zero
	hwndOutPeak = GetDlgItem(hDlg, IDC_OUTPEAKMETER);
	if (hwndOutPeak == NULL)
	{
		lRet = GetLastError();
		Diagnostics_Write(DVF_ERRORLEVEL, "GetDlgItem failed, code: %i", lRet);
		hr = DVERR_GENERIC;
		goto error_cleanup;
	}
	SendMessage(hwndOutPeak, PM_SETCUR, 0, 0);

	// Get the playback volume control hwnd
	hwndOutSlider = GetDlgItem(hDlg, IDC_OUTVOL_SLIDER);
	if (hwndOutSlider == NULL)
	{
		lRet = GetLastError();
		Diagnostics_Write(DVF_ERRORLEVEL, "GetDlgItem failed, code: %i", lRet);
		hr = DVERR_GENERIC;
		goto error_cleanup;
	}

	// Get the current waveOut volume and set the slider to that position
	hr = psinfo->GetWaveOutVolume(&dwVolume);
	if (FAILED(hr))
	{
		// couldn't get the volume - set the slider to top
		SendMessage(hwndOutSlider, TBM_SETPOS, 1, SendMessage(hwndOutSlider, TBM_GETRANGEMIN, 0, 0));
	}
	else
	{
		SendMessage(hwndOutSlider, TBM_SETPOS, 1, SendMessage(hwndOutSlider, TBM_GETRANGEMAX, 0, 0) - dwVolume);
	}
	
	// set the HWNDs
	psinfo->SetHWNDWizard(hwndWizard);
	psinfo->SetHWNDDialog(hDlg);
	psinfo->SetHWNDProgress(NULL);
	psinfo->SetHWNDInputPeak(hwndRecPeak);
	psinfo->SetHWNDOutputPeak(hwndOutPeak);
	psinfo->SetHWNDInputVolumeSlider(hwndRecSlider);
	psinfo->SetHWNDOutputVolumeSlider(NULL);
	psinfo->SetLoopbackFlags(0);

	// clear the voice detected flag
	psinfo->ClearVoiceDetected();

	// clear the mic test reg value
	psinfo->SetMicDetected(REGVAL_CRASHED);

	// fire up the loopback test thread
	hr = psinfo->CreateLoopbackThread();
	if (FAILED(hr))
	{
		// error, log it and bail
		Diagnostics_Write(DVF_ERRORLEVEL, "CreateLoopbackThread failed, code: %i", hr);
		goto error_cleanup;
	}
	
	// disable the buttons - they will be enabled
	// when the loopback test is up and running.
	PropSheet_SetWizButtons(hwndWizard, 0);

	DPF_EXIT();
	return FALSE;

error_cleanup:
	psinfo->GetHWNDParent(&hwndParent);
	DV_DisplayErrorBox(hr, hwndParent);
	psinfo->SetError(hr);
	psinfo->Abort(hDlg, hr);
	DPF_EXIT();
	return FALSE;
}

#undef DPF_MODNAME
#define DPF_MODNAME "MicTestLoopbackRunningHandler"
BOOL MicTestLoopbackRunningHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, CSupervisorInfo* psinfo)
{
	DPF_ENTER();
	
	LONG lRet;
	HWND hwndWizard = NULL;
	HWND hwndParent = NULL;
	HRESULT hr = DV_OK;
	HWND hwndRecordSlider;
	HWND hwndRecordAdvanced;

    // Get the parent window    
	psinfo->GetHWNDWizard(&hwndWizard);

	// lParam is an HRESULT sent by the loopback test thread
	hr = (HRESULT)lParam;
	if (FAILED(hr))
	{
		Diagnostics_Write(DVF_ERRORLEVEL, "LoopbackTestThread signaled error, code: %i", hr);
		goto error_cleanup;
	}

    hwndRecordSlider = GetDlgItem(hDlg, IDC_RECVOL_SLIDER);
    hwndRecordAdvanced = GetDlgItem( hDlg, IDC_RECADVANCED );

    if( hwndRecordSlider != NULL && hwndRecordAdvanced != NULL )
    {
        DWORD dwDeviceFlags;

        psinfo->GetDeviceFlags( &dwDeviceFlags );
        
        if( dwDeviceFlags & DVSOUNDCONFIG_NORECVOLAVAILABLE )
        {
            EnableWindow( hwndRecordAdvanced, FALSE );
            
            EnableWindow( hwndRecordSlider, FALSE );
        }
    }
    else
    {
        hr = DVERR_GENERIC;
        DPFX(DPFPREP,  DVF_ERRORLEVEL, "Unable to get record slider window" );
        goto error_cleanup;
    }

	// clear the voice detected flag
	psinfo->ClearVoiceDetected();

	// enable the next button
	PropSheet_SetWizButtons(hwndWizard, PSWIZB_NEXT|PSWIZB_BACK);

	DPF_EXIT();
	return FALSE;

error_cleanup:
	psinfo->GetHWNDParent(&hwndParent);
	DV_DisplayErrorBox(hr, hwndParent);
	psinfo->SetError(hr);
	psinfo->Abort(hDlg, hr);
	DPF_EXIT();
	return FALSE;
}

#undef DPF_MODNAME
#define DPF_MODNAME "MicTestRecordStartHandler"
BOOL MicTestRecordStartHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, CSupervisorInfo* psinfo)
{
	DPF_ENTER();

	// set the voice detected flag
	psinfo->SetVoiceDetected();

	DPF_EXIT();
	return FALSE;
}

#undef DPF_MODNAME
#define DPF_MODNAME "MicTestRecordStopHandler"
BOOL MicTestRecordStopHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, CSupervisorInfo* psinfo)
{
	DPF_ENTER();
	DPF_EXIT();
	return FALSE;
}


#undef DPF_MODNAME
#define DPF_MODNAME "MicTestNextHandler"
BOOL MicTestNextHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, CSupervisorInfo* psinfo)
{
	DPF_ENTER();
	
	HRESULT hr;
	HWND hwndWizard;
	HWND hwndSlider;
	BOOL fVoiceDetected;
	
	// Get the parent window
	psinfo->GetHWNDWizard(&hwndWizard);

	// If we heard a voice, go to the speaker test page.
	// Otherwise, go to the mic failed page
	psinfo->GetVoiceDetected(&fVoiceDetected);
	if (fVoiceDetected)
	{
		// save the current recording slider position
		hwndSlider = GetDlgItem(hDlg, IDC_RECVOL_SLIDER);
		if (hwndSlider == NULL)
		{
			Diagnostics_Write(DVF_ERRORLEVEL, "GetDlgItem failed, code: %i", GetLastError());
		}
		else
		{
			psinfo->SetInputVolumeSliderPos((LONG)SendMessage(hwndSlider, TBM_GETPOS, 0, 0));
		}

		// record the mic test result in the registry
		psinfo->SetMicDetected(REGVAL_PASSED);

		// move on to the speaker test.		
		SetWindowLongPtr(hDlg, DWLP_MSGRESULT, IDD_SPEAKER_TEST);
	}
	else
	{
		hr = psinfo->ShutdownLoopbackThread();
		if (FAILED(hr))
		{
			Diagnostics_Write(DVF_ERRORLEVEL, "ShutdownLoopbackThread failed, code: %i", hr);
		}

		// record the mic test result in the registry
		psinfo->SetMicDetected(REGVAL_FAILED);

		// go to the mic test failed page
		SetWindowLongPtr(hDlg, DWLP_MSGRESULT, IDD_MICTEST_FAILED);
	}

	DPF_EXIT();
	return TRUE;
}

#undef DPF_MODNAME
#define DPF_MODNAME "MicTestBackHandler"
BOOL MicTestBackHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, CSupervisorInfo* psinfo)
{
	DPF_ENTER();
	
	HRESULT hr;
	HWND hwndWizard;
	BOOL fVoiceDetected;
	
	// Get the parent window
	psinfo->GetHWNDWizard(&hwndWizard);

	hr = psinfo->ShutdownLoopbackThread();
	if (FAILED(hr))
	{
		Diagnostics_Write(DVF_ERRORLEVEL, "ShutdownLoopbackThread failed, code: %i", hr);
	}

	// shutdown the any volume controls we launched
	psinfo->CloseWindowsVolumeControl(TRUE);
	psinfo->CloseWindowsVolumeControl(FALSE);

	// make it look like the mic test was never run
	psinfo->SetMicDetected(REGVAL_NOTRUN);

	// go back to the full duplex test page
	SetWindowLongPtr(hDlg, DWLP_MSGRESULT, IDD_FULLDUPLEXTEST);

	DPF_EXIT();
	return TRUE;
}

#undef DPF_MODNAME
#define DPF_MODNAME "MicTestVScrollHandler"
BOOL MicTestVScrollHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, CSupervisorInfo* psinfo)
{
	DPF_ENTER();

	HWND hwndSlider;
	DWORD dwSliderPos;

	psinfo->GetHWNDInputVolumeSlider(&hwndSlider);
	if (hwndSlider == (HWND)lParam)
	{
		// the user is moving the input slider
		dwSliderPos = (DWORD) SendMessage(hwndSlider, TBM_GETPOS, 0, 0);

		// set the input volume to the user's request
		psinfo->SetRecordVolume(AmpFactorToDB(DBToAmpFactor(DSBVOLUME_MAX)-dwSliderPos));			
	}
	
	DPF_EXIT();
	return FALSE;
}

#undef DPF_MODNAME
#define DPF_MODNAME "MicTestRecAdvancedHandler"
BOOL MicTestRecAdvancedHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, CSupervisorInfo* psinfo)
{
	DPF_ENTER();

	HWND hwndWizard = GetParent(hDlg);
	if (hwndWizard == NULL)
	{
		Diagnostics_Write(DVF_ERRORLEVEL, "GetParent failed");
		DPF_EXIT();
		return FALSE;
	}
	psinfo->LaunchWindowsVolumeControl(hwndWizard, TRUE);

	DPF_EXIT();
	return FALSE;
}



#undef DPF_MODNAME
#define DPF_MODNAME "LoopbackTestThreadProc"
DWORD WINAPI LoopbackTestThreadProc(LPVOID lpvParam)
{
	DPF_ENTER();
	
	CSupervisorInfo* psinfo;
	HRESULT hr;
	LONG lRet;
	HWND hwnd;
	LPDIRECTPLAYVOICESERVER lpdpvs;
	LPDIRECTPLAYVOICECLIENT lpdpvc;
	PDIRECTPLAY8SERVER lpdp8;
	DWORD dwRet;
	HANDLE hEvent;
	DWORD dwWaveOutId;
	DWORD dwWaveInId;
	HWND hwndWizard;
	GUID guidCaptureDevice;
	GUID guidRenderDevice;
	DWORD dwFlags;
	DWORD dwSize;
	PDVSOUNDDEVICECONFIG pdvsdc = NULL;
	PBYTE pdvsdcBuffer = NULL;	
	BOOL fLoopbackStarted = FALSE;
	
	psinfo = (CSupervisorInfo*)lpvParam;
	psinfo->GetHWNDDialog(&hwnd);
	psinfo->GetHWNDWizard(&hwndWizard);
	psinfo->GetCaptureDevice(&guidCaptureDevice);
	psinfo->GetRenderDevice(&guidRenderDevice);
	psinfo->GetLoopbackFlags(&dwFlags);

	lpdpvs = NULL;
	lpdpvc = NULL;
	lpdp8 = NULL;

	// new thread, init COM
	hr = COM_CoInitialize(NULL);

	if( FAILED( hr ) )
	{
		Diagnostics_Write(DVF_ERRORLEVEL, "COM_CoInitialize failed, code: %i", hr);
		if (!PostMessage(hwnd, WM_APP_LOOPBACK_RUNNING, 0, (LPARAM)hr))
		{
			lRet = GetLastError();
			Diagnostics_Write(DVF_ERRORLEVEL, "PostMessage failed, code: %i", lRet);
		}
		goto error_cleanup;
	}

	hr = StartDirectPlay( &lpdp8 );

	if( FAILED( hr ) )
	{
		Diagnostics_Write(DVF_ERRORLEVEL, "StartDirectPlay failed, code: 0x%x", hr);
		goto error_cleanup;		
	}

	hr = StartLoopback(
		&lpdpvs, 
		&lpdpvc,
		&lpdp8, 
		(LPVOID)psinfo,
		hwndWizard,
		guidCaptureDevice,
		guidRenderDevice,
		dwFlags);

	if (FAILED(hr) )
	{
		Diagnostics_Write(DVF_ERRORLEVEL, "StartLoopback failed, code: %i", hr);
		if (!PostMessage(hwnd, WM_APP_LOOPBACK_RUNNING, 0, (LPARAM)hr))
		{
			lRet = GetLastError();
			Diagnostics_Write(DVF_ERRORLEVEL, "PostMessage failed, code: %i", lRet);
		}
		goto error_cleanup;
	}

	psinfo->SetLoopbackRunning( TRUE );

	if( !(dwFlags & DVSOUNDCONFIG_HALFDUPLEX) && hr == DV_HALFDUPLEX )
	{
		Diagnostics_Write(DVF_ERRORLEVEL, "StartLoopback failed with half duplex when expecting full duplex", hr);
		if (!PostMessage(hwnd, WM_APP_LOOPBACK_RUNNING, 0, (LPARAM)hr))
		{
			lRet = GetLastError();
			Diagnostics_Write(DVF_ERRORLEVEL, "PostMessage failed, code: %i", lRet);
		}
		goto error_cleanup;
	}

	// save the voice client interface for the other threads to play with
	psinfo->SetDPVC(lpdpvc);

	dwSize = 0;
	
	hr = lpdpvc->GetSoundDeviceConfig(pdvsdc, &dwSize);

	if( hr != DVERR_BUFFERTOOSMALL )
	{
		DPFX(DPFPREP,  DVF_ERRORLEVEL, "GetSoundDeviceConfig failed, hr: %i", hr );
		if (!FAILED(hr))
		{
			// map success codes to a generic failure, since we
			// did not expect success
			hr = DVERR_GENERIC;
		}
		goto error_cleanup;
	}

	pdvsdcBuffer = new BYTE[dwSize];

	if( pdvsdcBuffer == NULL )
	{
		DPFX(DPFPREP,  DVF_ERRORLEVEL, "Memory alloc failure" );
		hr = DVERR_OUTOFMEMORY;
		goto error_cleanup;
	}

	pdvsdc = (PDVSOUNDDEVICECONFIG) pdvsdcBuffer;
	pdvsdc->dwSize = sizeof( DVSOUNDDEVICECONFIG );

	hr = lpdpvc->GetSoundDeviceConfig(pdvsdc, &dwSize);	
	
	if (FAILED(hr))
	{
		Diagnostics_Write(DVF_ERRORLEVEL, "GetSoundDeviceConfig failed, hr: %i", hr);
		goto error_cleanup;
	}

	hr = DV_MapGUIDToWaveID(FALSE, pdvsdc->guidPlaybackDevice, &dwWaveOutId);
	if (FAILED(hr))
	{
		Diagnostics_Write(DVF_ERRORLEVEL, "DV_MapGUIDToWaveID failed, hr: %i", hr);
		goto error_cleanup;
	}
	hr = psinfo->SetWaveOutId(dwWaveOutId);
	if (FAILED(hr))
	{
		Diagnostics_Write(DVF_ERRORLEVEL, "SetWaveOutId failed, hr: %i", hr);
		goto error_cleanup;
	}
	
	hr = DV_MapGUIDToWaveID(TRUE, pdvsdc->guidCaptureDevice, &dwWaveInId);
	if (FAILED(hr))
	{
		Diagnostics_Write(DVF_ERRORLEVEL, "DV_MapGUIDToWaveID failed, hr: %i", hr);
		goto error_cleanup;
	}
	psinfo->SetWaveInId(dwWaveInId);
	psinfo->SetDeviceFlags( pdvsdc->dwFlags );

	// inform the app that loopback is up and running.
	hr = DV_OK;
	// Also send along the flags from GetSoundDeviceConfig
	if (!PostMessage(hwnd, WM_APP_LOOPBACK_RUNNING, 0, (LPARAM)hr))
	{
		lRet = GetLastError();
		Diagnostics_Write(DVF_ERRORLEVEL, "PostMessage failed, code: %i", lRet);
		goto error_cleanup;
	}

	// wait on the shutdown event
	hr = psinfo->WaitForLoopbackShutdownEvent();
	if (FAILED(hr))
	{
		Diagnostics_Write(DVF_ERRORLEVEL, "WaitForLoopbackShutdown failed, code: %i", hr);
		goto error_cleanup;
	}

	psinfo->SetLoopbackRunning( FALSE);		

	delete [] pdvsdcBuffer;	
	pdvsdcBuffer = NULL;

	// Null out the interface pointer in sinfo
	psinfo->SetDPVC(NULL);

	// shutdown the loopback test
	hr = StopLoopback(lpdpvs, lpdpvc, lpdp8);
	if (FAILED(hr))
	{
		Diagnostics_Write(DVF_ERRORLEVEL, "StopLoopback failed, code: %i", hr);
		goto error_cleanup;
	}

	hr = StopDirectPlay( lpdp8 );

	lpdp8 = NULL;

	if( FAILED( hr ) )
	{
		Diagnostics_Write(DVF_ERRORLEVEL, "StopDirectPlay failed, code: %i", hr);
		goto error_cleanup;
	}		

	// Signal the loopback thread exit event
	psinfo->SignalLoopbackThreadDone();

	COM_CoUninitialize();
	DPF_EXIT();
	return DV_OK;

error_cleanup:
	if (pdvsdcBuffer != NULL)
	{
		delete [] pdvsdcBuffer;
	}

	if (fLoopbackStarted)
	{
		StopLoopback(lpdpvs, lpdpvc, lpdp8);
	}

	if( lpdp8 )
	{
		StopDirectPlay( lpdp8 );
	}

	psinfo->SetLoopbackRunning( FALSE);

	psinfo->SignalLoopbackThreadDone();
	COM_CoUninitialize();
	DPF_EXIT();
	return hr;
}

#undef DPF_MODNAME
#define DPF_MODNAME "MicTestResetHandler"
BOOL MicTestResetHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, CSupervisorInfo* psinfo)
{
	DPF_ENTER();
	DPF_EXIT();
	return FALSE;
}

#undef DPF_MODNAME
#define DPF_MODNAME "CompleteProc"
INT_PTR CALLBACK CompleteProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
	DPF_ENTER();
	
	BOOL fRet;	
	LPNMHDR lpnm;
	
	CSupervisorInfo* psinfo = (CSupervisorInfo*)GetWindowLongPtr(hDlg, GWLP_USERDATA);

	fRet = FALSE;
	switch (message)
	{
	case WM_INITDIALOG :
		fRet = CompleteInitDialogHandler(hDlg, message, wParam, lParam, psinfo);
		break;

	case WM_NOTIFY :
		lpnm = (LPNMHDR) lParam;

		switch (lpnm->code)
		{
		case PSN_SETACTIVE : 
			fRet = CompleteSetActiveHandler(hDlg, message, wParam, lParam, psinfo);
			break;

		case PSN_WIZFINISH :
			fRet = CompleteFinishHandler(hDlg, message, wParam, lParam, psinfo);
			break;

		case PSN_RESET :
			fRet = CompleteResetHandler(hDlg, message, wParam, lParam, psinfo);
			break;

		default :
			break;
		}
		break;

	default:
		break;
	}

	DPF_EXIT();		
	return fRet;
}

#undef DPF_MODNAME
#define DPF_MODNAME "CompleteInitDialogHandler"
BOOL CompleteInitDialogHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, CSupervisorInfo* psinfo)
{
	DPF_ENTER();
	
	LONG lRet;
	HWND hwndWizard = NULL;
	HWND hwndParent = NULL;
	HWND hwndControl;
	HFONT hfTitle;
	HRESULT hr = DV_OK;
	
	// Get the shared data from PROPSHEETPAGE lParam value
	// and load it into GWLP_USERDATA
	psinfo = (CSupervisorInfo*)((LPPROPSHEETPAGE)lParam)->lParam;
	SetWindowLongPtr(hDlg, GWLP_USERDATA, (LONG_PTR)psinfo);
	
	// Get the parent window
	hwndWizard = GetParent(hDlg);
	if (hwndWizard == NULL)
	{
		lRet = GetLastError();
		Diagnostics_Write(DVF_ERRORLEVEL, "GetParent failed, code: %i", lRet);
		hr = DVERR_GENERIC;
		goto error_cleanup;
	}
	
	hwndControl = GetDlgItem(hDlg, IDC_TITLE);
	if (hwndControl == NULL)
	{
		// error, log it and bail
		lRet = GetLastError();
		Diagnostics_Write(DVF_ERRORLEVEL, "GetDlgItem failed, code: %i", lRet);
		hr = DVERR_GENERIC;
		goto error_cleanup;
	}
	psinfo->GetTitleFont(&hfTitle);
    (void)::SendMessage(hwndControl, WM_SETFONT, (WPARAM)hfTitle, (LPARAM)TRUE);

	DPF_EXIT();
	return FALSE;

error_cleanup:
	psinfo->GetHWNDParent(&hwndParent);
	DV_DisplayErrorBox(hr, hwndParent);
	psinfo->SetError(hr);
	psinfo->Abort(hDlg, hr);
	DPF_EXIT();
	return FALSE;
}

#undef DPF_MODNAME
#define DPF_MODNAME "CompleteSetActiveHandler"
BOOL CompleteSetActiveHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, CSupervisorInfo* psinfo)
{
	DPF_ENTER();
	
	LONG lRet;
	HWND hwndWizard;
	HWND hwndParent = NULL;
	HANDLE hEvent;
	HRESULT hr;

	// Get the parent window
	hwndWizard = GetParent(hDlg);
	if (hwndWizard == NULL)
	{
		// log it, and return, don't know how to terminate the wizard properly
		// without this handle!
		lRet = GetLastError();
		Diagnostics_Write(DVF_ERRORLEVEL, "GetParent failed, code: %i", lRet);
		hr = DVERR_GENERIC;
		goto error_cleanup;
	}

	// set the HWNDs
	psinfo->SetHWNDWizard(hwndWizard);
	psinfo->SetHWNDDialog(hDlg);
	psinfo->SetHWNDProgress(NULL);
	psinfo->SetHWNDInputPeak(NULL);
	psinfo->SetHWNDOutputPeak(NULL);
	psinfo->SetHWNDInputVolumeSlider(NULL);
	psinfo->SetHWNDOutputVolumeSlider(NULL);

	PropSheet_SetWizButtons(hwndWizard, PSWIZB_FINISH);

	DPF_EXIT();
	return FALSE;

error_cleanup:
	psinfo->GetHWNDParent(&hwndParent);
	DV_DisplayErrorBox(hr, hwndParent);
	psinfo->SetError(hr);
	psinfo->Abort(hDlg, hr);
	DPF_EXIT();
	return FALSE;
}

#undef DPF_MODNAME
#define DPF_MODNAME "CompleteFinishHandler"
BOOL CompleteFinishHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, CSupervisorInfo* psinfo)
{
	DPF_ENTER();

	psinfo->Finish();
	
	DPF_EXIT();
	return FALSE;
}

#undef DPF_MODNAME
#define DPF_MODNAME "CompleteResetHandler"
BOOL CompleteResetHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, CSupervisorInfo* psinfo)
{
	DPF_ENTER();	
	DPF_EXIT();
	return FALSE;
}

#undef DPF_MODNAME
#define DPF_MODNAME "MicTestFailedProc"
INT_PTR CALLBACK MicTestFailedProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
	DPF_ENTER();
	
	LONG lRet;
	BOOL fRet;
	LPNMHDR lpnm;
	
	CSupervisorInfo* psinfo = (CSupervisorInfo*)GetWindowLongPtr(hDlg, GWLP_USERDATA);

	fRet = FALSE;
	switch (message)
	{
	case WM_INITDIALOG :
		MicTestFailedInitDialogHandler(hDlg, message, wParam, lParam, psinfo);
		break;

	case WM_NOTIFY :
		lpnm = (LPNMHDR) lParam;

		switch (lpnm->code)
		{
		case PSN_SETACTIVE : 
			fRet = MicTestFailedSetActiveHandler(hDlg, message, wParam, lParam, psinfo);
			break;

		case PSN_WIZBACK :
			fRet = MicTestFailedBackHandler(hDlg, message, wParam, lParam, psinfo);
			break;

		case PSN_RESET :
			fRet = MicTestFailedResetHandler(hDlg, message, wParam, lParam, psinfo);
			break;

		case PSN_WIZFINISH :
			fRet = MicTestFailedFinishHandler(hDlg, message, wParam, lParam, psinfo);
			break;
			
		default :
			break;
		}
		break;

	default:
		break;
	}

	DPF_EXIT();
	return fRet;
}

#undef DPF_MODNAME
#define DPF_MODNAME "MicTestFailedInitDialogHandler"
BOOL MicTestFailedInitDialogHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, CSupervisorInfo* psinfo)
{
	DPF_ENTER();
	
	LONG lRet;
	HWND hwndWizard = NULL;
	HWND hwndParent = NULL;
	HWND hwndControl;
	HFONT hfTitle;
	HICON hIcon;
	HRESULT hr = DV_OK;

	// Get the shared data from PROPSHEETPAGE lParam value
	// and load it into GWLP_USERDATA
	psinfo = (CSupervisorInfo*)((LPPROPSHEETPAGE)lParam)->lParam;
	SetWindowLongPtr(hDlg, GWLP_USERDATA, (LONG_PTR)psinfo);
	
	// Get the parent window
	hwndWizard = GetParent(hDlg);
	if (hwndWizard == NULL)
	{
		lRet = GetLastError();
		Diagnostics_Write(DVF_ERRORLEVEL, "GetParent failed, code: %i", lRet);
		hr = DVERR_GENERIC;
		goto error_cleanup;
	}
	
	hwndControl = GetDlgItem(hDlg, IDC_TITLE);
	if (hwndControl == NULL)
	{
		// error, log it and bail
		lRet = GetLastError();
		Diagnostics_Write(DVF_ERRORLEVEL, "GetDlgItem failed, code: %i", lRet);
		hr = DVERR_GENERIC;
		goto error_cleanup;
	}
	psinfo->GetTitleFont(&hfTitle);
    (void)::SendMessage(hwndControl, WM_SETFONT, (WPARAM)hfTitle, (LPARAM)TRUE);

	// load the warning icon
	hIcon = LoadIcon(NULL, IDI_WARNING);
	SendDlgItemMessage(hDlg, IDC_WARNINGICON, STM_SETIMAGE, IMAGE_ICON, (LPARAM)hIcon);

	DPF_EXIT();
	return FALSE;

// error block
error_cleanup:
	psinfo->GetHWNDParent(&hwndParent);
	DV_DisplayErrorBox(hr, hwndParent);
	psinfo->SetError(hr);
	psinfo->Abort(hDlg, hr);
	DPF_EXIT();
	return FALSE;
}

#undef DPF_MODNAME
#define DPF_MODNAME "MicTestFailedSetActiveHandler"
BOOL MicTestFailedSetActiveHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, CSupervisorInfo* psinfo)
{
	DPF_ENTER();
	
	LONG lRet;
	HWND hwndWizard;
	HWND hwndParent = NULL;
	HWND hwndPeak;
	HANDLE hEvent;
	HRESULT hr;

	PlaySound( _T("SystemExclamation"), NULL, SND_ASYNC );			

	// Get the parent window
	hwndWizard = GetParent(hDlg);
	if (hwndWizard == NULL)
	{
		// log it, and return, don't know how to terminate the wizard properly
		// without this handle!
		lRet = GetLastError();
		Diagnostics_Write(DVF_ERRORLEVEL, "GetParent failed, code: %i", lRet);
		hr = DVERR_GENERIC;
		goto error_cleanup;
	}

	// set the HWNDs
	psinfo->SetHWNDWizard(hwndWizard);
	psinfo->SetHWNDDialog(hDlg);
	psinfo->SetHWNDProgress(NULL);
	psinfo->SetHWNDInputPeak(NULL);
	psinfo->SetHWNDOutputPeak(NULL);
	psinfo->SetHWNDInputVolumeSlider(NULL);
	psinfo->SetHWNDOutputVolumeSlider(NULL);

	// enable the finish and back buttons
	PropSheet_SetWizButtons(hwndWizard, PSWIZB_BACK|PSWIZB_FINISH);

	DPF_EXIT();
	return FALSE;

error_cleanup:
	psinfo->GetHWNDParent(&hwndParent);
	DV_DisplayErrorBox(hr, hwndParent);
	psinfo->SetError(hr);
	psinfo->Abort(hDlg, hr);
	DPF_EXIT();
	return FALSE;
}

#undef DPF_MODNAME
#define DPF_MODNAME "MicTestFailedRecordStopHandler"
BOOL MicTestFailedRecordStopHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, CSupervisorInfo* psinfo)
{
	DPF_ENTER();
	DPF_EXIT();
	return FALSE;
}

#undef DPF_MODNAME
#define DPF_MODNAME "MicTestFailedBackHandler"
BOOL MicTestFailedBackHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, CSupervisorInfo* psinfo)
{
	DPF_ENTER();

	// go back to the mic test page
	SetWindowLongPtr(hDlg, DWLP_MSGRESULT, IDD_MICTEST);

	DPF_EXIT();
	return TRUE;
}

#undef DPF_MODNAME
#define DPF_MODNAME "MicTestFailedResetHandler"
BOOL MicTestFailedResetHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, CSupervisorInfo* psinfo)
{
	DPF_ENTER();
	DPF_EXIT();
	return FALSE;
}

#undef DPF_MODNAME
#define DPF_MODNAME "MicTestFailedFinishHandler"
BOOL MicTestFailedFinishHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, CSupervisorInfo* psinfo)
{
	DPF_ENTER();

	psinfo->Finish();

	DPF_EXIT();
	return FALSE;
}

#undef DPF_MODNAME
#define DPF_MODNAME "SpeakerTestProc"
INT_PTR CALLBACK SpeakerTestProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
	DPF_ENTER();
	
	LONG lRet;
	BOOL fRet;
	LPNMHDR lpnm;
	
	CSupervisorInfo* psinfo = (CSupervisorInfo*)GetWindowLongPtr(hDlg, GWLP_USERDATA);

	fRet = FALSE;
	switch (message)
	{
	case WM_INITDIALOG :
		SpeakerTestInitDialogHandler(hDlg, message, wParam, lParam, psinfo);
		break;

	case WM_NOTIFY :
		lpnm = (LPNMHDR) lParam;

		switch (lpnm->code)
		{
		case PSN_SETACTIVE : 
			fRet = SpeakerTestSetActiveHandler(hDlg, message, wParam, lParam, psinfo);
			break;

		case PSN_WIZNEXT :
			fRet = SpeakerTestNextHandler(hDlg, message, wParam, lParam, psinfo);
			break;

		case PSN_WIZBACK :
			fRet = SpeakerTestBackHandler(hDlg, message, wParam, lParam, psinfo);
			break;

		case PSN_RESET :
			fRet = SpeakerTestResetHandler(hDlg, message, wParam, lParam, psinfo);
			break;

		default :
			break;
		}
		break;

	case WM_COMMAND:
		switch (LOWORD(wParam))
		{
		case IDC_RECADVANCED:
			fRet = SpeakerTestRecAdvancedHandler(hDlg, message, wParam, lParam, psinfo);
			break;

		case IDC_OUTADVANCED:
			fRet = SpeakerTestOutAdvancedHandler(hDlg, message, wParam, lParam, psinfo);
			break;

		default:
			break;
		}
		break;
		
	case WM_VSCROLL:
		fRet = SpeakerTestVScrollHandler(hDlg, message, wParam, lParam, psinfo);
		break;
		
	default:
		break;
	}

	DPF_ENTER();
	return fRet;
}

#undef DPF_MODNAME
#define DPF_MODNAME "SpeakerTestInitDialogHandler"
BOOL SpeakerTestInitDialogHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, CSupervisorInfo* psinfo)
{
	DPF_ENTER();
	
	LONG lRet;
	HWND hwndWizard = NULL;
	HWND hwndParent = NULL;
	HWND hwndControl;
	HFONT hfBold;
	HWND hwndRecPeak;
	HWND hwndOutPeak;
	HWND hwndRecSlider;
	HWND hwndOutSlider;
	HRESULT hr = DV_OK;
	
	// Get the shared data from PROPSHEETPAGE lParam value
	// and load it into GWLP_USERDATA
	psinfo = (CSupervisorInfo*)((LPPROPSHEETPAGE)lParam)->lParam;
	SetWindowLongPtr(hDlg, GWLP_USERDATA, (LONG_PTR)psinfo);
	
	// Get the parent window
	hwndWizard = GetParent(hDlg);
	if (hwndWizard == NULL)
	{
		lRet = GetLastError();
		Diagnostics_Write(DVF_ERRORLEVEL, "GetParent failed, code: %i", lRet);
		hr = DVERR_GENERIC;
		goto error_cleanup;
	}
	
	hwndControl = GetDlgItem(hDlg, IDC_TITLE);
	if (hwndControl == NULL)
	{
		// error, log it and bail
		lRet = GetLastError();
		Diagnostics_Write(DVF_ERRORLEVEL, "GetDlgItem failed, code: %i", lRet);
		hr = DVERR_GENERIC;
		goto error_cleanup;
	}
	psinfo->GetBoldFont(&hfBold);
    (void)::SendMessage(hwndControl, WM_SETFONT, (WPARAM)hfBold, (LPARAM)TRUE);

	// Init the recording peak meter
	hwndRecPeak = GetDlgItem(hDlg, IDC_RECPEAKMETER);
	if (hwndRecPeak == NULL)
	{
		lRet = GetLastError();
		Diagnostics_Write(DVF_ERRORLEVEL, "GetDlgItem failed, code: %i", lRet);
		hr = DVERR_GENERIC;
		goto error_cleanup;
	}
	SendMessage(hwndRecPeak, PM_SETMIN, 0, 0);
	SendMessage(hwndRecPeak, PM_SETMAX, 0, 99);
	SendMessage(hwndRecPeak, PM_SETCUR, 0, 0);

	// Init the recording volume slider
	hwndRecSlider = GetDlgItem(hDlg, IDC_RECVOL_SLIDER);
	if (hwndRecSlider == NULL)
	{
		lRet = GetLastError();
		Diagnostics_Write(DVF_ERRORLEVEL, "GetDlgItem failed, code: %i", lRet);
		hr = DVERR_GENERIC;
		goto error_cleanup;
	}
	SendMessage(hwndRecSlider, TBM_SETRANGEMIN, 0, DBToAmpFactor(DSBVOLUME_MAX) - DBToAmpFactor(DSBVOLUME_MAX));
	SendMessage(hwndRecSlider, TBM_SETRANGEMAX, 0, DBToAmpFactor(DSBVOLUME_MAX) - DBToAmpFactor(DSBVOLUME_MIN));
	SendMessage(hwndRecSlider, TBM_SETPOS, 0, DBToAmpFactor(DSBVOLUME_MAX) - DBToAmpFactor(DSBVOLUME_MAX));
	SendMessage(hwndRecSlider, TBM_SETTICFREQ, 
		(DBToAmpFactor(DSBVOLUME_MAX) - DBToAmpFactor(DSBVOLUME_MIN))/10, 0);
	SendMessage(hwndRecSlider, TBM_SETLINESIZE, 0,
		(DBToAmpFactor(DSBVOLUME_MAX) - DBToAmpFactor(DSBVOLUME_MIN))/20);
	SendMessage(hwndRecSlider, TBM_SETPAGESIZE, 0,
		(DBToAmpFactor(DSBVOLUME_MAX) - DBToAmpFactor(DSBVOLUME_MIN))/5);

	// Init the playback peak meter
	hwndOutPeak = GetDlgItem(hDlg, IDC_OUTPEAKMETER);
	if (hwndOutPeak == NULL)
	{
		lRet = GetLastError();
		Diagnostics_Write(DVF_ERRORLEVEL, "GetDlgItem failed, code: %i", lRet);
		hr = DVERR_GENERIC;
		goto error_cleanup;
	}
	SendMessage(hwndOutPeak, PM_SETMIN, 0, 0);
	SendMessage(hwndOutPeak, PM_SETMAX, 0, 99);
	SendMessage(hwndOutPeak, PM_SETCUR, 0, 0);

	// Init the playback volume slider
	hwndOutSlider = GetDlgItem(hDlg, IDC_OUTVOL_SLIDER);
	if (hwndOutSlider == NULL)
	{
		lRet = GetLastError();
		Diagnostics_Write(DVF_ERRORLEVEL, "GetDlgItem failed, code: %i", lRet);
		hr = DVERR_GENERIC;
		goto error_cleanup;
	}
	SendMessage(hwndOutSlider, TBM_SETRANGEMIN, 0, DBToAmpFactor(DSBVOLUME_MAX) - DBToAmpFactor(DSBVOLUME_MAX));
	SendMessage(hwndOutSlider, TBM_SETRANGEMAX, 0, DBToAmpFactor(DSBVOLUME_MAX) - DBToAmpFactor(DSBVOLUME_MIN));
	SendMessage(hwndOutSlider, TBM_SETPOS, 0, DBToAmpFactor(DSBVOLUME_MAX) - DBToAmpFactor(DSBVOLUME_MAX));
	SendMessage(hwndOutSlider, TBM_SETTICFREQ, 
		(DBToAmpFactor(DSBVOLUME_MAX) - DBToAmpFactor(DSBVOLUME_MIN))/10, 0);
	SendMessage(hwndOutSlider, TBM_SETLINESIZE, 0,
		(DBToAmpFactor(DSBVOLUME_MAX) - DBToAmpFactor(DSBVOLUME_MIN))/20);
	SendMessage(hwndOutSlider, TBM_SETPAGESIZE, 0,
		(DBToAmpFactor(DSBVOLUME_MAX) - DBToAmpFactor(DSBVOLUME_MIN))/5);

	DPF_EXIT();
	return FALSE;

error_cleanup:
	psinfo->GetHWNDParent(&hwndParent);
	DV_DisplayErrorBox(hr, hwndParent);
	psinfo->SetError(hr);
	psinfo->Abort(hDlg, hr);
	DPF_EXIT();
	return FALSE;
}

#undef DPF_MODNAME
#define DPF_MODNAME "SpeakerTestSetActiveHandler"
BOOL SpeakerTestSetActiveHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, CSupervisorInfo* psinfo)
{
	DPF_ENTER();
	
	LONG lRet;
	HWND hwndWizard = NULL;
	HWND hwndParent = NULL;
	HWND hwndRecPeak;
	HWND hwndOutPeak;
	HWND hwndRecSlider;
	HWND hwndOutSlider;
	HANDLE hEvent;
	HRESULT hr = DV_OK;
	DWORD dwVolume;
	HWND hwndRecAdvanced;

	// Get the parent window
	hwndWizard = GetParent(hDlg);
	if (hwndWizard == NULL)
	{
		lRet = GetLastError();
		Diagnostics_Write(DVF_ERRORLEVEL, "GetParent failed, code: %i", lRet);
		hr = DVERR_GENERIC;
		goto error_cleanup;
	}

	// Reset the recording peak meter
	hwndRecPeak = GetDlgItem(hDlg, IDC_RECPEAKMETER);
	if (hwndRecPeak == NULL)
	{
		lRet = GetLastError();
		Diagnostics_Write(DVF_ERRORLEVEL, "GetDlgItem failed, code: %i", lRet);
		hr = DVERR_GENERIC;
		goto error_cleanup;
	}
	SendMessage(hwndRecPeak, PM_SETCUR, 0, 0);

	// set the recording volume slider to match
	// the recording volume slider from the mic
	// test page.
	hwndRecSlider = GetDlgItem(hDlg, IDC_RECVOL_SLIDER);
	if (hwndRecSlider == NULL)
	{
		lRet = GetLastError();
		Diagnostics_Write(DVF_ERRORLEVEL, "GetDlgItem failed, code: %i", lRet);
		hr = DVERR_GENERIC;
		goto error_cleanup;
	}

	hwndRecAdvanced = GetDlgItem( hDlg, IDC_RECADVANCED );
	if (hwndRecAdvanced == NULL)
	{
		lRet = GetLastError();
		Diagnostics_Write(DVF_ERRORLEVEL, "GetDlgItem failed, code: %i", lRet);
		hr = lRet;
		goto error_cleanup;
	}
	
    DWORD dwDeviceFlags;

    psinfo->GetDeviceFlags( &dwDeviceFlags );

    if( dwDeviceFlags & DVSOUNDCONFIG_NORECVOLAVAILABLE )
    {
        EnableWindow( hwndRecSlider, FALSE );
        EnableWindow( hwndRecAdvanced, FALSE );
    }
    
	LONG lPos;
	psinfo->GetInputVolumeSliderPos(&lPos);
	SendMessage(hwndRecSlider, TBM_SETPOS, 1, lPos);

	// Reset the playback peak meter
	hwndOutPeak = GetDlgItem(hDlg, IDC_OUTPEAKMETER);
	if (hwndOutPeak == NULL)
	{
		lRet = GetLastError();
		Diagnostics_Write(DVF_ERRORLEVEL, "GetDlgItem failed, code: %i", lRet);
		hr = DVERR_GENERIC;
		goto error_cleanup;
	}
	SendMessage(hwndOutPeak, PM_SETCUR, 0, 0);

	// Grey out the playback volume slider - until we come back
	// to fix it
	hwndOutSlider = GetDlgItem(hDlg, IDC_OUTVOL_SLIDER);
	if (hwndOutSlider == NULL)
	{
		lRet = GetLastError();
		Diagnostics_Write(DVF_ERRORLEVEL, "GetDlgItem failed, code: %i", lRet);
		hr = DVERR_GENERIC;
		goto error_cleanup;
	}

	// Get the current waveOut volume and set the slider to that position
	hr = psinfo->GetWaveOutVolume(&dwVolume);
	if (FAILED(hr))
	{
		// couldn't get the volume - set the slider to top
		SendMessage(hwndOutSlider, TBM_SETPOS, 1, SendMessage(hwndOutSlider, TBM_GETRANGEMIN, 0, 0));
		// disable the slider
		EnableWindow(hwndOutSlider, FALSE);
	}
	else
	{
		SendMessage(hwndOutSlider, TBM_SETPOS, 1, SendMessage(hwndOutSlider, TBM_GETRANGEMAX, 0, 0) - dwVolume);
	}

	// set the HWNDs
	psinfo->SetHWNDWizard(hwndWizard);
	psinfo->SetHWNDDialog(hDlg);
	psinfo->SetHWNDProgress(NULL);
	psinfo->SetHWNDInputPeak(hwndRecPeak);
	psinfo->SetHWNDOutputPeak(hwndOutPeak);
	psinfo->SetHWNDInputVolumeSlider(hwndRecSlider);
	psinfo->SetHWNDOutputVolumeSlider(hwndOutSlider);

	// unmute the output
	hr = psinfo->Unmute();
	if (FAILED(hr))
	{
		Diagnostics_Write(DVF_ERRORLEVEL, "Unmute failed, code: %i", hr);
		goto error_cleanup;
	}
	
	// enable the next button
	PropSheet_SetWizButtons(hwndWizard, PSWIZB_BACK|PSWIZB_NEXT);

	DPF_EXIT();
	return FALSE;

error_cleanup:
	psinfo->GetHWNDParent(&hwndParent);
	DV_DisplayErrorBox(hr, hwndParent);
	psinfo->SetError(hr);
	psinfo->Abort(hDlg, hr);
	DPF_EXIT();
	return FALSE;
}

#undef DPF_MODNAME
#define DPF_MODNAME "SpeakerTestNextHandler"
BOOL SpeakerTestNextHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, CSupervisorInfo* psinfo)
{
	DPF_ENTER();
	
	HRESULT hr = DV_OK;
	HWND hwndWizard = NULL;
	HWND hwndParent = NULL;

	// get the parent window
	psinfo->GetHWNDWizard(&hwndWizard);

	// shutdown the loopback test
	hr = psinfo->ShutdownLoopbackThread();
	if (FAILED(hr))
	{
		Diagnostics_Write(DVF_ERRORLEVEL, "ShutdownLoopbackThread failed, code: %i", hr);
		goto error_cleanup;		
	}

	// close any volume controls that are open.
	psinfo->CloseWindowsVolumeControl(TRUE);
	psinfo->CloseWindowsVolumeControl(FALSE);

	// the next page is the completion page
	SetWindowLongPtr(hDlg, DWLP_MSGRESULT, IDD_COMPLETE);

	DPF_EXIT();
	return TRUE;

error_cleanup:
	psinfo->GetHWNDParent(&hwndParent);
	DV_DisplayErrorBox(hr, hwndParent);
	psinfo->SetError(hr);
	psinfo->Abort(hDlg, hr);
	DPF_EXIT();
	return FALSE;
}

#undef DPF_MODNAME
#define DPF_MODNAME "SpeakerTestBackHandler"
BOOL SpeakerTestBackHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, CSupervisorInfo* psinfo)
{
	DPF_ENTER();
	
	HRESULT hr = DV_OK;
	HWND hwndWizard = NULL;
	HWND hwndParent = NULL;

	// get the parent window
	psinfo->GetHWNDWizard(&hwndWizard);

	// shutdown the loopback test, so the mic test
	// page can start fresh.
	hr = psinfo->ShutdownLoopbackThread();
	if (FAILED(hr))
	{
		Diagnostics_Write(DVF_ERRORLEVEL, "ShutdownLoopbackThread failed, hr: %i", hr);
		goto error_cleanup;
	}

	// close the output volume control, if showing
	psinfo->CloseWindowsVolumeControl(FALSE);

	// go back to the mic test page
	SetWindowLongPtr(hDlg, DWLP_MSGRESULT, IDD_MICTEST);

	DPF_EXIT();
	return TRUE;

error_cleanup:
	psinfo->GetHWNDParent(&hwndParent);
	DV_DisplayErrorBox(hr, hwndParent);
	psinfo->SetError(hr);
	psinfo->Abort(hDlg, hr);
	DPF_EXIT();
	return FALSE;
}


#undef DPF_MODNAME
#define DPF_MODNAME "SpeakerTestResetHandler"
BOOL SpeakerTestResetHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, CSupervisorInfo* psinfo)
{
	DPF_ENTER();
	DPF_EXIT();
	return FALSE;
}

#undef DPF_MODNAME
#define DPF_MODNAME "SpeakerTestVScrollHandler"
BOOL SpeakerTestVScrollHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, CSupervisorInfo* psinfo)
{
	DPF_ENTER();

	HWND hwndSlider;
	DWORD dwSliderPos;

	psinfo->GetHWNDInputVolumeSlider(&hwndSlider);
	if (hwndSlider == (HWND)lParam)
	{
		// the user is moving the input slider
		dwSliderPos = (DWORD)SendMessage(hwndSlider, TBM_GETPOS, 0, 0);

		// set the input volume to the user's request
		psinfo->SetRecordVolume(AmpFactorToDB(DBToAmpFactor(DSBVOLUME_MAX)-dwSliderPos));			
	}

	psinfo->GetHWNDOutputVolumeSlider(&hwndSlider);
	if (hwndSlider == (HWND)lParam)
	{
		// the user is moving the output slider
		dwSliderPos = (DWORD) SendMessage(hwndSlider, TBM_GETPOS, 0, 0);

		// set the output volume
		psinfo->SetWaveOutVolume( ((DWORD) SendMessage(hwndSlider, TBM_GETRANGEMAX, 0, 0)) - dwSliderPos);			
	}
	
	DPF_EXIT();
	return FALSE;
}

#undef DPF_MODNAME
#define DPF_MODNAME "SpeakerTestRecAdvancedHandler"
BOOL SpeakerTestRecAdvancedHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, CSupervisorInfo* psinfo)
{
	DPF_ENTER();

	HWND hwndWizard = GetParent(hDlg);
	if (hwndWizard == NULL)
	{
		Diagnostics_Write(DVF_ERRORLEVEL, "GetParent failed");
		DPF_EXIT();
		return FALSE;
	}
	psinfo->LaunchWindowsVolumeControl(hwndWizard, TRUE);

	DPF_EXIT();
	return FALSE;
}

#undef DPF_MODNAME
#define DPF_MODNAME "SpeakerTestOutAdvancedHandler"
BOOL SpeakerTestOutAdvancedHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, CSupervisorInfo* psinfo)
{
	DPF_ENTER();

	HWND hwndWizard = GetParent(hDlg);
	if (hwndWizard == NULL)
	{
		Diagnostics_Write(DVF_ERRORLEVEL, "GetParent failed");
		DPF_EXIT();
		return FALSE;
	}
	psinfo->LaunchWindowsVolumeControl(hwndWizard, FALSE);

	DPF_EXIT();
	return FALSE;
}


#undef DPF_MODNAME
#define DPF_MODNAME "FullDuplexFailedProc"
INT_PTR CALLBACK FullDuplexFailedProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
	DPF_ENTER();
	
	BOOL fRet;	
	LPNMHDR lpnm;
	
	CSupervisorInfo* psinfo = (CSupervisorInfo*)GetWindowLongPtr(hDlg, GWLP_USERDATA);

	fRet = FALSE;
	switch (message)
	{
	case WM_INITDIALOG :
		fRet = FullDuplexFailedInitDialogHandler(hDlg, message, wParam, lParam, psinfo);
		break;

	case WM_NOTIFY :
		lpnm = (LPNMHDR) lParam;

		switch (lpnm->code)
		{
		case PSN_SETACTIVE : 
			fRet = FullDuplexFailedSetActiveHandler(hDlg, message, wParam, lParam, psinfo);
			break;

		case PSN_WIZFINISH :
			fRet = FullDuplexFailedFinishHandler(hDlg, message, wParam, lParam, psinfo);
			break;

		case PSN_WIZBACK :
			fRet = FullDuplexFailedBackHandler(hDlg, message, wParam, lParam, psinfo);
			break;

		case PSN_RESET :
			fRet = FullDuplexFailedResetHandler(hDlg, message, wParam, lParam, psinfo);
			break;

		default :
			break;
		}
		break;

	default:
		break;
	}

	DPF_EXIT();
	return fRet;
}

#undef DPF_MODNAME
#define DPF_MODNAME "FullDuplexFailedInitDialogHandler"
BOOL FullDuplexFailedInitDialogHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, CSupervisorInfo* psinfo)
{
	DPF_ENTER();
	
	LONG lRet;
	HWND hwndWizard = NULL;
	HWND hwndParent = NULL;
	HWND hwndControl;
	HFONT hfTitle;
	HICON hIcon;
	HRESULT hr = DV_OK;

	// Get the shared data from PROPSHEETPAGE lParam value
	// and load it into GWLP_USERDATA
	psinfo = (CSupervisorInfo*)((LPPROPSHEETPAGE)lParam)->lParam;
	SetWindowLongPtr(hDlg, GWLP_USERDATA, (LONG_PTR)psinfo);
	
	// Get the parent window
	hwndWizard = GetParent(hDlg);
	if (hwndWizard == NULL)
	{
		lRet = GetLastError();
		Diagnostics_Write(DVF_ERRORLEVEL, "GetParent failed, code: %i", lRet);
		hr = DVERR_GENERIC;
		goto error_cleanup;
	}
	
	hwndControl = GetDlgItem(hDlg, IDC_TITLE);
	if (hwndControl == NULL)
	{
		// error, log it and bail
		lRet = GetLastError();
		Diagnostics_Write(DVF_ERRORLEVEL, "GetDlgItem failed, code: %i", lRet);
		hr = DVERR_GENERIC;
		goto error_cleanup;
	}
	psinfo->GetTitleFont(&hfTitle);
    (void)::SendMessage(hwndControl, WM_SETFONT, (WPARAM)hfTitle, (LPARAM)TRUE);

	// load the warning icon
	hIcon = LoadIcon(NULL, IDI_WARNING);
	SendDlgItemMessage(hDlg, IDC_WARNINGICON, STM_SETIMAGE, IMAGE_ICON, (LPARAM)hIcon);

	DPF_EXIT();
	return FALSE;

error_cleanup:
	psinfo->GetHWNDParent(&hwndParent);
	DV_DisplayErrorBox(hr, hwndParent);
	psinfo->SetError(hr);
	psinfo->Abort(hDlg, hr);
	DPF_EXIT();
	return FALSE;
}

#undef DPF_MODNAME
#define DPF_MODNAME "FullDuplexFailedSetActiveHandler"
BOOL FullDuplexFailedSetActiveHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, CSupervisorInfo* psinfo)
{
	DPF_ENTER();
	
	LONG lRet;
	HWND hwndWizard;
	HWND hwndParent = NULL;
	HANDLE hEvent;
	HRESULT hr;

	PlaySound( _T("SystemExclamation"), NULL, SND_ASYNC );				

	// Get the parent window
	hwndWizard = GetParent(hDlg);
	if (hwndWizard == NULL)
	{
		lRet = GetLastError();
		Diagnostics_Write(DVF_ERRORLEVEL, "GetParent failed, code: %i", lRet);
		hr = DVERR_GENERIC;
		goto error_cleanup;
	}

	// set the HWNDs
	psinfo->SetHWNDWizard(hwndWizard);
	psinfo->SetHWNDDialog(hDlg);
	psinfo->SetHWNDProgress(NULL);
	psinfo->SetHWNDInputPeak(NULL);
	psinfo->SetHWNDOutputPeak(NULL);
	psinfo->SetHWNDInputVolumeSlider(NULL);
	psinfo->SetHWNDOutputVolumeSlider(NULL);

	PropSheet_SetWizButtons(hwndWizard, PSWIZB_BACK|PSWIZB_FINISH);

	DPF_EXIT();
	return FALSE;

error_cleanup:
	psinfo->GetHWNDParent(&hwndParent);
	DV_DisplayErrorBox(hr, hwndParent);
	psinfo->SetError(hr);
	psinfo->Abort(hDlg, hr);
	DPF_EXIT();
	return FALSE;
}

#undef DPF_MODNAME
#define DPF_MODNAME "FullDuplexFailedFinishHandler"
BOOL FullDuplexFailedFinishHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, CSupervisorInfo* psinfo)
{
	DPF_ENTER();
	
	psinfo->Finish();
	
	DPF_EXIT();
	return FALSE;
}

#undef DPF_MODNAME
#define DPF_MODNAME "FullDuplexFailedResetHandler"
BOOL FullDuplexFailedResetHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, CSupervisorInfo* psinfo)
{
	DPF_ENTER();
	DPF_EXIT();
	return FALSE;
}

#undef DPF_MODNAME
#define DPF_MODNAME "FullDuplexFailedBackHandler"
BOOL FullDuplexFailedBackHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, CSupervisorInfo* psinfo)
{
	DPF_ENTER();

	// go back to the full duplex test page
	SetWindowLongPtr(hDlg, DWLP_MSGRESULT, IDD_FULLDUPLEXTEST);
	
	DPF_EXIT();
	return TRUE;
}


#undef DPF_MODNAME
#define DPF_MODNAME "HalfDuplexFailedProc"
INT_PTR CALLBACK HalfDuplexFailedProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
	DPF_ENTER();
	
	BOOL fRet;	
	LPNMHDR lpnm;
	
	CSupervisorInfo* psinfo = (CSupervisorInfo*)GetWindowLongPtr(hDlg, GWLP_USERDATA);

	fRet = FALSE;
	switch (message)
	{
	case WM_INITDIALOG :
		fRet = HalfDuplexFailedInitDialogHandler(hDlg, message, wParam, lParam, psinfo);
		break;

	case WM_NOTIFY :
		lpnm = (LPNMHDR) lParam;

		switch (lpnm->code)
		{
		case PSN_SETACTIVE : 
			fRet = HalfDuplexFailedSetActiveHandler(hDlg, message, wParam, lParam, psinfo);
			break;

		case PSN_WIZFINISH :
			fRet = HalfDuplexFailedFinishHandler(hDlg, message, wParam, lParam, psinfo);
			break;

		case PSN_WIZBACK :
			fRet = HalfDuplexFailedBackHandler(hDlg, message, wParam, lParam, psinfo);
			break;
			
		case PSN_RESET :
			fRet = HalfDuplexFailedResetHandler(hDlg, message, wParam, lParam, psinfo);
			break;

		default :
			break;
		}
		break;

	default:
		break;
	}

	DPF_EXIT();
	return fRet;
}

#undef DPF_MODNAME
#define DPF_MODNAME "HalfDuplexFailedInitDialogHandler"
BOOL HalfDuplexFailedInitDialogHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, CSupervisorInfo* psinfo)
{
	DPF_ENTER();
	
	LONG lRet;
	HWND hwndWizard = NULL;
	HWND hwndParent = NULL;
	HWND hwndControl;
	HFONT hfTitle;
	HICON hIcon;
	HRESULT hr = DV_OK;

	// Get the shared data from PROPSHEETPAGE lParam value
	// and load it into GWLP_USERDATA
	psinfo = (CSupervisorInfo*)((LPPROPSHEETPAGE)lParam)->lParam;
	SetWindowLongPtr(hDlg, GWLP_USERDATA, (LONG_PTR)psinfo);
	
	// Get the parent window
	hwndWizard = GetParent(hDlg);
	if (hwndWizard == NULL)
	{
		lRet = GetLastError();
		Diagnostics_Write(DVF_ERRORLEVEL, "GetParent failed, code: %i", lRet);
		hr = DVERR_GENERIC;
		goto error_cleanup;
	}
	
	hwndControl = GetDlgItem(hDlg, IDC_TITLE);
	if (hwndControl == NULL)
	{
		// error, log it and bail
		lRet = GetLastError();
		Diagnostics_Write(DVF_ERRORLEVEL, "GetDlgItem failed, code: %i", lRet);
		hr = DVERR_GENERIC;
		goto error_cleanup;
	}
	psinfo->GetTitleFont(&hfTitle);
    (void)::SendMessage(hwndControl, WM_SETFONT, (WPARAM)hfTitle, (LPARAM)TRUE);

	// load the warning icon
	hIcon = LoadIcon(NULL, IDI_WARNING);
	SendDlgItemMessage(hDlg, IDC_WARNINGICON, STM_SETIMAGE, IMAGE_ICON, (LPARAM)hIcon);

	DPF_EXIT();
	return FALSE;

error_cleanup:
	psinfo->GetHWNDParent(&hwndParent);
	DV_DisplayErrorBox(hr, hwndParent);
	psinfo->SetError(hr);
	psinfo->Abort(hDlg, hr);
	DPF_EXIT();
	return FALSE;
}

#undef DPF_MODNAME
#define DPF_MODNAME "HalfDuplexFailedSetActiveHandler"
BOOL HalfDuplexFailedSetActiveHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, CSupervisorInfo* psinfo)
{
	DPF_ENTER();
	
	LONG lRet;
	HWND hwndWizard;
	HWND hwndParent = NULL;
	HANDLE hEvent;
	HRESULT hr;

	PlaySound( _T("SystemExclamation"), NULL, SND_ASYNC );			

	// Get the parent window
	hwndWizard = GetParent(hDlg);
	if (hwndWizard == NULL)
	{
		lRet = GetLastError();
		Diagnostics_Write(DVF_ERRORLEVEL, "GetParent failed, code: %i", lRet);
		hr = DVERR_GENERIC;
		goto error_cleanup;
	}

	// set the HWNDs
	psinfo->SetHWNDWizard(hwndWizard);
	psinfo->SetHWNDDialog(hDlg);
	psinfo->SetHWNDProgress(NULL);
	psinfo->SetHWNDInputPeak(NULL);
	psinfo->SetHWNDOutputPeak(NULL);
	psinfo->SetHWNDInputVolumeSlider(NULL);
	psinfo->SetHWNDOutputVolumeSlider(NULL);

	PropSheet_SetWizButtons(hwndWizard, PSWIZB_BACK|PSWIZB_FINISH);

	DPF_EXIT();
	return FALSE;

error_cleanup:
	psinfo->GetHWNDParent(&hwndParent);
	DV_DisplayErrorBox(hr, hwndParent);
	psinfo->SetError(hr);
	psinfo->Abort(hDlg, hr);
	DPF_EXIT();
	return FALSE;
}

#undef DPF_MODNAME
#define DPF_MODNAME "HalfDuplexFailedFinishHandler"
BOOL HalfDuplexFailedFinishHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, CSupervisorInfo* psinfo)
{
	DPF_ENTER();
	
	psinfo->Finish();
	
	DPF_EXIT();
	return FALSE;
}

#undef DPF_MODNAME
#define DPF_MODNAME "HalfDuplexFailedResetHandler"
BOOL HalfDuplexFailedResetHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, CSupervisorInfo* psinfo)
{
	DPF_ENTER();
	DPF_EXIT();
	return FALSE;
}

#undef DPF_MODNAME
#define DPF_MODNAME "HalfDuplexFailedBackHandler"
BOOL HalfDuplexFailedBackHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, CSupervisorInfo* psinfo)
{
	DPF_ENTER();

	// go back to the full duplex test page
	SetWindowLongPtr(hDlg, DWLP_MSGRESULT, IDD_FULLDUPLEXTEST);
	
	DPF_EXIT();
	return TRUE;
}