Month: January 2011

C# : modificare un campo readonly ? Si può con la Reflection !

Tutti sappiamo che definendo il campo di una classe con la keyword readonly, quest’ultimo assumerà un valore a runtime che potrà essere assegnato solo ed esclusivamente nel costruttore della classe ma non potrà più essere modificato successivamente.

Ad esempio, consideriamo la definizione della seguente classe :

public class MyClass
{
    private readonly int rField;

    public MyClass(int field)
    {
        this.rField = field;
    }

    public int RField
    {
        get
        {
            return this.rField;
        }
        set
        {
            this.rField = value;
        }
    }
}

Se proviamo a compilare tale classe, il compilatore restituirà il seguente errore :

A readonly field cannot be assigned to (except in a constructor or a variable initializer)

Attraverso la Reflection è invece possibile modificare un campo definito readonly in un qualsiasi momento dell’esecuzione.

Per fare questo, bisogna modificare il set della property RField nel modo seguente :

set
{
    this.GetType()
        .GetField("rField", BindingFlags.NonPublic | BindingFlags.Instance)
        .SetValue(this, value);
}

Utilizzando la Reflection, si accede al campo readonly rField e se ne modifica il valore.

In questo modo, non otteniamo alcun errore di compilazione ed eseguendo il seguente programma di test :

static void Main(string[] args)
{
    MyClass myClass = new MyClass(10);
    Console.WriteLine("MyClass.RField = {0} after ctor", myClass.RField);
    myClass.RField = 5;
    Console.WriteLine("MyClass.RField = {0} after setting property", myClass.RField);
}

otteniamo il seguente output :

2626.readonly_field_2DC466D6

dal quale si evince che il campo readonly è stato modificato !

Hacking su un campo private e readonly

Andando oltre il semplice esempio didattico suddetto, questa tecnica si potrebbe utilizzare per eseguire un hacking su un campo privato readonly di una classe. Supponiamo che la classe suddetta sia di questo tipo :

public class MyClass
{
    private readonly int rField;

    public MyClass(int field)
    {
        this.rField = field;
    }

    public int RField
    {
        get
        {
            return this.rField;
        }
    }
}

Il campo rField non è accessibile dall’esterno (private), non è modificabile a runtime (readonly) e la relativa property RField ne permette esclusivamente la lettura (get). Sfruttando però la Reflection con la tecnica vista prima, possiamo comunque modificare il campo nel modo seguente :

static void Main(string[] args)
{

    MyClass myClass = new MyClass(10);
    Console.WriteLine("MyClass.RField = {0} after ctor", myClass.RField);
    myClass.GetType()
           .GetField("rField", BindingFlags.NonPublic | BindingFlags.Instance)
           .SetValue(myClass, 5);
    Console.WriteLine("MyClass.RField = {0} after setting property", myClass.RField);

}

che in esecuzione darà il seguente output :

5148.readonly_field_5D5A6701

A tutti gli effetti abbiamo eseguito un hacking su un campo che era stato previsto non accessibile in scrittura e non modificabile dallo sviluppatore !

CLR : Inside memory model (Parte 2)

Introduzione

Nell’articolo precedente, abbiamo iniziato ad approfondire cosa accade nel momento in cui il CLR alloca un oggetto sulla memoria Heap e come vengono distribuite  le informazioni contenute nell’oggetto stesso, sia in termini di dati propri dell’oggetto che di dati aggiunti dal CLR.

In questo articolo, vedremo nel dettaglio come avviene l’invocazione dei metodi sugli oggetti con e senza polimorfismo.

La Method Table

Ciascun Type Object allocato sull’Heap ed associato ad uno specifico tipo, contiene al suo interno la Method Table, ossia la tabella dei metodi esposti dal tipo stesso che viene utilizzata dal CLR per l’invocazione di questi ultimi sugli oggetti.

Utilizzando il debugger avanzato SOS di Visual Studio, è possibile visualizzare la Method Table di un tipo. Eseguiamo in primo luogo il dump dell’oggetto bClass in modo da poter ricavare l’indirizzo della Method Table all’interno del corrispondete Type ObjectBaseClass Type Obj.

!DumpObj 00c2c3b0
PDB symbol for clr.dll not loaded
Name:        ConsoleApplication1.BaseClass
MethodTable: 009b391c
EEClass:     009b14d8
Size:        16(0x10) bytes
File:        C:\Documents and Settings\Developer\My Documents\Visual Studio 2010\Projects\ConsoleApplication1\ConsoleApplication1\bin\Debug\ConsoleApplication1.exe
Fields:
MT    Field   Offset                 Type VT     Attr    Value Name
79b9f9ac  4000001        4        System.String  0 instance 00c2c3c0 instanceString
79ba2978  4000002        8         System.Int32  1 instance        1 instanceInt
79ba2978  4000003       24         System.Int32  1   static        3 staticInt

