Leaked source code of windows server 2003
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.
 
 
 
 
 
 

1189 lines
29 KiB

/*++
Copyright (c) 1999-2000 Microsoft Corporation
Module Name:
RemoteDesktopUtils
Abstract:
Misc. RD Utils
Author:
Tad Brockway 02/00
Revision History:
--*/
#ifdef TRC_FILE
#undef TRC_FILE
#endif
#define TRC_FILE "_rdsutl"
#include <regapi.h>
#include <winsock2.h>
#include <RemoteDesktop.h>
#include "RemoteDesktopUtils.h"
#include "base64.h"
#include "tsremdsk.h"
//#include "RemoteDesktopDBG.h"
#include "rassistance_i.c"
#include "rassistance.h"
BSTR
CreateConnectParmsString(
IN DWORD protocolType,
IN CComBSTR &machineAddressList,
IN CComBSTR &assistantAccount,
IN CComBSTR &assistantAccountPwd,
IN CComBSTR &helpSessionID,
IN CComBSTR &helpSessionName,
IN CComBSTR &helpSessionPwd,
IN CComBSTR &protocolSpecificParms
)
/*++
Routine Description:
Create a connect parms string. Format is:
"protocolType,machineAddressList,assistantAccount,assistantAccountPwd,helpSessionName,helpSessionPwd,protocolSpecificParms"
Arguments:
protocolType - Identifies the protocol type.
See RemoteDesktopChannels.h
machineAddressList - Identifies network address of server machine.
assistantAccountName - Account name for initial log in to server
machine, ignore for Whistler
assistantAccountNamePwd - Password for assistantAccountName
helpSessionID - Help session identifier.
helpSessionName - Help session name.
helpSessionPwd - Password to help session once logged in to server
machine.
protocolSpecificParms - Parameters specific to a particular protocol.
Return Value:
--*/
{
CComBSTR result;
WCHAR buf[256];
UNREFERENCED_PARAMETER(assistantAccount);
//
// Add a version stamp for our connect parm.
wsprintf(buf, TEXT("%ld"), SALEM_CURRENT_CONNECTPARM_VERSION);
result = buf;
result += TEXT(",");
wsprintf(buf, TEXT("%ld"), protocolType);
result += buf;
result += TEXT(",");
result += machineAddressList;
result += TEXT(",");
result += assistantAccountPwd;
result += TEXT(",");
result += helpSessionID;
result += TEXT(",");
result += helpSessionName;
result += TEXT(",");
result += helpSessionPwd;
if (protocolSpecificParms.Length() > 0) {
result += TEXT(",");
result += protocolSpecificParms;
}
return result.Detach();
}
DWORD
ParseConnectParmsString(
IN BSTR parmsString,
OUT DWORD* pdwConnParmVersion,
OUT DWORD *protocolType,
OUT CComBSTR &machineAddressList,
OUT CComBSTR &assistantAccount,
OUT CComBSTR &assistantAccountPwd,
OUT CComBSTR &helpSessionID,
OUT CComBSTR &helpSessionName,
OUT CComBSTR &helpSessionPwd,
OUT CComBSTR &protocolSpecificParms
)
/*++
Routine Description:
Parse a connect string created by a call to CreateConnectParmsString.
Arguments:
Return Value:
ERROR_SUCCESS on success. Otherwise, an error code is returned.
--*/
{
DC_BEGIN_FN("ParseConnectParmsString");
BSTR tmp;
WCHAR *tok;
DWORD result = ERROR_SUCCESS;
DWORD len;
DWORD dwVersion = 0;
UNREFERENCED_PARAMETER(assistantAccount);
//
// Make a copy of the input string so we can parse it.
//
tmp = SysAllocString(parmsString);
if (tmp == NULL) {
TRC_ERR((TB, TEXT("Can't allocate parms string.")));
result = ERROR_OUTOFMEMORY;
goto CLEANUPANDEXIT;
}
//
// Retrieve connect parm version stamp, Whistler beta 1
// connect parm does not have version stamp, bail out,
// sessmgr/termsrv will wipe out pending help.
//
tok = wcstok(tmp, L",");
if (tok != NULL) {
dwVersion = _wtol(tok);
}
else {
result = ERROR_INVALID_USER_BUFFER;
goto CLEANUPANDEXIT;
}
//
// SECURITY: connect parm must have security blob, pre-XP version
// does not have security blob and we shouldn't be supporting those
// ticket as hacker can change our ticket and expert has no way to
// tell if he/she connect to right machine.
//
if( dwVersion < SALEM_CONNECTPARM_SECURITYBLOB_VERSION ) {
//
// connect parm is whistler beta 1
//
result = ERROR_NOT_SUPPORTED;
goto CLEANUPANDEXIT;
}
*pdwConnParmVersion = dwVersion;
//
// We have no use for version at this time,
// future update on connect parm should
// take make the necessary change
//
//
// Protocol.
//
tok = wcstok(NULL, L",");
if (tok != NULL) {
*protocolType = _wtoi(tok);
}
//
// Machine Name
//
tok = wcstok(NULL, L",");
if (tok != NULL) {
machineAddressList = tok;
}
else {
result = ERROR_INVALID_USER_BUFFER;
goto CLEANUPANDEXIT;
}
if( machineAddressList.Length() == 0 ) {
result = ERROR_OUTOFMEMORY;
goto CLEANUPANDEXIT;
}
//
// Assistant Account Password
//
tok = wcstok(NULL, L",");
if (tok != NULL) {
assistantAccountPwd = tok;
}
else {
result = ERROR_INVALID_USER_BUFFER;
goto CLEANUPANDEXIT;
}
if( assistantAccountPwd.Length() == 0 ) {
result = ERROR_OUTOFMEMORY;
goto CLEANUPANDEXIT;
}
//
// Help Session ID
//
tok = wcstok(NULL, L",");
if (tok != NULL) {
helpSessionID = tok;
}
else {
result = ERROR_INVALID_USER_BUFFER;
goto CLEANUPANDEXIT;
}
if( helpSessionID.Length() == 0 ) {
result = ERROR_OUTOFMEMORY;
goto CLEANUPANDEXIT;
}
//
// Help Session Name
//
tok = wcstok(NULL, L",");
if (tok != NULL) {
helpSessionName = tok;
}
else {
result = ERROR_INVALID_USER_BUFFER;
goto CLEANUPANDEXIT;
}
if( helpSessionName.Length() == 0 ) {
result = ERROR_OUTOFMEMORY;
goto CLEANUPANDEXIT;
}
//
// Help Session Password
//
tok = wcstok(NULL, L",");
if (tok != NULL) {
helpSessionPwd = tok;
}
else {
result = ERROR_INVALID_USER_BUFFER;
goto CLEANUPANDEXIT;
}
if( helpSessionPwd.Length() == 0 ) {
result = ERROR_OUTOFMEMORY;
goto CLEANUPANDEXIT;
}
//
// RDP Protcol specific parameter
// is our security block - hashed TS public key
//
tok = wcstok(NULL, L",");
if( tok != NULL ) {
protocolSpecificParms = tok;
}
else {
result = ERROR_INVALID_USER_BUFFER;
goto CLEANUPANDEXIT;
}
if( protocolSpecificParms.Length() == 0 ) {
result = ERROR_OUTOFMEMORY;
}
CLEANUPANDEXIT:
if (result != ERROR_SUCCESS) {
TRC_ERR((TB, TEXT("Error parsing %s"), parmsString));
}
if (tmp != NULL) {
SysFreeString(tmp);
}
DC_END_FN();
return result;
}
BSTR
ReallocBSTR(
IN BSTR origStr,
IN DWORD requiredByteLen
)
/*++
Routine Description:
Realloc a BSTR
Arguments:
Return Value:
The realloc'd string on success. Otherwise, NULL is returned.
--*/
{
DC_BEGIN_FN("ReallocBSTR");
BSTR tmp;
DWORD len;
DWORD origLen;
//
// Allocate the new string.
//
tmp = SysAllocStringByteLen(NULL, requiredByteLen);
if (tmp == NULL) {
TRC_ERR((TB, TEXT("Failed to allocate %ld bytes."), requiredByteLen));
goto CLEANUPANDEXIT;
}
//
// Copy data from the original string.
//
origLen = SysStringByteLen(origStr);
len = origLen <= requiredByteLen ? origLen : requiredByteLen;
memcpy(tmp, origStr, len);
//
// Release the old string.
//
SysFreeString(origStr);
CLEANUPANDEXIT:
DC_END_FN();
return tmp;
}
DWORD
CreateSystemSid(
PSID *ppSystemSid
)
/*++
Routine Description:
Create a SYSTEM SID.
Arguments:
Return Value:
ERROR_SUCCESS on success. Otherwise, an error code is returned.
--*/
{
DC_BEGIN_FN("CreateSystemSid");
DWORD dwStatus = ERROR_SUCCESS;
PSID pSid;
SID_IDENTIFIER_AUTHORITY SidAuthority = SECURITY_NT_AUTHORITY;
TRC_ASSERT(ppSystemSid != NULL, (TB, L"ppSystemSid != NULL"));
if( TRUE == AllocateAndInitializeSid(
&SidAuthority,
1,
SECURITY_LOCAL_SYSTEM_RID,
0, 0, 0, 0, 0, 0, 0,
&pSid
) )
{
*ppSystemSid = pSid;
}
else
{
dwStatus = GetLastError();
}
DC_END_FN();
return dwStatus;
}
BOOL
IsSystemToken(
HANDLE TokenHandle,
PSID pSystemSid
)
/*++
Routine Description:
Returns whether the current token is running under SYSTEM security.
Arguments:
TokenHandle - Param1 Thread or process token
pSystemSid - System SID.
Return Value:
TRUE if System token. FALSE otherwise.
--*/
{
DC_BEGIN_FN("IsSystemToken");
BOOL Result = FALSE;
ULONG ReturnLength, BufferLength;
DWORD dwStatus;
PTOKEN_USER pTokenUser = NULL;
TRC_ASSERT(NULL != pSystemSid, (TB, L"NULL != pSystemSid"));
// Get user SID.
ReturnLength = 0;
Result = GetTokenInformation(
TokenHandle,
TokenUser,
NULL,
0,
&ReturnLength
);
if( ReturnLength == 0 )
{
TRC_ERR((TB, L"GetTokenInformation: %08X", GetLastError()));
Result = FALSE;
CloseHandle( TokenHandle );
goto CLEANUPANDEXIT;
}
BufferLength = ReturnLength;
pTokenUser = (PTOKEN_USER)LocalAlloc( LPTR, BufferLength );
if( pTokenUser == NULL )
{
TRC_ERR((TB, L"LocalAlloc: %08X", GetLastError()));
Result = FALSE;
CloseHandle( TokenHandle );
goto CLEANUPANDEXIT;
}
Result = GetTokenInformation(
TokenHandle,
TokenUser,
pTokenUser,
BufferLength,
&ReturnLength
);
CloseHandle( TokenHandle );
if( TRUE == Result ) {
Result = EqualSid( pTokenUser->User.Sid, pSystemSid);
}
else {
TRC_ERR((TB, L"GetTokenInformation: %08X", GetLastError()));
}
CLEANUPANDEXIT:
if( pTokenUser )
{
LocalFree( pTokenUser );
}
DC_END_FN();
return Result;
}
BOOL
IsCallerSystem(
PSID pSystemSid
)
/*++
Routine Description:
Returns whether the current thread is running under SYSTEM security.
NOTE: Caller should be impersonated prior to invoking this function.
Arguments:
pSystemSid - System SID.
Return Value:
TRUE if System. FALSE otherwise.
--*/
{
DC_BEGIN_FN("IsCallerSystem");
BOOL Result;
HANDLE TokenHandle;
//
// Open the thread token and check if System token.
//
Result = OpenThreadToken(
GetCurrentThread(),
TOKEN_QUERY,
TRUE,
&TokenHandle
);
if( TRUE == Result ) {
//
// This token should not be released. This function does not leak
// handles.
//
Result = IsSystemToken(TokenHandle, pSystemSid);
}
else {
TRC_ERR((TB, L"OpenThreadToken: %08X", GetLastError()));
}
DC_END_FN();
return Result;
}
void
AttachDebugger(
LPCTSTR pszDebugger
)
/*++
Routine Description:
Attach debugger to our process or process hosting our DLL.
Parameters:
pszDebugger : Debugger command, e.g. ntsd -d -g -G -p %d
Returns:
None.
Note:
Must have "-p %d" since we don't know debugger's parameter for process.
--*/
{
//
// Attach debugger
//
if( !IsDebuggerPresent() ) {
TCHAR szCommand[256];
PROCESS_INFORMATION ProcessInfo;
STARTUPINFO StartupInfo;
//
// ntsd -d -g -G -p %d
//
wsprintf( szCommand, pszDebugger, GetCurrentProcessId() );
ZeroMemory(&StartupInfo, sizeof(StartupInfo));
StartupInfo.cb = sizeof(StartupInfo);
if (!CreateProcess(NULL, szCommand, NULL, NULL, FALSE, 0, NULL, NULL, &StartupInfo, &ProcessInfo)) {
return;
}
else {
CloseHandle(ProcessInfo.hProcess);
CloseHandle(ProcessInfo.hThread);
while (!IsDebuggerPresent())
{
Sleep(500);
}
}
} else {
DebugBreak();
}
return;
}
void
AttachDebuggerIfAsked(HINSTANCE hInst)
/*++
Routine Description:
Check if debug enable flag in our registry HKLM\Software\Microsoft\Remote Desktop\<module name>,
if enable, attach debugger to running process.
Parameter :
hInst : instance handle.
Returns:
None.
--*/
{
CRegKey regKey;
DWORD dwStatus;
TCHAR szModuleName[MAX_PATH+1];
TCHAR szFileName[MAX_PATH+1];
CComBSTR bstrRegKey(_TEXT("Software\\Microsoft\\Remote Desktop\\"));
TCHAR szDebugCmd[256];
DWORD cbDebugCmd = sizeof(szDebugCmd)/sizeof(szDebugCmd[0]);
dwStatus = GetModuleFileName( hInst, szModuleName, MAX_PATH+1 );
if( 0 == dwStatus ) {
//
// Can't attach debugger with name.
//
return;
}
szModuleName[dwStatus] = L'\0';
_tsplitpath( szModuleName, NULL, NULL, szFileName, NULL );
bstrRegKey += szFileName;
//
// Check if we are asked to attach/break into debugger
//
dwStatus = regKey.Open( HKEY_LOCAL_MACHINE, bstrRegKey );
if( 0 != dwStatus ) {
return;
}
dwStatus = regKey.QueryValue( szDebugCmd, _TEXT("Debugger"), &cbDebugCmd );
if( 0 != dwStatus || cbDebugCmd > 200 ) {
// 200 chars is way too much for debugger command.
return;
}
AttachDebugger( szDebugCmd );
return;
}
DWORD
HashSecurityData(
IN PBYTE const pbData,
IN DWORD cbData,
OUT CComBSTR& bstrHashedData
)
/*++
Routine Description:
Hash a blob of data and return hased data in BSTR
Parameters:
pbData : Pointer to data to be hashed.
cbData : Size of data to be hashed.
bstrHashedData : Return hashed data in BSTR form.
Returns:
ERROR_SUCCESS or error code.
--*/
{
DC_BEGIN_FN("HashSecurityData");
DWORD dwStatus;
LPSTR pbEncodedData = NULL;
DWORD cbEncodedData = 0;
PBYTE pbHashedData = NULL;
DWORD cbHashedData = 0;
DWORD dwSize;
HCRYPTPROV hCryptProv = NULL;
HCRYPTHASH hHash = NULL;
BOOL bSuccess;
bSuccess = CryptAcquireContext(
&hCryptProv,
NULL,
NULL,
PROV_RSA_FULL,
CRYPT_VERIFYCONTEXT
);
if( FALSE == bSuccess ) {
dwStatus = GetLastError();
TRC_ERR((TB, L"CryptAcquireContext: %08X", dwStatus));
goto CLEANUPANDEXIT;
}
bSuccess = CryptCreateHash(
hCryptProv,
CALG_SHA1,
0,
0,
&hHash
);
if( FALSE == bSuccess ) {
dwStatus = GetLastError();
TRC_ERR((TB, L"CryptCreateHash: %08X", dwStatus));
goto CLEANUPANDEXIT;
}
bSuccess = CryptHashData(
hHash,
pbData,
cbData,
0
);
if( FALSE == bSuccess ) {
dwStatus = GetLastError();
TRC_ERR((TB, L"CryptHashData: %08X", dwStatus));
goto CLEANUPANDEXIT;
}
dwSize = sizeof( cbHashedData );
bSuccess = CryptGetHashParam(
hHash,
HP_HASHSIZE,
(PBYTE)&cbHashedData,
&dwSize,
0
);
if( FALSE == bSuccess ) {
dwStatus = GetLastError();
TRC_ERR((TB, L"CryptGetHashParam with HP_HASHSIZE : %08X", dwStatus));
goto CLEANUPANDEXIT;
}
pbHashedData = (PBYTE)LocalAlloc(LPTR, cbHashedData);
if( NULL == pbHashedData ) {
dwStatus = GetLastError();
goto CLEANUPANDEXIT;
}
bSuccess = CryptGetHashParam(
hHash,
HP_HASHVAL,
pbHashedData,
&cbHashedData,
0
);
if( FALSE == bSuccess ) {
dwStatus = GetLastError();
TRC_ERR((TB, L"CryptGetHashParam with HP_HASHVAL : %08X", dwStatus));
goto CLEANUPANDEXIT;
}
//
// Hash data and convert to string form.
//
dwStatus = LSBase64EncodeA(
pbHashedData,
cbHashedData,
NULL,
&cbEncodedData
);
if( ERROR_SUCCESS != dwStatus ) {
TRC_ERR((TB, L"LSBase64EncodeA : %08X", dwStatus));
goto CLEANUPANDEXIT;
}
pbEncodedData = (LPSTR) LocalAlloc( LPTR, cbEncodedData+1 );
if( NULL == pbEncodedData ) {
dwStatus = GetLastError();
goto CLEANUPANDEXIT;
}
dwStatus = LSBase64EncodeA(
pbHashedData,
cbHashedData,
pbEncodedData,
&cbEncodedData
);
if( ERROR_SUCCESS == dwStatus ) {
//
// Base64 encoding always add '\r', '\n' at the end,
// remove it
//
if( pbEncodedData[cbEncodedData - 1] == '\n' &&
pbEncodedData[cbEncodedData - 2] == '\r' )
{
pbEncodedData[cbEncodedData - 2] = 0;
cbEncodedData -= 2;
}
bstrHashedData = pbEncodedData;
}
else {
TRC_ERR((TB, L"LSBase64EncodeA : %08X", dwStatus));
}
CLEANUPANDEXIT:
if( NULL != pbEncodedData ) {
LocalFree( pbEncodedData );
}
if( NULL != pbHashedData ) {
LocalFree( pbHashedData );
}
if( NULL != hHash ) {
CryptDestroyHash( hHash );
}
if( NULL != hCryptProv ) {
CryptReleaseContext( hCryptProv, 0 );
}
DC_END_FN();
return dwStatus;
}
DWORD
WaitForRAGPDisableNotification(
IN HANDLE hShutdown
)
/*++
Routine Description:
Routine to notify RA disabled.
Parameters:
hShutdown : a manual event to sign we should terminate notification.
Returns:
ERROR_SUCCESS RA is disabled.
ERROR_SHUTDOWN_IN_PROGRESS hShutdown signaled
other error code. other error condition.
Note:
Code modified from regapi's WaitForTSConnectionsPolicyChanges(),
we can't use WaitForTSConnectionsPolicyChanges() because it takes
a parameter bWaitForAccept which has nothing to do with our GP.
TODO: Need to optimize, group policy always overwrite machine level
policy, idea is if we sees group policy is set, the we can ignore
machine policy, if group policy is not configured, then we check/wait
together with machine policy.
--*/
{
HKEY hMachineControlKey = NULL;
HKEY hPoliciesKey = NULL;
HANDLE hEvents[3] = {NULL, NULL, NULL};
DWORD status;
DWORD whichObject;
DC_BEGIN_FN("WaitForRAGPDisableNotification");
if( hShutdown == NULL ) {
status = ERROR_INVALID_PARAMETER;
goto CLEANUPANDEXIT;
}
status = RegOpenKeyEx(
HKEY_LOCAL_MACHINE,
REG_CONTROL_TSERVER,
0,
KEY_READ,
&hMachineControlKey
);
if( ERROR_SUCCESS != status ) {
TRC_ERR((TB, L"RegOpenKeyEx with REG_CONTROL_TSERVER : %08X", status));
goto CLEANUPANDEXIT;
}
// We wait for HKLM\Policies instead of Terminal Service because gpedit
// might delete it because updating new data.
status = RegOpenKeyEx(
HKEY_LOCAL_MACHINE,
TEXT("SOFTWARE\\Policies"),
0,
KEY_READ,
&hPoliciesKey
);
if( ERROR_SUCCESS != status ) {
TRC_ERR((TB, L"RegOpenKeyEx with SOFTWARE\\Policies : %08X", status));
goto CLEANUPANDEXIT;
}
//
// First event for hMachineControlKey change.
//
hEvents[0] = CreateEvent(NULL, TRUE, FALSE,NULL);
if( !hEvents[0]) {
status = GetLastError();
TRC_ERR((TB, L"CreateEvent failed : %08X", status));
goto CLEANUPANDEXIT;
}
//
// Second event for hPolicy change.
//
hEvents[1] = CreateEvent(NULL, TRUE, FALSE,NULL);
if( !hEvents[1]) {
TRC_ERR((TB, L"CreateEvent failed : %08X", status));
status = GetLastError();
goto CLEANUPANDEXIT;
}
//
// Last event for shutdown notification.
//
hEvents[2] = hShutdown;
//
// Forever Loop until RA is diabled.
//
while( TRUE ) {
status = RegNotifyChangeKeyValue(
hMachineControlKey,
FALSE,
REG_NOTIFY_CHANGE_LAST_SET,
hEvents[0],
TRUE
);
if( status != ERROR_SUCCESS ) {
TRC_ERR((TB, L"RegNotifyChangeKeyValue failed : %08X", status));
goto CLEANUPANDEXIT;
}
status = RegNotifyChangeKeyValue(
hPoliciesKey,
TRUE,
REG_NOTIFY_CHANGE_LAST_SET | REG_NOTIFY_CHANGE_NAME,
hEvents[1],
TRUE
);
if( status != ERROR_SUCCESS ) {
TRC_ERR((TB, L"RegNotifyChangeKeyValue failed : %08X", status));
goto CLEANUPANDEXIT;
}
if( TRUE == RegIsMachinePolicyAllowHelp() ) {
whichObject = WaitForMultipleObjects( 3, hEvents, FALSE, INFINITE );
}
else {
// if RA policy is not to allow get help, we immediately
// get into Sleep mode and during these time, if RA changed,
// we will honor the change, otherwise, just return.
whichObject = WAIT_OBJECT_0;
}
if( whichObject == WAIT_OBJECT_0 + 2 ) {
//
// shutdown event has signaled, exit.
//
TRC_NRM((TB, L"Shutting down notification"));
status = ERROR_SHUTDOWN_IN_PROGRESS;
break;
} else if( whichObject == WAIT_OBJECT_0 || whichObject == WAIT_OBJECT_0 + 1 ) {
//
// gpedit might delete the policy registry key and update with new set
// of value so we can't read the value right away, wait 30 seconds
//
status = WaitForSingleObject( hShutdown, DELAY_SHUTDOWN_SALEM_TIME );
if( status == WAIT_OBJECT_0 ) {
// shutdown has signalled.
status = ERROR_SHUTDOWN_IN_PROGRESS;
goto CLEANUPANDEXIT;
}
else if( status != WAIT_TIMEOUT ) {
// expecting a WAIT_TIMEOUT or WAIT_OBJECT_0, otherwise,
// an error codition has occurred.
status = GetLastError();
TRC_ERR((TB, L"WaitForSingleObject failed : %08X", status));
goto CLEANUPANDEXIT;
}
if( FALSE == RegIsMachinePolicyAllowHelp() ) {
// RA has been disabled.
TRC_NRM((TB, L"RA is disabled..."));
status = ERROR_SUCCESS;
break;
}
// Reset the one reg. notification that has signalled, don't
// touch the other as event might get signaled while we were
// processing.
if( whichObject == WAIT_OBJECT_0 ) {
ResetEvent( hEvents[0] );
}
else {
ResetEvent( hEvents[1] );
}
}
else {
// error in Wait, return
status = ERROR_INTERNAL_ERROR;
TRC_ASSERT(FALSE, (TB, L"WaitForMultipleObjects failed %d", whichObject));
}
}
CLEANUPANDEXIT:
if( hEvents[0] ) {
CloseHandle( hEvents[0] );
}
if( hEvents[1] ) {
CloseHandle( hEvents[1] );
}
if( hPoliciesKey ) {
RegCloseKey( hPoliciesKey );
}
if( hMachineControlKey ) {
RegCloseKey( hMachineControlKey );
}
DC_END_FN();
return status;
}
void
LogRemoteAssistanceEventString(
IN DWORD dwEventType,
IN DWORD dwEventId,
IN DWORD numStrings,
IN LPTSTR* lpStrings
)
/*++
Routine Description:
This is central routine in Salem to invoke IRAEventLog in RACPLDLG.DLL to log
necessary remote assistance event, refer to MSDN for detail in event logging.
Parameters:
dwEventType: Information, warning, error, refer to ReportEvent()
dwEventId: Event ID
numStrings: number of strings to merge
lpStrings: array of strings to merge
Returns:
None.
--*/
{
HRESULT hRes = S_OK;
VARIANT EventStrings;
DWORD index;
CComPtr<IRAEventLog> pEventLog;
DC_BEGIN_FN("LogRemoteAssistanceEventString");
// we only have three string to be included in the event log.
SAFEARRAY* psa = NULL;
SAFEARRAYBOUND bounds;
BSTR* bstrArray = NULL;
hRes = pEventLog.CoCreateInstance(CLSID_RAEventLog);
if( FAILED(hRes) )
{
TRC_ERR((TB, L"CoCreateInstance: %08X", hRes));
goto CLEANUPANDEXIT;
}
if( numStrings == 0 )
{
// we are logging msg without any parameter
hRes = pEventLog->LogRemoteAssistanceEvent(
dwEventType,
dwEventId,
NULL
);
goto CLEANUPANDEXIT;
}
bounds.cElements = numStrings;
bounds.lLbound = 0;
VariantInit(&EventStrings);
//
// Create a safearray to pass all event string
//
psa = SafeArrayCreate(VT_BSTR, 1, &bounds);
if( NULL == psa )
{
TRC_ERR((TB, L"SafeArrayCreate: %08X", hRes));
goto CLEANUPANDEXIT;
}
// Required, lock the safe array
hRes = SafeArrayAccessData(psa, (void **)&bstrArray);
if( SUCCEEDED(hRes) )
{
for(index=0; index < numStrings; index++)
{
bstrArray[index] = SysAllocString(lpStrings[index]);
}
EventStrings.vt = VT_ARRAY | VT_BSTR;
EventStrings.parray = psa;
hRes = SafeArrayUnaccessData( psa );
if( FAILED(hRes) )
{
TRC_ERR((TB, L"SafeArrayUnaccessData: %08X", hRes));
goto CLEANUPANDEXIT;
}
hRes = pEventLog->LogRemoteAssistanceEvent(
dwEventType,
dwEventId,
&EventStrings
);
}
CLEANUPANDEXIT:
// MSDN on VariantClear() is not clear whether it will
// free safearray but SafeArrayDestroy() did say it will call
// SysFreeString() on each element so we do it ourself.
hRes = SafeArrayDestroy( psa );
if( FAILED(hRes) )
{
TRC_ERR((TB, L"SafeArrayDestroy: %08X", hRes));
}
EventStrings.vt = VT_EMPTY;
hRes = VariantClear(&EventStrings);
if( FAILED(hRes) )
{
TRC_ERR((TB, L"VariantClear: %08X", hRes));
}
DC_END_FN();
return;
}