You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
593 lines
14 KiB
593 lines
14 KiB
/////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Module: rdpsnd.c
|
|
//
|
|
// Purpose: User-mode audio driver for terminal server
|
|
// audio redirection
|
|
//
|
|
// Copyright(C) Microsoft Corporation 2000
|
|
//
|
|
// History: 4-10-2000 vladimis [created]
|
|
//
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
#include "rdpsnd.h"
|
|
#include <stdio.h>
|
|
#include <aclapi.h>
|
|
#include <psapi.h>
|
|
|
|
#define WINLOGON_EXE L"\\??\\%SystemRoot%\\system32\\winlogon.exe"
|
|
|
|
const CHAR *ALV = "TSSND::ALV - ";
|
|
const CHAR *INF = "TSSND::INF - ";
|
|
const CHAR *WRN = "TSSND::WRN - ";
|
|
const CHAR *ERR = "TSSND::ERR - ";
|
|
const CHAR *FATAL = "TSSND::FATAL - ";
|
|
|
|
HANDLE g_hHeap = NULL;
|
|
|
|
HANDLE g_hMixerThread = NULL;
|
|
|
|
HINSTANCE g_hDllInst = NULL;
|
|
|
|
BOOL g_bPersonalTS = FALSE;
|
|
|
|
BOOL bInitializedSuccessfully = FALSE;
|
|
|
|
/*
|
|
* Function:
|
|
* DllInstanceInit
|
|
*
|
|
* Description:
|
|
* Dll main enrty point
|
|
*
|
|
*/
|
|
BOOL
|
|
DllInstanceInit(
|
|
HINSTANCE hDllInst,
|
|
DWORD dwReason,
|
|
LPVOID fImpLoad
|
|
)
|
|
{
|
|
BOOL rv = FALSE;
|
|
|
|
if (DLL_PROCESS_ATTACH == dwReason)
|
|
{
|
|
TRC(ALV, "DLL_PROCESS_ATTACH\n");
|
|
TSHEAPINIT;
|
|
|
|
if (!TSISHEAPOK)
|
|
{
|
|
TRC(FATAL, "DllInstanceInit: can't create heap\n");
|
|
goto exitpt;
|
|
}
|
|
|
|
__try {
|
|
InitializeCriticalSection(&g_cs);
|
|
rv = TRUE;
|
|
}
|
|
__except (EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
rv = FALSE;
|
|
}
|
|
|
|
if (!rv)
|
|
{
|
|
TRC(FATAL, "DllInstanceInit: can't initialize critical section\n");
|
|
goto exitpt;
|
|
}
|
|
|
|
DisableThreadLibraryCalls(hDllInst);
|
|
g_hDllInst = hDllInst;
|
|
|
|
rv = TRUE;
|
|
|
|
} else if (DLL_PROCESS_DETACH == dwReason)
|
|
{
|
|
TRC(ALV, "DLL_PROCESS_DETACH\n");
|
|
TSHEAPDESTROY;
|
|
rv = TRUE;
|
|
}
|
|
|
|
exitpt:
|
|
return rv;
|
|
}
|
|
|
|
BOOL
|
|
IsPersonalTerminalServicesEnabled(
|
|
VOID
|
|
)
|
|
{
|
|
BOOL fRet;
|
|
DWORDLONG dwlConditionMask;
|
|
OSVERSIONINFOEX osVersionInfo;
|
|
|
|
RtlZeroMemory(&osVersionInfo, sizeof(OSVERSIONINFOEX));
|
|
osVersionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
|
|
osVersionInfo.wProductType = VER_NT_WORKSTATION;
|
|
osVersionInfo.wSuiteMask = VER_SUITE_SINGLEUSERTS;
|
|
|
|
dwlConditionMask = 0;
|
|
VER_SET_CONDITION(dwlConditionMask, VER_PRODUCT_TYPE, VER_EQUAL);
|
|
VER_SET_CONDITION(dwlConditionMask, VER_SUITENAME, VER_OR);
|
|
|
|
fRet = VerifyVersionInfo(
|
|
&osVersionInfo,
|
|
VER_PRODUCT_TYPE | VER_SUITENAME,
|
|
dwlConditionMask
|
|
);
|
|
|
|
return(fRet);
|
|
}
|
|
|
|
BOOL
|
|
RunningInWinlogon()
|
|
{
|
|
WCHAR szWinlogonOrg[128];
|
|
WCHAR szFile[128];
|
|
static BOOL s_bWinlogonChecked = FALSE;
|
|
static BOOL s_bWinlogonResult = FALSE;
|
|
|
|
if ( s_bWinlogonChecked )
|
|
{
|
|
goto exitpt;
|
|
}
|
|
|
|
szWinlogonOrg[0] = 0;
|
|
if (!ExpandEnvironmentStrings( WINLOGON_EXE, szWinlogonOrg, RTL_NUMBER_OF( szWinlogonOrg )))
|
|
{
|
|
TRC( ERR, "Failed to get winlogon path: GetLastError=%d\n", GetLastError());
|
|
goto exitpt;
|
|
}
|
|
|
|
if ( !GetModuleFileNameEx(
|
|
GetCurrentProcess(),
|
|
GetModuleHandle( NULL ),
|
|
szFile,
|
|
RTL_NUMBER_OF( szFile )))
|
|
{
|
|
TRC( ERR, "GetModuleFileNameEx failed: GetLastError=%d\n", GetLastError());
|
|
goto exitpt;
|
|
}
|
|
|
|
s_bWinlogonChecked = TRUE;
|
|
|
|
s_bWinlogonResult = ( 0 == _wcsicmp( szFile, szWinlogonOrg ));
|
|
|
|
exitpt:
|
|
return s_bWinlogonResult;
|
|
}
|
|
|
|
HANDLE
|
|
_CreateInitEvent(
|
|
VOID
|
|
)
|
|
{
|
|
SECURITY_ATTRIBUTES SA;
|
|
PSECURITY_DESCRIPTOR pSD = NULL;
|
|
EXPLICIT_ACCESS ea[2];
|
|
SID_IDENTIFIER_AUTHORITY siaWorld = SECURITY_WORLD_SID_AUTHORITY;
|
|
SID_IDENTIFIER_AUTHORITY siaNT = SECURITY_NT_AUTHORITY;
|
|
PSID pSidWorld = NULL;
|
|
PSID pSidNT = NULL;
|
|
PACL pNewDAcl = NULL;
|
|
DWORD dwres;
|
|
HANDLE hWaitToInit = NULL;
|
|
|
|
//
|
|
// wait, before creating this event, check if we are running inside winlogon
|
|
// only then create this event, because only then we have to delay
|
|
// audio rendering of the logon sound (aka "TADA")
|
|
//
|
|
if ( !RunningInWinlogon() )
|
|
{
|
|
goto exitpt;
|
|
} else {
|
|
TRC( INF, "running in winlogon, delayed audio rendering\n" );
|
|
}
|
|
|
|
pSD = (PSECURITY_DESCRIPTOR) TSMALLOC(SECURITY_DESCRIPTOR_MIN_LENGTH);
|
|
if ( NULL == pSD )
|
|
goto exitpt;
|
|
|
|
if ( !AllocateAndInitializeSid(
|
|
&siaWorld, 1, SECURITY_WORLD_RID,
|
|
0, 0, 0, 0, 0, 0, 0, &pSidWorld))
|
|
{
|
|
goto exitpt;
|
|
}
|
|
|
|
if ( !AllocateAndInitializeSid(
|
|
&siaNT, 1, SECURITY_LOCAL_SYSTEM_RID,
|
|
0, 0, 0, 0, 0, 0, 0, &pSidNT ))
|
|
{
|
|
goto exitpt;
|
|
}
|
|
|
|
ZeroMemory(ea, sizeof(ea));
|
|
ea[0].grfAccessPermissions = EVENT_MODIFY_STATE;
|
|
ea[0].grfAccessMode = GRANT_ACCESS;
|
|
ea[0].Trustee.TrusteeForm = TRUSTEE_IS_SID;
|
|
ea[0].Trustee.ptstrName = (LPTSTR)pSidWorld;
|
|
ea[1].grfAccessPermissions = GENERIC_ALL;
|
|
ea[1].grfAccessMode = GRANT_ACCESS;
|
|
ea[1].Trustee.TrusteeForm = TRUSTEE_IS_SID;
|
|
ea[1].Trustee.ptstrName = (LPTSTR)pSidNT;
|
|
|
|
dwres = SetEntriesInAcl(2, ea, NULL, &pNewDAcl );
|
|
if ( ERROR_SUCCESS != dwres )
|
|
{
|
|
goto exitpt;
|
|
}
|
|
|
|
if (!InitializeSecurityDescriptor(pSD,
|
|
SECURITY_DESCRIPTOR_REVISION))
|
|
goto exitpt;
|
|
|
|
if (!SetSecurityDescriptorDacl(pSD,
|
|
TRUE, // specifying a disc. ACL
|
|
pNewDAcl,
|
|
FALSE)) // not a default disc. ACL
|
|
goto exitpt;
|
|
|
|
SA.nLength = sizeof( SA );
|
|
SA.lpSecurityDescriptor = pSD;
|
|
SA.bInheritHandle = FALSE;
|
|
|
|
hWaitToInit = CreateEvent( &SA,
|
|
FALSE,
|
|
FALSE,
|
|
TSSND_WAITTOINIT );
|
|
if ( NULL == hWaitToInit )
|
|
{
|
|
TRC( ALV, "Failed to create WaitToInit event:%d\n",
|
|
GetLastError());
|
|
} else if ( ERROR_ALREADY_EXISTS == GetLastError() )
|
|
{
|
|
CloseHandle( hWaitToInit );
|
|
TRC( ALV, "Init event already exists\n" );
|
|
hWaitToInit = NULL;
|
|
}
|
|
|
|
exitpt:
|
|
|
|
if ( NULL != pNewDAcl )
|
|
{
|
|
LocalFree( pNewDAcl );
|
|
}
|
|
|
|
if ( NULL != pSidNT )
|
|
{
|
|
LocalFree( pSidNT );
|
|
}
|
|
|
|
if ( NULL != pSidWorld )
|
|
{
|
|
LocalFree( pSidWorld );
|
|
}
|
|
|
|
if ( NULL != pSD )
|
|
{
|
|
TSFREE( pSD );
|
|
}
|
|
|
|
return hWaitToInit;
|
|
}
|
|
|
|
/*
|
|
* Function:
|
|
* drvEnable
|
|
*
|
|
* Description:
|
|
* Initializes the driver
|
|
*
|
|
*/
|
|
LRESULT
|
|
drvEnable(
|
|
VOID
|
|
)
|
|
{
|
|
LRESULT rv = 1;
|
|
|
|
EnterCriticalSection(&g_cs);
|
|
|
|
if (bInitializedSuccessfully)
|
|
{
|
|
rv = 1;
|
|
goto exitpt;
|
|
}
|
|
|
|
if ( NULL == g_hDataReadyEvent )
|
|
g_hDataReadyEvent = OpenEvent(EVENT_ALL_ACCESS,
|
|
FALSE,
|
|
TSSND_DATAREADYEVENT);
|
|
|
|
if (NULL == g_hDataReadyEvent)
|
|
{
|
|
TRC(ALV, "DRV_ENABLE: can't open %S: %d\n",
|
|
TSSND_DATAREADYEVENT,
|
|
GetLastError());
|
|
|
|
rv = 0;
|
|
}
|
|
|
|
if ( NULL == g_hStreamIsEmptyEvent )
|
|
g_hStreamIsEmptyEvent = OpenEvent(EVENT_ALL_ACCESS,
|
|
FALSE,
|
|
TSSND_STREAMISEMPTYEVENT);
|
|
if (NULL == g_hDataReadyEvent)
|
|
{
|
|
TRC(ALV, "DRV_ENABLE: can't open %S: %d\n",
|
|
TSSND_STREAMISEMPTYEVENT,
|
|
GetLastError());
|
|
rv = 0;
|
|
}
|
|
|
|
if ( NULL == g_hStreamMutex )
|
|
g_hStreamMutex = OpenMutex(SYNCHRONIZE,
|
|
FALSE,
|
|
TSSND_STREAMMUTEX);
|
|
if (NULL == g_hDataReadyEvent)
|
|
{
|
|
TRC(ALV, "DRV_ENABLE: can't open %S: %d\n",
|
|
TSSND_STREAMMUTEX,
|
|
GetLastError());
|
|
rv = 0;
|
|
}
|
|
|
|
if ( NULL == g_hMixerEvent )
|
|
g_hMixerEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
|
|
if (NULL == g_hMixerEvent)
|
|
{
|
|
TRC(ERR, "DRV_ENABLE: can't create mixer event: %d\n",
|
|
GetLastError());
|
|
rv = 0;
|
|
}
|
|
|
|
if ( NULL == g_hStream )
|
|
g_hStream = OpenFileMapping(
|
|
FILE_MAP_ALL_ACCESS,
|
|
FALSE,
|
|
TSSND_STREAMNAME
|
|
);
|
|
|
|
if (NULL == g_hStream)
|
|
{
|
|
TRC(ALV, "Can't open the stream mapping\n");
|
|
rv = 0;
|
|
}
|
|
|
|
if ( (NULL == g_Stream) && (NULL != g_hStream) )
|
|
g_Stream = MapViewOfFile(
|
|
g_hStream,
|
|
FILE_MAP_ALL_ACCESS,
|
|
0, 0, // offset
|
|
sizeof(*g_Stream)
|
|
);
|
|
|
|
if (NULL == g_Stream)
|
|
{
|
|
TRC(ALV, "drvEnale: "
|
|
"can't map the stream view: %d\n",
|
|
GetLastError());
|
|
rv = 0;
|
|
}
|
|
|
|
g_bPersonalTS = IsPersonalTerminalServicesEnabled();
|
|
|
|
if ( 1 == rv &&
|
|
NULL != g_Stream &&
|
|
0 != ( g_Stream->dwSoundCaps & TSSNDCAPS_INITIALIZED ))
|
|
{
|
|
rv = _EnableMixerThread();
|
|
}
|
|
|
|
exitpt:
|
|
|
|
if (0 != rv)
|
|
{
|
|
bInitializedSuccessfully = TRUE;
|
|
}
|
|
|
|
//
|
|
// waiting for initialization ?
|
|
//
|
|
if (( 0 == rv ||
|
|
NULL == g_Stream ||
|
|
0 == ( g_Stream->dwSoundCaps & TSSNDCAPS_INITIALIZED )) &&
|
|
!AudioRedirDisabled() &&
|
|
NULL == g_hWaitToInitialize )
|
|
{
|
|
g_hWaitToInitialize = _CreateInitEvent();
|
|
}
|
|
|
|
LeaveCriticalSection(&g_cs);
|
|
|
|
return rv;
|
|
}
|
|
|
|
/*
|
|
* Function:
|
|
* drvDisable
|
|
*
|
|
* Description:
|
|
* Driver cleanup
|
|
*
|
|
*/
|
|
LRESULT
|
|
drvDisable(
|
|
VOID
|
|
)
|
|
{
|
|
HANDLE hStreamMutex;
|
|
|
|
EnterCriticalSection(&g_cs);
|
|
|
|
hStreamMutex = g_hStreamMutex;
|
|
|
|
_waveAcquireStream();
|
|
|
|
TRC( INF, "drvDisable PID(0x%x)\n", GetCurrentProcessId() );
|
|
|
|
if (NULL == g_hMixerThread ||
|
|
NULL == g_hMixerEvent)
|
|
goto exitpt;
|
|
//
|
|
// Disable the mixer
|
|
//
|
|
g_bMixerRunning = FALSE;
|
|
|
|
//
|
|
// Kick the mixer thread, so it exits
|
|
//
|
|
SetEvent(g_hMixerEvent);
|
|
|
|
WaitForSingleObject(g_hMixerThread, INFINITE);
|
|
|
|
exitpt:
|
|
|
|
if ( NULL != g_hWaitToInitialize )
|
|
{
|
|
CloseHandle( g_hWaitToInitialize );
|
|
}
|
|
|
|
if (NULL != g_hDataReadyEvent)
|
|
CloseHandle(g_hDataReadyEvent);
|
|
|
|
if (NULL != g_hStreamIsEmptyEvent)
|
|
CloseHandle(g_hStreamIsEmptyEvent);
|
|
|
|
if (NULL != g_hMixerEvent)
|
|
CloseHandle(g_hMixerEvent);
|
|
|
|
if (NULL != g_Stream)
|
|
UnmapViewOfFile(g_Stream);
|
|
|
|
if (NULL != g_hStream)
|
|
CloseHandle(g_hStream);
|
|
|
|
if (NULL != g_hMixerThread)
|
|
CloseHandle(g_hMixerThread);
|
|
|
|
g_hWaitToInitialize = NULL;
|
|
g_hDataReadyEvent = NULL;
|
|
g_hStreamIsEmptyEvent = NULL;
|
|
g_hMixerEvent = NULL;
|
|
g_hStreamMutex = NULL;
|
|
g_Stream = NULL;
|
|
g_hStream = NULL;
|
|
g_hMixerThread = NULL;
|
|
g_bMixerRunning = FALSE;
|
|
|
|
bInitializedSuccessfully = FALSE;
|
|
|
|
if (NULL != hStreamMutex)
|
|
CloseHandle(hStreamMutex); // this will release the stream
|
|
|
|
LeaveCriticalSection(&g_cs);
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* Function:
|
|
* DriverProc
|
|
*
|
|
* Description:
|
|
* Driver main entry point
|
|
*
|
|
*/
|
|
LRESULT
|
|
DriverProc(
|
|
DWORD_PTR dwDriverID,
|
|
HANDLE hDriver,
|
|
UINT uiMessage,
|
|
LPARAM lParam1,
|
|
LPARAM lParam2
|
|
)
|
|
{
|
|
LRESULT rv = 0;
|
|
|
|
// Here, we don't do anything but trace and call DefDriverProc
|
|
//
|
|
switch (uiMessage)
|
|
{
|
|
case DRV_LOAD:
|
|
TRC(ALV, "DRV_LOAD\n");
|
|
rv = 1;
|
|
break;
|
|
|
|
case DRV_FREE:
|
|
TRC(ALV, "DRV_FREE\n");
|
|
|
|
rv = 1;
|
|
break;
|
|
|
|
case DRV_OPEN:
|
|
TRC(ALV, "DRV_OPEN\n");
|
|
rv = 1;
|
|
break;
|
|
|
|
case DRV_CLOSE:
|
|
TRC(ALV, "DRV_CLOSE\n");
|
|
rv = 1;
|
|
break;
|
|
|
|
case DRV_ENABLE:
|
|
TRC(ALV, "DRV_ENABLE\n");
|
|
rv = 1;
|
|
break;
|
|
|
|
case DRV_DISABLE:
|
|
TRC(ALV, "DRV_DISABLE\n");
|
|
rv = drvDisable();
|
|
break;
|
|
|
|
case DRV_QUERYCONFIGURE:
|
|
TRC(ALV, "DRV_QUERYCONFIGURE\n");
|
|
rv = 0;
|
|
break;
|
|
|
|
case DRV_CONFIGURE:
|
|
TRC(ALV, "DRV_CONFIGURE\n");
|
|
rv = 0;
|
|
break;
|
|
|
|
default:
|
|
rv = DefDriverProc(
|
|
dwDriverID,
|
|
hDriver,
|
|
uiMessage,
|
|
lParam1,
|
|
lParam2
|
|
);
|
|
TRC(ALV, "DRV_UNKNOWN(%d): DefDriverProc returned %d\n",
|
|
uiMessage, rv);
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
VOID
|
|
_cdecl
|
|
_DebugMessage(
|
|
LPCSTR szLevel,
|
|
LPCSTR szFormat,
|
|
...
|
|
)
|
|
{
|
|
CHAR szBuffer[256];
|
|
va_list arglist;
|
|
|
|
if (ALV == szLevel)
|
|
return;
|
|
|
|
va_start (arglist, szFormat);
|
|
_vsnprintf (szBuffer, RTL_NUMBER_OF(szBuffer), szFormat, arglist);
|
|
va_end (arglist);
|
|
szBuffer[ RTL_NUMBER_OF( szBuffer ) - 1 ] = 0;
|
|
|
|
OutputDebugStringA(szLevel);
|
|
OutputDebugStringA(szBuffer);
|
|
}
|