2009-09-03 5 views
5

Edit: Ho aggiornato il codice qui sotto in modo che funzioni ora, grazie alla risposta di Rob.Come posso popolare un ComboBox al momento dell'installazione in WiX?

ho trovato un paio di pagine che mostrano come fare questo (http://www.cmcrossroads.com/content/view/13160/120/, http://www.mail-archive.com/[email protected]/msg05103.html) e guardò attraverso il codice sorgente per WAI (http://wai.codeplex.com/), ma io non riesco a farlo funzionare nel mio programma di installazione non importa cosa cerco. Se qualcuno riesce a capire cosa sto facendo male sarei molto grato. Il mio frammento WiX per il dialogo si presenta così:

<UI> 
    <Dialog> 

...snip... 

    <Control Id="WebsiteName" Type="ComboBox" ComboList="yes" Sorted="yes" Property="IIS_WEBSITENAME" X="20" Y="73" Width="150" Height="17"/> 

...snip... 

    <!-- We want our custom action to fill in the WebsiteName ComboBox above 
     however, if no ComboBox entries exist at compile time then the 
     ComboBox table is not created in the MSI and we can't add to it in 
     the custom action. So we have this hidden dummy list box to force 
     the table to appear. --> 
    <Control Id="DummyComboBox" Hidden="yes" Type="ComboBox" Sorted="yes" ComboList="yes" Property="DUMMYPROPERTY" X="65" Y="60" Width="150" Height="18"> 
     <ComboBox Property="DUMMYPROPERTY"> 
     <ListItem Text="Dummy" Value="Dummy"/> 
     </ComboBox> 
    </Control> 
    </Dialog> 
</UI> 

<Property Id="DUMMYPROPERTY">Dummy</Property> 
<Property Id="IIS_WEBSITENAME"/> 
<CustomAction Id="FillWebsiteNameList" BinaryKey="WiXCustomAction.dll" DllEntry="FillWebsiteNameList" Execute="immediate" /> 
<InstallUISequence> 
    <Custom Action="FillWebsiteNameList" After="CostFinalize"/> 
</InstallUISequence> 

mio codice di azione personalizzata è:

[CustomAction] 
public static ActionResult FillWebsiteNameList(Session xiSession) 
{ 
    xiSession.Log("Begin FillWebsiteNameList"); 

    xiSession.Log("Opening view"); 

    View lView = xiSession.Database.OpenView("SELECT * FROM ComboBox"); 
    lView.Execute(); 

    xiSession.Log("Creating directory entry"); 

    DirectoryEntry lIis = new DirectoryEntry("IIS://localhost/w3svc"); 

    xiSession.Log("Checking each child entry"); 

    int lIndex = 1; 
    foreach (DirectoryEntry lEntry in lIis.Children) 
    { 
    if (lEntry.SchemaClassName == "IIsWebServer") 
    { 
     xiSession.Log("Found web server entry: " + lEntry.Name); 

     string lWebsiteName = (string)lEntry.Properties["ServerComment"].Value; 
     xiSession.Log("Website name: " + lWebsiteName); 

     xiSession.Log("Creating record"); 
     Record lRecord = xiSession.Database.CreateRecord(4); 

     xiSession.Log("Setting record details"); 
     lRecord.SetString(1, "IIS_WEBSITENAME"); 
     lRecord.SetInteger(2, lIndex); 
     lRecord.SetString(3, lEntry.Name); // Use lWebsiteName only if you want to look up the site by name. 
     lRecord.SetString(4, lWebsiteName); 

     xiSession.Log("Adding record"); 
     lView.Modify(ViewModifyMode.InsertTemporary, lRecord); 

     ++lIndex; 
    } 
    } 

    xiSession.Log("Closing view"); 

    lView.Close(); 

    xiSession.Log("Return success"); 

    return ActionResult.Success; 
} 

Ci deve essere utilizzato due problemi:

1) Il codice di cui sopra non è riuscito durante l'esecuzione dell'azione personalizzata con "Funzione non riuscita durante l'esecuzione Database: Tabella (s) Aggiornamento non riuscito." - Questo a causa del problema di indicizzazione che ha causato il tentativo del codice e di scrivere una stringa su una colonna int.

2) Se cambio la linea

lRecord.SetString(2, lWebsiteName); 

a

lRecord.SetString(2, lEntry.Name); 

poi guardando traccia l'azione sembra avere successo, ma quando il programma di installazione eseguito casella combinata non ha voci tra cui scegliere.

se cambio la casella combinata di avere valori hardcoded tutto funziona bene, anche se mi hardcode l'equivalente di lWebsiteName.

