Blog Stats
  • Posts - 7
  • Articles - 0
  • Comments - 319
  • Trackbacks - 5

 

martedì 8 maggio 2007

Libreria di Classi C++ - Conversione da String a char

In questi giorni ho trovato un po' di difficoltà nel trovare documentazione e informazioni sulla conversione di Stringhe in char.
Il problema non è tanto in un progetto WindowsForms o Console (linguaggi C++ .NET) dove l'operazione è piuttosto semplice, ovvero basta scrivere:

#include <stdlib.h>        
#include <stdio.h>
#include <malloc.h>

void main( void )
{
DWORD len =50;

//ESEGUO L'ALLOCAZIONE DELL'ARRAY DI CHAR DI DIMENSIONE 50
char* c1 = (char
*) malloc( len ); //oppure basta scrivere char* c1 = new char[50];

String *str = "Hello World";

//INSERISCO I VARI CARATTERI ALL'INTERNO DELL'ARRAY DI CHAR
for (int
i=0;i<str->Length; i++){
     c1[i] = Convert::ToChar(str->Substring(i,1));
}}}

Però se voglio fare lo stesso per un progetto DLL libreria di classi scritto in C++ come faccio?
C'è un esempio di microsoft piuttosto interessante che spiega come risolvere il problema.
L'esempio di trova a questo http://support.microsoft.com/kb/311259/

In particolare il codice è questo:

using namespace System::Runtime::InteropServices;

int _tmain(void)
{

  

//Grazie al Metodo Marshal::StringToHGlobalAnsi riesco a convertire una stringa in un'array di char e mi ritorna il puntatore al primo elemento
//dell'array.
char* str2 = (char*)(void*)Marshal::StringToHGlobalAnsi(str);

....eseguo le operazioni su str2

//E' molto importante ricorda che la classe Marshal offre un insieme di metodi per l'allocazione della memoria non gestita.
//
Quindi quando si lavora con la memoria non gestita non è più il Garbage ad occuparsi della deallocazione della memoria,
//ma
 sarà nostro compito (cioè compito del programmatore) occuparsi di ciò. Per la classe Marshal il metodo da richiamare è:
   Marshal::FreeHGlobal(str2);

    return 0;
}

Spero possa esservi d'aiuto questo blog.
Ciao

Sara

martedì 24 aprile 2007

ImageButton e LinkButton nel Datagrid

Come si può inserire nel codice di un progetto Asp.net un'ImageButton oppure un linkButton in un Datagrid??

Nei forum si trovano notizie frammentate e quindi ho deciso di raccogliere tutte qui in questo post.

Vediamo come creare intanto un ImageButton:

public class ImageButtonColumn : TemplateColumn
{
private ImageButtonItem imgItem;

public event ImageClickEventHandler Click
{
add
{
imgItem.Click += value;
}
remove
{
imgItem.Click -= value;
}}

 public bool AutoPostBack
{
set
{
imgItem.AutoPostBack = value;
}
get
{
return imgItem.AutoPostBack;
}}

public ImageButtonColumn()
{
imgItem = new ImageButtonItem(false);
imgItem.AutoPostBack = false;
this.ItemTemplate = imgItem as ITemplate;
}}

 

internal class ImageButtonItem : ITemplate
{
private bool readOnly = true;
public event ImageClickEventHandler Click;

public ImageButtonItem(bool editable) 

readOnly = (editable==true)?false:true;
}

void ITemplate.InstantiateIn(Control container)
{
ImageButton ib = new ImageButton();
ib.ImageUrl = "info.gif"; 
//Immagine da Associare al Controllo
ib.Click += new ImageClickEventHandler(this.OnClick);
container.Controls.Add(ib);
}

private void OnClick(object sender, ImageClickEventArgs e)
{
Click(sender, e);
}
private bool autoPostBack=false;

public bool AutoPostBack
{
set
{
autoPostBack = value;
}
get
{
return autoPostBack;
}}}

 

e ora vediamo la definizione della classe LinkButton:

 

