2009-09-03 14 views
5

Edytuj: Zaktualizowałem poniższy kod, aby teraz działał, dzięki odpowiedzi Roba.Jak wypełnić ComboBox podczas instalacji w WiX?

Znalazłem kilka stron, które pokazują, jak to zrobić (http://www.cmcrossroads.com/content/view/13160/120/, http://www.mail-archive.com/[email protected]/msg05103.html) i przejrzałem kodu źródłowego WAI (http://wai.codeplex.com/), ale nie wydaje się uzyskać go do pracy w moim instalatora bez względu na to, co próbuję. Jeśli ktoś zauważy, co robię źle, byłbym bardzo wdzięczny. Moja WiX fragment dla dialogu wygląda następująco:

<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> 

mój kodu niestandardowego działania jest:

[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; 
} 

Kiedyś dwa problemy:

1) Powyższy kod nie powiodło się podczas biegu akcji niestandardowej z "Funkcja nie powiodła się podczas wykonywania Baza danych: Tabela (y) Aktualizacja nie powiodła się." - Było to spowodowane problemem indeksowania powodującym, że kod próbował napisać ciąg do kolumny int.

2) Jeśli zmienię linię

lRecord.SetString(2, lWebsiteName); 

do

lRecord.SetString(2, lEntry.Name); 

następnie patrząc na ślad akcja wydaje się uda, ale jeśli uruchomić instalator ComboBox nie ma wpisów do wyboru.

Jeśli zmienię combobox tak, aby zawierał wartości zakodowane na stałe, wszystko działa poprawnie, nawet jeśli zakodowuję kod równoważny lWebsiteName.

+0

więc instalator musi być uruchomiony jako administrator?lub w jaki sposób uniknąć odmowy dostępu podczas próby uzyskania listy stron internetowych? –

+0

To musi być uruchamiane jako administrator. Ciesz się z rozwiązania tego problemu. – jcmcbeth

Odpowiedz

3

Nie używam DTF (wszystkie naturalne C++ CustomActions dla mnie), ale Record są oparte na 1. Czy próbowałeś przesuwać wszystkie swoje wywołania SetRecord() przez jeden indeks?

Również powyższy kod .wxs sugeruje, że używasz "DUMMYPROPERTY", ponieważ właściwość Control dla składnika ComboBox nie jest "IIS_WEBSITENAME", podobnie jak kod .cs.

+0

Dzięki, wypróbowałem 1 oparty w pewnym momencie, ale próbowałem tak wielu rzeczy, których nie zrobiłbym z tym kodem! Spróbuję ponownie. Używam DUMMYPROPERTY w drugiej, ukrytej kontroli combo, aby upewnić się, że tabela ComboBox zostanie utworzona, spróbuję wskazać ją w tej samej właściwości, co w przypadku rzeczywistej kontroli, i sprawdzić, czy to coś robi. – Dan

+0

To było indeksowanie (które wyjaśniało oba problemy). Dzięki jeszcze raz. – Dan

0

Ten jest dość stary, ale miałem podobny problem i chciałbym podzielić się tym, co znalazłem, może to uratuje czyjś czas.

aby upewnić stołowego ComboBox jest tworzony użytkowania EnsureTable, zapewniają CA nie nadpisuje zdefiniowaną wartość:

<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> 

Mam funkcji JavaScript do napełniania ListItems: (tak, wiem, że niektórzy z was don” t jak JS działań niestandardowych, ale nadal jest wystarczająco wygodne)

// 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; 
} 

nazywam go z mojej innej funkcji (jest to nazywane jako niestandardowego działania) tak:

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; 
    } 
} 

UWAGA: Usunąłem nieistotne fragmenty kodu z ostatniej funkcji, aby umożliwić ich odczytanie. P.S. zawsze (mam na myśli zawsze) używać null, zerowej długości i sprawdzanie błędów, try/catch i zapewnić zalogowaniu się coś takiego:

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; 
} 
Powiązane problemy