Dall’esecuzione del comando di DumpObj, si evince che la Method Table si trova all’indirizzo 0x009b391c. Su tale indirizzo, possiamo eseguire il comando DumpMT :

!DumpMT -MD 009b391c
EEClass:      009b14d8
Module:       009b2e9c
Name:         ConsoleApplication1.BaseClass
mdToken:      4a0ec1b602000002
File:         C:\Documents and Settings\Developer\My Documents\Visual Studio 2010\Projects\ConsoleApplication1\ConsoleApplication1\bin\Debug\ConsoleApplication1.exe
BaseSize:        0x10
ComponentSize:   0x0
Slots in VTable: 9
Number of IFaces in IFaceMap: 0
————————————–
MethodDesc Table
Entry MethodDesc      JIT Name
79aaa7e0   79884934   PreJIT System.Object.ToString()
79aae2e0   7988493c   PreJIT System.Object.Equals(System.Object)
79aae1f0   7988495c   PreJIT System.Object.GetHashCode()
79b31600   79884970   PreJIT System.Object.Finalize()
009bc048   009b38e8     NONE ConsoleApplication1.BaseClass.VirtualMethod()
009bc038   009b38cc      JIT ConsoleApplication1.BaseClass..ctor()
009bc058   009b3904      JIT ConsoleApplication1.BaseClass..cctor()
009bc040   009b38d8     NONE ConsoleApplication1.BaseClass.InstanceMethod()
009bc050   009b38f4     NONE ConsoleApplication1.BaseClass.StaticMethod()

In primo luogo, osserviamo che il numero di slot nella tabella sono 9 (quanti sono i metodi invocabili sul tipo BaseClass). I primi quattro metodi sono ereditati dalla classe Object, che come sappiamo è la classe base per un qualsiasi oggetto nel .Net Framework. Si evince che essi hanno l’entry point (colonna Entry) ad un indirizzo di memoria completamente diverso dagli altri, in quanto l’oggetto Object Type Object, viene istanziato dal CLR all’avvio in una propria zona della memoria Heap ed il codice viene subito compilato dal JIT compiler (da notare la voce PreJIT).

Subito dopo, ci sono i tre metodi della classe BaseClass, che non essendo stati ancora invocati, non sono stati compilati dal JIT compiler (valore NONE). Viceversa, i due costruttori.ctor e .cctor (di istanza e di tipo, rispettivamente) sono stati eseguiti al momento della creazione dell’oggetto bClass e quindi compilati dal JIT compiler.

Invocazione di un metodo di istanza non virtuale…

A questo punto, eseguiamo la seguente riga di codice :

bClass.InstanceMethod();
Il metodo in questione è un metodo di istanza non virtuale, per cui il JIT compiler deve semplicemente individuare il tipo con cui è stato dichiarato l’oggetto bClassBaseClass, ed accedere alla Method Table del BaseClass Type Object corrispondente. Individuato il metodo, lo compila e lo esegue.
3730.mem_alloc_4_4DBEDD07

Figura 1

Se rieseguiamo il dump della tabella dei metodi :

009bc048   009b38e8     NONE ConsoleApplication1.BaseClass.VirtualMethod()
009bc038   009b38cc      JIT ConsoleApplication1.BaseClass..ctor()
009bc058   009b3904      JIT ConsoleApplication1.BaseClass..cctor()
009bc040   009b38d8      JIT ConsoleApplication1.BaseClass.InstanceMethod()
009bc050   009b38f4     NONE ConsoleApplication1.BaseClass.StaticMethod()

Osserviamo che il metodo InstanceMethod() risulta essere stato compilato dal JIT compiler.

… di un metodo virtuale …

Passiamo adesso ad invocare il metodo virtuale :

bClass.VirtualMethod();
In questo caso, avendo a che fare con un metodo virtuale, non basta al JIT compiler sapere con quale tipo è stata dichiarata la variabile bClass, perchè utilizzando l’ereditarietà, potremmo anche far puntare la variabile bClass (dichiarata come BaseClass) ad una istanza della classe derivata DerivedClass. In questo caso, il tipo utilizzato per la dichiarazione della variabile non coinciderebbe con il tipo dell’effettivo oggetto puntato (come vedremo successivamente) e la tecnica utilizzata per l’invocazione di un metodo di istanza non virtuale non funzionerebbe.
In una situazione del genere, il JIT compiler deve accedere all’oggetto effettivamente puntato dalla variabile bClass, cioè nel nostro caso il BaseClass Obj, e deve analizzare il campo Type Object Pointer per poter ricavare l’effettivo Type Object. In questo caso, esso è ancora ilBaseClass Type Obj. A questo punto, il JIT accede alla Method Table, individua il metodo, lo compila e lo esegue.
8802.mem_alloc_5_35C7D014

