background
Domenica, 23 Dicembre 2012 11:47

Come interrogare la Farm Citrix in C#

Questo tutorial vi mostrerà come poter creare semplici applicazioni in C# per interagire con la farm Citrix.

L'articolo si basa su una farm citrix composta da server con Citrix Presentation server 4.5. Se avete versioni differenti vi consiglio di leggere comunque questo tutorial. Seguendo la guida sarete in grado di recuperare informazioni quali, per esempio, il nome di ciascun server della farm, quali sono gli utenti che utilizzano una determinata applicazione pubblicata, il nome dei client collegati (così come la versione del client Citrix) e così via. Potrete sfruttare le conoscenze acquisite anche con altre versioni di Citrix.

Per questo tutorial darò per scontato che voi abbiate le seguenti conoscenze/prerequisiti:

  1. Conoscenza base di C# e Visual Studio.
  2. Buona conoscenza di Citrix Presentation server 4.5

 

L'ambiente di esempio

L'articolo si basa su codice che ho scritto ed utilizzato nelle mie precedenti esperienze lavorative, spiegando passo passo (dall'installazione dei componenti Citrix arrivando alla stesura del codice) come realizzare applicazioni in grado di interagire con la farm Citrix. Ovviamente non posso utilizzare i nomi dei server delle società per le quali ho lavorato, così li ho sostituiti con nomi di fantasia. Come ho già scritto, non preoccupatevi se il vostro ambiente è diverso da quello dell'esempio. Se leggerete tutto il tutorial sarete in grado di apportare le modifiche necessarie per adattare il codice alle vostre esigenze.

La nostra farm di esempio consiste di quattro server Citrix: srvctx001, srvctx002, srvctx003 e srvctx004. Tutti a 32 bit, con Windows server 2003 e Citrix Presentation server 4.5.
Tutti fanno parte del dominio: domain.local. La nostra macchina di sviluppo, chiamata wsdev010 (anch'essa facente parte del dominio: domain.local) è una Windows 7 a 32 bit con visual studio 2010 (ma altre versioni andranno bene lo stesso). Il nostro account di dominio (quello che usiamo per collegarci alla macchina di sviluppo) è usr003. L'account è amministratore di macchina.

 

Cominciamo

Per prima cosa dobbiamo scaricare il pacchetto Citrix Presentation Server SDK (MPSSDK) 4.5 dal link: http://support.citrix.com/article/CTX114194. Dopodiché dobbiamo installarlo sulla nostra macchina di sviluppo (che nell'esempio è wsdev010) seguendo i passi qui riportati:

  1. Eseguire MPSSDK.msi. Non date importanza al messaggio "The system does not have a supported C++ compiler installed, you may not be able to compile the exaple program", andate avanti.
  2. A questo punto completare i passi dell'installazione:
    1. Leggere ed accettare (se si è concordi) i termini di licenza
    2. Specificare la cartella di installazione del pacchetto MPSSDK; la cartella di default è %SystemDrive%\Program Files\Citrix\MPSSDK.
    3. Registrare il componente MFCOM per l'accesso remoto ed inserire il nome di uno dei server della farm citrix. Nel nostro esempio ho inserito srvctx001. Potete cambiarlo dopo.
  3. Assicurarsi quindi di avere il componente DCOM chiamato "MetaFrame COM Server". Andare su Control Panel -> System and Security -> Administrative Tools -> Component Services (o lanciare il comando DCOMCNFG), andare su "DCOM Config" e controllare la presenza dell'icona "MetaFrame COM Server" come illustrato nell'immagine in basso. Se non trovate l'oggetto dcom allora dovrete lanciare questi comandi come amministratori (tramite il Run as administrator se avete Windows 7 e l'uac attivo):
    1. "mpssdk home directory\utils\mfreg.exe /u".
    2. "mpssdk home directory\utils\mfreg.exe nome_server_citrix". Per questo esempio ho inserito srvctx001. Se volete potrete cambiarlo dopo nelle proprietà dell'oggetto "MetaFrame COM Server" (la nostra dcom).