internal class LinkButtonItem: ITemplate
{
private bool readOnly = true;
public event EventHandler Click;

public LinkButtonItem(bool editable) {
readOnly = (editable==true)?false:true;
}

void ITemplate.InstantiateIn(Control container) {
LinkButton lb = new LinkButton();
lb.DataBinding += new EventHandler(this.BindLabelColumn);
lb.Click += new EventHandler(this.OnClick);
container.Controls.Add(lb);
}

public void BindLabelColumn(object sender, EventArgs e) {
LinkButton lb = (LinkButton)sender;
DataGridItem container =(DataGridItem)lb.NamingContainer;
lb.Text = Convert.ToString(System.Web.UI.DataBinder.Eval(container, "DataItem.ID"));
//CAMPO ASSOCIATO AL DATAGRID - ESTRAZIONE DEL CAMPO ID
}

private void OnClick(object sender, EventArgs e) {
Click(sender, e);
}

private bool autoPostBack=true;
public bool AutoPostBack {
set
{
autoPostBack = value;
}
get
{
return autoPostBack;
} } }

public class LinkButtonColumn : TemplateColumn
{
private LinkButtonItem imgItem;
public string idRecord;

public event EventHandler Click {
add {
imgItem.Click += value;
}
remove {
imgItem.Click -= value;
} }

public bool AutoPostBack {
set
{
imgItem.AutoPostBack = value;
}
get
{
return imgItem.AutoPostBack;
} }

public LinkButtonColumn()
{
imgItem = new LinkButtonItem(false);
imgItem.AutoPostBack = false;
this.ItemTemplate = imgItem as ITemplate;
}
}

 

ora prima di eseguire il Bind del Datagrid sarà necessario l'ImageButton o il LinkButton

 

//Supponiamo che il nostro DataTable si componga di più colonne. La colonna "ID" però la visualizzo
//come campo LinkButton e la colonna "Colore" la visualizzo come ImageButton

foreach (DataColumn dc in dt.Columns) {

if (dc.ColumnName.ToLower() == "id") {

LinkButtonColumn linkcol = new LinkButtonColumn(); 
// LINKBUTTON NELLA COLONNA ID
linkcol .HeaderText= dc.ColumnName;
linkcol .Click +=new EventHandler(linkcol_Click); 
//ASSOCIO L'EVENTO CLICK
MyDataGrid.Columns.Add(linkcol);
}

else if (dc.ColumnName.ToLower() == "colore") {

ImageButtonColumn imgcol = new ImageButtonColumn();  //
IMAGEBUTTON NELLA COLONNA COLORE
imgcol.HeaderText= dc.ColumnName;
imgcol.Click +=new EventHandler(imgcol_Click);   //ASSOCIO L'EVENTO CLICK
MyDataGrid.Columns.Add(imgcol);

}
else {
BoundColumn b=new BoundColumn();
b.DataField=dc.ColumnName;
b.DataFormatString="{0}";
b.HeaderText=dc.ColumnName;
MyDataGrid.Columns.Add(b);
} }

MyDataGrid.DataSource = dt;
MyDataGrid.DataBind();

//I DUE EVENTI SARANNO DEFINITI COME SEGUE:

private void imgcol_Click(object sender, ImageClickEventArgs e) { 
//CODICE
}

private void linkcol_Click(object sender, EventArgs e) {

LinkButton lbtn = (LinkButton)sender;
DataGridItem dgi = (DataGridItem)lbtn.NamingContainer;  //estraggo la colonna del datagrid
//....

}

 

 

Ciao a tutti e spero possa essere  d'aiuto il post.

martedì 27 febbraio 2007

Classe per leggere e scrivere da Registro

using namespace Microsoft::Win32;
using namespace System;

La classe seguente permette di leggere e scrivere nel Registro di Sistema
(classe in C++.net - Framework 1.1)