Figura 2

… infine di un metodo statico

Infine, vediamo cosa accade per l’invocazione del metodo statico :

BaseClass.StaticMethod();

Questa situazione, è molto simile all’invocazione del metodo di istanza non virtuale, con la differenze che il JIT compiler conosce immediatamente la classe a cui appartiene il metodo (considerato il modo in cui viene eseguita l’invocazione) e quindi conosce subito il Type Object (in questo caso il BaseClass Type Obj) nel quale cercare il metodo nella tabella.

Allocazione di un oggetto della classe derivata …

Passiamo ora a vedere cosa accade utilizzando un riferimento del tipo BaseClass, per puntare ad un oggetto della classe derivata DerivedClass e quindi invocare su di essa il metodo virtuale sfruttando a tutti gli effetti il concetto di polimorfismo.

In primo luogo, un’istanza della classe DerivedClass deve essere allocata :

bClass = new DerivedClass();

Con l’istruzione precedente, il CLR deve allocare in memoria Heap un’istanza della classeDerivedClass e fare in modo che il suo Type Object Pointer punti, ovviamente, alDerivedClass Type Obj. L’indirizzo assegnato al nuovo oggetto allocato verrà restituito nella variabile bClass.

3858.mem_alloc_6_5B4127A8

Figura 3

Osservando la Figura 3, si evince che il nuovo oggetto DerivedClass Obj è stato allocato sull’Heap e che la variabile bClass punta ad esso. Non c’è più alcun riferimento all’oggettoBaseClass Obj che quindi sarà sottoposto alla pulizia da parte del Garbage Collector.

Analizziamo nel dettaglio l’area di memoria in cui è stato allocato il nuovo oggetto :

5353.mem_alloc_7_15C8FAF2

Figura 4

In riferimento alla Figura 4, di seguito c’è il dettaglio dei valori contenuti in memoria :

  • Offset –4 (val. 00000000) : è il Sync Block Index;
  • Offset 0 (val. 009b39a4) : è il Type Object Pointer;
  • Offset +4 (val. 00c2c3c0) : campo instanceString, ereditato dalla classe baseBaseClass. Da osservare che l’indirizzo è ovviamente il medesimo;
  • Offset +8 (val. 00000001) : campo instanceInt, ereditato dalla classe base BaseClass;

… ed invocazione di un metodo virtuale overridden

Per concludere, vediamo cosa accade durante l’invocazione del metodo virtuale di cui la classe derivata ne fa l’override :

bClass.VirtualMethod();

In questo caso, il JIT compiler parte dalla variabile bClass che punta all’oggetto DerivedClass Obj sulla memoria Heap. A partire da tale oggetto, analizza il suo Type Object Pointer che punterà al DerivedClass Type Obj. In questo modo, il JIT compiler arriva alla Method Tabledella DerivedClass, compila ed esegue il metodo virtuale giusto e non quello della classe base.

0702.mem_alloc_8_27C518B2

Figura 5

Con questa seconda parte, si conclude l’approfondimento relativo al CLR memory model mettendo in evidenza che nonostante le notevoli semplificazioni che il .Net Framework ci fornisce per sviluppare le nostre applicazione nel modo più rapido possibile, l’architettura sottostante del sistema non è assolutamente semplice.

Windows Embedded CE 6.0 R3 QFEs : Dicembre 2010

Sono stati rilasciati i QFEs di Dicembre 2010 per Windows Embedded CE 6.0 R3 e sono scaricabili al seguente indirizzo :

Windows Embedded CE 6.0 Monthly Update December 2010

Riporto di seguito i principali fix, come descritto nel file allegato al download :

Component:  Connection Manager

  • 101207_KB982219 – Phone may not be able to enter suspend mode if SYSGEN_CONNMGR is enabled.

Component:  Kernel

  • 101202_KB982221 – This update allows kernel to keep track of the original cache attributes.

Component:  RDP

Component:  Security

Component:  TimeSVC

CLR : Inside memory model (Parte 1)

Introduzione

Quotidianamante, sviluppando software con il .Net Framework ed in particolare con un linguaggio come il C#, ci preoccupiamo sempre molto poco di come si comporta il CLR durante l’esecuzione ed in particolare modo dell’allocazione e deallocazione della memoria, dell’esecuzione dei metodi sugli oggetti e quant’altro.

Ovviamente, siamo concentrati su ciò che riguarda l’implementazione della Business Logic nel nostro dominio applicativo, considerando che tutto il resto verrà eseguito per noi dal CLR.

Può essere molto utile, però, approfondire alcuni aspetti interni del .Net Framewok per ottenere un certo beneficio in termini di performance delle applicazioni sviluppate.

In particolare, con questo articolo, intendo descrivere alcuni meccanismi che sottintendono all’allocazione della memoria ed all’invocazione dei metodi sugli oggetti da parte del CLR.

