About Terminal Server, Citrix, Delphi and other stuff
I don’t do much programming in .NET based languages but I have to for some things like the Windows Live Writer plugin I am creating.
I didn’t expect this but the String.IndexOf Method is by default case sensitive.
But we can make it case insensitive if we use one of the overloads: IndexOf(String, StringComparison).
Example:
On a Citrix XenApp 5 environment a user reported that he was unable to start a Full Screen session on a Dual Monitor Configuration.
He received this error message:
Citrix has a KB Article: “How to Allow More Memory for Session Graphics on Windows Server 2003” that explains exactly how we can solve this.
We need to change the MaxLVBMem registry value and we can use the Excel Sheet from the KB Article to calculate the proper value.
Please don’t set this value too high because a higher value means you will restrict other kernel memory pools.
You also need to deny the SYSTEM account the SetValue permission on the HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Memory Management key to prevent the Citrix IMA service from overwriting the new value.
So I wrote a small PowerShell script to change the permission and set the value:
# Set Identity to SYSTEM via it’s Well Known SID
[System.Security.Principal.SecurityIdentifier]$ident = "S-1-5-18"
# Open Registry Key (with Write Permissions)
$regKey = Get-Item "HKLM:"
$regKey = $regKey.OpenSubKey($keyName, $true)
# Fetch Existing permissions
$acl = $regKey.GetAccessControl()
# Construct a new Ace
$rights = [Enum]::Parse([Security.AccessControl.RegistryRights], "SetValue")
$deny = [Enum]::Parse([Security.AccessControl.AccessControlType], "Deny")
$rule = New-Object Security.AccessControl.RegistryAccessRule($ident, $rights, $deny)
# Add the new Ace to the Acl
$acl.AddAccessRule($rule)
# Apply the new Acl to the Registry key:
$regKey.SetAccessControl($acl)
# Now set the required Value
$regKey.SetValue($valueName, $value)
# Close the key
$regKey.Close()
If you want to be able save the password in Office Communicator you must create the key HKLM\Software\Microsoft\Communicator or on x64 OS HKLM\Software\Wow6432Node\Policies\Microsoft\Communicator and set the DWORD value SavePassword to 1.
Now you will have the Save my password checkbox (which will save the encrpted password to HKCU\Software\Microsoft\Communicator\AccountPassword
A few days ago I wrote about a PowerShell Script to Install Printer Drivers.
I noticed there was a problem with this script: some drivers fail to load with error 1797 which means ERROR_UNKNOWN_PRINTER_DRIVER.
I reread the AddPrinterConnection documentation on MSDN but it didn’t mention anything about additional required permissions or anything.
But then I read the remarks sections of the Win32_Printer Class and it mentions that for some operations the SeLoadDriverPrivilege is required.
In VBScript we can indicate it like this:
But how to do this in PowerShell?
I didn’t find a way to enable a specific privilege but we can enable all by setting Scope.Options.EnablePrivileges to $true.
So I modified the script like this:
I wrote a PowerShell script to install all printer drivers on a Citrix or Terminal Server.
Actually the script isn’t specific to Citrix or Terminal Server but on such environments we need to preload all drivers because users do not have the permissions to do that.
I have chosen for PowerShell because you can do it in a one-liner which makes it easy to run this script from my Altiris server on all Citrix Servers.
The idea is that we enumerate all the shared printers on a Printer Server and make a connection to each printer. This will make sure that the driver is installed if it wasn’t already present.
The script could even be scheduled to enforce that newly added printer drivers are added to each Citrix Server.
In Exchange it’s possible to hide a Mailbox from the (Global) Address List. You can do that in the Exchange System Manager:
But after you have hidden a Mailbox you cannot create an Outlook profile for it (or add it as an extra mailbox).
When you click Check Name in the wizard you’ll get an error:
The common workaround is to remove the “Hide from Exchange address lists” setting, create the profile (or add the Mailbox) and afterwards set it again.
Once the profile is created it all keeps working.
There is an easier solution though!
Just a quick note: if you want to determine the page size of the OS (Windows) you can use the GetSystemInfo function.
Example:
Note that MSDN recommends to use the GetNativeSystemInfo function when running in a 32 bit app on an x64 OS (and you can use the IsWow64Process function to determine that).
One example where you need to know the PageSize is when you want to create a Paging File using the NtCreatePagingFile function because this function requires that the MinimumSize and MaximumSize parameters are a multiple of the PageSize.
Some interesting links on the subject:
I am doing a project involving a Citrix Xenapp environment running on VMWare vSphere.
The physical machines are powered by two Eaton Uninterruptable Power Supplies that both a network card.
I received some documentation that describes how to implement automatic shutdown in a VMWare vSphere environment.
This documentation describes that a vSphere Management Assistant (vMA) must be deployed in which we need to install some software from Eaton.
I followed the documentation that even described the needed iptables rules needed for their software.
In the last step a discovery is done and the UPS is supposed to be found. And you have probably guessed by now: it didn’t!
At first I figured that maybe the iptables configuration was still too tight so I stopped the iptables service but that didn’t help.
As you may know, you can enumerate processes of a specific Terminal Server or Citrix session using the NtQuerySystemInformation function.
On x86 system the code below works fine:
SessionInfo.SessionId := SessionId;
SessionInfo.Buffer := nil;
SessionInfo.SizeOfBuf := 0;
Status := NtQuerySystemInformation(SystemSessionProcessesInformation, @SessionInfo, SizeOf(SessionInfo), @RetLength);
while (Status = STATUS_INFO_LENGTH_MISMATCH) do
begin
SessionInfo.SizeOfBuf := RetLength;
SessionInfo.Buffer := GetMemory(RetLength);
try
Status := NtQuerySystemInformation(SystemSessionProcessesInformation, @SessionInfo, SizeOf(SessionInfo), @RetLength);
if NT_SUCCESS(Status) then
begin
CurrentProcess := SessionInfo.Buffer;
while True do
begin
// Your code here
if (CurrentProcess^.NextEntryDelta = 0) then
Break;
Cardinal(CurrentProcess) := Cardinal(CurrentProcess) + CurrentProcess^.NextEntryDelta;
end;
Exit;
end;
finally
FreeMemory(SessionInfo.Buffer);
end;
end;
SetLastError(RtlNtStatusToDosError(Status));
RaiseLastOSError;
end;
While this works fine on Windows XP and 2003 x86, it fails to work correctly on the x64 versions of Windows XP and 2003 (or maybe even higher).
The problem is that RetLength is always SizeOf(SYSTEM_SESSION_PROCESS_INFORMATION) and thus we are in an endless loop!
I just recorded a SasLibEx Screencast, it shows some of the very powerfull features of SasLibEx.
The following features are shown:
Active Directory Altiris bug Citrix Dell Delphi Exchange Exchange2003 Exchange2010 Hewlett-Packard HP iOS Jailbreak Java LinkedIn Linux MSI MySQL Navigation Objects Office Outlook Passat PowerPoint PowerShell referall was returned RNS315 RNS510 SasLibEx script slow Terminal Server ThinApp TSAdmin TSAdminEx VBS VCDS Vista VMWare Volkswagen Windows PE WLAN Wordpress WTSWaitSystemEvent wts_event_flush
WP Cumulus Flash tag cloud by Roy Tanck requires Flash Player 9 or better.