public __gc class Registro
{
private
:

String *CU; //= S"CU"; //current user
String *LM;
// = S"LM"; //Local_Machine
String *CR;
// = S"CR"; //classes_root
String *UR;
// = S"UR"; // Users
String *CC; // = S"CC"; // current_config

public:
Registro(){
CU = S"CU";
LM = S"LM";
CR = S"CR";
UR = S"UR";
CC = S"CC";
};

String* GetValue(String* key, String* subKey, String* entry);
int
CreateKey(String* key, String* subKey);
int
setValue(String* key, String* subKey, String* item, Object* _value);
int
deleteValue(String* key, String *subKey, String *entry);
};


Il seguente metodo permette di ottenere un valore da una chiave di Registro.
Il primo parametro deve essere CU,LM,CR,UR oppure CC.
Il secondo parametro è il nodo di Registro ad es. "Software\Microsoft\Microsoft Sql Server"
Il terzo parametro è la stringa di Registro dal quale ottenere il valore
In caso di errore ritorna il valore null altrimenti ritorna il valore della chiave di Registro.

String* Registro::GetValue(String* key, String* subKey, String* entry){
RegistryKey *reg;
try{
if
(key == S"CU")
  reg = Registry::CurrentUser->OpenSubKey(subKey);
else if
(key == S"LM")
  reg = Registry::LocalMachine->OpenSubKey(subKey);
else if
(key == S"UR")
  reg = Registry::Users->OpenSubKey(subKey);
else if
(key == "CC")
  reg = Registry::CurrentConfig->OpenSubKey(subKey);
else if
(key == "CR")
  reg = Registry::Users->OpenSubKey(subKey);
else
 
reg = Registry::LocalMachine->OpenSubKey(subKey);

return
reg->GetValue(entry, 0)->ToString();
}
catch
(Exception* ){
return
0;
}};

 

Il seguente metodo permette di cancellare una chiave di Registro.
Il primo parametro deve essere CU,LM,CR,UR oppure CC.
Il secondo parametro è il nodo di Registro ad es. "Software\Microsoft\Microsoft Sql Server"
Il terzo parametro è la stringa di Registro da cancellare
In caso di errore ritorna il valore 1, altrimenti valore 0.

int Registro::deleteValue(String *key, String *subKey, String *entry){
RegistryKey *reg;
try
{

if (key == S"CU")
  reg = Registry::CurrentUser->CreateSubKey(subKey);
else if
(key == S"LM")
  reg = Registry::LocalMachine->CreateSubKey(subKey);
else if
(key == S"UR")
 
reg = Registry::Users->CreateSubKey(subKey);
else if
(key == "CC")
  reg = Registry::CurrentConfig->CreateSubKey(subKey);
else if
(key == "CR")
  reg = Registry::Users->CreateSubKey(subKey);
else
 
reg = Registry::Users->CreateSubKey(subKey);

reg->DeleteValue(entry);
return
0;
}
catch
(Exception* ){
return
1;
}};


Il seguente metodo permette di creare un nodo di Registro.
Il primo parametro deve essere CU,LM,CR,UR oppure CC.
Il secondo parametro è il nodo di Registro da creare ad es. "Software\Microsoft\Microsoft Sql Server\Test"
In caso di errore ritorna il valore 1, altrimenti valore 0.
int
Registro::CreateKey(String* key, String* subKey){

RegistryKey* reg;
try
{

if (key == S"CU")
  reg = Registry::CurrentUser->CreateSubKey(subKey);
else if
(key == S"LM")
  reg = Registry::LocalMachine->CreateSubKey(subKey);
else if
(key == S"UR")
  reg = Registry::Users->CreateSubKey(subKey);
else if
(key == "CC")
  reg = Registry::CurrentConfig->CreateSubKey(subKey);
else if
(key == "CR")
  reg = Registry::Users->CreateSubKey(subKey);
else
 
reg = Registry::Users->CreateSubKey(subKey);

return
0;
}
catch
(Exception* )
{
return
1;
}};

 

Il seguente metodo permette di creare una chiave di Registro e di assegnargli un valore.
Il primo parametro deve essere CU,LM,CR,UR oppure CC.
Il secondo parametro è il nodo di Registro già presente ad es. "Software\Microsoft\Microsoft Sql Server"
Il terzo parametro è la chiave di Registro da creare e l'ultimo parametro è il valore che vogliamo assegnargli.
In caso di errore ritorna il valore 1, altrimenti valore 0.