dcom

Se guardate nella cartella di installazione del MPSSDK potrete trovare molti esempi scritti in linguaggi differenti.

Nota: le applicazioni che svilupperete utilizzando il .NET ed i componenti del pacchetto MPSSDK funzioneranno solo su macchine su cui è stato installato questo pacchetto o su server che hanno Citrix Presentation Server installato. In ogni caso in questo tutorial vi spiegherò tutto passo passo. Vi consiglio di leggere anche la guida THE ULTIMATE GUIDE TO MFCOM per approfondire meglio tutti questi concetti.

 

Sistemare i permessi

Il codice scritto interagisce con la farm Citrix tramite l'account (o più precisamente il security context dell'utente) che esegue il codice, nel nostro esempio è usr003. Assicuratevi quindi che l'utente abbia i giusti permessi per interrogare la farm Citrix; nel nostro esempio ho chiesto agli amministratori della farm Citrix di dare all'utente usr003 i giusti permessi per leggere la configurazione e le proprietà della farm. Inoltre l'utente deve essere un membro del gruppo locale "Distributed COM Users" del server che voi avete inserito nell'oggetto dcom della vostra macchina di sviluppo (nelle proprietà dell'oggetto dcom "MetaFrame COM Server"). Quindi io ho fatto inserire l'utente usr003 nel gruppo "Distributed COM Users" del server srvctx001. Di seguito potete trovare le eccezioni più comuni che verranno generate dal vostro codice in caso di errore:

  1. Se l'utente non è membro del gruppo "Distributed COM Users" del server remoto (nel nostro caso srvctx001) riceverete il seguente errore: System.UnauthorizedAccessException: Retrieving the COM class factory for component with CLSID {ED62F4E2-63C2-11D4-94D8-00C04FB0F326} failed due to the following error: 80070005 Access is denied. (Exception from HRESULT: 0x80070005 (E_ACCESSDENIED)).
  2. Se l'utente non può leggere la configurazione della farm Citrix riceverete il seguente errore: System.UnauthorizedAccessException: Access is denied. (Exception from HRESULT: 0x80070005 (E_ACCESSDENIED))
  3. Se il server remoto (nel nostro esempio srvctx001) non è raggiungibile riceverete il seguente errore: System.Runtime.InteropServices.COMException (0x800706BA): Retrieving the COM class factory for component with CLSID {ED62F4E2-63C2-11D4-94D8-00C04FB0F326} failed due to the following error: 800706ba The RPC server is unavailable. (Exception from HRESULT: 0x800706BA).

Prima di iniziare a scrivere il codice bisogna assicurarsi di avere i permessi corretti. Se dovete solo leggere dati dalla farm allora vi basteranno diritti di lettura. Se invece dovete scrivere codice per cambiare le proprietà della farm (come ad esempio pubblicare applicazioni, cambiare utenti e così via) allora vi serviranno anche i diritti di scrittura.

 

Primo esempio: recuperare il nome di tutti i server Citrix

Sulla vostra macchina di sviluppo (nel nostro esempio wsdev010) aprite visual studio e create un nuovo progetto in C# (di tipo console application) chiamato, per esempio, listCtxServer. Io ho usato il Framework .NET 4.0 ma voi potete usare anche altre versioni. Ora dovete aggiungere al progetto una nuova reference, essa ci permetterà di chiamare le classi e gli oggetti dell' mfcom. Dovete aggiungere la dll seguente: "Home directory dell'mpssdk\Bin.Net\mfcom.dll" come indicato anche nella figura sottostante.



I commenti nel codice sottostante vi permetteranno di capire il funzionamento del programma.

//The following four namespaces are added by default
//when you create a C# Console application (with .NET 4.0)
//if you use other version of .NET Framework namespaces will
//probably be different. Don't worry about that. We don't need all of them.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

//the below namespace allows us to use classes and objects of the mfcom.
//REMEMBER: you have to add mfcom.dll as reference
using MetaFrameCOM;


namespace listCtxServer
{
    class Program
    {
        static void Main(string[] args)
        {
            try //with a try catch block we can We can handle exceptions
            {
                //create and initialize the Citrix MetaFrame Farm object
                MetaFrameFarm Farm = new MetaFrameFarm();
                Farm.Initialize(MetaFrameObjectType.MetaFrameWinFarmObject);

                //loop trhow each server of the citrix farm
                foreach (IMetaFrameServer6 ctxServer in Farm.Servers)
                {
                    //print the server name
                    Console.WriteLine(ctxServer.ServerName);
                }

                //wait until the user presses any key on the keyboard
                Console.WriteLine("Press any key to continue . . .");
                Console.ReadKey(); 
            }
            catch (Exception ex)
            {
                //if an error has occured then print the error message
                Console.WriteLine("AN ERROR HAS OCCURED:\n");
                Console.WriteLine(ex.ToString());
                //wait until the user presses any key on the keyboard
                Console.WriteLine("Press any key to continue . . .");
                Console.ReadKey(); 
            }

        }
    }
}

Come potete vedere il codice di questo esempio è molto semplice.

 

Secondo esempio: recuperare la versione del client Citrix

Questo esempio vi permetterà di capire come recuperare la versione dei client Citrix collegati alla farm. Il codice stamperà a video anche il nome utente ed il nome macchina dei PC collegati alla farm. Quindi, aprite Visual Studio e create un nuovo progetto in C# (sempre una console application) e chiamatelo, per esempio, listCtxClients. Come fatto prima dovete aggiungere la dll mfcom.dll

Sfortunatamente per noi, quando interroghiamo la farm tramite mfcom, la farm non ci restituisce la versione dei client bensì un numero (chiamato Client Build) che dovremo decodificare. Questo topic del forum della Citrix mi ha aiutato parecchio.

Ecco il codice:

//The following four namespaces are added by default
//when you create a C# Console application (with .NET 4.0)
//if you use other version of .NET Framework namespaces will 
//probably be different. Don't worry about that. We don't need all of them.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

//the below namespace allows us to use classes and objects of the mfcom.
//REMEMBER: first of all, you have to add mfcom.dll as reference
//then you can use MetaFrameCOM
using MetaFrameCOM;

namespace listCtxClients
{
    class Program
    {
        static void Main(string[] args)
        {
            try //with a try catch block we can We can handle exceptions
            {
                //into this variable will save the client version
                int clientVersion = 0;
                string MainVer = "";

                //create and initialize the Citrix MetaFrame Farm object
                MetaFrameFarm Farm = new MetaFrameFarm();
                Farm.Initialize(MetaFrameObjectType.MetaFrameWinFarmObject);

                //for every session of every user on all servers in the Citrix Farm
                foreach (IMetaFrameSession6 UserSession in Farm.Sessions)
                {

                    /*rarely the method UserSession.ClientBuild jumps to COM Exception:
                      "System.Runtime.InteropServices.COMException (0x80004005): Error HRESULT E_FAIL has been returned 
                      from a call to a COM component" ,This is because, probably, the session has been 
                      broken down/no longer exists or the client has some problems.
                      I don't want exit from the loop for this Exception. So I put here another try catch
                     */

                    try
                    {
                        //if we succeed to get ClientBuild than save it
                        clientVersion = UserSession.ClientBuild;
                    }
                    catch
                    {
                        //else, save the error code "-555"
                        //later, with that code, we will print the message:
                        //"Unknown build code (code is: number of client build)"
                        clientVersion = -555;
                    }

                    //get the client version
                    //this helped me to decode the client version: http://forums.citrix.com/message.jspa?messageID=633558
                    switch (clientVersion)
                    {

                        case 581:
                            MainVer = "4.00";
                            break;
                        case 686:
                            MainVer = "4.00";
                            break;
                        case 715:
                            MainVer = "4.20";
                            break;
                        case 727:
                            MainVer = "4.20";
                            break;
                        case 741:
                            MainVer = "4.20";
                            break;
                        case 779:
                            MainVer = "4.21";
                            break;
                        case 910:
                            MainVer = "6.00";
                            break;
                        case 963:
                            MainVer = "6.01";
                            break;
                        case 964:
                            MainVer = "6.01";
                            break;
                        case 967:
                            MainVer = "6.01";
                            break;
                        case 985:
                            MainVer = "6.20";
                            break;
                        case 1050:
                            MainVer = "6.30";
                            break;
                        case 1051:
                            MainVer = "6.31";
                            break;
                        case 17534:
                            MainVer = "7.00";
                            break;
                        case 20497:
                            MainVer = "7.01";
                            break;
                        case 21845:
                            MainVer = "7.10";
                            break;
                        case 22650:
                            MainVer = "7.10";
                            break;
                        case 24737:
                            MainVer = "8.00";
                            break;
                        case 29670:
                            MainVer = "8.10";
                            break;
                        case 32649:
                            MainVer = "9.00";
                            break;
                        case 36280:
                            MainVer = "9.10";
                            break;
                        case 39151:
                            MainVer = "9.15";
                            break;
                        case 44376:
                            MainVer = "9.20";
                            break;
                        case 50211:
                            MainVer = "9.23";
                            break;
                        case 45418:
                            MainVer = "10.00";
                            break;
                        case 49686:
                            MainVer = "10.00";
                            break;
                        case 52110:
                            MainVer = "10.00";
                            break;
                        case 55836:
                            MainVer = "10.100";
                            break;
                        case 58643:
                            MainVer = "10.150";
                            break;
                        case 2650:
                            MainVer = "10.20";
                            break;
                        case 5284:
                            MainVer = "11.00 (beta)";
                            break;
                        case 5357:
                            MainVer = "11.00";
                            break;
                        case 27334:
                            MainVer = "11.1 (Linux)";
                            break;
                        case 31560:
                            MainVer = "11.2.0";
                            break;
                        case 3:
                            MainVer = "11.2.2";
                            break;
                        case 6410:
                            MainVer = "12.0.0";
                            break;
                        case 6:
                            MainVer = "12.0.3";
                            break;
                        case 13:
                            MainVer = "13.0.0 (beta)";
                            break;
                        case -555:
                            MainVer = "Error to get client version";
                            break;
                        default:
                            //Since I didn't want to decode them all (they are are so many)
                            //I use a default message for those not encoded:
                            MainVer = "Unknown build code (code is: " + UserSession.ClientBuild.ToString() + ")";
                            break;
                    }

                    //print the client version
                    Console.WriteLine("User: {0}, Client Name: {1}, Client Version: {2}", UserSession.UserName, UserSession.ClientName, MainVer);
                }

                //wait until the user presses any key on the keyboard
                Console.WriteLine("Press any key to continue . . .");
                Console.ReadKey();
            }
            catch (Exception ex)
            {
                //if an error has occured then print the error message
                Console.WriteLine("AN ERROR HAS OCCURED:\n");
                Console.WriteLine(ex.ToString());
                //wait until the user presses any key on the keyboard
                Console.WriteLine("Press any key to continue . . .");
                Console.ReadKey();
            }
        }
    }
}

 

Terzo esempio: recuperare tutte le applicazioni pubblicate

Questo esempio vi mostrerà come recuperare tutte le applicazioni pubblicate con i gruppi e gli utenti ad esse abilitati. Quindi, aprite Visual Studio e create un nuovo progetto in C# (sempre una console application) e chiamatelo, per esempio, listCtxApps. Come al solito dovete aggiungere la dll mfcom.dll

Nel codice (riportato sotto) ho inserito un importante metodo: LoadData(1), senza di esso non è possibile recuperare informazioni essenziali, quali ad esempio il nome dell'applicazione, gli utenti abilitati ecc.

I commenti nel codice vi permetteranno di capirne il funzionamento:

//The following four namespaces are added by default
//when you create a C# Console application (with .NET 4.0)
//if you use other version of .NET Framework namespaces will 
//probably be different. Don't worry about that. We don't need all of them.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

//the below namespace allows us to use classes and objects of the mfcom.
//REMEMBER: first of all, you have to add mfcom.dll as reference
//then you can use MetaFrameCOM
using MetaFrameCOM;

namespace listCtxApps
{
    class Program
    {
        static void Main(string[] args)
        {
            try //with a try catch block we can We can handle exceptions
            {

                //create and initialize the Citrix MetaFrame Farm object
                MetaFrameFarm Farm = new MetaFrameFarm();
                Farm.Initialize(MetaFrameObjectType.MetaFrameWinFarmObject);

                foreach (IMetaFrameApplication app in Farm.Applications)
                {
                    //Application's data must be loaded first before you can use it...
                    app.LoadData(1); //however this option will slow down a little bit all the code

                    //Now (after loaded Application's data) you cant print all application's properties
                    
                    Console.WriteLine("Application Name = {0}",app.AppName); //print the application name
                    
                    //print all the users who are enabled to the current application
                    foreach (IMetaFrameUser user in app.Users)
                    {
                        Console.WriteLine("   |-User = {0}", user.UserName);
                    }

                    //print all the groups that are enabled to the current application
                    foreach (IMetaFrameGroup6 group in app.Groups)
                    {
                        Console.WriteLine("   |-Group = {0}", group.GroupName);
                    }

                    //separate each application by a blank line
                    Console.WriteLine();
                }

                //wait until the user presses any key on the keyboard
                Console.WriteLine("Press any key to continue . . .");
                Console.ReadKey();
            }
            catch (Exception ex)
            {
                //if an error has occured then print the error message
                Console.WriteLine("AN ERROR HAS OCCURED:\n");
                Console.WriteLine(ex.ToString());
                //wait until the user presses any key on the keyboard
                Console.WriteLine("Press any key to continue . . .");
                Console.ReadKey();
            }
        }
    }
}

 

Farm multiple

Supponiamo di avere una farm Citrix di test ed una di produzione. La cosa giusta da fare sarebbe quella di provare le nostre applicazioni prima sulla farm di test e poi su quella di produzione (soprattutto se scriviamo codice per modificare alcune proprietà della farm). Per fare questo dobbiamo cambiare il nome del server remoto dell'oggetto dcom "MetaFrame COM Server" (che nel nostro esempio è installato sulla macchina wsdev010) inserendo un server che appartiene alla farm di test. Tutto ciò è mostrato nell'immagine sottostante:

remote server

 

Conclusione

Quello che vi ho mostrato è solo un assaggio di ciò che potete fare con le API fornite dall'oggetto MFCOM di Citrix. Ci sono moltissimi esempi disponibili su internet (vi suggerisco di effettuare una ricerca con google) che mostrano le potenzialità dell'MFCOM. Inoltre esso offre classi e metodi che si possono utilizzare con molti linguaggi di programmazione.

Durante lo sviluppo, soprattutto le prime volte, potrete ricevere parecchi errori relativi agli oggetti COM (come descritto qualche paragrafo fa). Dovrete avere pazienza nel cercarne le cause. Aiutatevi con google. Ricordate sempre di utilizzare i blocchi Try-Catch. Questo è tutto. Spero l'articolo sia stato di vostro gradimento.

Alan Pipitone

I am the owner.

Website: www.alan-pipitone.com
Latest from Alan Pipitone
  • Più letti
Utilizzare Kinect con Mac OSX

In questo articolo vi mostrerò come sia possibile utilizzare Microsoft... Read more

Come interrogare la Farm Citrix in C#

Questo tutorial vi mostrerà come poter creare semplici applicazioni in... Read more

« December 2018 »
Mon Tue Wed Thu Fri Sat Sun
          1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31