+0

quindi l'installer deve essere eseguito come amministratore?o come posso evitare l'accesso negato durante il tentativo di ottenere l'elenco dei siti Web? –

+0

Deve essere eseguito come amministratore. Divertiti a risolvere questo problema. – jcmcbeth

risposta

3

Io non uso (CustomActions tutto naturale C++ per me), ma DTF Record sono basati 1. Hai provato a spostare tutte le tue chiamate SetRecord() con un indice?

Inoltre, il codice .wxs sopra sembra suggerire che si sta utilizzando "DUMMYPROPERTY" come proprietà di controllo per il ComboBox non "IIS_WEBSITENAME" come il codice .cs sta usando.

+0

Grazie, ho provato 1 basato a un certo punto, ma ho provato così tante cose che non avrei potuto farlo con questo codice! Ci riproverò Sto usando DUMMYPROPERTY nel secondo controllo combo nascosto per assicurarmi che la tabella di ComboBox venga creata, proverò a puntarla con la stessa proprietà del controllo reale e vedrò se fa qualcosa. – Dan

+0

Era l'indicizzazione (che spiegava entrambi i problemi). Grazie ancora. – Dan

0

questo è piuttosto vecchio, ma ho avuto problema simile e vorrei condividere ciò che ho trovato, forse questo consente di risparmiare tempo di qualcuno.

per assicurarsi tavolo ComboBox uso è creato EnsureTable, assicurarsi CA non sovrascrive valore definito:

<EnsureTable Id="ComboBox"/> 
<Property Id="RS_INSTANCES" Secure="yes"/> 
<CustomAction Id="GetRSintances" BinaryKey="JSCommon" Return="ignore" 
       JScriptCall="GetRSintances" Execute="immediate" /> 

<InstallUISequence> 
    <Custom Action="GetRSintances" After="AppSearch"> 
    <![CDATA[NOT Installed AND NOT RS_INSTANCES]]> 
    </Custom> 
</InstallUISequence> 

<InstallExecuteSequence> 
    <Custom Action="GetRSintances" After="AppSearch"> 
    <![CDATA[NOT Installed AND NOT RS_INSTANCES]]> 
    </Custom> 
</InstallExecuteSequence> 

<!-- UI part --> 
<Control Id="ComboBox1" Type="ComboBox" X="20" Y="160" Width="100" Height="20" Property="RS_INSTANCES" Sorted="yes" > 
    <ComboBox Property="RS_INSTANCES"> 
     <!-- dynamicly filled during installation --> 
    </ComboBox> 
    </Control> 

Ho una funzione JavaScript per ListItems riempimento: (sì, lo so che alcuni di voi don' t come JS per azioni personalizzate, ma è ancora abbastanza conveniente)

// Add ListItem to ComboBox or ListView at install time 
function AddListItemToMSI(Property, Order, Value, Text, Table) { 
    try { 
    var controlView = Session.Database.OpenView("SELECT * FROM " + Table); 
    controlView.Execute(); 

    var record = Session.Installer.CreateRecord(4); 
    record.StringData(1) = Property; 
    record.IntegerData(2) = Order; 
    record.StringData(3) = Value; 
    record.StringData(4) = Text; 

    controlView.Modify(7, record); 
    controlView.Close(); 
    } 
    catch (err) { 
    ShowMessage('Couldn\'t add ListItem entry, error occured: ' + err.message, msiMessageTypeInfo); 
    } 

    return 1; 
} 

lo chiamo io dalla mia altra funzione (si chiama come azione personalizzata) in questo modo:

var ComboBoxProperty = 'RS_INSTANCES'; 
var InstanceFullName; 
for (i = 0; i < Names.length; i++) { 
    InstanceFullName = GetInstanceName(Names[i]); //this function looks up full name in the registry 
    AddListItemToMSI(ComboBoxProperty, i, InstanceFullName, '', 'ComboBox'); 
    if (i == 0) { 
     Session.Property(ComboBoxProperty) = InstanceFullName; 
    } 
} 

NOTA: ho rimosso pezzi non rilevanti di codice da ultima funzione per renderlo leggibile. P.S. sempre (intendo SEMPRE) uso null, lunghezza zero e controllo degli errori, try/catch e garantire la registrazione con qualcosa del genere:

function ShowMessage(text, options) { 
    if (options == null) { 
     var options = msiMessageTypeUser; 
    } 
    var oRecord = Session.Installer.CreateRecord(1); 
    oRecord.StringData(1) = text; 
    var response = Session.Message(options, oRecord); 
    oRecord.ClearData(); 
    oRecord = null; 
    response = null; 
}