int
Registro::setValue(String* key, String* subKey, String* item, Object* _value){

RegistryKey* reg;
try
{

if (key == S"CU")
  reg = Registry::CurrentUser->OpenSubKey(subKey, true
);
else if
(key == S"LM")
  reg = Registry::LocalMachine->OpenSubKey(subKey, true
);
else if
(key == S"UR")
  reg = Registry::Users->OpenSubKey(subKey, true
);
else if
(key == "CC")
  reg = Registry::CurrentConfig->OpenSubKey(subKey, true
);
else if
(key == "CR")
  reg = Registry::Users->OpenSubKey(subKey, true
);
else
  reg = Registry::Users->OpenSubKey(subKey, true
);

reg->SetValue(item, _value);
reg->Close();
return
0;
}

catch(Exception* )
{
return 1;
}};

giovedì 4 agosto 2005

Apertura PDF su una determinata pagina

In questo post vediamo come poter aprire un PDF da un'applicazione e come poter aprire il file specificando la pagina desiderata.
Se si apre una command line e si prova ad aprire un file direttamente da qui alla pagina desiderata basterà scrivere:
C:\Programmi\Adobe\..\acrobat.exe /A page=2 C:\abc.pdf
Ovvero con l'opzione "/A" si specificano i parametri con cui aprire il pdf e di seguito si indica il path dello stesso file da aprire.
Per maggiori informazioni sui parametri da specificare basta guardare qui.

In questo caso però nella mia macchina è installato Adobe Acrobat, se invece è installato solamente l'Acrobat Reader allora da command line si dovrà scrivere:
C:\Programmi\Adobe\..\acrord32.exe /A page = 2 C:\abc.pdf

A questo punto dobbiamo fare lo stesso da codice!
Dobbiamo recuperare il path di Installazione di Adobe, prestando attenzione alla versione e a quale Adobe è stato installato.
(Codice VB.NET)

Dim buf As String = "C:\abc.pdf" 'Path del file PDF da aprire
Dim pathKey As
String
Dim key As RegistryKey

'Setto il percorso di Adobe nel Registro di Sistema
'Controllo prima la presenza di Acrobat Reader e in seguito quella di Adobe Acrobat standard.

pathKey = "Software\Adobe\Acrobat Reader\"
key = Registry.LocalMachine.OpenSubKey(pathKey)

If (key Is Nothing)
Then
 
pathKey = "Software\Adobe\Adobe Acrobat\"
  key = Registry.LocalMachine.OpenSubKey(pathKey)
End If

If (key Is Nothing) Then
 
'Non sono state trovate nè le chiavi di Acrobat Reader nè quelle di Adobe Acrobat
 
'Viene aperto il pdf sulla prima pagina!
 ApriFile(buf, Nothing
)
 
Return
End
If

'Per trovare dove è installato l'Adobe dobbiamo trovare la chiave di registro corretta che solitamente si trova in:
' Software\Adobe\Adobe Acrobat\7.0\InstallPath oppure
' Software\Adobe\Acrobat Reader\5.0\InstallPath
'Quello che è importante trovare è la versione di Adobe installata. Se ci sono più versioni installate,
'viene presa l'ultima versione presente
.

Dim values As String() = key.GetSubKeyNames()
Dim maxVersion As String
= "0"

For Each s As String In values
 
Try
   If (Double.Parse(s) > Double.Parse(maxVersion))
Then
        
maxVersion = s
   End
If
 Catch ex As
Exception
 End
Try
Next

If (maxVersion = "0")
Then
  'Se non è stata trovata una versione di Adobe installata, il pdf viene aperto sulla prima pagina

 
ApriFile(buf, Nothing)
Else
   'Il percorso nel registro è stato costruito. Estraiamo il percorso di installazione di Adobe che solitamente è:
   ' C:\Programmi\Adobe\Acrobat 7.0\Acrobat oppure C:\Programmi\Adobe\Acrobat 7.0\Reader

  
