/************************************************************************* * compatfl.c * * Routines used to get Citrix application compatibility flags * * Copyright (C) 1997-1999 Microsoft Corp. *************************************************************************/ #include "precomp.h" #pragma hdrstop #include typedef VOID (*GETDOSAPPNAME)(LPSTR); ULONG gCompatFlags = 0xFFFFFFFF; DWORD gdwAppType = 0; WCHAR * Ctx_wcsistr( WCHAR * pString, WCHAR * pPattern ) { WCHAR * pBuf1; WCHAR * pBuf2; WCHAR * pCh; if ( pString == NULL ) return( NULL ); pBuf1 = RtlAllocateHeap( RtlProcessHeap(), 0, (wcslen(pString) * sizeof(WCHAR)) + sizeof(WCHAR) ); if ( pBuf1 == NULL ) return( NULL ); wcscpy( pBuf1, pString ); pBuf2 = _wcslwr( pBuf1 ); pCh = wcsstr( pBuf2, pPattern ); RtlFreeHeap( RtlProcessHeap(), 0, pBuf1 ); if ( pCh == NULL ) return( NULL ); return( pString + (pCh - pBuf2) ); } //***************************************************************************** // GetAppTypeAndModName // // Returns the application type and module name of the running application. // // Parameters: // LPDWORD pdwAppType (IN) - (IN optional) ptr to app type // (OUT) - Application Type // PWCHAR ModName (OUT) - Module Name // Length (IN) - Maximum length of ModName including NULL // // Return Value: // TRUE on successfully finding the application name, FALSE otherwise. // // Notes: // // If the caller knows that this is a win32 app, they can set pdwAppType // to TERMSRV_COMPAT_WIN32 to save some overhead. // //***************************************************************************** BOOL GetAppTypeAndModName(OUT LPDWORD pdwAppType, OUT PWCHAR ModName, ULONG Length) { PWCHAR pwch, pwchext; WCHAR pwcAppName[MAX_PATH+1]; CHAR pszAppName[MAX_PATH+1]; HANDLE ntvdm = NULL; GETDOSAPPNAME GetDOSAppNamep = NULL; ANSI_STRING AnsiString; UNICODE_STRING UniString; PRTL_PERTHREAD_CURDIR pRtlInfo; PRTL_USER_PROCESS_PARAMETERS pUserParam; // Get the path of the executable name pUserParam = NtCurrentPeb()->ProcessParameters; // Get the executable name, if there's no \ just use the name as it is pwch = wcsrchr(pUserParam->ImagePathName.Buffer, L'\\'); if (pwch) { pwch++; } else { pwch = pUserParam->ImagePathName.Buffer; } wcscpy(pwcAppName, pwch); pwch = pwcAppName; // If it's not a Win32 app, do the extra work to get the image name if (!(*pdwAppType & TERMSRV_COMPAT_WIN32)) { *pdwAppType = TERMSRV_COMPAT_WIN32; // Default to a Win32 app // Check if it's a DOS or Win16 app by checking if the app is ntvdm.exe if (!_wcsicmp(pwch, L"ntvdm.exe")) { pRtlInfo = RtlGetPerThreadCurdir(); // If there's per-thread data, it's a Win16 app if (pRtlInfo) { *pdwAppType = TERMSRV_COMPAT_WIN16; wcscpy(pwcAppName, pRtlInfo->ImageName->Buffer); } else { // Load NTVDM if ((ntvdm = LoadLibrary(L"ntvdm.exe"))) { // Get the address of GetDOSAppName if ((GetDOSAppNamep = (GETDOSAPPNAME)GetProcAddress( ntvdm, "GetDOSAppName"))) { RtlInitUnicodeString(&UniString, pwcAppName); UniString.MaximumLength = MAX_PATH; // // Use pszAppName only if not NULL otherwise we are processing the PIF // so go w/ NTVDM as the name. // GetDOSAppNamep(pszAppName); if (*pszAppName != '\0') { RtlInitAnsiString(&AnsiString, pszAppName); RtlAnsiStringToUnicodeString(&UniString, &AnsiString, FALSE); } pwch = UniString.Buffer; *pdwAppType = TERMSRV_COMPAT_DOS; FreeLibrary(ntvdm); } else { #if DBG DbgPrint( "KERNEL32: Couldn't get GetDOSAppName entry point\n" ); #endif FreeLibrary(ntvdm); return (FALSE); } } else { #if DBG DbgPrint( "KERNEL32: Couldn't load ntvdm.exe\n" ); #endif return(FALSE); } } } else if (!_wcsicmp(pwch, L"os2.exe")) { *pdwAppType = TERMSRV_COMPAT_OS2; // Look in the command line for /p, which is fully qualified path pwch = wcsstr(pUserParam->CommandLine.Buffer, L"/P"); if (!pwch) { pwch = wcsstr(pUserParam->CommandLine.Buffer, L"/p"); } if (pwch) { pwch += 3; // skip over /p and blank if (pwchext = wcschr(pwch, L' ')) { wcsncpy(pwcAppName, pwch, (size_t)(pwchext - pwch)); pwcAppName[pwchext - pwch] = L'\0'; } else { return (FALSE); } } else{ return (FALSE); } } // Get rid of the app's path, if necessary if (pwch = wcsrchr(pwcAppName, L'\\')) { pwch++; } else { pwch = pwcAppName; } // //Set global gdwAppType only if we did the real type checking. // gdwAppType = *pdwAppType; } // Remove the extension if (pwchext = wcsrchr(pwch, L'.')) { *pwchext = '\0'; } // Copy out the Module name if (((wcslen(pwch) + 1) * sizeof(WCHAR)) > Length) { return(FALSE); } wcscpy(ModName, pwch); return(TRUE); } //***************************************************************************** // GetCtxPhysMemoryLimits // // Returns the Physical Memory limits for the current application. // // Parameters: // LPDWORD pdwAppType (IN) - (IN optional) ptr to app type // (OUT) - Application Type // LPDWORD pdwPhysMemLim (OUT) - Value of physical memory limit // // Return Value: // TRUE on successfully finding a limit, ZERO if no limit. // // Notes: // // If the caller knows that this is a win32 app, they can set pdwAppType // to TERMSRV_COMPAT_WIN32 to save some overhead. // //***************************************************************************** ULONG GetCtxPhysMemoryLimits(OUT LPDWORD pdwAppType, OUT LPDWORD pdwPhysMemLim) { WCHAR ModName[MAX_PATH+1]; ULONG ulrc = FALSE; ULONG dwCompatFlags; *pdwPhysMemLim = 0; if (!GetAppTypeAndModName(pdwAppType, ModName, sizeof(ModName))) { goto CtxGetPhysMemReturn; } // Get the compatibility flags to look for memory limits flag ulrc = GetTermsrCompatFlags(ModName, &dwCompatFlags, CompatibilityApp); if ( ulrc & ((dwCompatFlags & TERMSRV_COMPAT_PHYSMEMLIM ) && (dwCompatFlags & *pdwAppType)) ) { NTSTATUS NtStatus; OBJECT_ATTRIBUTES ObjectAttributes; UNICODE_STRING UniString; HKEY hKey = 0; ULONG ul, ulcbuf; ULONG DataLen; PKEY_VALUE_PARTIAL_INFORMATION pKeyValInfo = NULL; LPWSTR UniBuff = NULL; RtlInitUnicodeString( &UniString, NULL ); // we test for this below ulrc = TRUE; *pdwPhysMemLim = TERMSRV_COMPAT_DEFAULT_PHYSMEMLIM; ul = sizeof(TERMSRV_COMPAT_APP) + (wcslen(ModName) + 1)*sizeof(WCHAR); UniBuff = RtlAllocateHeap(RtlProcessHeap(), 0, ul); if (UniBuff) { wcscpy(UniBuff, TERMSRV_COMPAT_APP); wcscat(UniBuff, ModName); RtlInitUnicodeString(&UniString, UniBuff); } // Determine the value info buffer size ulcbuf = sizeof(KEY_VALUE_FULL_INFORMATION) + MAX_PATH*sizeof(WCHAR) + sizeof(ULONG); pKeyValInfo = RtlAllocateHeap(RtlProcessHeap(), 0, ulcbuf); // Did everything initialize OK? if (UniString.Buffer && pKeyValInfo) { InitializeObjectAttributes(&ObjectAttributes, &UniString, OBJ_CASE_INSENSITIVE, NULL, NULL ); NtStatus = NtOpenKey(&hKey, KEY_READ, &ObjectAttributes); if (NT_SUCCESS(NtStatus)) { RtlInitUnicodeString(&UniString, TERMSRV_PHYSMEMLIM ); NtStatus = NtQueryValueKey(hKey, &UniString, KeyValuePartialInformation, pKeyValInfo, ulcbuf, &DataLen); if (NT_SUCCESS(NtStatus) && (REG_DWORD == pKeyValInfo->Type)) { *pdwPhysMemLim = *(PULONG)pKeyValInfo->Data; ulrc = TRUE; } NtClose(hKey); } } // Free up the buffers we allocated // Need to zero out the buffers, because some apps (MS Internet Assistant) // won't install if the heap is not zero filled. if (UniBuff) { memset(UniBuff, 0, ul); RtlFreeHeap( RtlProcessHeap(), 0, UniBuff ); } if (pKeyValInfo) { memset(pKeyValInfo, 0, ulcbuf); RtlFreeHeap( RtlProcessHeap(), 0, pKeyValInfo ); } } else { ulrc = FALSE; } CtxGetPhysMemReturn: //#if DBG //DbgPrint("CtxGetPhysMemLim returning %d; PhysMemLim=%d\n", ulrc, *pdwPhysMemLim); //#endif return(ulrc); } //***************************************************************************** // GetCtxAppCompatFlags - // // Returns the Citrix compatibility flags for the current application. // // Parameters: // LPDWORD pdwCompatFlags (OUT) - Ptr to DWORD return value for flags // LPDWORD pdwAppType (IN) - (IN optional) ptr to app type // (OUT) - Application Type // // Return Value: // TRUE on success, FALSE on failure. // // Notes: // // If the caller knows that this is a win32 app, they can set pdwAppType // to TERMSRV_COMPAT_WIN32 to save some overhead. // // Flag values are defined in syslib.h: // // TERMSRV_COMPAT_DOS - Compatibility flags are for DOS app // TERMSRV_COMPAT_OS2 - Compatibility flags are for OS2 app // TERMSRV_COMPAT_WIN16 - Compatibility flags are for Win16 app // TERMSRV_COMPAT_WIN32 - Compatibility flags are for Win32 app // TERMSRV_COMPAT_ALL - Compatibility flags are for any app // TERMSRV_COMPAT_USERNAME - Return Username instead of Computername // TERMSRV_COMPAT_MSBLDNUM - Return MS build number, not Citrix build no. // TERMSRV_COMPAT_INISYNC - Sync user ini file with system version //***************************************************************************** ULONG GetCtxAppCompatFlags(OUT LPDWORD pdwCompatFlags, OUT LPDWORD pdwAppType) { WCHAR ModName[MAX_PATH+1]; if((gCompatFlags != 0xFFFFFFFF) && (gdwAppType == TERMSRV_COMPAT_WIN32)) { *pdwCompatFlags = gCompatFlags; *pdwAppType = gdwAppType; return (TRUE); } if (!GetAppTypeAndModName(pdwAppType, ModName, sizeof(ModName))) { return (FALSE); } // Get the flags return (GetTermsrCompatFlags(ModName, pdwCompatFlags, CompatibilityApp)); } //***************************************************************************** // GetTermsrCompatFlags - // // Returns the Citrix compatibility flags for the specified task. // // Parameters: // LPWSTR lpModName (IN) - Image name to look up in registry // LPDWORD pdwCompatFlags (OUT) - Ptr to DWORD return value for flags // TERMSRV_COMPATIBILITY_CLASS CompatType (IN) - Indicates app or inifile // // Return Value: // TRUE on success, FALSE on failure. // // Notes: // Assumes it's being called in the context of the current application - // we use the current Teb to get the compatibility flags. // // Flag values are defined in syslib.h. // //***************************************************************************** ULONG GetTermsrCompatFlags(LPWSTR lpModName, LPDWORD pdwCompatFlags, TERMSRV_COMPATIBILITY_CLASS CompatType) { NTSTATUS NtStatus; OBJECT_ATTRIBUTES ObjectAttributes; UNICODE_STRING UniString; HKEY hKey = 0; ULONG ul, ulcbuf; PKEY_VALUE_PARTIAL_INFORMATION pKeyValInfo = NULL; ULONG ulRetCode = FALSE; LPWSTR UniBuff = NULL; *pdwCompatFlags = 0; // If terminal services aren't enabled, just return if (!IsTerminalServer()) { return(TRUE); } UniString.Buffer = NULL; if (CompatType == CompatibilityApp) { if (gCompatFlags != 0xFFFFFFFF) { //DbgPrint( "GetTermsrCompatFlags: Return cached compatflags (gCompatFlags)%lx for app %ws\n",gCompatFlags,lpModName ); *pdwCompatFlags = gCompatFlags; return TRUE; } // Look and see if the compat flags in the Teb are valid (right now // they're only valid for Win16 apps). Don't set them for DOS apps // unless you can have a mechanism to have unique values for each // DOS app in a VDM. // if (wcsstr(NtCurrentPeb()->ProcessParameters->ImagePathName.Buffer, L"ntvdm.exe")) { // PVOID Ra; // ASSERT(gpTermsrvTlsIndex != 0xFFFFFFFF); // Ra = TlsGetValue( gpTermsrvTlsIndex ); // if (Ra != NULL) { // //DbgPrint( "GetTermsrCompatFlags: Return cached compatflags (Ra)%lx for app %ws\n",Ra,lpModName ); // *pdwCompatFlags = (DWORD)PtrToUlong(Ra); // return TRUE; // } // } #if 0 if (NtCurrentTeb()->CtxCompatFlags & TERMSRV_COMPAT_TEBVALID) { *pdwCompatFlags = NtCurrentTeb()->CtxCompatFlags; return(TRUE); } #endif ul = sizeof(TERMSRV_COMPAT_APP) + (wcslen(lpModName) + 1)*sizeof(WCHAR); UniBuff = RtlAllocateHeap(RtlProcessHeap(), 0, ul); if (UniBuff) { wcscpy(UniBuff, TERMSRV_COMPAT_APP); wcscat(UniBuff, lpModName); RtlInitUnicodeString(&UniString, UniBuff); } } else { RtlInitUnicodeString(&UniString, (CompatType == CompatibilityIniFile) ? TERMSRV_COMPAT_INIFILE : TERMSRV_COMPAT_REGENTRY ); } // Determine the value info buffer size ulcbuf = sizeof(KEY_VALUE_FULL_INFORMATION) + MAX_PATH*sizeof(WCHAR) + sizeof(ULONG); pKeyValInfo = RtlAllocateHeap(RtlProcessHeap(), 0, ulcbuf); // Did everything initialize OK? if (UniString.Buffer && pKeyValInfo) { InitializeObjectAttributes(&ObjectAttributes, &UniString, OBJ_CASE_INSENSITIVE, NULL, NULL ); NtStatus = NtOpenKey(&hKey, KEY_READ, &ObjectAttributes); if (NT_SUCCESS(NtStatus)) { // If we're not checking for a registry entry, just try to get // the value for the key if (CompatType != CompatibilityRegEntry) { RtlInitUnicodeString(&UniString, CompatType == CompatibilityApp ? COMPAT_FLAGS : lpModName); NtStatus = NtQueryValueKey(hKey, &UniString, KeyValuePartialInformation, pKeyValInfo, ulcbuf, &ul); if (NT_SUCCESS(NtStatus) && (REG_DWORD == pKeyValInfo->Type)) { *pdwCompatFlags = *(PULONG)pKeyValInfo->Data; ulRetCode = TRUE; } // // Cache the appcompatiblity flags // // if (CompatType == CompatibilityApp) { // if (wcsstr(NtCurrentPeb()->ProcessParameters->ImagePathName.Buffer, L"ntvdm.exe")) { // TlsSetValue(gpTermsrvTlsIndex,(PVOID)((*pdwCompatFlags)| TERMSRV_COMPAT_TEBVALID)); // //DbgPrint( "GetTermsrCompatFlags: Setting cached compatflags (gCompatFlags)%lx for WOW app %ws\n",((*pdwCompatFlags)| TERMSRV_COMPAT_TEBVALID),lpModName ); // } else { // gCompatFlags = *pdwCompatFlags; // //DbgPrint( "GetTermsrCompatFlags: Setting cached compatflags (gCompatFlags)%lx for app %ws\n",gCompatFlags,lpModName ); // } // } if (CompatType == CompatibilityApp) { if(!gdwAppType) { DWORD dwTmp = 0; GetAppTypeAndModName(&dwTmp, NULL, 0); } if (gdwAppType == TERMSRV_COMPAT_WIN32) { gCompatFlags = *pdwCompatFlags; } } // For registry keys, we need to enumerate all of the keys, and // check if the the substring matches our current path. } else { PWCH pwch; ULONG ulKey = 0; PKEY_VALUE_FULL_INFORMATION pKeyFullInfo; pKeyFullInfo = (PKEY_VALUE_FULL_INFORMATION)pKeyValInfo; // Get to the software section pwch = Ctx_wcsistr(lpModName, L"\\software"); // Skip past the next backslash if (pwch) { pwch = wcschr(pwch + 1, L'\\'); } // We don't need to look for a key if this isn't in the user // software section if (pwch) { // Skip over the leading backslash pwch++; // Go through each value, looking for this path while (NtEnumerateValueKey(hKey, ulKey++, KeyValueFullInformation, pKeyFullInfo, ulcbuf, &ul) == STATUS_SUCCESS) { if (!_wcsnicmp(pKeyFullInfo->Name, pwch, pKeyFullInfo->NameLength/sizeof(WCHAR))) { *pdwCompatFlags = *(PULONG)((PCHAR)pKeyFullInfo + pKeyFullInfo->DataOffset); ulRetCode = TRUE; break; } } } } NtClose(hKey); } else { if (NtStatus == STATUS_OBJECT_NAME_NOT_FOUND && CompatType == CompatibilityApp) { if(!gdwAppType) { DWORD dwTmp = 0; GetAppTypeAndModName(&dwTmp, NULL, 0); } if (gdwAppType == TERMSRV_COMPAT_WIN32) { gCompatFlags = 0; ulRetCode = TRUE; } } } } // Free up the buffers we allocated // Need to zero out the buffers, because some apps (MS Internet Assistant) // won't install if the heap is not zero filled. if (UniBuff) { memset(UniBuff, 0, UniString.MaximumLength); RtlFreeHeap( RtlProcessHeap(), 0, UniBuff ); } if (pKeyValInfo) { memset(pKeyValInfo, 0, ulcbuf); RtlFreeHeap( RtlProcessHeap(), 0, pKeyValInfo ); } return(ulRetCode); } //***************************************************************************** // CtxGetBadAppFlags - // // Gets the Citrix badapp and compatibility flags for the specified task. // // Parameters: // LPWSTR lpModName (IN) - Image name to look up in registry // PBADAPP pBadApp (OUT) - Structure to used to return flags // // Return Value: // TRUE on success, FALSE on failure. // // Flag values are defined in syslib.h. // //***************************************************************************** BOOL CtxGetBadAppFlags(LPWSTR lpModName, PBADAPP pBadApp) { NTSTATUS NtStatus; OBJECT_ATTRIBUTES ObjectAttributes; UNICODE_STRING UniString; HKEY hKey = 0; ULONG ul, ulcnt, ulcbuf, ulrc = FALSE; PKEY_VALUE_PARTIAL_INFORMATION pKeyValueInfo = NULL; LPWSTR UniBuff; PWCHAR pwch; static ULONG badappregdefaults[3] = {1,15,5}; static BOOL fgotdefaults = FALSE; WCHAR *pbadappNameValue[] = { COMPAT_MSGQBADAPPSLEEPTIMEINMILLISEC, COMPAT_FIRSTCOUNTMSGQPEEKSSLEEPBADAPP, COMPAT_NTHCOUNTMSGQPEEKSSLEEPBADAPP, COMPAT_FLAGS }; // Get the executable name only, no path. pwch = wcsrchr(lpModName, L'\\'); if (pwch) { pwch++; } else { pwch = lpModName; } // Get the buffers we need ul = sizeof(TERMSRV_COMPAT_APP) + (wcslen(pwch) + 1)*sizeof(WCHAR); UniBuff = RtlAllocateHeap(RtlProcessHeap(), 0, ul); ulcbuf = sizeof(KEY_VALUE_PARTIAL_INFORMATION) + sizeof(ULONG); pKeyValueInfo = RtlAllocateHeap(RtlProcessHeap(), 0, ulcbuf); if (UniBuff && pKeyValueInfo) { if (!fgotdefaults) { // Get the default values from the registry RtlInitUnicodeString(&UniString, TERMSRV_REG_CONTROL_NAME ); InitializeObjectAttributes(&ObjectAttributes, &UniString, OBJ_CASE_INSENSITIVE, NULL, NULL ); NtStatus = NtOpenKey(&hKey, KEY_READ, &ObjectAttributes); if (NT_SUCCESS(NtStatus)) { for (ulcnt = 0; ulcnt < 3; ulcnt++) { RtlInitUnicodeString(&UniString, pbadappNameValue[ulcnt]); NtStatus = NtQueryValueKey(hKey, &UniString, KeyValuePartialInformation, pKeyValueInfo, ulcbuf, &ul); if (NT_SUCCESS(NtStatus) && (REG_DWORD == pKeyValueInfo->Type)) { badappregdefaults[ulcnt] = *(PULONG)pKeyValueInfo->Data; } } NtClose(hKey); } fgotdefaults = TRUE; } wcscpy(UniBuff, TERMSRV_COMPAT_APP); wcscat(UniBuff, pwch); // Remove the extension if (pwch = wcsrchr(UniBuff, L'.')) { *pwch = '\0'; } RtlInitUnicodeString(&UniString, UniBuff ); InitializeObjectAttributes(&ObjectAttributes, &UniString, OBJ_CASE_INSENSITIVE, NULL, NULL ); NtStatus = NtOpenKey(&hKey, KEY_READ, &ObjectAttributes); if (NT_SUCCESS(NtStatus)) { ulrc = TRUE; for (ulcnt = 0; ulcnt < 4; ulcnt++) { RtlInitUnicodeString(&UniString, pbadappNameValue[ulcnt]); NtStatus = NtQueryValueKey(hKey, &UniString, KeyValuePartialInformation, pKeyValueInfo, ulcbuf, &ul); if (NT_SUCCESS(NtStatus) && (REG_DWORD == pKeyValueInfo->Type)) { switch (ulcnt) { case 0: pBadApp->BadAppTimeDelay = RtlEnlargedIntegerMultiply( *(PULONG)pKeyValueInfo->Data, -10000 ); break; case 1: pBadApp->BadAppFirstCount = *(PULONG)pKeyValueInfo->Data; break; case 2: pBadApp->BadAppNthCount = *(PULONG)pKeyValueInfo->Data; break; case 3: pBadApp->BadAppFlags = *(PULONG)pKeyValueInfo->Data; break; } } else { switch (ulcnt) { case 0: pBadApp->BadAppTimeDelay = RtlEnlargedIntegerMultiply( badappregdefaults[ulcnt], -10000 ); break; case 1: pBadApp->BadAppFirstCount = badappregdefaults[ulcnt]; break; case 2: pBadApp->BadAppNthCount = badappregdefaults[ulcnt]; break; case 3: pBadApp->BadAppFlags = 0; break; } } } NtClose(hKey); } } // Free the memory we allocated // Need to zero out the buffers, because some apps (MS Internet Assistant) // won't install if the heap is not zero filled. if (UniBuff) { memset(UniBuff, 0, UniString.MaximumLength); RtlFreeHeap( RtlProcessHeap(), 0, UniBuff ); } if (pKeyValueInfo) { memset(pKeyValueInfo, 0, ulcbuf); RtlFreeHeap( RtlProcessHeap(), 0, pKeyValueInfo ); } return(ulrc); } //***************************************************************************** // GetCitrixCompatClipboardFlags - // // Returns the Citrix compatibility clipboard flags for the specified // application // // Parameters: // LPWSTR lpModName (IN) - Image name to look up in registry // LPDWORD pdwCompatFlags (OUT) - Ptr to DWORD return value for clipboard flags // // Return Value: // TRUE on success, FALSE on failure. // // Notes: // Flag values are defined in syslib.h. // //***************************************************************************** ULONG GetCitrixCompatClipboardFlags(LPWSTR lpModName, LPDWORD pdwClipboardFlags) { NTSTATUS NtStatus; OBJECT_ATTRIBUTES ObjectAttributes; UNICODE_STRING UniString; HKEY hKey = 0; ULONG ul, ulcbuf; PKEY_VALUE_PARTIAL_INFORMATION pKeyValInfo = NULL; ULONG ulRetCode = FALSE; LPWSTR UniBuff = NULL; UniString.Buffer = NULL; ul = sizeof(TERMSRV_COMPAT_APP) + (wcslen(lpModName) + 1)*sizeof(WCHAR); UniBuff = RtlAllocateHeap(RtlProcessHeap(), 0, ul); if (UniBuff) { wcscpy(UniBuff, TERMSRV_COMPAT_APP); wcscat(UniBuff, lpModName); RtlInitUnicodeString(&UniString, UniBuff); } // Determine the value info buffer size ulcbuf = sizeof(KEY_VALUE_FULL_INFORMATION) + MAX_PATH*sizeof(WCHAR) + sizeof(ULONG); pKeyValInfo = RtlAllocateHeap(RtlProcessHeap(), 0, ulcbuf); // Did everything initialize OK? if (UniString.Buffer && pKeyValInfo) { InitializeObjectAttributes(&ObjectAttributes, &UniString, OBJ_CASE_INSENSITIVE, NULL, NULL ); NtStatus = NtOpenKey(&hKey, KEY_READ, &ObjectAttributes); if (NT_SUCCESS(NtStatus)) { RtlInitUnicodeString(&UniString, COMPAT_CLIPBOARDFLAGS ); NtStatus = NtQueryValueKey(hKey, &UniString, KeyValuePartialInformation, pKeyValInfo, ulcbuf, &ul); if (NT_SUCCESS(NtStatus) && (REG_DWORD == pKeyValInfo->Type)) { *pdwClipboardFlags = *(PULONG)pKeyValInfo->Data; ulRetCode = TRUE; } NtClose(hKey); } } // Free up the buffers we allocated // Need to zero out the buffers, because some apps (MS Internet Assistant) // won't install if the heap is not zero filled. if (UniBuff) { memset(UniBuff, 0, UniString.MaximumLength); RtlFreeHeap( RtlProcessHeap(), 0, UniBuff ); } if (pKeyValInfo) { memset(pKeyValInfo, 0, ulcbuf); RtlFreeHeap( RtlProcessHeap(), 0, pKeyValInfo ); } return(ulRetCode); } //***************************************************************************** // CitrixGetAppModuleName - // // Extracts the module name for a given process handle. The directory // path and the file extension are stripped off. // // Parameters: // HANDLE ProcHnd (IN) - Handle to the process // LPWSTR buffer (IN) - buffer used to return the module // LPWSTR lpModName (IN) - Available size of the buffer in bytes // LPDWORD pdwCompatFlags (OUT) - Ptr to DWORD return value for clipboard flags // // Return Value: // TRUE on success, FALSE on failure. // // Notes: // Function only works for 32 bit windows applications // //***************************************************************************** BOOLEAN CitrixGetAppModuleName ( HANDLE ProcHnd, LPWSTR Buffer, ULONG Length ) { PROCESS_BASIC_INFORMATION ProcInfo; ULONG retLen; PEB peb; RTL_USER_PROCESS_PARAMETERS params; WCHAR pwcAppName[MAX_PATH]; PWCHAR pwch; if ( NtQueryInformationProcess( ProcHnd, ProcessBasicInformation, (PVOID) &ProcInfo, sizeof(ProcInfo), &retLen ) ) { return ( FALSE ); } if ( !ProcInfo.PebBaseAddress ) { return ( FALSE ); } if ( ! ReadProcessMemory(ProcHnd, (PVOID)ProcInfo.PebBaseAddress, &peb, sizeof(peb), NULL ) ) { return ( FALSE ); } if ( !ReadProcessMemory(ProcHnd, peb.ProcessParameters, ¶ms, sizeof(params), NULL ) ) { return ( FALSE ); } if ( !ReadProcessMemory( ProcHnd, params.ImagePathName.Buffer, pwcAppName, sizeof(pwcAppName), NULL) ) { return ( FALSE ); } pwch = wcsrchr(pwcAppName, L'\\'); if ( pwch ) { pwch++; } else { pwch = pwcAppName; } if ( wcslen(pwch) >= (Length / sizeof(WCHAR)) ) { return ( FALSE ); } wcscpy(Buffer, pwch); // Remove the extension if (pwch = wcsrchr(Buffer, L'.')) { *pwch = '\0'; } return ( TRUE ); } // Globals for logging // We cache the compatibility flags for the running 32 bit app. // If the logging is enabled for ntvdm, we'll check the flags of the // Win16 or DOS app on each object create. DWORD CompatFlags = 0; BOOL CompatGotFlags = FALSE; DWORD CompatAppType = TERMSRV_COMPAT_WIN32; void CtxLogObjectCreate(PUNICODE_STRING ObjName, PCHAR ObjType, PVOID RetAddr) { CHAR RecBuf[2 * MAX_PATH]; CHAR ObjNameA[MAX_PATH]; PCHAR DllName; WCHAR FileName[MAX_PATH]; WCHAR ModName[MAX_PATH]; ANSI_STRING AnsiString; PRTL_PROCESS_MODULES LoadedModules; PRTL_PROCESS_MODULE_INFORMATION Module; HANDLE LogFile; OVERLAPPED Overlapped; NTSTATUS Status; ULONG i; DWORD BytesWritten; DWORD lCompatFlags; // For Win16 or DOS Apps DWORD AppType = 0; ULONG AllocSize = 4096; BOOL NameFound = FALSE; // Determine the log file name if (GetEnvironmentVariableW(OBJ_LOG_PATH_VAR, FileName, MAX_PATH)) { if (GetAppTypeAndModName(&AppType,ModName,sizeof(ModName))) { if (AppType != TERMSRV_COMPAT_WIN32 ) { // Logging was enabled for ntvdm - check the // compatibility flags of the Win16 or DOS app if (!GetTermsrCompatFlags(ModName, &lCompatFlags, CompatibilityApp) || !(lCompatFlags & TERMSRV_COMPAT_LOGOBJCREATE)) return; } if ((wcslen(FileName) + wcslen(ModName) + 2) <= MAX_PATH) { lstrcatW(FileName, L"\\"); lstrcatW(FileName,ModName); lstrcatW(FileName,L".log"); } else return; } else return; } else return; //Format the log record AnsiString.Buffer = ObjNameA; AnsiString.MaximumLength = MAX_PATH; RtlUnicodeStringToAnsiString(&AnsiString, ObjName, FALSE); // Try to get the DLL name of the caller AllocSize = 4096; for (;;) { LoadedModules = (PRTL_PROCESS_MODULES) RtlAllocateHeap(RtlProcessHeap(), 0, AllocSize); if (!LoadedModules) { return; } Status = LdrQueryProcessModuleInformation(LoadedModules, AllocSize, NULL); if (NT_SUCCESS(Status)) { break; } if (Status == STATUS_INFO_LENGTH_MISMATCH) { RtlFreeHeap( RtlProcessHeap(), 0, LoadedModules ); LoadedModules = NULL; AllocSize += 4096; continue; } // Other error; RtlFreeHeap( RtlProcessHeap(), 0, LoadedModules ); return; } for (i=0,Module = &LoadedModules->Modules[0]; iNumberOfModules; i++, Module++ ) { if ((RetAddr >= Module->ImageBase) && ((ULONG_PTR) RetAddr < (((ULONG_PTR)Module->ImageBase) + Module->ImageSize))) { NameFound = TRUE; DllName = Module->FullPathName; break; } } if (!NameFound) { DllName = "DLL Not Found"; } sprintf(RecBuf,"Create %s name: %s Return Addr: %p (%s)\n", ObjType, ObjNameA, RetAddr, DllName); if (LoadedModules) { RtlFreeHeap( RtlProcessHeap(), 0, LoadedModules ); LoadedModules = NULL; } // Write log record if ((LogFile = CreateFileW(FileName, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, 0, NULL)) == INVALID_HANDLE_VALUE ) { return; } // Lock the file exclusive since we always write at the end. // We get mutual exclusion by always locking the first 64k bytes Overlapped.Offset = 0; Overlapped.OffsetHigh = 0; Overlapped.hEvent = NULL; LockFileEx(LogFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 0x10000, 0, &Overlapped); // Write at the end of the file SetFilePointer(LogFile, 0, NULL, FILE_END); WriteFile(LogFile, RecBuf, strlen(RecBuf), &BytesWritten, NULL); UnlockFileEx(LogFile, 0, 0x10000, 0, &Overlapped); CloseHandle(LogFile); } //***************************************************************************** // CtxGetCrossWinStationDebug - // // Gets the Citrix Cross Winstation debug flag // // Parameters: // NONE // Return Value: // TRUE Cross WinStation Debug enabled // FALSE CrosS WinStation Debug Disabled // // //***************************************************************************** BOOL CtxGetCrossWinStationDebug() { NTSTATUS NtStatus; OBJECT_ATTRIBUTES ObjectAttributes; UNICODE_STRING UniString; HKEY hKey = 0; ULONG ul, ulcnt, ulcbuf, ulrc = FALSE; PKEY_VALUE_PARTIAL_INFORMATION pKeyValueInfo = NULL; LPWSTR UniBuff; ULONG Flag = 0; // Get the buffers we need ul = sizeof(TERMSRV_REG_CONTROL_NAME); UniBuff = RtlAllocateHeap(RtlProcessHeap(), 0, ul); ulcbuf = sizeof(KEY_VALUE_PARTIAL_INFORMATION) + sizeof(ULONG); pKeyValueInfo = RtlAllocateHeap(RtlProcessHeap(), 0, ulcbuf); if (UniBuff && pKeyValueInfo) { RtlInitUnicodeString(&UniString, TERMSRV_REG_CONTROL_NAME ); InitializeObjectAttributes(&ObjectAttributes, &UniString, OBJ_CASE_INSENSITIVE, NULL, NULL ); NtStatus = NtOpenKey(&hKey, KEY_READ, &ObjectAttributes); if (NT_SUCCESS(NtStatus)) { RtlInitUnicodeString(&UniString, TERMSRV_CROSS_WINSTATION_DEBUG); NtStatus = NtQueryValueKey(hKey, &UniString, KeyValuePartialInformation, pKeyValueInfo, ulcbuf, &ul); if ( NT_SUCCESS(NtStatus) ) { if ( REG_DWORD == pKeyValueInfo->Type ) { Flag = *(PULONG)pKeyValueInfo->Data; } } NtClose(hKey); } } // Free the memory we allocated // Need to zero out the buffers, because some apps (MS Internet Assistant) // won't install if the heap is not zero filled. if (UniBuff) { memset(UniBuff, 0, UniString.MaximumLength); RtlFreeHeap( RtlProcessHeap(), 0, UniBuff ); } if (pKeyValueInfo) { memset(pKeyValueInfo, 0, ulcbuf); RtlFreeHeap( RtlProcessHeap(), 0, pKeyValueInfo ); } return( Flag ? TRUE : FALSE ); } //***************************************************************************** // CtxGetModuleBadClpbrdAppFlags - // // Gets the Citrix BadClpbrdApp and compatibility flags for the specified // module. // // Parameters: // LPWSTR lpModName (IN) - Image name to look up in registry // PBADCLPBRDAPP pBadClpbrdApp (OUT) - Structure to used to return flags // // Return Value: // TRUE on success, FALSE on failure. // // The BADCLPBRDAPP structure is defined in: // base\client\citrix\compatfl.h and user\inc\user.h. // //***************************************************************************** BOOL CtxGetModuleBadClpbrdAppFlags(LPWSTR lpModName, PBADCLPBRDAPP pBadClpbrdApp) { NTSTATUS NtStatus; OBJECT_ATTRIBUTES ObjectAttributes; UNICODE_STRING UniString; HKEY hKey = 0; ULONG ul, ulcnt, ulcbuf, ulrc = FALSE; PKEY_VALUE_PARTIAL_INFORMATION pKeyValueInfo = NULL; LPWSTR UniBuff; PWCHAR pwch; WCHAR *pbadappNameValue[] = { COMPAT_OPENCLIPBOARDRETRIES, COMPAT_OPENCLIPBOARDDELAYINMILLISECS, COMPAT_CLIPBOARDFLAGS, NULL }; // Get the executable name only, no path. pwch = wcsrchr(lpModName, L'\\'); if (pwch) { pwch++; } else { pwch = lpModName; } // Get the buffers we need ul = sizeof(TERMSRV_COMPAT_APP) + (wcslen(pwch) + 1)*sizeof(WCHAR); UniBuff = RtlAllocateHeap(RtlProcessHeap(), 0, ul); ulcbuf = sizeof(KEY_VALUE_PARTIAL_INFORMATION) + sizeof(ULONG); pKeyValueInfo = RtlAllocateHeap(RtlProcessHeap(), 0, ulcbuf); if (UniBuff && pKeyValueInfo) { wcscpy(UniBuff, TERMSRV_COMPAT_APP); wcscat(UniBuff, pwch); // Remove the extension if (pwch = wcsrchr(UniBuff, L'.')) { *pwch = '\0'; } RtlInitUnicodeString(&UniString, UniBuff ); InitializeObjectAttributes(&ObjectAttributes, &UniString, OBJ_CASE_INSENSITIVE, NULL, NULL ); NtStatus = NtOpenKey(&hKey, KEY_READ, &ObjectAttributes); if (NT_SUCCESS(NtStatus)) { ulrc = TRUE; for (ulcnt = 0; pbadappNameValue[ulcnt]; ulcnt++) { RtlInitUnicodeString(&UniString, pbadappNameValue[ulcnt]); NtStatus = NtQueryValueKey(hKey, &UniString, KeyValuePartialInformation, pKeyValueInfo, ulcbuf, &ul); if (NT_SUCCESS(NtStatus) && (REG_DWORD == pKeyValueInfo->Type)) { switch (ulcnt) { case 0: pBadClpbrdApp->BadClpbrdAppEmptyRetries = *(PULONG)pKeyValueInfo->Data; break; case 1: pBadClpbrdApp->BadClpbrdAppEmptyDelay = *(PULONG)pKeyValueInfo->Data; break; case 2: pBadClpbrdApp->BadClpbrdAppFlags = *(PULONG)pKeyValueInfo->Data; break; } } else { switch (ulcnt) { case 0: pBadClpbrdApp->BadClpbrdAppEmptyRetries = 0; break; case 1: pBadClpbrdApp->BadClpbrdAppEmptyDelay = 50; break; case 2: pBadClpbrdApp->BadClpbrdAppFlags = 0; break; } } } NtClose(hKey); } } // Free the memory we allocated // Need to zero out the buffers, because some apps (MS Internet Assistant) // won't install if the heap is not zero filled. if (UniBuff) { memset(UniBuff, 0, UniString.MaximumLength); RtlFreeHeap( RtlProcessHeap(), 0, UniBuff ); } if (pKeyValueInfo) { memset(pKeyValueInfo, 0, ulcbuf); RtlFreeHeap( RtlProcessHeap(), 0, pKeyValueInfo ); } return(ulrc); } //***************************************************************************** // CtxGetBadClpbrdAppFlags - // // Gets the Citrix BadClpbrdApp and compatibility flags for the // current task. // // Parameters: // LPWSTR lpModName (IN) - Image name to look up in registry // PBADCLPBRDAPP pBadClpbrdApp (OUT) - Structure to used to return flags // // Return Value: // TRUE on success, FALSE on failure. // // The BADCLPBRDAPP structure is defined in: // base\client\citrix\compatfl.h and user\inc\user.h. // //***************************************************************************** BOOL CtxGetBadClpbrdAppFlags(OUT PBADCLPBRDAPP pBadClpbrdApp) { WCHAR ModName[MAX_PATH+1]; DWORD dwAppType = 0; if (!GetAppTypeAndModName(&dwAppType, ModName, sizeof(ModName))) { return (FALSE); } // Get the flags return (CtxGetModuleBadClpbrdAppFlags(ModName, pBadClpbrdApp)); } //***************************************************************************** // // Same as GetTermsrCompatFlags(), except that the first argument is name // of an executable module with possible path and extention. // This func will strip path and extension, and then call GetTermsrCompatFlags() // with just the module name // //***************************************************************************** ULONG GetTermsrCompatFlagsEx(LPWSTR lpModName, LPDWORD pdwCompatFlags, TERMSRV_COMPATIBILITY_CLASS CompatType) { // drop the path and extension from the module name WCHAR *p, *e; int size; size = wcslen(lpModName); p = &lpModName[size-1]; // move to to the end of string // walk back to the start, break if you hit a back-slash while (p != lpModName) { if ( *p == TEXT('\\') ) { ++p; //move past the back-slash break; } --p; } // p is at the begining of the name of an executable. // get rid of the extension, set end pointer e to the start of str // move forward until you hit '.' e = p; while (*e) { if (*e == TEXT('.') ) { *e = TEXT('\0'); // terminate at "." break; } e++; } // 'p' is the module/executable name, no path, and no extension. return ( GetTermsrCompatFlags( p, pdwCompatFlags, CompatType) ); }