Windows-Server-2003/multimedia/directx/dplay/dplay4/dplaysvr/dplaysvr.c

907 lines
21 KiB
C

/*==========================================================================
*
* Copyright (C) 1995-1997 Microsoft Corporation. All Rights Reserved.
*
* File: dplaysvr.c
* Content: dplay winsock shared .exe - allows multiple apps to share
* a single winsock port
* History:
* Date By Reason
* ==== == ======
* 2/10/97 andyco created it from ddhelp
* 29-jan-98 sohailm added support for stream enum sessions
* 1/12/2000 aarono added support for rsip
* 11/29/2000 aarono B#228292: prefix bug initialize path in WinMain()
*
***************************************************************************/
#ifdef WINNT
#ifdef DBG
#undef DEBUG
#define DEBUG
#endif
#endif
#include <windows.h>
#include "dplaysvr.h"
#include "newdpf.h"
#include "memalloc.h"
#include "dphelp.h"
#if USE_RSIP
#include "rsip.h"
#elif USE_NATHELP
#include "dpnathlp.h"
#endif
#if USE_RSIP
BOOL bRsip;
#endif
#if USE_NATHELP
extern BOOL natGetCapsUpdate(VOID);
extern BOOL natInit(VOID);
extern VOID natFini(VOID);
extern HRESULT natRegisterUDPPort(DWORD port);
extern IDirectPlayNATHelp *g_pINatHelp;
#endif
HANDLE hInstApp;
BOOL bNoCallbacks;
CRITICAL_SECTION gcsCritSection; // the crit section we take in winmain
// this is a global so dphelp can take it before
// forwarding enum requests that come in on its
// receive thread (manbugs 3907)
int gnCSCount; // dplaysvr lock count
/*
* Externs
*/
extern RECEIVELIST gReceiveList;
extern FDS gReadfds;
// we watch every dplay process so when it exits we
// make sure it cleaned up...
typedef struct _PROCESSDATA
{
struct _PROCESSDATA *link;
DWORD pid;
} PROCESSDATA, *LPPROCESSDATA;
LPPROCESSDATA lpProcessList; // list of all processes that are registered
// with us
//**********************************************************************
// Globals
//**********************************************************************
BOOL g_fDaclInited = FALSE;
SECURITY_ATTRIBUTES g_sa;
BYTE g_abSD[SECURITY_DESCRIPTOR_MIN_LENGTH];
PSECURITY_ATTRIBUTES g_psa = NULL;
PACL g_pEveryoneACL = NULL;
/*
* ThreadProc
*
* Open a process and wait for it to terminate
*/
DWORD WINAPI ThreadProc( LPVOID *pdata )
{
HANDLE hproc;
DWORD rc;
LPPROCESSDATA ppd;
LPPROCESSDATA curr;
LPPROCESSDATA prev;
PROCESSDATA pd;
DPHELPDATA hd;
ppd = (LPPROCESSDATA) pdata;
/*
* get a handle to the process that attached to DDRAW
*/
DPF( 2, "Watchdog thread started for pid %08lx", ppd->pid );
hproc = OpenProcess( PROCESS_QUERY_INFORMATION | SYNCHRONIZE,
FALSE, ppd->pid );
if( hproc == NULL )
{
DPF( 1, "OpenProcess for %08lx failed!", ppd->pid );
ExitThread( 0 );
}
/*
* wait for process to die
*/
rc = WaitForSingleObject( hproc, INFINITE );
if( rc == WAIT_FAILED )
{
DPF( 1, "Wait for process %08lx failed", ppd->pid );
CloseHandle( hproc );
ExitThread( 0 );
}
/*
* remove process from the list of watched processes
*/
ENTER_DPLAYSVR();
pd = *ppd;
curr = lpProcessList;
prev = NULL;
while( curr != NULL )
{
if( curr == ppd )
{
if( prev == NULL )
{
lpProcessList = curr->link;
}
else
{
prev->link = curr->link;
}
DPF( 2, "PID %08lx removed from list", ppd->pid );
MemFree( curr );
break;
}
prev = curr;
curr = curr->link;
}
if( bNoCallbacks )
{
DPF( 1, "No callbacks allowed: leaving thread early" );
LEAVE_DPLAYSVR();
CloseHandle( hproc );
ExitThread( 0 );
}
// clean up!
memset(&hd,0,sizeof(hd));
hd.pid = pd.pid;
DPlayHelp_DeleteServer(&hd,TRUE);
LEAVE_DPLAYSVR();
CloseHandle( hproc );
ExitThread( 0 );
return 0;
} /* ThreadProc */
/*
* MainWndProc
*/
LONG_PTR __stdcall MainWndProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam )
{
switch(message)
{
case WM_ENDSESSION:
/*
* shoot ourselves in the head
*/
if( lParam == FALSE )
{
DPF( 3, "WM_ENDSESSION" );
ENTER_DPLAYSVR();
DPF( 1, "Setting NO CALLBACKS" );
bNoCallbacks = TRUE;
LEAVE_DPLAYSVR();
}
else
{
DPF( 3, "User logging off" );
}
break;
}
return DefWindowProc(hWnd, message, wParam, lParam);
} /* MainWndProc */
/*
* WindowThreadProc
*/
void WindowThreadProc( LPVOID pdata )
{
static char szClassName[] = "DPlayHelpWndClass";
WNDCLASS cls;
MSG msg;
HWND hwnd;
/*
* build class and create window
*/
cls.lpszClassName = szClassName;
cls.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
cls.hInstance = hInstApp;
cls.hIcon = NULL;
cls.hCursor = NULL;
cls.lpszMenuName = NULL;
cls.style = 0;
cls.lpfnWndProc = MainWndProc;
cls.cbWndExtra = 0;
cls.cbClsExtra = 0;
if( !RegisterClass( &cls ) )
{
DPF( 1, "RegisterClass FAILED!" );
ExitThread( 0 );
}
hwnd = CreateWindow( szClassName, szClassName,
WS_POPUP, 0, 0, 0, 0, NULL, NULL, hInstApp, NULL);
if( hwnd == NULL )
{
DPF( 1, "No monitor window!" );
ExitThread( 0 );
}
/*
* pump the messages
*/
while( GetMessage( &msg, NULL, 0, 0 ) )
{
TranslateMessage( &msg );
DispatchMessage( &msg );
}
DPF( 1, "Exiting WindowThreadProc" );
ExitThread( 1 );
} /* WindowThreadProc */
//
// called by by DPlayHelp_AddServer when we get a new process attached.
// we wait for the process to go away, and then make sure it cleaned
// all its registered servers up.
//
void WatchNewPid(LPDPHELPDATA phd)
{
LPPROCESSDATA ppd;
BOOL found;
DWORD tid;
DPF( 1, "watching new pid" );
ENTER_DPLAYSVR();
ppd = lpProcessList;
found = FALSE;
while( ppd != NULL )
{
if( ppd->pid == phd->pid )
{
DPF( 2, "Have thread for process %08lx already", phd->pid );
found = TRUE;
break;
}
ppd = ppd->link;
}
/*
* couldn't find anyone waiting on this process, so create
* a brand spanking new thread
*/
if( !found )
{
DPF( 2, "Allocating new thread for process %08lx",phd->pid );
ppd = MemAlloc( sizeof( PROCESSDATA ) );
if( ppd != NULL )
{
HANDLE h;
ppd->link = lpProcessList;
lpProcessList = ppd;
ppd->pid = phd->pid;
h = CreateThread(NULL,
0,
(LPTHREAD_START_ROUTINE) ThreadProc,
(LPVOID)ppd,
0,
(LPDWORD)&tid);
if( h != NULL )
{
DPF( 2, "Thread %08lx created",tid);
CloseHandle( h );
}
else
{
#ifdef DEBUG
DPF( 0, "COULD NOT CREATE HELPER THREAD FOR PID %08lx", phd->pid );
DebugBreak(); //_asm int 3;
#endif
}
}
else
{
#ifdef DEBUG
DPF( 0, "OUT OF MEMORY CREATING HELPER THREAD FOR PID %08lx", phd->pid );
DebugBreak(); //_asm int 3;
#endif
}
}
LEAVE_DPLAYSVR();
} // WatchNewPid
typedef DWORD (WINAPI *PFNREGISTERSERVICE)(DWORD,DWORD);
// nt's winbase.h doesn't have these constants - we need them
// so we can compile. taken from \proj\dev\inc\winbase.h
#ifndef RSP_UNREGISTER_SERVICE
#define RSP_UNREGISTER_SERVICE 0x00000000
#endif
#ifndef RSP_SIMPLE_SERVICE
#define RSP_SIMPLE_SERVICE 0x00000001
#endif
// on Win95, we want to call RegisterServiceProcess
// but, it's not available on NT, so we can't import it directly
// here we try to find it dynamically in kernel32. if we find it,
// we call it, otherwise we assume we're on NT and it's not avaible
void MakeMeService()
{
HANDLE hLib;
PFNREGISTERSERVICE pfnRegisterServiceProcess;
hLib = LoadLibrary("kernel32.dll");
if (!hLib)
{
// wacky!
DPF(1,"could not load library kernel32 to register service proc");
return;
}
pfnRegisterServiceProcess = (PFNREGISTERSERVICE)GetProcAddress(hLib,"RegisterServiceProcess");
if (!pfnRegisterServiceProcess)
{
// this is expected on NT
DPF(3,"could not register service process - expected on NT");
FreeLibrary(hLib);
return ;
}
pfnRegisterServiceProcess( 0, RSP_SIMPLE_SERVICE );
FreeLibrary(hLib);
return ;
} // MakeMeService
// on Win95, we want to call RegisterServiceProcess to Unregister
// (see MakeMeService)
void StopServiceProcess()
{
HANDLE hLib;
PFNREGISTERSERVICE pfnRegisterServiceProcess;
hLib = LoadLibrary("kernel32.dll");
if (!hLib)
{
// wacky!
DPF(1,"could not load library kernel32 to register service proc");
return;
}
pfnRegisterServiceProcess = (PFNREGISTERSERVICE)GetProcAddress(hLib,"RegisterServiceProcess");
if (!pfnRegisterServiceProcess)
{
// this is expected on NT
DPF(3,"could not unregister service process - not avail - not tragic");
FreeLibrary(hLib);
return ;
}
// unregistered!
pfnRegisterServiceProcess( 0, RSP_UNREGISTER_SERVICE );
FreeLibrary(hLib);
return ;
} // StopServiceProcess
//**********************************************************************
// ------------------------------
// DNGetNullDacl - Get a SECURITY_ATTRIBUTE structure that specifies a
// NULL DACL which is accessible by all users.
// Taken from IDirectPlay8 code base.
//
// Entry: Nothing
//
// Exit: PSECURITY_ATTRIBUTES
// ------------------------------
#undef DPF_MODNAME
#define DPF_MODNAME "DNGetNullDacl"
PSECURITY_ATTRIBUTES DNGetNullDacl()
{
PSID psidEveryone = NULL;
SID_IDENTIFIER_AUTHORITY siaWorld = SECURITY_WORLD_SID_AUTHORITY;
DWORD dwAclSize;
// This is done to make this function independent of DNOSIndirectionInit so that the debug
// layer can call it before the indirection layer is initialized.
if (!g_fDaclInited)
{
if (!InitializeSecurityDescriptor((SECURITY_DESCRIPTOR*)g_abSD, SECURITY_DESCRIPTOR_REVISION))
{
DPF(0, "Failed to initialize security descriptor" );
goto Error;
}
// Create SID for the Everyone group.
if (!AllocateAndInitializeSid(&siaWorld, 1, SECURITY_WORLD_RID, 0,
0, 0, 0, 0, 0, 0, &psidEveryone))
{
DPF(0, "Failed to allocate Everyone SID" );
goto Error;
}
dwAclSize = sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE) + GetLengthSid(psidEveryone) - sizeof(DWORD);
// Allocate the ACL, this won't be a tracked allocation and we will let process cleanup destroy it
g_pEveryoneACL = (PACL)HeapAlloc(GetProcessHeap(), 0, dwAclSize);
if (g_pEveryoneACL == NULL)
{
DPF(0, "Failed to allocate ACL buffer" );
goto Error;
}
// Intialize the ACL.
if (!InitializeAcl(g_pEveryoneACL, dwAclSize, ACL_REVISION))
{
DPF(0, "Failed to initialize ACL" );
goto Error;
}
// Add the ACE.
if (!AddAccessAllowedAce(g_pEveryoneACL, ACL_REVISION, GENERIC_ALL, psidEveryone))
{
DPF(0, "Failed to add ACE to ACL" );
goto Error;
}
// We no longer need the SID that was allocated.
FreeSid(psidEveryone);
psidEveryone = NULL;
// Add the ACL to the security descriptor..
if (!SetSecurityDescriptorDacl((SECURITY_DESCRIPTOR*)g_abSD, TRUE, g_pEveryoneACL, FALSE))
{
DPF(0, "Failed to add ACL to security descriptor" );
goto Error;
}
g_sa.nLength = sizeof(SECURITY_ATTRIBUTES);
g_sa.lpSecurityDescriptor = g_abSD;
g_sa.bInheritHandle = FALSE;
g_psa = &g_sa;
g_fDaclInited = TRUE;
}
Error:
if (psidEveryone)
{
FreeSid(psidEveryone);
psidEveryone = NULL;
}
return g_psa;
}
//**********************************************************************
/*
* WinMain
*/
int PASCAL WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
DWORD tid;
DWORD rc;
OSVERSIONINFOA VersionInfo;
BOOL fUseGlobalNamespace;
HANDLE hstartevent;
HANDLE hstartupevent;
HANDLE hmutex;
HANDLE hackevent;
LPDPHELPDATA phd;
HANDLE hsharedmem;
HANDLE h;
char szSystemDir[MAX_PATH*sizeof(WCHAR)];
DWORD tWait;
#if (USE_RSIP || USE_NATHELP)
DWORD tLast;
DWORD tNow;
#define RSIP_RENEW_TEST_INTERVAL 60000
#endif
DPFINIT();
DPF( 2, "*** dplaysvr STARTED, PID=%08lx ***", GetCurrentProcessId() );
if( !MemInit() )
{
DPF( 1, "Could not init memory manager" );
return 0;
}
/*
* Set our working directory to the system directory.
* This prevents us from holding network connections open
* forever if the first DirectDraw app that we run is across
* a network connection.
*/
memset(szSystemDir, 0, sizeof(WCHAR));
GetSystemDirectory(szSystemDir, sizeof(szSystemDir));
SetCurrentDirectory(szSystemDir);
// Determine if we're running on NT.
memset(&VersionInfo, 0, sizeof(VersionInfo));
VersionInfo.dwOSVersionInfoSize = sizeof(VersionInfo);
if (GetVersionExA(&VersionInfo))
{
if (VersionInfo.dwPlatformId == VER_PLATFORM_WIN32_NT)
{
DPF(2, "Running on NT version %u.%u.%u, using global namespace.",
VersionInfo.dwMajorVersion, VersionInfo.dwMinorVersion, VersionInfo.dwBuildNumber);
fUseGlobalNamespace = TRUE;
}
else
{
DPF(2, "Running on 9x version %u.%u.%u, not using global namespace.",
VersionInfo.dwMajorVersion, VersionInfo.dwMinorVersion, LOWORD(VersionInfo.dwBuildNumber));
fUseGlobalNamespace = FALSE;
}
}
else
{
rc = GetLastError();
DPF(0, "Could not determine OS version (err = %u), assuming global namespace not needed.", rc);
fUseGlobalNamespace = FALSE;
}
// try to register ourselves as a service so user can't see us
// in task list
MakeMeService();
#if 0
// andyco - not sure if we need this...
/*
* We must guarantee that DPHELP unloads after the last ddraw app,
* since ctrl-alt-del may have happened while an app held the ddraw
* lock, and DPHELP needs to clean up orphaned cheap ddraw mutex
* locks.
*/
if ( ! SetProcessShutdownParameters(0x100,SHUTDOWN_NORETRY) )
{
DPF(0,"dplaysvr could not set itself to shutdown last!");
}
#endif
hInstApp = hInstance;
/*
* create startup event
*/
if (fUseGlobalNamespace)
{
hstartupevent = CreateEvent( DNGetNullDacl(), TRUE, FALSE, "Global\\" DPHELP_STARTUP_EVENT_NAME );
}
else
{
hstartupevent = CreateEvent( NULL, TRUE, FALSE, DPHELP_STARTUP_EVENT_NAME );
}
if( hstartupevent == NULL )
{
DPF( 1, "Could not create startup event!" );
return 0;
}
/*
* create shared memory area
*/
if (fUseGlobalNamespace)
{
hsharedmem = CreateFileMapping( INVALID_HANDLE_VALUE, DNGetNullDacl(),
PAGE_READWRITE, 0, sizeof( DPHELPDATA ),
"Global\\" DPHELP_SHARED_NAME );
}
else
{
hsharedmem = CreateFileMapping( INVALID_HANDLE_VALUE, NULL,
PAGE_READWRITE, 0, sizeof( DPHELPDATA ),
DPHELP_SHARED_NAME );
}
if( hsharedmem == NULL )
{
DPF( 1, "Could not create file mapping!" );
return 0;
}
/*
* create mutex for people who want to use the shared memory area
*/
if (fUseGlobalNamespace)
{
hmutex = CreateMutex( DNGetNullDacl(), FALSE, "Global\\" DPHELP_MUTEX_NAME );
}
else
{
hmutex = CreateMutex( NULL, FALSE, DPHELP_MUTEX_NAME );
}
if( hmutex == NULL )
{
DPF( 1, "Could not create mutex " DPHELP_MUTEX_NAME );
CloseHandle( hsharedmem );
return 0;
}
/*
* create events
*/
if (fUseGlobalNamespace)
{
hstartevent = CreateEvent( DNGetNullDacl(), FALSE, FALSE, "Global\\" DPHELP_EVENT_NAME );
}
else
{
hstartevent = CreateEvent( NULL, FALSE, FALSE, DPHELP_EVENT_NAME );
}
if( hstartevent == NULL )
{
DPF( 1, "Could not create event " DPHELP_EVENT_NAME );
CloseHandle( hmutex );
CloseHandle( hsharedmem );
return 0;
}
if (fUseGlobalNamespace)
{
hackevent = CreateEvent( DNGetNullDacl(), FALSE, FALSE, "Global\\" DPHELP_ACK_EVENT_NAME );
}
else
{
hackevent = CreateEvent( NULL, FALSE, FALSE, DPHELP_ACK_EVENT_NAME );
}
if( hackevent == NULL )
{
DPF( 1, "Could not create event " DPHELP_ACK_EVENT_NAME );
CloseHandle( hmutex );
CloseHandle( hsharedmem );
CloseHandle( hstartevent );
return 0;
}
/*
* Create window so we can get messages
*/
h = CreateThread(NULL,
0,
(LPTHREAD_START_ROUTINE) WindowThreadProc,
NULL,
0,
(LPDWORD)&tid );
if( h == NULL )
{
DPF( 1, "Create of WindowThreadProc FAILED!" );
CloseHandle( hackevent );
CloseHandle( hmutex );
CloseHandle( hsharedmem );
CloseHandle( hstartevent );
return 0;
}
CloseHandle( h );
/*
* serialize access to us
*/
INIT_DPLAYSVR_CSECT();
if (!gbIPStarted)
{
rc = StartupIP();
if (FAILED(rc))
{
DPF_ERR("dphelp : could not init wsock ! not adding server");
return (rc);
}
}
#if USE_RSIP
bRsip=rsipInit();
if(bRsip){
rsipListenPort(FALSE, SERVER_DGRAM_PORT, NULL, NULL);
}
#endif
#if USE_NATHELP
natInit();
if(g_pINatHelp){
natRegisterUDPPort(SERVER_DGRAM_PORT);
}
#endif
/*
* let invoker and anyone else who comes along know we exist
*/
SetEvent( hstartupevent );
tLast=tNow=timeGetTime();
/*
* loop forever, processing requests
*/
while( 1 )
{
wait:
tWait=(tLast+RSIP_RENEW_TEST_INTERVAL)-tNow;
if((int)tWait < 0){
tWait=0;
}
ASSERT(!(tWait &0x80000000));
/*
* wait to be notified of a request
*/
DPF( 1, "Waiting for next request" );
rc = WaitForSingleObject( hstartevent, tWait );
#if (USE_RSIP || USE_NATHELP)
tNow=timeGetTime();
if(rc==WAIT_TIMEOUT)
{
tLast=tNow;
#if USE_RSIP
if(bRsip) {rsipPortExtend(tNow);}
#elif USE_NATHELP
if(g_pINatHelp){natGetCapsUpdate();}
#endif
goto wait;
}
#endif
if( rc == WAIT_FAILED )
{
DPF( 1, "Wait FAILED!!!" );
continue;
}
ENTER_DPLAYSVR();
phd = (LPDPHELPDATA) MapViewOfFile( hsharedmem, FILE_MAP_ALL_ACCESS, 0, 0, 0 );
if( phd == NULL )
{
DPF( 1, "Could not create view of file!" );
LEAVE_DPLAYSVR();
continue;
}
/*
* find out what we need to do
*/
switch( phd->req )
{
case DPHELPREQ_SUICIDE:
DPF( 1, "DPHELPREQ_SUICIDE" );
#if USE_RSIP
if(bRsip){
rsipFini();
}
#endif
#if USE_NATHELP
if(g_pINatHelp){
natFini();
}
#endif
DPlayHelp_FreeServerList();
SetEvent( hackevent );
CloseHandle( hmutex );
UnmapViewOfFile( phd );
CloseHandle( hsharedmem );
CloseHandle( hstartevent );
if (gReceiveList.pConnection)
{
MemFree(gReceiveList.pConnection);
}
if (gReadfds.pfdbigset)
{
MemFree(gReadfds.pfdbigset);
}
FINI_DPLAYSVR_CSECT();
// This should be done after functions that use a Dacl will no longer be
// called (CreateMutex, CreateFile, etc).
if (g_pEveryoneACL)
{
HeapFree(GetProcessHeap(), 0, g_pEveryoneACL);
g_pEveryoneACL = NULL;
g_psa = NULL;
g_fDaclInited = FALSE;
}
#ifdef DEBUG
MemState();
#endif
DPF( 3, "Good Night Gracie" );
TerminateProcess( GetCurrentProcess(), 0 );
break;
case DPHELPREQ_RETURNHELPERPID:
DPF( 2, "DDHELPREQ_RETURNHELPERPID" );
phd->pid = GetCurrentProcessId();
break;
case DPHELPREQ_DPLAYADDSERVER:
DPF( 2, "DPHELPREQ_DPLAYADDSERVER" );
phd->hr = DPlayHelp_AddServer(phd);
#if USE_RSIP
if(!bRsip){
bRsip=rsipInit();
if(bRsip){
rsipListenPort(FALSE, SERVER_DGRAM_PORT, NULL, NULL);
}
}
#endif
#if USE_NATHELP
if(!g_pINatHelp){
natInit();
if(g_pINatHelp){
natRegisterUDPPort(SERVER_DGRAM_PORT);
}
}
#endif
break;
case DPHELPREQ_DPLAYDELETESERVER:
DPF( 2, "DPHELPREQ_DPLAYDELETESERVER" );
DPlayHelp_DeleteServer(phd,FALSE);
break;
default:
DPF( 1, "helper - Unknown Request???" );
break;
}
/*
* let caller know we've got the news
*/
UnmapViewOfFile( phd );
SetEvent( hackevent );
LEAVE_DPLAYSVR();
}
StopServiceProcess();
} /* WinMain */