Un pò di codice …

Supponiamo di avere la dichiarazione delle due seguenti classi :

internal class BaseClass
{
    private string instanceString = "InstanceString";
    private int instanceInt = 1;
    private static int staticInt = 2;

    public BaseClass() { }
    public int InstanceMethod() { return 0; }
    public virtual int VirtualMethod() { return 0; }
    public static int StaticMethod() { return 0; }
}

internal class DerivedClass : BaseClass
{
    public override int VirtualMethod() { return 1; }
}

e di avere il metodo Main() di un programma contenente le seguenti righe di codice :

BaseClass bClass = new BaseClass();
bClass.InstanceMethod();
bClass.VirtualMethod();
BaseClass.StaticMethod();
bClass = new DerivedClass();
bClass.VirtualMethod();
In corrispondenza di questa banalissima sequenza di istruzioni, cosa accade di preciso ? Come si comporta il CLR e come predispone il sistema per permetterci l’utilizzo dell’oggettobClass ?
I Type Object
In primo luogo, il CLR si assicura che gli assemblies nei quali sono definiti i tipi BaseClass eDerivedClass siano stati già caricati (altrimenti li carica). Successivamente, utilizza i metadati contenuti negli assemblies per allocare alcune strutture in memoria, i cosiddetti Type Object.
3364.mem_alloc_1_3939D87B
Figura 1
Prima di analizzare nel dettaglio la Figura 1, va fatta un’importante premessa. Ogni qual volta viene allocato un oggetto nella memoria Heap, esso non conterrà solo ed esclusivamente i dati che sono stati dichiarati al suo interno ma il CLR aggiungerà alcune informazioni necessarie per l’utilizzo dell’oggetto stesso. In particolare, vengono sempre aggiunti i seguenti 8 byte :
  • Sync Block Index (4 byte) : è un indice con il quale il CLR può puntare all’interno di una tabella con cui gestire alcune operazioni quali il lock su un oggetto (in contesti multithreading) oppure ricavare un hash code per l’oggetto stesso (metodoGetHashCode() ereditato dalla classe base Object);
  • Type Object Pointer (4 byte) : il puntatore all’oggetto rappresentativo del tipo (Type Object);

Facendo riferimento alla Figura 1, il CLR ha instanziato i due Type Object relativi ai tipiBaseClass e DerivedClass. Indipendentemente da quante istanze di queste due classi noi andremo ad allocare nel corso del programma, le istanze dei corrispondenti Type Objectrimarrano solo ed esclusivamente queste due.

Cosa contiente un Type Object ? Di seguito gli elementi fondamentali :

  • Sync Block Index e Type Object Pointer : essendo un oggetto a tutti gli effetti, anche un Type Object deve avere queste due informazioni;
  • Static Fields : i campi statici del tipo, considerando che essi saranno comuni a tutte le istanze del tipo stesso;
  • Method Table : la tabella dei metodi che viene utilizzata dal CLR nel momento in cui c’è un’invocazione di un metodo su un oggetto. In questo modo, si individua il metodo che dovrà essere eventualmente compilato dal JIT (in caso di prima invocazione) e poi eseguito;

Ma a cosa punta il Type Object Pointer di un Type Object ?

Type Object non sono nient’altro che istanze del tipo Type, molto usato con la Reflection. Esisterà, quindi, anche un’istanza del Type Object del tipo Type…ossia, scusate il gioco di parole, il Type Type Object ! Quest’ultimo sarà a sua volta un oggetto e quindi sarà dotato di un Type Object Pointer che punterà a se stesso.

Allocazione di un oggetto

A questo punto cosa accade quando andiamo ad allocare un’istanza della classe BaseClass(riga 1) ?

Viene predisposta l’area di memoria sull’Heap per accogliere l’oggetto e l’istruzione newritornerà l’indirizzo di tale area. Questo indirizzo sarà quindi assegnato alla variabile bClass, allocata sullo Stack.

1373.mem_alloc_2_79FA888F

Figura 2

Dalla Figura 2, si evince che l’istanza della classe BaseClass contiene :

  • Sync Block Index e Type Object Pointer : da notare come il Type Object Pointerpunti al BaseClass Type Object;
  • Instance Fields : i campi di istanza che quindi sono esclusivi per tale oggetto;
  • String Literals : le stringhe direttamente definite nella classe (literals);

Analisi avanzata in memoria

Per comprendere meglio la distribuzione di queste strutture in memoria, possiamo eseguire il codice e ispezionare la memoria.

8780.mem_alloc_3_265CA05B

Figura 3