pathKey = pathKey & maxVersion & "\InstallPath\"
   key = Registry.LocalMachine.OpenSubKey(pathKey)

   'La variabile pathAdobe dovrà contenere il percorso di installazione di Adobe.E' necessario controllare su è presente Acrobat Reader
   '(l'eseguibile è acrord32.exe) oppure Adobe Acrobat (l'eseguibile è acrobat.exe)

   Dim pathAdobe As String
   pathAdobe = key.GetValue("") & "\acrord32.exe"

   If (New FileInfo(pathAdobe).Exists = False) Then 
    
pathAdobe = key.GetValue("") & "\acrobat.exe"
     If (New FileInfo(pathAdobe).Exists = False)
Then
           ApriFile(buf, Nothing
)
          
Return
     End
If
   End
If

   ApriFile(pathAdobe, "/A page=" & pagina & " " & buf)
End
If

Infine il metodo per lanciare l'apertura del pdf:

Private Sub ApriFile(ByVal pathFile As String, ByVal argomenti As String)
Dim info As System.Diagnostics.ProcessStartInfo = New System.Diagnostics.ProcessStartInfo(pathFile)
info.UseShellExecute = True
info.Verb = "Open"

If (argomenti <> Nothing And argomenti <> "") Then
  
info.Arguments = argomenti
End If

System.Diagnostics.Process.Start(info)
End Sub

 

P.S: Questo post l'ho potuto scrivere anche grazie all'aiuto di Raffaele Rialdi che mi ha aiutato nella risoluzione del problema nel forum di microsoft (qui). Quindi un doveroso grazie va anche a lui!!!

lunedì 27 giugno 2005

Richieste Http e Proxy

In questo post vediamo come poter scrivere del codice C# per effettuare richieste Http da un'applicazione.
I problemi che si possono incontrare in queste richieste Http possono essere soprattutto relativi al Proxy, in quanto non
permette all'applicazione di effettuare richieste internet.

Per prima cosa vediamo quali sono le classi che verranno utilizzate:

using System.Net.Sockets;
using System.Net;
using System.IO;
using System.Text;

Nel mio progetto ho inserito un oggetto Button e quando questo viene premuto viene richiamato l'evento click che effettua la richiesta Http. Questo è il codice:

private void button2_Click(object sender, System.EventArgs e)
{
string valVerifica= "";
try
{
//Indirizzo dell'url Http o Https
string urlRegistro = @http://www......;

HttpWebRequest wrInfoCam =(HttpWebRequest) HttpWebRequest.Create(urlRegistro);
wrInfoCam.KeepAlive = false;

//La proprietà KeepAlive a false permette di NON impostare connessione persistenti ai server che
//le supportano.

//IMPOSTAZIONI PROXY.
//questa parte di codice può essere omessa nel caso non si utilizzi un proxy

//Definizione credenziali
NetworkCredential cred = new NetworkCredential("sara", "xxx");

CredentialCache myCache = new CredentialCache();
myCache.Add(new Uri("
http://master.lan.xxxx.it:8080/"), "Basic", cred);

WebProxy _proxy = new WebProxy();
_proxy = (WebProxy)(wrInfoCam.Proxy);

Uri _uri = new Uri("http://master.lan.xxxx.it:8080/");
_proxy.Address = _uri;
_proxy.Credentials = myCache;

wrInfoCam.Proxy = _proxy;
//FINE IMPOSTAZIONE PROXY

RequestStateHttp reqState = new RequestStateHttp();
reqState.request = wrInfoCam;

reqState.request.Method="POST";
wrInfoCam.ContentType= "application/x-www-form-urlencoded";

IAsyncResult result =(IAsyncResult) (wrInfoCam.BeginGetRequestStream(new AsyncCallback(ReadCallbackHttp), reqState));

//Attendo la risposta del sito http o https mediante l'oggetto HttpWebResponse.
HttpWebResponse myHttpWebResponse=(HttpWebResponse) (wrInfoCam.GetResponse());

Stream streamResponse=myHttpWebResponse.GetResponseStream();
StreamReader streamRead = new StreamReader(streamResponse);
char [] readBuff = new Char[256];
int count = streamRead.Read(readBuff, 0, 256);

while (count > 0)
{
valVerifica = new String(readBuff, 0, count);
count = streamRead.Read(readBuff, 0, 256);
}
streamResponse.Close();
streamRead.Close();
myHttpWebResponse.Close();

MessageBox.Show(valVerifica);

}
catch(Exception ex)
{
MessageBox.Show(ex.ToString());
}
}

A questo punto manca da definire la nostra classe RequestStateHttp, che consente di passare i dati su chiamate asincrone:

public class RequestStateHttp
{
 static int BUFFER_SIZE = 1024;

 public StringBuilder requestData;
 public Byte [] bufferRead;
 public HttpWebRequest  request;
 public HttpWebResponse  response;
 public Stream responseStream;

 public RequestStateHttp()
 {
  bufferRead = new Byte[BUFFER_SIZE];
  requestData = new StringBuilder("");
  request = null;
  responseStream = null;
 }
}

E rimane da definire il metodo ReadCallbackHttp che consente di implementare il metodo di callback asincrono per la lettura del flusso di risposta. I dati ricevuti dalla risorsa Internet vengono trasferiti nella proprietà ResponseData dell'istanza di RequestStateHttp, quindi si inizia un'altra lettura asincrona del flusso di risposta finché non viene più restituito alcun dato.

void ReadCallbackHttp(IAsyncResult async)
{
 try
 {
  RequestStateHttp myRequestState=(RequestStateHttp)(async.AsyncState);
  HttpWebRequest myHttpWebRequest2=(HttpWebRequest)(myRequestState.request);
    
  Stream postStream=myHttpWebRequest2.EndGetRequestStream(async);

  ASCIIEncoding encoder = new ASCIIEncoding();

  string parametriUrl = "...eventuali parametri da passare all'url...";
  //se ci sono più parametri richiesti da passare all'url allora è necessario utilizzare l'&, ovvero:
  //"USer=xx&Password=yy"

  Byte [] byteArray = encoder.GetBytes(parametriUrl);

  postStream.Write(byteArray,0,parametriUrl.Length);
  postStream.Close();
 }
 catch (Exception ex)
 {
  MessageBox.Show(ex.ToString());
 }
}

Tutto il codice qui inserito è testato e funzionante.
Se avete un proxy impostato sulla vostra rete e durante l'esecuzione del codice nel metodo ReadCallBackHttp, nel metodo EndGetRequestStream si genera un'eccezione del tipo "Connessione sottostate chiusa. Errore 407: Autenticazione Proxy mancante" ci possono essere due tipo di errori principalmente:
1. i dati relativi all'indirizzo del proxy o le credenziali sono errate. Per es. l'indirizzo del proxy deve essere scritto come:
http://master.lan.xxxx.it, mentre l'uri dev'essere nel formato: http://master.lan.xxxx.it:PORTA/.

2. Il proxy utilizza due tipi di autenticazione, quella Basic e quella NTLM ovvero autenticazione windows. Ho potuto notare che la sola autenticazione Basic settata, la richiesta all'indirizzo http/https funziona correttamente, mentre se è settata anche l'autenticazione NTLM ci possono essere dei problemi con le credenziali, che possono non venire riconosciute.

 

P.S: Questo post l'ho potuto scrivere anche grazie all'aiuto di Gabriele Gaggi che mi ha aiutato nella risoluzione del problema. Quindi un doveroso grazie va anche a lui!!!

lunedì 7 febbraio 2005

File Resources

Per poter creare un file resources dove inserire Icone o Immagini da poter utilizzare all'interno del vostro applicativo basterà scrivere:
Icon ic = new Icon(@"C:\myIco.ico";);
IResourceWriter rw = new
ResourceWriter(@"C:\Image.resources"
;);
rw.AddResource("Icon", ic);
rw.Close();

oppure basta scaricarsi il programmino Resources a questo link:
http://www.aisto.com/roeder/dotnet/

A questo basta aggiungere il file resources al vostro progetto. (Menu File->Aggiungi Elemento Esistente).

Per poter invece caricare un'immagine da file resources:

ResourceManager rm = new ResourceManager("nomeNamespace.NomeFileResources", this.GetType().Assembly);
Icon iconaForm = (Icon)rm.GetObject("Icon");

La stessa cosa può essere fatta per gli oggetti Image.

giovedì 3 febbraio 2005

Progetto di Installazione

Il seguente articolo chiarisce 4 punti:
1- come creare un progetto di distribuzione (o installazione);
2- come inserire nuove finestre di dialogo al progetto di distribuzione;
3- come estendere la classe Installer (creazione dll) ed associarla al progetto di distribuzione;
4- come poter effettuare il passaggio dei parametri tra il progetto di distribuzione
e la dll che estende la classe Installer.
(Codice C#)

Partiamo dal primo punto, come creare un progetto di distribuzione:
per prima cosa vi conviene creare per esempio una semplice applicazione Windows con C# che chiameremo Test.
Una volta creata, andare nel menu File, cliccare su Aggiungi Progetto -> Nuovo progetto.
Scegliere la voce "Progetto di Installazione e Distribuzione", assegnare un nome al Progetto e dare Ok.
A questo punto si dovrebbe aprire in automatico il File System del progetto di Installazione, se non fosse così selezionare il progetto di distribuzione in Esplora Soluzioni e dal menu Visualizza, scegliere la voce Editor->File System.
Sarà possibile vedere 3 cartelle: Cartella Applicazione, Desktop utente e Manu Programmi Utente.
Cliccare su cartella applicazione con il tasto sx del mouse e cliccare su Aggiungi->Output Progetto.

In automatico verrà scelto il progetto da associare, ovvero Test.
Selezionare la voce "Output primario" e dare ok.

A questo punto aggiungiamo una finestra di dialogo al progetto.
Dal menu Visualizza, scegliere la voce Editor-> Interfaccia Utente, così facendo possiamo vedere l'elenco delle finestre di dialogo che verranno visualizzate durante l'installazione del programma Test.
Per esempio inseriamo una finestra di dialogo con 2 CheckBox.

Per fare questo bisogna cliccare con il tasto sinistro del mouse sulla voce "Avvio" e cliccare su Agguingi
finestra di dialogo.A questo punto viene visualizzato un elenco di finestre di dialogo che si possono aggiungere, alcune contengono radio button, altre semplici label, ecc...
Nel nostro caso aggiungiamo una Casella di Controllo e la inseriamo tra "Introduzione" e "Cartella di Installazione".
N.B. Per spostare le finestre di dialogo basta trascinarle con il mouse.
Se guardiamo le proprietà della finestra di dialogo possiamo notare che ci sono 4 CheckBox ognuna delle quali ha:
- una Label o etichetta della casella di controllo;
- una Property ovvero il nome della casella di controllo. Questo valore è molto importante perchè servirà quando dovremmo passare dei valori alla classe Installer.
- un valore che indica se la check Box risultà già spuntata;
- un valore che indica se la check Box è visibile.
Per es. sulla label della prima check Box scriviamo "Crea File" e sulla label della seconda check box scriviamo "Elimina File" mentre le altre due check box le rendiamo "non visibili".

Passiamo al p.to 3 ovvero alla creazione della dll.
E' necessario avviare un'altro progetto come libreria di classi.
A questo punto la classe la definiamo così:

using System;
using System.ComponentModel;
using System.Collections;
using System.Configuration.Install;

namespace XXInstaller
{
 [RunInstaller(true)]
 public class MyInstaller: Installer
 {
  public MyInstaller()
  {
  }
  
  public override void Install(IDictionary savedState)
  {
   base.Install(savedState);
  }

  public override void Commit(IDictionary savedState)
  {
   base.Commit(savedState);
  }

  public override void Rollback(IDictionary savedState)
  {
   base.Rollback(savedState);
  }

  public override void Uninstall(IDictionary savedState)
  {
   base.Uninstall(savedState);
  }
 }
}

In questo modo ridefiniamo le funzioni Install, Commit, Rollback e Unistall.
Compiliamo il progetto e generiamo la dll.
La nostra dll per ora non fa niente di particolare..richiama solamente le funzioni della classe Installer.
Attenzione che MSDN dice che è necessario, senza darne un motivo ben preciso, che la chiamata alla classe base avvenga prima del codice "custom"!!!

A questo associamo la dll al nostro progetto di distribuzione.
Visualizzare l'editor del file system e con il tasto destro del mouse cliccare su "Cartella Applicazione" e aggiungere la dll appena creata al progetto di installazione.
A questo punto visualizzare l'editor delle azioni personalizzate, cliccare sempre con il tasto destro del mouse su "Azione personalizzata" e sceglire la voce "Aggiungi azione personalizzata". Viene aperta una finestra dalla quale effettuare la scelta, quindi cliccare su "Cartella Applicazione" e scegliere la dll che è stata aggiunta
precedentemente al progetto di installazione.

4. Ora passiamo ad analizzare come effettuare il passaggio dei parametri dal progetto di installazione
alla dll che estende la classe Installer.
Per prima cosa nel progetto di installazione dobbiamo:
1. visualizzare l'editor delle azioni personalizzate dove precedentemente avevamo associato la nostra dll ai metodi Install, Commit, Rollback e Unistall.
2.Visualizzaziamo le proprietà della dll relativa al metodo Install.
Possiamo notare la proprietà CustomActionData che serve proprio per passare i parametri al metodo Install relativo.
Allora su CustomActionData scriveremo:
/nome1=[CHECKBOXA1] /nome2=[CHECKBOXA2]
dove CHECKBOXA1 e CHECKBOXA2 sono i nomi delle caselle di controllo.
Mentre nome1 e nome2 siamo noi ad assegnarli, sono praticamente i nomi delle variabili che passaremo alla dll.
La proprietà CustomActionData assume il formato /nome=valore.
Più valori devono essere separati da uno spazio singolo: /nome1=valore1 /nome2=valore2.
Se un valore ha uno spazio al suo interno, deve essere racchiuso fra virgolette:
/nome="un valore".
Le proprietà di Windows Installer possono essere passate utilizzando le parentesi quadre: /nome=[NOMEPROPRIETÀ].

A questo punto passiamo ad aggiungere un po' di codice sul progetto di estensione della classe Installer. In questo caso inserisco solamente il codice delle modifiche al metodo Install:
public override void Install(IDictionary savedState)
  {
   base.Install(savedState);

   //System.Diagnostics.Debugger.Break();

   string nome1 = this.Context.Parameters["nome1"];
   string nome2 = this.Context.Parameters["nome2"];

   /*
   Le variabili nome1 e nome2 possono assumere valore 1 se sono
   state spuntate, altrimenti possono assumere valore 0 oppure
   valore nullo. Per non dover controllare sempre se nome1 e nome2
   sono uguali a 0 o a null...eseguo l'assegnazione seguente.
   */
   if (nome1 != "1")
    nome1 = "0";

   if (nome2 != "1")
    nome2 = "0";

   /*
    * Se per caso non è spuntata nè nome1 nè nome2 allora blocco l'installazione
    **/

   if (nome1 == "0" && nome2 == "0")
   {
    throw new Exception("Deve essere spuntata almeno una delle due opzioni!");
   }

   if (nome1 == "1")
   {
    //Creazione di un file
    try
    {
     FileInfo fi = new FileInfo(@"C:\testInstaller.txt");
     if (fi.Exists == false)
      fi.Create();
    }
    catch(Exception){}
   }

   if (nome2=="1")
   {
    //Eliminazione del file
    try
    {
     FileInfo fi = new FileInfo(@"C:\testInstaller.txt");
     if (fi.Exists == true)
      fi.Delete();
    }
    catch(Exception){}
   }
  }
  

A questo punto ricreare la dll. 
Creare il progetto di installazione, fare attenzione a prendere la dll con l'estensione della classe Installer appena creata!
  
A questo punto il nostro progetto di installazione è completato!

 

 

Copyright © Sara Fabris