Remko Weijnen's Blog (Remko's Blog)

About Terminal Server, Citrix, Delphi and other stuff


965 views

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:

int preFrom = value.IndexOf(prePrefix, System.StringComparison.CurrentCultureIgnoreCase);
  • 0 Comments
  • Filed under: .NET, C#
  • 1,481 views

    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:

    foutmelding (2)

    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:

    $keyName = "SYSTEM\CurrentControlSet\Control\Terminal Server\Wds\icawd\thin16"
    $valueName = "MaxLVBMem"
    # Calculate your value! http://support.citrix.com/article/CTX114497
    [int]$value = 0xc00000

    # 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()

    1,358 views

    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

    image

  • 1 Comment
  • Filed under: General
  • 1,913 views

    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:

    Set objPrinter = GetObject( _
        "winmgmts:{impersonationLevel=Impersonate ," _
        & "(LoadDriver)}!//./root/cimv2:Win32_Printer")

    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:

    powershell.exe "& { $Wmi = ([wmiclass]‘Win32_Printer’) ; $Wmi.Scope.Options.EnablePrivileges = $true; gwmi win32_printer -ComputerName ‘ADNRD02′ -Filter ‘shared=true’ | foreach {$Wmi.AddPrinterConnection( [string]::Concat(‘\\’, $_.__SERVER, ‘\’, $_.ShareName) )} }"
    4,339 views

    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.

    Read the rest of this entry »

    3,116 views

    In Exchange it’s possible to hide a Mailbox from the (Global) Address List. You can do that in the Exchange System Manager:

    image

    But after you have hidden a Mailbox you cannot create an Outlook profile for it (or add it as an extra mailbox).

    image

    When you click Check Name in the wizard you’ll get an error:

    image

    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!

    Read the rest of this entry »

    1,058 views

    Just a quick note: if you want to determine the page size of the OS (Windows) you can use the GetSystemInfo function.

    Example:

    var
      SystemInfo: SYSTEM_INFO;
      PageSize: DWORD;
    begin
      GetSystemInfo(SystemInfo);
      PageSize := SystemInfo.dwPageSize;

    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:

    902 views

    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.

    Read the rest of this entry »

    1,672 views

    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:

    var
      Status : NTSTATUS;
      RetLength : DWORD;
      SessionInfo :  SYSTEM_SESSION_PROCESS_INFORMATION;
      CurrentProcess : PSystemProcesses;
    begin

      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!

    Read the rest of this entry »

    1,451 views

    SasLibEx Screencast

    I just recorded a SasLibEx Screencast, it shows some of the very powerfull features of SasLibEx.

    The following features are shown:

    • Simulate Ctrl Alt Del (Secure Attention Sequence)
    • Cancel Ctrl Alt Del
    • Lock Workstation
    • Unlock Workstation (without credentials)
    • Disable Ctrl Alt Del
    • Enable Ctrl Alt Del again
    • Cancel pending UAC request
    • Is Desktop Locked
    SasLibEx Feature Demo #1

    Profile

    Recent Tweets

    Views