2009-09-03 16 views
5

Editar: He actualizado el código a continuación para que ahora funcione, gracias a la respuesta de Rob.¿Cómo lleno un ComboBox en el momento de la instalación en WiX?

he encontrado un par de páginas que muestran cómo hacer esto (http://www.cmcrossroads.com/content/view/13160/120/, http://www.mail-archive.com/[email protected]/msg05103.html) y se veía a través del código fuente de la WAI (http://wai.codeplex.com/), pero me parece que no puede conseguir que funcione en mi instalador no importa lo que intento. Si alguien puede detectar lo que estoy haciendo mal, estaría muy agradecido. Mi fragmento de WiX para el diálogo es el siguiente:

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

mi código de acción personalizada es:

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

Antes había dos problemas:

1) El código anterior fallado durante la ejecución de la acción personalizada con "Función fallida durante la ejecución. Base de datos: la (s) actualización (es) de tabla (s) fallaron". - Esto fue debido al problema de indexación que hace que el código intente y escriba una cadena en una columna int.

2) Si cambio de la línea

lRecord.SetString(2, lWebsiteName); 

a

lRecord.SetString(2, lEntry.Name); 

luego mirando traza la acción parece tener éxito, pero cuando el instalador ejecuta el cuadro combinado no tiene entradas para escoger.

Si cambio el cuadro combinado para tener valores codificados, todo funciona bien, incluso si codigo el equivalente de lWebsiteName.

+0

por lo que el instalador debe ejecutarse como administrador?o ¿cómo evito el acceso denegado cuando trato de obtener una lista de sitios web? –

+0

Debe ejecutarse como administrador. Disfruta de resolver ese problema. – jcmcbeth

Respuesta

3

No uso DTF (todas las acciones personalizadas de C++ son naturales) pero las de Record están basadas en 1. ¿Has intentado cambiar todas tus llamadas a SetRecord() por un índice?

Además, el código .wxs anterior parece sugerir que está utilizando "DUMMYPROPERTY" como propiedad de control para el ComboBox no como "IIS_WEBSITAME" como el código .cs está usando.

+0

Gracias, he intentado 1 basado en algún punto, pero he intentado tantas cosas que quizás no lo haya hecho con este código. Intentaré de nuevo. Estoy usando DUMMYPROPERTY en el segundo control de combo oculto para asegurarme de que se crea la tabla ComboBox, intentaré señalarlo en la misma propiedad que el control real y ver si eso hace algo. – Dan

+0

Era la indexación (lo que explicaba ambos problemas). Gracias de nuevo. – Dan

0

Este es bastante viejo, sin embargo tuve un problema similar y me gustaría compartir lo que he encontrado, tal vez esto le ahorre tiempo a alguien.

para asegurarse de mesa ComboBox uso se crea EnsureTable, asegúrese de CA no sobrescribir valor definido:

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

Tengo una función JavaScript para el llenado ListItems: (sí, sé que algunos de ustedes don' t como JS de acciones personalizadas, pero todavía es lo suficientemente cómodo)

// 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 llamo de mi otra función (que se llama como acción personalizada) como esto:

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: Eliminé fragmentos de código no relevantes de la última función para que sean legibles. P.S. siempre (me refiero a SIEMPRE) usar nulo, longitud cero y comprobación de errores, probar/atrapar y asegurar el inicio de sesión con algo como esto:

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