Dalla Figura 3, possiamo in primo luogo osservare che la variabile bClass (allocata sullo Stack) ha come contenuto il valore 0x00c2c3B0 (indirizzo della memoria Heap); essa non punta esattamente all’inizio dell’oggetto (Sync Block Index) ma punta 4 byte dopo (Type Object Pointer). E’ da sottolineare che la rappresentazione dei byte è di tipo Little Endian (processore Intel).

Di seguito il dettaglio della memoria allocata per l’oggetto bClass, riportata per offeset rispetto al puntatore della variabile sullo Stack :

  • Offest –4 (val. 00000000) : è il Sync Block Index. Poichè non è stata eseguita alcuna istruzione di lock sull’oggetto, esso è zero.
  • Offeset 0 (val. 009b391c) : è il Type Object Pointer;
  • Offeset +4 (val 00c2c3c0) : campo instanceString che essendo di tipo String, quindi un reference type, è ovviamente un puntatore all’oggetto contenente la string literal “InstanceString”;
  • Offeset +8 (val. 00000001) : campo instanceInt il cui valore è 1;
  • Offeset + 12 (val. 00000080) : da qui inizia l’istanza dell’oggetto String relativa al campo instanceString; in questa posizione c’è il Sync Block Index che è diverso da 0, in quanto la string literal “InstanceString” potrebbe essere utilizzata in altre parti di codice ma allocata una sola volta;
  • Offeset +16 (val. 79b9f9ac) : è il Type Object Pointer dell’istanza String di cui sopra;
  • Offeset +20 (val. 0000000e) : è la dimensione della stringa “InstanceString” (14 caratteri);
  • Offeset +24 : da qui inizia la stringa “InstanceString” che occupa complessivamente 28 byte (14 caratteri), poichè la rappresentazione è Unicode (2 byte per carattere);

Questi valori possono essere confrontati con un’analisi eseguiti utilizzando la libreria di debug avanzato sos.dll attraverso la Immediate Window di Visual Studio.

!DumpObj 0x00c2c3b0
Name:        ConsoleApplication1.BaseClass
MethodTable: 009b391c
EEClass:     009b14d0
Size:        16(0x10) bytes
File:

C:\Documents and Settings\Developer\My Documents\Visual Studio 2010\Projects\ConsoleApplication1\ConsoleApplication1\bin\Debug\ConsoleApplication1.exe
Fields:
MT    Field   Offset                 Type VT     Attr    Value Name
79b9f9ac  4000001        4        System.String  0 instance 00c2c3c0 instanceString
79ba2978  4000002        8         System.Int32  1 instance        1 instanceInt
79ba2978  4000003       24         System.Int32  1   static        2 staticInt

Eseguendo il comando DumpObj sull’indirizzo dell’oggetto, osserviamo che il valore del Type Object Pointer (009b391c) viene riportato come MethodTable, proprio perchè nel Type Object è contenuta la tabella dei metodi del tipo. Vengono inoltre elencati i campi dell’oggetto con i corrispondenti tipi, nomi e valori.

Inoltre, se eseguiamo il dump delle informazioni sul campo instanceString (indirizzo 00c2c3c0) otteniemo :

!DumpObj 00c2c3c0
Name:        System.String
MethodTable: 79b9f9ac
EEClass:     798d8bb0
Size:        42(0x2a) bytes
File:        C:\WINDOWS\Microsoft.Net\assembly\GAC_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
String:      InstanceString
Fields:
MT    Field   Offset                 Type VT     Attr    Value Name
79ba2978  40000ed        4         System.Int32  1 instance       14 m_stringLength
79ba1dc8  40000ee        8          System.Char  1 instance       49 m_firstChar
79b9f9ac  40000ef        8        System.String  0   shared   static Empty

Da cui osserviamo proprio che all’offeset 4 c’è la dimensione della stringa e subito dopo inizia la stringa stessa con il primo carattere.

Nel prossimo articolo, vedremo cosa accade durante l’invocazione dei metodi su un oggetto ed in particolare per una classe derivata.

La struttura dati Union in C# ? Si può…

Tutti coloro che sviluppano in C/C++ si trovano molto spesso ad utilizzare una particolare struttura dati che in molti casi permette un notevole risparmio di memoria, in quanto ci fornisce la possibilità di utilizzare la stessa area per salvare in momenti diversi, valori di tipo diverso.

Ovviamente sto parlando della union, tipo sconosciuto a chi sviluppa da sempre solo ed esclusivamente in C#.

A questa mancanza, si può però ovviare utilizzando due strumenti che il C# ed il .Net Framework ci forniscono :

  • Il tipo struct;
  • L’attributo StructLayout (nel namespace System.Runtime.InteropServices);

In che modo ?

L’attributo StructLayout, permette di definire la modalità con cui il CLR disporrà in memoria i campi che costituiscono una classe oppure una struct. I valori ammessi da questo attributo sono i seguenti :

  • Auto : è il CLR a decidere come disporre i valori dei campi in memoria, non rispettando necessariamente l’ordine di dichiarazione degli stessi. Questa opzione è molto utile, in quanto permette di risparmiare, da parte del CLR, l’utilizzo dei byte di padding che servono a fare in modo che i campi siano allineati a 1,4 oppure 8 byte in base all’architettura del sistema. Una non perfetta distribuzione dei campi in memoria può implicare una dimensione complessiva maggiore del dato rispetto a quella strettamente necessaria. L’opzione Auto è quella predefinita per le classi;
  • Sequential : i valori vengono predisposti in memoria in maniera sequenziale secondo l’ordine di dichiarazione. In questo caso, si corre ovviamente il rischio dell’aggiunta di byte di padding inseriti ad hoc dal CLR, se non abbiamo dichiarato secondo una opportuna sequenza i campi. Questo è il valore predefinito per le struct;
  • Explicit : è possibile esplicitare la posizione/offeset dei campi rispetto all’indirizzo di partenza della classe o struttura, utilizzando l’ulteriore attributo FieldOffset da applicare a ciascun campo. Sarà questo valore che ci verrà in aiuto per la dichiarazione di una union;

La dichiarazione di una union, possiamo immaginarla nel modo seguente :

[StructLayout(LayoutKind.Explicit)]
public struct MyUnion
{
	[FieldOffset(0)]
	public byte byteValue;
	[FieldOffset(0)]
	public int intValue;
	[FieldOffset(0)]
	public short shortValue;
}
Come possiamo osservare, è stato specificato che rispetto all’indirizzo di partenza di allocazione della struct, tutti i campi hanno offeset pari a zero. Ciò vuol dire che si sovrapporranno l’uno con l’altro.
Eseguiamo ad esempio il seguente frammento di codice :
MyUnion myUnion = new MyUnion();
myUnion.byteValue = 5;
myUnion.intValue = 1234567890;
myUnion.shortValue = 9876;

int size = Marshal.SizeOf(myUnion);

Dopo l’esecuzione dell’istruzione 2, osserviamo che l’area di memoria allocata per la struct appare nel modo seguente :

3618.union_mem_1_thumb_0A0251AB

Dove si può osservare il valore 5 all’indirizzo di partenza, assegnato al campo byteValue. A seguito dell’istruzione 3, invece, troviamo la seguente condizione :

6747.union_mem_2_2944F87E
Il valore 5 è stato sovrascritto dalla rappresentazione binaria del numero intero 1234567890 che abbiamo assegnato al campo intValue. Infine, eseguendo l’istruzione 4, abbiamo :

4152.union_mem_3_11CA444D

Dove si evince la presenza della rappresentazione binaria del valore 9876 assegnato al campo shortValue.

Nell’ultima istruzione, viene calcolata la dimensione della struttura dati che stiamo utilizzando e come possiamo osservare dalla figura seguente, essa è 4 byte.

4617.union_mem_4_24329502

Tale dimensione è ovviamente determinata dal tipo di dato più grande che abbiamo dichiarato nella struct, ossia l’int (alias C# per il tipo Int32 del CLS) che occupa appunto 4 byte e non dalla somma delle dimensioni dei campi dichiarati nella struct (1 + 4 + 2 = 7).

Ciò che abbiamo ottenuto è a tutti gli effetti una union…in C# !!

Windows Embedded Compact 7 – Hands On (Parte 2) : Creazione OS Design ed avvio Windows CE 7

Per iniziare a testare le funzionalità di una nuova versione di Windows CE, si è sempre ricorso all’utilizzo di un target device emulato attraverso il Device Emulator che fino alla versione 6.0 R3 era basato sull’architettura ARM. Con Windows Embedded Compact 7, la Microsoft ha deciso di rilasciare un emulatore basato su piattaforma x86, la nota CEPC.
La motivazione di tale passaggio è legata soprattutto al fatto che molti sviluppatori, in passato, hanno sempre preferito testare le nuove funzionalità del sistema operativo su un hardware di tipo x86 (CEPC) e non di tipo ARM, per cui la Microsoft ha deciso di adattarsi a questo utilizzo e fornire direttamente un emulatore Virtual CEPC in modo da evitare i costi di acquisto di un hardware in fase di valutazione del prodotto.

Per poter utilzzare la piattaforma Virtual CEPC è necessario Virtual PC, con il quale possiamo emulare un hardware standard x86 sul quale poi poter eseguire il sistema operativo generato con il Platform Builder 7.

CREAZIONE DI UN PROGETTO PER L’OS DESIGN

Il primo passo da eseguire è quello di creare un nuovo progetto per il nostro OS Design. La procedura è la medesima della versione precedente :

  • In Visual Studio 2008, andiamo sul menu File e poi clicchiamo su New e Project;
  • Nell’elenco dei Project Types, selezioniamo la voce Platform Builder 2008 (che viene aggiunta in fase di installazione del Platform Builder 7) e quindi il template OS Design;
  • Attraverso il campo di testo Name, assegniamo un nome al nostro progetto (ad esempio “VCEPC”);
  • Utilizzando il campo Location, possiamo decidere di cambiare la posizione sul disco del progetto che verrà generato. In generale, è bene lasciare il percorso di default (C:\WINCE700\OSDesigns);
  • Nel campo Solution Name, possiamo decidere di cambiare il nome alla solution (che di default sarà uguale a quello del progetto);
  • Clicciamo sul tasto OK per passare alla configurazione dell’OS Design;

3302.wce7_vcepc_step1_thumb_39CFC4BC

CONFIGURAZIONE OS DESIGN

Concluso lo step precedente, viene avviato l’OS Design Wizard, attraverso il quale dovremmo selezionare un template per il nostro sistema operativo ed eseguire poi la customizzazione aggiungendo o rimuovendo le features a noi non necessarie.
Cliccando su Next alla schermata di Welcome, ci verrà proposto l’elenco delle BSP fornite dall’installazione; selezioniamo Virtual PC;

8204.wce7_vcepc_step2_thumb_2255108B

A questo punto, dobbiamo selezionare un template di sistema operativo dal quale partire per poi eseguire una customizzazione, in base all’hardware che
avremo a disposizione. Un buon template di partenza per un Virtual CEPC è l’Enterprise WebPad che troviamo sotto PDA Device; selezioniamolo e clicchiamo su Next.

3884.wce7_vcepc_step3_thumb_18ACA255

Nella schermata successiva Applications and Media, possiamo selezionare a piacimento le funzionalità da aggiungere al nostro sistema operativo oppure eventualmente rimuovere quelle selezionate di default. Successivamente clicchiamo su Next.
Per quanto concerne le funzionalità di Networking and Communications, selezioniamo laWired Local Area Network che troviamo sotto Local Area Network (LAN) per fornire al sistema le funzionalità di accesso alla rete; clicchiamo su Next.

8233.wce7_vcepc_step4_thumb_642BD00C

Verrà visualizzata l’ultima schermata relativa al completamento del wizard. Deselezioniamo la checkbox Build the following configuration after I click Finish, in modo da evitare che parta in automatico il build dell’immagine del sistema operativo ed in modo da avere la possibilità di configurarlo in maniera più fine prima di avviarne la compilazione.
Cliccando su Finish, verrà visualizzata una schermata di warning in relazione ad alcuni component (OBEX Server) che sono stati inclusi nell’immagine; clicchiamo su Acknowledge.

A questo punto abbiamo la possibilità di customizzare la nostra immagine del sistema operativo, aggiungendo e rimuovendo i componenti utilizzando la Catalog Items View (se non risulta visibile andate sul menu View poi su Other Windows ed infine su Catalog Items View).
Ad esempio, se volessimo aggiungere il supporto per Silverlight dovremmo seguire il percorso Core OS -> Windows Embedded Compact -> Shell and User Interface -> Silverlight for Windows Embedded ed aggiungere il catalog item Silverlight for Windows Embedded.

4048.wce7_vcepc_step5_thumb_27B35B62

Prima di concludere la configurazione, dobbiamo aggiungere il driver per la tastiera in base alla versione di Virtual PC che andremo ad utilizzare (nel mio
caso Virtual PC 2007). Sempre nella Catalog Items View, seguiamo il percorso BSP –> Virtual PC -> Device Drivers -> Keyboard e selezioniamo il driver.

7848.wce7_vcepc_step6_thumb_23E590C5

A questo punto salviamo tutto e prepariamoci per il Build del sistema operativo.

BUILD OS DESIGN

Siamo pronti per eseguire il build del sistema operativo, lanciando il comando attraverso la voce Build Solution nel menu Build. Durante questa operazione, il Platform Builder compila tutti i moduli che saranno inclusi nel sistema operativo e possiamo vederne l’andamento attraverso la finestra di Output. Al percorso C:\WINCE700\OSDesigns (a meno che non lo abbiamo cambiato al primo step) viene creata una cartella relativa al nostro OS Design
(in questo caso VCEPC) ed all’interno di essa una sottocartella del tipo \RelDir\VirtualPC_x86_Release, all’interno della quale verrà creato il file binario finale (NK.bin) che rappresenterà l’immagine del sistema operativo.

CONFIGURAZIONE VIRTUAL PC 2007

Avviamo Virtal PC 2007 ed attraverso il relativo wizard, creiamo una nuova macchina virtuale specificandone il percorso ed il nome.
Alla schermata che chiede di specificare il sistema operativo guest, selezioniamo Other(Altro) mentre nella schermata successiva specifichiamo  di voler utilizzare la quantità di RAM consigliato (default 128 MB).
L’ultimo passo necessario è quello di specificare un hard disk virtuale (file .vhd) che verrà utilizzato dal sistema operativo guest. Per questo scopo, l’installazione del Platform Builder ci fornisce un hard disk già predisposto al seguente percorso
C:\WINCE700\PLATFORM\VIRTUALPC\VM\hd0_sample.vhd; copiamolo nella cartella in cui stiamo creando la nostra macchina virtuale con Virtual PC 2007.
Nel wizard, selezioniamo di voler utilizzare un hard disk esistente (An existing virtual hard disk) ed individuiamo il file che abbiamo appena copiato.
La macchina virtuale è stata predisposta con successo, ora non ci resta che eseguire il nostro sistema operativo su di essa.

PREPARAZIONE PLATFORM BUILDER AL DOWNLOAD DELL’IMMAGINE

L’immagine NK.bin appena creata, va scaricata sul target che nel nostro caso è una macchina virtuale; per fare ciò è necessario preparare il Platform Builder
a tale operazione :

  • In Visual Studio, andiamo sul menu Target poi Connectivity Options ed infine clicchiamo su Add Device;
  • Assegnamo un nome al target (es. “VCEPC”) e selezioniamo Windows CE alla voceAssociated OS Design/SDK. Infine, clicchiamo su Add;
  • Verranno visualizzate una serie di opzioni relative a Kernel Download, Kernel Transport e Kernel Debugger. Selezioniamo Ethernet in corrispondenza di
    Kernel Download, per far eseguire il download dell’immagine via Ethernet;
  • Infine, apriamo la dialog box Ethernet Download Settings cliccando su Settings in corrispondenza di Kernel Download. Lasciamo questa finestra aperta, perchè
    sarà necessaria in seguito.

AVVIO VIRTUAL CEPC BOOT LOADER

Avviamo la macchina virtuale che abbiamo creato in Virtual PC 2007 e premiamo la barra spaziatrice nel momento in cui sulla console appaiono i seguenti messaggi :

Hit space to enter configuration menu 5…
Hit space to enter configuration menu 4…
Hit space to enter configuration menu 3…
Hit space to enter configuration menu 2…
Hit space to enter configuration menu 1…

A questo punto seguiamo la seguente procedura :

Il bootloader visualizzerà un menu nell’ambito del quale andiamo a selezionare la voce 2 (Select Boot Source). Verranno visualizzate le due seguenti voci
per selezionare il device di boot :

[1] Boot Drive
[2] DEC21140 at PCI bus 0 dev 10 fnc 0

Selezionando la voce 2, verrà utilizzata la scheda di rete del PC host per fornire le funzionalità di rete al bootloader ed attendere il download dell’immagine.

Dal menu principale selezioniamo la voce 1 per visualizzare i settaggi attuli e verifichiamo che siano uguali ad i seguenti :

Boot source: DEC21140 at PCI bus 0 dev 10 fnc 0
KITL device: DEC21140 at PCI bus 0 dev 10 fnc 0
KITL config: enabled, interrupt mode, VMINI enabled
DHCP: enabled
Display: 1024 x 768 x 16 / Full Screen
Debug Port: COM1 38400 baud

Se non dovesse essere disponibile un server DHCP nella vostra rete, sarà necessario impostare i parametri di rete attraverso la voce 4 (Network settings).

Selezioniamo la voce 7 per salvare ed infine 0 per avviare il processo di boot;

A questo punto, il bootloader invia in broadcast sulla rete un pacchetto di BOOTME, con cui fornisce la disponibilità di poter ricevere via rete un’immagine del sistema operativo da poter avviare.

DOWNLOAD IMMAGINE SISTEMA OPERATIVO

Tornando al Platform Builder, possiamo osservare che nella finestra Ethernet Download Settings che avevamo lasciato aperta, compare il nome dei target che sta inviando il messaggio di BOOTME; selezioniamolo e clicchiamo su Apply.

5277.wce7_vcepc_step7_thumb_410B38CF

Per avviare il download :

  • In Visual Studio, selezioniamo il nome del nostro device (“VCEPC”) nel drop-down menu Device;
  • Andiamo sul menu Target e clicchiamo su Attach Device per avviare il download;
  • Lo stato di avanzamento del download verrà visualizzato nella finestra di Download Status;

2437.wce7_vcepc_step8_thumb_7079A79B

Nella console del Virtual PC, possiamo osservare che il bootloader ha accettato il file NK.bin e sta scaricando l’immagine per poi avviarla.

Al termine del downoload, ecco Windows Embedded Compact 7 in esecuzione.

0383.wce7_vcepc_step9_thumb_74A37F60

Avviando Internet Explorer, possiamo navigare nel mio blog e vedere proprio l’articolo che state leggendo !!

4722.wce7_vcepc_step10_thumb_48731A93