Dicas e truques para WiX

264

Estamos usando o WiX há um tempo e, apesar das queixas usuais sobre a facilidade de uso, está indo razoavelmente bem. O que estou procurando é um conselho útil sobre:

  • Configurando um projeto WiX (layout, referências, padrões de arquivo)
  • Integração do WiX em soluções e processos de criação / lançamento
  • Configurando instaladores para novas instalações e atualizações
  • Quaisquer bons hacks WiX que você gostaria de compartilhar
si618
fonte
Dê uma olhada em gui4wix.codeplex.com
TarunG
10
Fechado como não construtivo? Eu aprendi montes de fazer esta pergunta! A pouca consistência de StackOverflow também seria bom ... por exemplo stackoverflow.com/questions/550632/...
si618
15
Ele tem '203' ups, isso é suficiente para provar sua utilidade.
TarunG
As perguntas do SO devem ter uma resposta definitiva e correta; perguntas abertas fazem com que as perguntas que as pessoas fazem sobre problemas reais caiam da primeira página. faq @Si .: Essa política sempre esteve presente no AFAIK, mas agora é melhor aplicada; essa pergunta tem quase três anos.
Jim Dagg
Bem, Jim, é uma pergunta em aberto, e acho que cabe à comunidade SO decidir, mas devo dizer que encerrá-lo como não construtivo parece estranho, considerando que eu e, pelo que parece, muitas outras pessoas acharam esta pergunta útil (por exemplo, goo.gl/Zqp2X ) e se encaixa muito bem com a practical, answerable questions based on actual problems that you faceparte do FAQ.
si618

Respostas:

157
  1. Mantenha as variáveis ​​em um wxiarquivo de inclusão separado . Permite a reutilização, as variáveis ​​são mais rápidas de encontrar e (se necessário) permitem uma manipulação mais fácil por uma ferramenta externa.

  2. Definir variáveis ​​de plataforma para construções x86 e x64

    <!-- Product name as you want it to appear in Add/Remove Programs-->
    <?if $(var.Platform) = x64 ?>
      <?define ProductName = "Product Name (64 bit)" ?>
      <?define Win64 = "yes" ?>
      <?define PlatformProgramFilesFolder = "ProgramFiles64Folder" ?>
    <?else ?>
      <?define ProductName = "Product Name" ?>
      <?define Win64 = "no" ?>
      <?define PlatformProgramFilesFolder = "ProgramFilesFolder" ?>
    <?endif ?>
  3. Armazene o local da instalação no registro, permitindo que as atualizações encontrem o local correto. Por exemplo, se um usuário definir um diretório de instalação personalizado.

     <Property Id="INSTALLLOCATION">
        <RegistrySearch Id="RegistrySearch" Type="raw" Root="HKLM" Win64="$(var.Win64)"
                  Key="Software\Company\Product" Name="InstallLocation" />
     </Property>

    Nota : o guru da WiX, Rob Mensching , publicou uma excelente entrada no blog, que entra em mais detalhes e corrige um caso extremo quando as propriedades são definidas na linha de comando.

    Exemplos usando 1. 2. e 3.

    <?include $(sys.CURRENTDIR)\Config.wxi?>
    <Product ... >
      <Package InstallerVersion="200" InstallPrivileges="elevated"
               InstallScope="perMachine" Platform="$(var.Platform)"
               Compressed="yes" Description="$(var.ProductName)" />

    e

    <Directory Id="TARGETDIR" Name="SourceDir">
      <Directory Id="$(var.PlatformProgramFilesFolder)">
        <Directory Id="INSTALLLOCATION" Name="$(var.InstallName)">
  4. A abordagem mais simples é sempre fazer grandes atualizações , pois permite novas instalações e atualizações no MSI único. UpgradeCode é fixado em um Guid exclusivo e nunca será alterado, a menos que não desejemos atualizar o produto existente.

    Nota : No WiX 3.5, há um novo elemento MajorUpgrade que facilita ainda mais a vida !

  5. Criando um ícone em Adicionar / Remover Programas

    <Icon Id="Company.ico" SourceFile="..\Tools\Company\Images\Company.ico" />
    <Property Id="ARPPRODUCTICON" Value="Company.ico" />
    <Property Id="ARPHELPLINK" Value="http://www.example.com/" />
  6. Nas versões de lançamento, fazemos a versão de nossos instaladores, copiando o arquivo msi para um diretório de implantação. Um exemplo disso usando um destino wixproj chamado de destino AfterBuild:

    <Target Name="CopyToDeploy" Condition="'$(Configuration)' == 'Release'">
      <!-- Note we append AssemblyFileVersion, changing MSI file name only works with Major Upgrades -->
      <Copy SourceFiles="$(OutputPath)$(OutputName).msi" 
            DestinationFiles="..\Deploy\Setup\$(OutputName) $(AssemblyFileVersion)_$(Platform).msi" />
    </Target>
  7. Use o calor para coletar arquivos com o Guid curinga (*). Útil se você deseja reutilizar arquivos WXS em vários projetos (veja minha resposta em várias versões do mesmo produto). Por exemplo, esse arquivo em lote colhe automaticamente a saída do RoboHelp.

    @echo off  
    robocopy ..\WebHelp "%TEMP%\WebHelpTemp\WebHelp" /E /NP /PURGE /XD .svn  
    "%WIX%bin\heat" dir "%TEMP%\WebHelp" -nologo -sfrag -suid -ag -srd -dir WebHelp -out WebHelp.wxs -cg WebHelpComponent -dr INSTALLLOCATION -var var.WebDeploySourceDir 

    Há um pouco de coisa acontecendo: robocopyremover os metadados das cópias de trabalho do Subversion antes de coletar; a -drreferência do diretório raiz é configurada para o local da instalação em vez do TARGETDIR padrão; -varé usado para criar uma variável para especificar o diretório de origem (saída de implementação da web).

  8. Maneira fácil de incluir a versão do produto no título da caixa de diálogo de boas-vindas usando Strings.wxl para localização. (Crédito: saschabeaumont . Adicionado como essa ótima dica está oculta em um comentário)

    <WixLocalization Culture="en-US" xmlns="http://schemas.microsoft.com/wix/2006/localization">
        <String Id="WelcomeDlgTitle">{\WixUI_Font_Bigger}Welcome to the [ProductName] [ProductVersion] Setup Wizard</String>
    </WixLocalization>
  9. Poupe um pouco de dor e siga o conselho de Wim Coehen de um componente por arquivo. Isso também permite que você deixe de fora (ou curinga *) o GUID do componente .

  10. Rob Mensching tem uma maneira elegante de rastrear rapidamente problemas nos arquivos de log MSI pesquisando value 3. Observe os comentários sobre internacionalização.

  11. Ao adicionar recursos condicionais, é mais intuitivo definir o nível de recurso padrão como 0 (desativado) e depois definir o nível de condição para o valor desejado. Se você definir o nível de recurso padrão> = 1, o nível de condição deverá ser 0 para desativá-lo, o que significa que a lógica da condição deve ser o oposto do que você esperaria, o que pode ser confuso :)

    <Feature Id="NewInstallFeature" Level="0" Description="New installation feature" Absent="allow">
      <Condition Level="1">NOT UPGRADEFOUND</Condition>
    </Feature>
    <Feature Id="UpgradeFeature" Level="0" Description="Upgrade feature" Absent="allow">
      <Condition Level="1">UPGRADEFOUND</Condition>
    </Feature>
si618
fonte
Sobre a adição do ícone em Adicionar / Remover Programas, EXATAMENTE o que eu estava procurando. Onde você coloca essas três linhas? +1 para uma grandiosidade incrível.
Everett
Costumo colocá-los logo após (e obviamente abaixo) do elemento <Package>. Ter um olhar para o esquema de validade wix.sourceforge.net/manual-wix3/schema_index.htm
si618
+1, gostaria de poder +100, esta é a parte mais útil das informações sobre o Wix que encontrei.
Tim Long
Obrigado Tim! Rob Mensching, Bob Arson, Wim Coehen e outros merecem elogios por compartilhar seus conhecimentos.
si618
38

Verificando se o IIS está instalado:

<Property Id="IIS_MAJOR_VERSION">
    <RegistrySearch Id="CheckIISVersion" Root="HKLM" Key="SOFTWARE\Microsoft\InetStp" Name="MajorVersion" Type="raw" />
</Property>

<Condition Message="IIS must be installed">
    Installed OR IIS_MAJOR_VERSION
</Condition>

Verificando se a compatibilidade da metabase do IIS 6 está instalada no Vista +:

<Property Id="IIS_METABASE_COMPAT">
    <RegistrySearch Id="CheckIISMetabase" Root="HKLM" Key="SOFTWARE\Microsoft\InetStp\Components" Name="ADSICompatibility" Type="raw" />
</Property>

<Condition Message="IIS 6 Metabase Compatibility feature must be installed">
    Installed OR ((VersionNT &lt; 600) OR IIS_METABASE_COMPAT)
</Condition>
Simon Steele
fonte
34

Mantenha todos os IDs em namespaces separados

  • Os recursos começam com F. Exemplos: F.Documentation, F.Binaries, F.SampleCode.
  • Os componentes começam com C. Ex: C.ChmFile, C.ReleaseNotes, C.LicenseFile, C.IniFile, C.Registry
  • CustomActions são CA. Ex: CA.LaunchHelp, CA.UpdateReadyDlg, CA.SetPropertyX
  • Arquivos são Fi.
  • Diretórios são Di.
  • e assim por diante.

Acho que isso ajuda imensamente a acompanhar todos os vários IDs em todas as categorias.

Cheeso
fonte
Não uso espaços para nome, mas anexo os IDs; por exemplo: ExamplesFeature, ChmFileComponent. Eu acho que eu amo digitação ;-)
dvdvorle
25

Pergunta fantástica. Adoraria ver algumas práticas recomendadas.

Eu tenho muitos arquivos que distribuí, então configurei meu projeto em vários arquivos de origem wxs.

Eu tenho um arquivo de origem de nível superior que chamo Product.wxs, que basicamente contém a estrutura da instalação, mas não os componentes reais. Este arquivo possui várias seções:

<Product ...>
  <Package ...>
    <Media>... 
   <Condition>s ...
   <Upgrade ..>
   <Directory> 
        ...
   </Directory>
   <Feature>
      <ComponentGroupRef ... > A bunch of these that
   </Feature>
   <UI ...>
   <Property...>
   <Custom Actions...>
   <Install Sequences....
  </Package>
</Product>

O restante dos arquivos .wix é composto por fragmentos que contêm ComponentGroups que são referenciados na marca Feature nos Product.wxs. Meu projeto contém um bom agrupamento lógico dos arquivos que distribuo

<Fragment>
   <ComponentGroup>
     <ComponentRef>
     ....
    </ComponentGroup>
    <DirectoryRef>
      <Component... for each file
      .... 
    </DirectoryRef>
</Fragment>

Isso não é perfeito, meu senso de aranha OO formiga um pouco porque os fragmentos precisam fazer referência a nomes no arquivo Product.wxs (por exemplo, o DirectoryRef), mas acho mais fácil manter esse arquivo de origem grande e único.

Eu adoraria ouvir comentários sobre isso, ou se alguém tiver alguma dica boa também!

Peter Tate
fonte
Nossa configuração também é muito semelhante a essa abordagem. É bom porque podemos usar nosso equivalente a Products.wxs como nossa configuração básica para uma variedade de produtos.
si618
@ Peter Tate: seu senso de aranha está correto. Veja minha resposta sobre o alias de diretório.
26610 Wim Coenen
Eu tomo a mesma abordagem: Product.wxs com layout é estática, e uma tarefa de compilação (heat.exe) gera meu arquivo Content.wxs
timvw
20

Adicione uma caixa de seleção à caixa de diálogo de saída para iniciar o aplicativo ou o arquivo de ajuda.

...

<!-- CA to launch the exe after install -->
<CustomAction Id          ="CA.StartAppOnExit"
              FileKey     ="YourAppExeId"
              ExeCommand  =""
              Execute     ="immediate"
              Impersonate ="yes"
              Return      ="asyncNoWait" />

<!-- CA to launch the help file -->
<CustomAction Id         ="CA.LaunchHelp"
              Directory  ="INSTALLDIR"
              ExeCommand ='[WindowsFolder]hh.exe IirfGuide.chm'
              Execute    ="immediate"
              Return     ="asyncNoWait" />

<Property Id="WIXUI_EXITDIALOGOPTIONALCHECKBOXTEXT"
          Value="Launch MyApp when setup exits." />

<UI>
  <Publish Dialog  ="ExitDialog"
           Control ="Finish"
           Order   ="1"
           Event   ="DoAction"
           Value   ="CA.StartAppOnExit">WIXUI_EXITDIALOGOPTIONALCHECKBOXTEXT</Publish>
</UI>

Se você fizer dessa maneira, a aparência "padrão" não estará correta. A caixa de seleção é sempre um plano de fundo cinza, enquanto a caixa de diálogo é branca:

texto alternativo http://www.dizzymonkeydesign.com/blog/misc/adding-and-customizing-dlgs-in-wix-3/images/exit_dlg_1.gif

Uma maneira de contornar isso é especificar o seu próprio ExitDialog personalizado, com uma caixa de seleção diferente . Isso funciona, mas parece muito trabalho apenas para alterar a cor de um controle. Outra maneira de resolver o mesmo é pós-processar o MSI gerado para alterar os campos X, Y na tabela Control para esse controle CheckBox específico. O código javascript fica assim:

var msiOpenDatabaseModeTransact = 1;
var filespec = WScript.Arguments(0);
var installer = new ActiveXObject("WindowsInstaller.Installer");
var database = installer.OpenDatabase(filespec, msiOpenDatabaseModeTransact);
var sql = "UPDATE `Control` SET `Control`.`Height` = '18', `Control`.`Width` = '170'," +
          " `Control`.`Y`='243', `Control`.`X`='10' " +
          "WHERE `Control`.`Dialog_`='ExitDialog' AND " + 
          "  `Control`.`Control`='OptionalCheckBox'";
var view = database.OpenView(sql);
view.Execute();
view.Close();
database.Commit();

A execução desse código como um script de linha de comando (usando cscript.exe) após a geração do MSI (a partir do light.exe) produzirá um ExitDialog com aparência mais profissional:

texto alternativo http://www.dizzymonkeydesign.com/blog/misc/adding-and-customizing-dlgs-in-wix-3/images/exit_dlg_2.gif

Cheeso
fonte
Ha! Não é o meu blog. Eu também li. E eu tenho um link para a entrada do blog no texto acima. Mas eles fizeram isso de maneira diferente do que eu. Eu gosto do meu jeito melhor. !!
Cheeso
1
Obrigado pelo js, ​​muito útil! Uma coisa que eu tive que mudar no wxs é substituir WIXUI_EXITDIALOGOPTIONALCHECKBOXpor WIXUI_EXITDIALOGOPTIONALCHECKBOX = 1 and NOT Installeddentro #<Publish>
Alexander Kojevnikov
Existe uma maneira de marcar a caixa de seleção por padrão?
Alek Davis
Para marcar a caixa por padrão, usei o seguinte: <ID da propriedade = "WIXUI_EXITDIALOGOPTIONALCHECKBOX" Valor = "1" />
Alek Davis
Parece uma solução bacana, mas como eu a uso? Existe alguma maneira de colocar os js dentro do elemento <AfterBuild> no meu wixproj? Ou como você se refere à execução na linha de comando, é melhor como um evento pós-compilação; nesse caso, o que é um bom interpretador de linha de comando js para Windows?
vanmelle
18

Criando versões ao vivo, teste, treinamento ... usando os mesmos arquivos de origem.

Em poucas palavras: Crie um UpgradeCode exclusivo para cada instalador e defina automaticamente o primeiro caractere de cada Guid para cada instalador, deixando os 31 restantes únicos.

Pré-requisitos

Premissas

  • As variáveis ​​WiX são usadas para definir UpgradeCode, ProductName, InstallName.
  • Você já tem um instalador ativo. Eu não tentaria isso até você.
  • Todos os seus componentes são mantidos em um arquivo (Components.wxs). Esse processo funcionará se você tiver vários arquivos, haverá apenas mais trabalho a fazer.

Estrutura de Diretórios

  • Setup.Library
    • Todos os arquivos wxs (componentes, recursos, caixas de diálogo da interface do usuário, ...)
    • Common.Config.wxi (ProductCode = "*", ProductVersion, PlatformProgramFilesFolder, ...)
  • Setup.Live (wixproj)
    • Vincule todos os arquivos Setup.Library usando "Adicionar arquivo existente" -> "Adicionar como link" (o pequeno botão de seta para baixo ao lado do botão Adicionar no Visual Studio)
    • Config.wxi (possui UpgradeCode, ProductName, InstallName, ...) exclusivos
  • Setup.Test , ...
    • conforme ao vivo, mas o Config.wxi está configurado para o ambiente de teste.

Processo

  • Crie o diretório Setup.Library e mova todos os seus arquivos wxs e wxi (exceto Config.wxi) do projeto existente.
  • Crie Setup.Live, Setup.Test, etc, conforme o wixproj normal.
  • Adicione o destino BeforeBuild no wixproj em Setup.Live, etc, para executar o Arquivo de Tarefas da Comunidade MSBuild, para modificar Guids (usei A para Live, B para Teste e C para treinamento)
  • Adicione o destino AfterBuild para reverter os Guias Components.wxs de volta para 0.
  • Verifique com o Orca se cada componente em cada MSI possui o guia modificado.
  • Verifique se os guias originais foram restaurados.
  • Verifique se cada MSI está instalando (e atualizando) o produto e o local corretos.

Exemplo Config.wxi

<?xml version="1.0" encoding="utf-8"?>
<Include>
<!-- Upgrade code should not change unless you want to install 
     a new product and have the old product remain installed, 
     that is, both products existing as separate instances. -->
<?define UpgradeCode = "YOUR-GUID-HERE" ?>

<!-- Platform specific variables -->
<?if $(var.Platform) = x64 ?>
  <!-- Product name as you want it to appear in Add/Remove Programs-->
  <?define ProductName = "Foo 64 Bit [Live]" ?>
<?else ?>
  <?define ProductName =  "Foo [Live]" ?>
<?endif ?>

<!-- Directory name used as default installation location -->
<?define InstallName = "Foo [Live]" ?>

<!-- Registry key name used to store installation location -->
<?define InstallNameKey = "FooLive" ?>

<?define VDirName = "FooLive" ?>
<?define AppPoolName = "FooLiveAppPool" ?>
<?define DbName = "BlahBlahLive" ?>
</Include>

Exemplo Config.Common.wxi

<?xml version="1.0" encoding="utf-8"?>
<Include>
<!-- Auto-generate ProductCode for each build, release and upgrade -->
<?define ProductCode = "*" ?>

<!-- Note that 4th version (Revision) is ignored by Windows Installer -->
<?define ProductVersion = "1.0.0.0" ?>

<!-- Minimum version supported if product already installed and this is an upgrade -->
<!-- Note that 4th version (Revision) is ignored by Windows Installer -->
<?define MinimumUpgradeVersion = "0.0.0.0" ?>

<!-- Platform specific variables -->
<?if $(var.Platform) = x64 ?>
   <?define Win64 = "yes" ?>
   <?define PlatformProgramFilesFolder = "ProgramFiles64Folder" ?>
<?else ?>
   <?define Win64 = "no" ?>
   <?define PlatformProgramFilesFolder = "ProgramFilesFolder" ?>
<?endif ?>

<?define ProductManufacturer = "Foo Technologies"?>

<!-- Decimal Language ID (LCID) for the Product. Used for localization. -->
<?define ProductLanguage = "1033" ?>

<?define WebSiteName = "DefaultWebSite" ?>
<?define WebSitePort = "80" ?>

<?define DbServer = "(local)" ?>
</Include>

Exemplo Components.wxs

<?xml version="1.0" encoding="utf-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
  <!-- The pre-processor variable which allows the magic to happen :) -->
  <?include $(sys.CURRENTDIR)\Config.wxi?>
  <?include ..\Setup.Library\Config.Common.wxi?>
  <Fragment Id="ComponentsFragment">
    <Directory Id="TARGETDIR" Name="SourceDir">
      <Directory Id="$(var.PlatformProgramFilesFolder)">
        <Directory Id="INSTALLLOCATION" Name="$(var.InstallName)">
          <Component Id="ProductComponent" Guid="0XXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" KeyPath="yes">
          ...

Nota: agora sugiro deixar o atributo Guid fora do Component (equivalente a *), usando um arquivo por componente e definindo o arquivo como o caminho-chave. Isso elimina a necessidade de chamadas ModifyComponentsGuidse RevertComponentsGuidsdestinos mostrados abaixo. Isso pode não ser possível para todos os seus componentes.

Exemplo Setup.Live.wixproj

<Import Project="$(MSBuildExtensionsPath)\MSBuildCommunityTasks\MSBuild.Community.Tasks.Targets" />
<Target Name="BeforeBuild">
  <CallTarget Targets="ModifyComponentsGuids" />
</Target>
<Target Name="AfterBuild">
  <CallTarget Targets="RevertComponentsGuids" />
</Target>
<!-- Modify the first character of every Guid to create unique value for Live, Test and Training builds -->
<Target Name="ModifyComponentsGuids">
  <FileUpdate Files="..\Setup.Library\Components.wxs" Regex="Guid=&quot;([a-f]|[A-F]|\d)" ReplacementText="Guid=&quot;A" />
</Target>
<!-- Revert the first character of every Guid back to initial value -->
<Target Name="RevertComponentsGuids">
  <FileUpdate Files="..\Setup.Library\Components.wxs" Regex="Guid=&quot;([a-f]|[A-F]|\d)" ReplacementText="Guid=&quot;0" />
</Target>

Pensamentos finais

  • Esse processo também deve funcionar para criar instaladores diferentes para diferentes módulos de mesclagem (Live, Test, ... como recursos) para o mesmo instalador. Fui com instaladores diferentes, pois parecia uma opção mais segura; há mais risco de alguém atualizar o Live em vez do Training se eles estiverem na mesma caixa e você usar apenas recursos para os diferentes módulos de mesclagem.
  • Se você usar o MSI para realizar atualizações, bem como novas instalações, ou seja, a abordagem somente para atualização principal e salvar o local da instalação no registro, lembre-se de criar uma variável para o nome da chave em cada instalação.
  • Também criamos variáveis ​​em cada Config.wxi para habilitar nomes exclusivos de diretórios virtuais, pools de aplicativos, nomes de bancos de dados etc. para cada instalador.

ATUALIZAÇÃO 1: Guids de componente de geração automática remove a necessidade de chamar a tarefa FileUpdate se você criar componente com Guid = "*" para cada arquivo, configurando o arquivo como o caminho da chave.

ATUALIZAÇÃO 2: Um dos problemas que enfrentamos é que, se você não gerar automaticamente os Guids de seu componente e a compilação falhar, os arquivos temporários precisarão ser excluídos manualmente.

ATUALIZAÇÃO 3: Foi encontrada uma maneira de remover a dependência do svn: externals e da criação temporária de arquivos. Isso torna o processo de compilação mais resistente (e é a melhor opção se você não pode curinga seus Guids) e menos quebradiço se houver uma falha na compilação com luz ou vela.

ATUALIZAÇÃO 4: O suporte para várias instâncias usando transformações de instância está no WiX 3.0+, definitivamente também vale uma olhada.

si618
fonte
+1 para a referência de tarefas da comunidade MSBuild, adoro esse pacote
BozoJoe 12/11/2010
17

Usando o log de diagnóstico Msi para obter informações detalhadas sobre falhas

msiexec /i Package.msi /l*v c:\Package.log

Onde

Package.msi
é o nome do seu pacote e
c: \ Package.log
é onde você deseja a saída do log

Códigos de erro Msi

Vídeo introdutório do Wix
Oh e vídeo introdutório aleatório do Wix, apresentando "Mr. WiX", Rob Mensching é útil na "imagem conceitual".

Terrance
fonte
2
+1 Seria muito melhor se pudéssemos habilitar o log no Wix em vez da linha de comando.
si618
3
WiX faz. Defina a propriedade MsiLogging. Suportado apenas pelo Windows Installer 4.0+.
precisa
Muito obrigado "Sr. Wix". Tenho que verificar isso.
Terrance
17

Use Javascript CustomActions, porque são muuuuito fáceis

As pessoas disseram que Javascript é a coisa errada a ser usada nas MSI CustomActions . Razões apresentadas: difícil de depurar, difícil de torná-lo confiável. Eu não concordo Não é difícil depurar, certamente não é mais difícil que o C ++. É apenas diferente. Achei que escrever CustomActions em Javascript era super fácil, muito mais fácil do que usar C ++. Muito mais rapido. E igualmente confiável.

Há apenas uma desvantagem: as Javascript CustomActions podem ser extraídas via Orca, enquanto uma CA C / C ++ exigiria engenharia reversa. Se você considera a mágica do instalador protegida como propriedade intelectual, convém evitar o script.

Se você usa script, você só precisa começar com alguma estrutura. Aqui estão alguns para você começar.


Código Javascript "boilerplate" para CustomAction:

//
// CustomActions.js 
// 
// Template for WIX Custom Actions written in Javascript.
// 
// 
// Mon, 23 Nov 2009  10:54
// 
// ===================================================================


// http://msdn.microsoft.com/en-us/library/sfw6660x(VS.85).aspx
var Buttons = {
        OkOnly           : 0,
        OkCancel         : 1,
        AbortRetryIgnore : 2,
        YesNoCancel      : 3
};

var Icons = {
        Critical         : 16,
        Question         : 32,
        Exclamation      : 48,
        Information      : 64
};

var MsgKind = {
        Error            : 0x01000000,
        Warning          : 0x02000000,
        User             : 0x03000000,
        Log              : 0x04000000
};

// http://msdn.microsoft.com/en-us/library/aa371254(VS.85).aspx
var MsiActionStatus = {
        None             : 0,
        Ok               : 1, // success
        Cancel           : 2,
        Abort            : 3,
        Retry            : 4, // aka suspend?
        Ignore           : 5  // skip remaining actions; this is not an error.
};


function MyCustomActionInJavascript_CA() {
    try {
        LogMessage("Hello from MyCustomActionInJavascript");
        // ...do work here...
        LogMessage("Goodbye from MyCustomActionInJavascript");
    }
    catch (exc1) {
        Session.Property("CA_EXCEPTION") = exc1.message ;
        LogException(exc1);
        return MsiActionStatus.Abort;
    }
    return MsiActionStatus.Ok;
}

// Pop a message box.  also spool a message into the MSI log, if it is enabled. 
function LogException(exc) {
    var record = Session.Installer.CreateRecord(0);
    record.StringData(0) = "CustomAction: Exception: 0x" + decimalToHexString(exc.number) + " : " + exc.message;
    Session.Message(MsgKind.Error + Icons.Critical + Buttons.btnOkOnly, record);
}


// spool an informational message into the MSI log, if it is enabled. 
function LogMessage(msg) {
    var record = Session.Installer.CreateRecord(0);
    record.StringData(0) = "CustomAction:: " + msg;
    Session.Message(MsgKind.Log, record);
}


// http://msdn.microsoft.com/en-us/library/d5fk67ky(VS.85).aspx
var WindowStyle = {
    Hidden : 0,
    Minimized : 1,
    Maximized : 2
};

// http://msdn.microsoft.com/en-us/library/314cz14s(v=VS.85).aspx
var OpenMode = {
    ForReading : 1,
    ForWriting : 2,
    ForAppending : 8
};

// http://msdn.microsoft.com/en-us/library/a72y2t1c(v=VS.85).aspx
var SpecialFolders = {
    WindowsFolder : 0, 
    SystemFolder :  1, 
    TemporaryFolder : 2
};

// Run a command via cmd.exe from within the MSI
function RunCmd(command)
{
    var wshell = new ActiveXObject("WScript.Shell");
    var fso = new ActiveXObject("Scripting.FileSystemObject");
    var tmpdir = fso.GetSpecialFolder(SpecialFolders.TemporaryFolder);
    var tmpFileName = fso.BuildPath(tmpdir, fso.GetTempName());

    LogMessage("shell.Run("+command+")");

    // use cmd.exe to redirect the output
    var rc = wshell.Run("%comspec% /c " + command + "> " + tmpFileName, WindowStyle.Hidden, true);
    LogMessage("shell.Run rc = "  + rc);

    // here, optionally parse the output of the command 
    if (parseOutput) {
        var textStream = fso.OpenTextFile(tmpFileName, OpenMode.ForReading);
        while (!textStream.AtEndOfStream) {
            var oneLine = textStream.ReadLine();
            var line = ParseOneLine(oneLine);
                ...
        }
        textStream.Close();
    }

    if (deleteOutput) {
        fso.DeleteFile(tmpFileName);
    }

    return {
        rc : rc,
        outputfile : (deleteOutput) ? null : tmpFileName
    };
}

Em seguida, registre a ação personalizada com algo como isto:

<Fragment>
  <Binary Id="IisScript_CA" SourceFile="CustomActions.js" />

  <CustomAction Id="CA.MyCustomAction"
              BinaryKey="IisScript_CA"
              JScriptCall="MyCustomActionInJavascript_CA"
              Execute="immediate"
              Return="check" />
</Fragmemt>

Obviamente, você pode inserir quantas funções Javascript desejar, para várias ações personalizadas. Um exemplo: usei o Javascript para fazer uma consulta WMI no IIS, para obter uma lista dos sites existentes, nos quais um filtro ISAPI poderia ser instalado. Essa lista foi usada para preencher uma caixa de listagem mostrada posteriormente na sequência da interface do usuário. Tudo muito fácil.

No IIS7, não há provedor WMI para o IIS, então usei o shell.Run() abordagem para chamar o appcmd.exe para executar o trabalho. Fácil.

Pergunta relacionada: Sobre Javascript CustomActions

Cheeso
fonte
2
+1 Acho que a abordagem DTF é fácil de configurar, mas o javascript também pode ser útil.
265 de si618
12

Peter Tate já mostrou como você pode definir definições reutilizáveis ​​do ComponentGroup em fragmentos separados do wix. Alguns truques adicionais relacionados a isso:

Alias ​​de diretório

Os fragmentos do grupo de componentes não precisam conhecer os diretórios definidos pelo produto principal wxs. No fragmento do seu grupo de componentes, você pode falar sobre uma pasta como esta:

<DirectoryRef Id="component1InstallFolder">
...
</DirectoryRef>

Em seguida, o produto principal pode criar um alias de um de seus diretórios (por exemplo, "productInstallFolder") como este:

<Directory Id="productInstallFolder" Name="ProductName">
   <!-- not subfolders (because no Name attribute) but aliases for parent! -->
   <Directory Id="component1InstallFolder"/> 
   <Directory Id="component2InstallFolder"/> 
</Directory>

Gráfico de dependência

Os elementos ComponentGroup podem conter elementos filho ComponentGroupRef. Isso é ótimo se você tiver um grande conjunto de componentes reutilizáveis ​​com um gráfico de dependência complexo entre eles. Você acabou de configurar um ComponentGroup em seu próprio fragmento para cada componente e declarar as dependências assim:

<ComponentGroup Id="B">
   <ComponentRef Id="_B" />
   <ComponentGroupRef Id="A">
</ComponentGroup>

Se você agora fizer referência ao grupo de componentes "B" na sua configuração porque é uma dependência direta do seu aplicativo, ele puxará automaticamente o grupo de componentes "A", mesmo que o autor do aplicativo nunca tenha percebido que era uma dependência de "B". "Funciona" desde que você não tenha nenhuma dependência circular.

Wixlib reutilizável

A idéia do gráfico de dependência acima funciona melhor se você compilar os componentes big-pool-o-reusable em um wixlib reutilizável com lit.exe. Ao criar uma configuração de aplicativo, você pode fazer referência a este wixlib como um arquivo wixobj. O vinculador candle.exe eliminará automaticamente todos os fragmentos que não são "puxados" pelos arquivos wxs do produto principal.

Wim Coenen
fonte
12

Estou surpreso que ninguém tenha mencionado o uso do T4 para gerar o arquivo WXS durante a compilação. Eu aprendi sobre isso por meio de Henry Lee @ New Age Solutions .

Essencialmente, você cria uma tarefa MSBuild personalizada para executar um modelo T4, e esse modelo gera o WXS imediatamente antes da compilação do projeto Wix. Isso permite que você (dependendo de como implementá-lo) inclua automaticamente todos os resultados de montagens da compilação de outra solução (o que significa que você não precisa mais editar os wxs sempre que adicionar um novo assembly).

Peter T. LaComb Jr.
fonte
2
O +1 é muito bom, não estou muito preocupado com montagens, mas nossos projetos da Web podem ter problemas com páginas aspx e outros artefatos (imagens, css) adicionados ao projeto, mas não com o WiX.
si618
4
Para futuros visitantes, o Wix 3.5 possui um utilitário heat.exe que faz essa colheita automaticamente
Mrchief
@Mrchief - Eu não acredito que o Heat pegue assemblies referenciados que são copiados localmente - isso aparentemente está planejado para 4.0. Referência: sourceforge.net/tracker/…
Peter T. LaComb Jr.
O calor não capta os conjuntos referenciados.
Tofutim
Quais são alguns bons exemplos de como usar o T4 para gerar o arquivo WXS?
Tofutim
12

Usando o Heat.exe para esmagar o rosto e infligir "Epic Pwnage" em instalações dolorosamente grandes

Expandindo as respostas de Si e Robert-P sobre o calor.

Tradução: (usando o heat para evitar a digitação manual de arquivos individuais no projeto e para automatizar as compilações para um processo mais fácil em geral.)

WiX 2.0 Heat Syntax

Para versões mais recentes (nem todas tão diferentes das versões anteriores, mas existem alterações de sintaxe potencialmente irritantes ...), vá para o diretório Heat is do cmd.exe e digite apenas heat, mas eu tenho um exemplo aqui para obter ajuda com versões mais recentes, se necessário.

Adicionando o seguinte ao seu Evento de Construção no visual studio 2010.
(Clique com o botão direito do mouse em Projeto-> Propriedades -> Eventos de Construção-> Eventos de Pré-Construção)

$(WIX)bin\heat.exe" dir "$(EnviromentVariable)" -cg GroupVariable -gg -scom -sreg -sfrag - srd -dr INSTALLLOCATION -var env.LogicPath -out "$(FragmentDir)\FileName.wxs

-gg 

Gera Guids quando o calor é executado (como quando você executa o comando acima)

-scom 

Não pegue "arquivos COM"

-sreg 

Não pegue "Arquivos do Registro"

-sfrag 

Não pegue "Fragmentos"

-srd 

Não pegue o "diretório raiz"

dir

dir indica que você deseja que o Heat procure em uma pasta

"$ (EnviromentVariable)"

O nome da variável que você adicionaria às variáveis ​​de pré-processador na seção Propriedades do projeto (projeto de clique com o botão direito do mouse, vá para propriedades) -> Seção Build, onde diz Definir variáveis ​​de pré-processador (assume visual studio 2010)

Exemplo:
EnviromentVariable = C: \ Projeto \ bin \ Debug;
Não há aspas duplas, mas termina com um ponto-e-vírgula

-cg GroupVariable 

O ComponentGroup que será referenciado do fragmento criado para o arquivo wxs principal

FragmentDir

O diretório do fragmento em que o fragmento wxs de saída será armazenado

FileName.wxs

O nome do arquivo

Tutorial completo aqui, muito útil

Parte 1 Parte 2

Terrance
fonte
Há outra ferramenta útil para finalidades ligeiramente diferentes: Parafina ( wintellect.com/CS/blogs/jrobbins/archive/2010/03/10/4107.aspx )
ralf.w.
9

Incluindo objetos COM:

heatgera quase todas (se não todas) as entradas do registro e outras configurações necessárias para elas. Alegrar!

Incluindo objetos COM gerenciados (também conhecidos como objetos .NET ou C # COM)

Usar heatum objeto COM gerenciado fornecerá um documento wix quase completo.

Se você não precisa da biblioteca disponível no GAC (ou seja, disponível globalmente: na maioria das vezes, você não precisa disso nos assemblies .NET de qualquer maneira - provavelmente já fez algo errado neste momento, se não se destina a isso. uma biblioteca compartilhada), você deve atualizar a CodeBasechave do registro a ser configurada [#ComponentName]. Se você está planejando instalá-lo no GAC (por exemplo, você criou uma nova biblioteca comum incrível que todo mundo vai querer usar), remova essa entrada e adicione dois novos atributos ao Fileelemento: Assemblye KeyPath. O conjunto deve ser definido como ".net" e KeyPath"sim".

No entanto, alguns ambientes (especialmente qualquer coisa com memória gerenciada, como linguagens de script) também precisarão acessar o Typelib. Certifique-se de executar heatno seu typelib e incluí-lo. heatirá gerar todas as chaves de registro necessárias. Quão legal é isso?

Robert P
fonte
8

Instalando no C:\ProductName

Alguns aplicativos precisam ser instalados C:\ProductNameou algo semelhante, mas 99,9% (se não 100%) dos exemplos na rede são instalados em C:\Program Files\CompanyName\ProductName.

O código a seguir pode ser usado para definir a TARGETDIRpropriedade como raiz da C:unidade (retirada da lista de usuários do WiX ):

<CustomAction Id="AssignTargetDir" Property="TARGETDIR" Value="C:\" Execute="firstSequence" />
<InstallUISequence>
    <Custom Action="AssignTargetDir" Before="CostInitialize">TARGETDIR=""</Custom>
</InstallUISequence>
<InstallExecuteSequence>
    <Custom Action="AssignTargetDir" Before="CostInitialize">TARGETDIR=""</Custom>
</InstallExecuteSequence>

NOTA: Por padrão, TARGETDIR não aponta para C:\! Em vez disso, aponta para o ROOTDRIVEque, por sua vez, aponta para a raiz da unidade com mais espaço livre ( veja aqui ) - e esse não é necessariamente oC: unidade. Pode haver outro disco rígido, partição ou unidade USB!

Em seguida, em algum lugar abaixo da sua <Product ...>tag, você precisará das seguintes tags de diretório, como de costume:

<Directory Id="TARGETDIR" Name="SourceDir">
    <Directory Id="APPLICATIONFOLDER" Name="$(var.ProductName)">
        <!-- your content goes here... -->
    </Directory>
</Directory>
gehho
fonte
Não seria mais simples instalar apenas WindowsVolume?
Wim Coenen
1
Sim, mas você precisaria usar uma solução alternativa, porque a WindowsVolumepropriedade não pode ser usada como Directory(o compilador fornece um erro / aviso), conforme indicado aqui e aqui . Pessoalmente, acho essa solução alternativa confusa.
Gehho 19/08/10
7

Variáveis ​​Ambientais

Ao compilar seus documentos Wxs para o código wixobj, você pode usar variáveis ​​ambientais para determinar várias informações. Por exemplo, digamos que você queira alterar quais arquivos serão incluídos em um projeto. Digamos que você tenha uma variável ambiental chamada RELEASE_MODE, que você definiu logo antes de criar seu MSI (seja com um script ou manualmente, isso não importa) Na sua fonte wix, você pode fazer algo como:

<define FILESOURCE = c:\source\output\bin\$(env.RELEASE_MODE) >

e, posteriormente, em seu código, use-o no local para alterar rapidamente o documento wxs, por exemplo:

<Icon Id="myicon.ico" SourceFile="$(var.FILESOURCE)" />
Robert P
fonte
1
Também estão disponíveis variáveis ​​de compilação, como $ (Configuration) e $ (Platform). Também um monte mais em msdn.microsoft.com/en-us/library/aa302186.aspx
si618
1
@ Si - Antes de hoje, esse link não está mais ativo. Não consegui encontrar a mais recente.
Pedro M
7

Caixas de diálogo de edição

Uma boa capacidade de editar caixas de diálogo é usar o SharpDevelop na versão 4.0.1.7090 (ou superior). Com a ajuda dessa ferramenta, uma caixa de diálogo independente (arquivos wxs de fontes WiX como, por exemplo, InstallDirDlg.wxs) pode ser aberta, visualizada e editada no modo Design.

user432758
fonte
Incrível, não sabia que a SharpDevelop suportava isso.
anton.burger
6

Definindo o sinalizador enable32BitAppOnWin64 do IIS http://trycatchfail.com/blog/post/WiX-Snippet-change-enable32BitAppOnWin64.aspx

<InstallExecuteSequence>
   <RemoveExistingProducts After="InstallFinalize" />
   <Custom Action="ConfigureAppPool" After="InstallFinalize" >
     <![CDATA[NOT Installed AND VersionNT64 >= 600]]>         
   </Custom>
</InstallExecuteSequence>

<CustomAction Id="ConfigureAppPool" Return="check" Directory="TARGETDIR" ExeCommand="[SystemFolder]inetsrv\appcmd set apppool /apppool.name:[APPPOOLNAME] /enable32BitAppOnWin64:false" />
EdmundYeung99
fonte
5

Modifique o "Pronto para instalar?" caixa de diálogo (também conhecida como VerifyReadyDlg) para fornecer um resumo das escolhas feitas.

É assim:
alt text http://i46.tinypic.com/s4th7t.jpg

Faça isso com uma CustomAction Javascript:


Código Javascript:

// http://msdn.microsoft.com/en-us/library/aa372516(VS.85).aspx
var MsiViewModify = 
    {
        Refresh          : 0,
        Insert           : 1,
        Update           : 2,
        Assign           : 3,
        Replace          : 4,
        Merge            : 5,
        Delete           : 6,
        InsertTemporary  : 7,   // cannot permanently modify the MSI during install
        Validate         : 8,
        ValidateNew      : 9,
        ValidateField    : 10,
        ValidateDelete   : 11
    };


// http://msdn.microsoft.com/en-us/library/sfw6660x(VS.85).aspx
var Buttons = 
    {
        OkOnly           : 0,
        OkCancel         : 1,
        AbortRetryIgnore : 2,
        YesNoCancel      : 3
    };

var Icons= 
    {
        Critical         : 16,
        Question         : 32,
        Exclamation      : 48,
        Information      : 64
    }

var MsgKind =
    {
        Error            : 0x01000000,
        Warning          : 0x02000000,
        User             : 0x03000000,
        Log              : 0x04000000
    };

// http://msdn.microsoft.com/en-us/library/aa371254(VS.85).aspx
var MsiActionStatus = 
    {
        None             : 0,
        Ok               : 1, // success
        Cancel           : 2,
        Abort            : 3,
        Retry            : 4, // aka suspend?
        Ignore           : 5  // skip remaining actions; this is not an error.
    };

function UpdateReadyDialog_CA(sitename)
{
    try 
    {
        // can retrieve properties from the install session like this:
        var selectedWebSiteId = Session.Property("MSI_PROPERTY_HERE");

        // can retrieve requested feature install state like this:
        var fInstallRequested   = Session.FeatureRequestState("F.FeatureName");

        var text1 = "This is line 1 of text in the VerifyReadyDlg";

        var text2 = "This is the second line of custom text";

        var controlView     = Session.Database.OpenView("SELECT * FROM Control");
        controlView.Execute();

        var rec             = Session.Installer.CreateRecord(12);
        rec.StringData(1)   = "VerifyReadyDlg";    // Dialog_
        rec.StringData(2)   = "CustomVerifyText1"; // Control - can be any name
        rec.StringData(3)   = "Text";              // Type
        rec.IntegerData(4)  = 25;                  // X
        rec.IntegerData(5)  = 60;                  // Y
        rec.IntegerData(6)  = 320;                 // Width
        rec.IntegerData(7)  = 85;                  // Height
        rec.IntegerData(8)  = 2;                   // Attributes
        rec.StringData(9)   = "";                  // Property
        rec.StringData(10)  = vText1;              // Text
        rec.StringData(11)  = "";                  // Control_Next
        rec.StringData(12)  = "";                  // Help
        controlView.Modify(MsiViewModify.InsertTemporary, rec);

        rec                 = Session.Installer.CreateRecord(12);
        rec.StringData(1)   = "VerifyReadyDlg";    // Dialog_
        rec.StringData(2)   = "CustomVerifyText2"; // Control - any unique name
        rec.StringData(3)   = "Text";              // Type
        rec.IntegerData(4)  = 25;                  // X
        rec.IntegerData(5)  = 160;                 // Y
        rec.IntegerData(6)  = 320;                 // Width
        rec.IntegerData(7)  = 65;                  // Height
        rec.IntegerData(8)  = 2;                   // Attributes
        rec.StringData(9)   = "";                  // Property
        rec.StringData(10)  = text2;               // Text
        rec.StringData(11)  = "";                  // Control_Next
        rec.StringData(12)  = "";                  // Help
        controlView.Modify(MsiViewModify.InsertTemporary, rec);

        controlView.Close();
    }
    catch (exc1)
    {
        Session.Property("CA_EXCEPTION") = exc1.message ;
        LogException("UpdatePropsWithSelectedWebSite", exc1);
        return MsiActionStatus.Abort;
    }
    return MsiActionStatus.Ok;
}


function LogException(loc, exc)
{
    var record = Session.Installer.CreateRecord(0);
    record.StringData(0) = "Exception {" + loc + "}: " + exc.number + " : " + exc.message;
    Session.Message(MsgKind.Error + Icons.Critical + Buttons.btnOkOnly, record);
}

Declare a CA Javascript:

<Fragment>
  <Binary Id="IisScript_CA" SourceFile="CustomActions.js" />

  <CustomAction Id="CA.UpdateReadyDialog"
              BinaryKey="IisScript_CA"
              JScriptCall="UpdateReadyDialog_CA"
              Execute="immediate"
              Return="check" />
</Fragment>

Anexe a CA a um botão. Neste exemplo, a CA é acionada quando clicar em Avançar no CustomizeDlg:

<UI ...>
  <Publish Dialog="CustomizeDlg" Control="Next" Event="DoAction" 
           Value="CA.UpdateReadyDialog" Order="1"/>
</UI>

Pergunta relacionada ao SO: Como definir, em tempo de execução, o texto a ser exibido no VerifyReadyDlg?

Cheeso
fonte
Certamente não deveria ser JScript, a linguagem de script do Windows, e sim JavaScript, a linguagem de script DHTML. Possivelmente um pouco pedante, mas pode ser um pouco confuso para algumas pessoas.
caveman_dick
5

Coloque componentes que podem ser corrigidos individualmente dentro de seus próprios fragmentos

Isso vale para os instaladores e patches de produtos que, se você incluir qualquer componente em um fragmento, deverá incluir todos os componentes nesse fragmento. No caso de criar um instalador, se você perder alguma referência a componente, receberá um erro de vinculação no light.exe. No entanto, quando você cria um patch, se incluir uma referência de componente único em um fragmento, todos os componentes alterados desse fragmento serão exibidos no seu patch.

como isso:

<Fragment>
    <DirectoryRef Id="SampleProductFolder">
        <Component Id="SampleComponent1" Guid="{C28843DA-EF08-41CC-BA75-D2B99D8A1983}" DiskId="1">
            <File Id="SampleFile1" Source=".\$(var.Version)f\Sample1.txt" />
        </Component>
    </DirectoryRef>
</Fragment>

<Fragment>
    <DirectoryRef Id="SampleProductFolder">
        <Component Id="SampleComponent2" Guid="{6CEA5599-E7B0-4D65-93AA-0F2F64402B22}" DiskId="1">
           <File Id="SampleFile2" Source=".\$(var.Version)f\Sample2.txt" />
        </Component>
    </DirectoryRef>
</Fragment>

<Fragment>
    <DirectoryRef Id="SampleProductFolder">
        <Component Id="SampleComponent3" Guid="{4030BAC9-FAB3-426B-8D1E-DC1E2F72C2FC}" DiskId="1">
           <File Id="SampleFile3" Source=".\$(var.Version)f\Sample3.txt" />
        </Component>
    </DirectoryRef>
</Fragment>

em vez disso:

<Fragment>
    <DirectoryRef Id="SampleProductFolder">
        <Component Id="SampleComponent1" Guid="{C28843DA-EF08-41CC-BA75-D2B99D8A1983}" DiskId="1">
            <File Id="SampleFile1" Source=".\$(var.Version)\Sample1.txt" />
        </Component>

        <Component Id="SampleComponent2" Guid="{6CEA5599-E7B0-4D65-93AA-0F2F64402B22}" DiskId="1">
           <File Id="SampleFile2" Source=".\$(var.Version)\Sample2.txt" />
        </Component>

        <Component Id="SampleComponent3" Guid="{4030BAC9-FAB3-426B-8D1E-DC1E2F72C2FC}" DiskId="1">
           <File Id="SampleFile3" Source=".\$(var.Version)\Sample3.txt" />
        </Component>
    </DirectoryRef>
</Fragment>

Além disso, ao aplicar patches usando o tópico "Using Purely WiX" no arquivo de ajuda WiX.chm, use este procedimento para gerar o patch:

torch.exe -p -xi 1.0\product.wixpdb 1.1\product.wixpdb -out patch\diff.wixmst
candle.exe patch.wxs
light.exe patch.wixobj -out patch\patch.wixmsp
pyro.exe patch\patch.wixmsp -out patch\patch.msp -t RTM patch\diff.wixmst

não basta apenas ter a versão 1.1 do product.wixpdb criada usando os componentes em fragmentos separados. Portanto, certifique-se de fragmentar seu produto corretamente antes do envio.

Dave Andersen
fonte
5

Impressão do EULA a partir do Wix3.0 e posterior

1) Quando você compila seu código-fonte wix, o light.exe deve fazer referência ao WixUIExtension.dll na linha de comando. Use a opção de linha de comando -ext para isso.

2) Se quando você adicionar a referência ao WixUIExtension.dll, seu projeto falhar na compilação, isso provavelmente ocorrerá devido a conflitos de IDs de Diálogo, ou seja, seu projeto estava usando os mesmos IDs de diálogos que alguns diálogos padrão no WixUIExtension.dll, forneça IDs diferentes para suas caixas de diálogo. Este é um problema bastante comum.

3) O seu diálogo de licença deve ter o controle ScrollableText com o ID "LicenseText". O Wix procura exatamente esse nome de controle quando é impresso.

<Control Id="LicenseText" Type="ScrollableText" X="20" Y="60" Width="330" Height="160" Sunken="yes" TabSkip="no">
    <Text SourceFile="License.rtf" />
</Control>

e um PushButton que se refere à ação personalizada

<Control Type="PushButton" Id="PrintButton" Width="57" Height="17" X="19" Y="244" Text="Print">
    <Publish Event="DoAction" Value="PrintEula">1</Publish>
</Control>

4) Defina CustomAction com o Id = "PrintEula" assim:

<CustomAction Id="PrintEula" BinaryKey="WixUIWixca" DllEntry="PrintEula" Return="ignore" Execute="immediate" />

Nota: BinaryKey é diferente no Wix3.0 em comparação ao Wix2.0 e deve ser exatamente "WixUIWixca" (diferencia maiúsculas de minúsculas).

Quando o usuário pressionar o botão, ele será presenteado com a caixa de diálogo Selecionar impressora padrão e poderá imprimir a partir daí.

farfareast
fonte
5
  • Exibimos a versão do produto em algum lugar (minúsculo) na primeira tela da GUI. Porque as pessoas tendem a cometer erros ao escolher a versão certa sempre. (E mantenha-nos desenvolvedores procurando por idades ..)

  • Configuramos o TFSBuild para também gerar transformações (arquivos .mst) com a configuração para nossos diferentes ambientes. (Conhecemos todos os ambientes nos quais precisamos implantar).

Como o post original de Grant Holliday está desativado, copio e colo seu conteúdo aqui:


Tarefa MSBuild para gerar arquivos MSI Transform a partir do XMLMarch 11 2008

Na minha postagem anterior, descrevi como você pode usar arquivos MSI Transform (* .mst) para separar as configurações específicas do ambiente de um pacote MSI genérico.

Embora isso ofereça um nível de flexibilidade em sua configuração, existem dois aspectos negativos dos arquivos do Transform:

  1. Eles são um formato binário
  2. Você não pode "editar" ou "visualizar" um arquivo de transformação. Você deve aplicá-lo ou recriá-lo para ver quais alterações ele inclui.

Felizmente, podemos usar a Biblioteca de Objetos do Microsoft Windows Installer (c: windowssystem32msi.dll) para abrir “bancos de dados” MSI e criar arquivos de transformação.

Os créditos vão novamente para Alex Shevchuk - Do MSI ao WiX - Parte 7 - Personalizando a instalação usando o Transforms para nos mostrar como conseguir isso com o VbScript. Basicamente, tudo o que fiz foi usar o exemplo de Alex e, usando o Interop.WindowsInstaller.dll, implementei uma tarefa MSBuild. A tarefa MSBuild

Faça o download do código-fonte e do exemplo transforms.xml aqui (~ 7Kb Zipped VS2008 Solution)


thijs
fonte
2
Redefinimos o WelcomeDlgTitle no meu arquivo de localização - funciona muito bem! <String Id = "WelcomeDlgTitle"> {\ WixUI_Font_Bigger} Bem-vindo ao Assistente de instalação [ProductName] [ProductVersion] </String>
saschabeaumont 20/08/2009
5

Antes de implantar um pacote de instalação, eu sempre controlo o conteúdo dele.

É apenas uma simples chamada na linha de comando (de acordo com a publicação Terrences), abra a linha de comando e digite

msiexec /a Package.msi /qb TARGETDIR="%CD%\Extract" /l*vx "%CD\install.log%"

Isso extrairá o conteúdo do pacote para um subdiretório 'Extrair' com o caminho atual.

tobaer
fonte
4

Em vez de ORCA, use InstEd, que é uma boa ferramenta para visualizar tabelas MSI. Também tem a capacidade de diferenciar dois pacotes pelo Transform -> Compare To ...

Além disso, está disponível uma versão Plus com funcionalidade adicional. Mas também a versão gratuita oferece uma boa alternativa para o Orca.

user432758
fonte
4

Registrando assemblies .NET para interoperabilidade COM com compatibilidade x86 / x64

Nota: este fragmento é essencialmente o mesmo que o REGASM Assembly.dll / codebase

Algumas coisas estão acontecendo neste exemplo, então aqui está o código e eu explicarei depois ...

  <Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
  <?include $(sys.CURRENTDIR)\Config.wxi?>
  <?if $(var.Win64) ?>
  <?define CLSIDRoots = "CLSID;Wow6432Node\CLSID"?>
  <?else ?>
  <?define CLSIDRoots = "CLSID"?>
  <?endif?>
  <!-- ASCOM Driver Assembly with related COM registrations -->
  <Fragment>
    <DirectoryRef Id="INSTALLLOCATION" />
  </Fragment>
  <Fragment>
    <ComponentGroup Id="cgAscomDriver">
      <Component Id="cmpAscomDriver" Directory="INSTALLLOCATION" Guid="{0267031F-991D-4D88-A748-00EC6604171E}">
        <File Id="filDriverAssembly" Source="$(var.TiGra.Astronomy.AWRDriveSystem.TargetPath)" KeyPath="yes" Vital="yes" Assembly=".net" AssemblyApplication="filDriverAssembly"  />
        <RegistryKey Root="HKCR" Key="$(var.DriverId)"  Action="createAndRemoveOnUninstall">
          <RegistryValue Type="string" Value="$(var.DriverTypeName)"/>
          <RegistryKey Key="CLSID">
            <RegistryValue Type="string" Value="$(var.DriverGuid)" />
          </RegistryKey>
        </RegistryKey>
        <?foreach CLSID in $(var.CLSIDRoots) ?>
        <RegistryKey Root="HKCR" Key="$(var.CLSID)" Action="none">
          <RegistryKey Key="$(var.DriverGuid)" Action="createAndRemoveOnUninstall">
            <RegistryValue Type="string" Value="$(var.DriverTypeName)"/>
            <RegistryKey Key="InprocServer32">
              <RegistryValue Type="string" Value="mscoree.dll" />
              <RegistryValue Type="string" Name="ThreadingModel" Value="Both"/>
              <RegistryValue Type="string" Name="Class" Value="$(var.DriverTypeName)"/>
              <RegistryValue Type="string" Name="Assembly" Value="!(bind.assemblyFullname.filDriverAssembly)" />
              <RegistryValue Type="string" Name="RuntimeVersion" Value="v2.0.50727"/>
              <RegistryValue Type="string" Name="CodeBase" Value="file:///[#filDriverAssembly]" />
              <RegistryKey Key="!(bind.fileVersion.filDriverAssembly)" >
                <RegistryValue Type="string" Name="Class" Value="$(var.DriverTypeName)"/>
                <RegistryValue Type="string" Name="Assembly" Value="!(bind.assemblyFullname.filDriverAssembly)" />
                <RegistryValue Type="string" Name="RuntimeVersion" Value="v2.0.50727"/>
                <RegistryValue Type="string" Name="CodeBase" Value="file:///[#filDriverAssembly]" />
              </RegistryKey>
            </RegistryKey>
            <RegistryKey Key="ProgId" Action="createAndRemoveOnUninstall">
              <RegistryValue Type="string" Value="$(var.DriverId)" />
            </RegistryKey>
            <RegistryKey Key="Implemented Categories" Action="createAndRemoveOnUninstall" >
              <RegistryKey Key="{62C8FE65-4EBB-45e7-B440-6E39B2CDBF29}" Action="createAndRemoveOnUninstall" />
            </RegistryKey>
          </RegistryKey>
        </RegistryKey>
        <?endforeach?>
      </Component>
    </ComponentGroup>
  </Fragment>
</Wix>

Se você estava se perguntando, isso é realmente para um driver de telescópio ASCOM .

Primeiro, segui os conselhos acima e criei algumas variáveis ​​de plataforma em um arquivo separado, você pode ver aquelas espalhadas pelo XML.

A parte if-then-else, próxima à parte superior, lida com a compatibilidade x86 x x64. Meu assembly tem como alvo 'Qualquer CPU', em um sistema x64, preciso registrá-lo duas vezes, uma vez no registro de 64 bits e outra nas Wow6432Nodeáreas de 32 bits . O if-then-else me define para isso, os valores são usados ​​em um foreachloop posteriormente. Dessa forma, só preciso criar as chaves do registro uma vez (princípio DRY).

O elemento file especifica a DLL de montagem real que está sendo instalada e registrada:

<File Id="filDriverAssembly" Source="$(var.TiGra.Astronomy.AWRDriveSystem.TargetPath)" KeyPath="yes" Vital="yes" Assembly=".net" AssemblyApplication="filDriverAssembly"  />

Nada revolucionário, mas observe o Assembly=".net"- esse atributo sozinho faria com que o assembly fosse colocado no GAC, o que NÃO era o que eu queria. Usar o AssemblyApplicationatributo para apontar de volta para si mesmo é simplesmente uma maneira de parar o Wix de colocar o arquivo no GAC. Agora que o Wix sabe que é um assembly .net, ele me permite usar certas variáveis ​​do fichário dentro do meu XML, como !(bind.assemblyFullname.filDriverAssembly)para obter o nome completo do assembly.

Tim Long
fonte
3

Defina a DISABLEADVTSHORTCUTSpropriedade para forçar todos os atalhos anunciados em seu instalador a se tornarem atalhos regulares, e você não precisa incluir uma chave de registro fictícia para ser usada como caminho de chave.

<Property Id="DISABLEADVTSHORTCUTS" Value="1"/>

Eu acho que o Windows Installer 4.0 ou superior é um requisito .

Dave Andersen
fonte
2

É uma estrutura legal, mas com base na minha experiência, pergunto-me como você lida com essas condições:

R. Todas as suas instalações parecem pousar no mesmo destino. Se um usuário precisar instalar todas as 3 versões ao mesmo tempo, seu processo permitirá isso. Eles podem dizer sem ambiguidade qual versão de cada executável está sendo acionada?

B. Como você lida com novos arquivos que existem no TEST e / ou TRAINING, mas ainda não no LIVE?


fonte
Oi Blaine, A. Não, eles não. InstallName está no Config.wxi, que é o único arquivo não referenciado por svn: externals. Portanto, isso é único para cada instalação, ou seja, para cada produto. É também por isso que modificamos os Guids para cada versão. B. GOTO A. :) Eles são MSI separados com seu próprio UpgradeCode.
236 de
1
a propósito, entendo por que você respondeu minha pergunta com uma pergunta, mas depois de obter pontos de representação suficientes, mova sua pergunta para os comentários da resposta; caso contrário, será difícil seguir o tópico.
23620 si618
2

Aqui está uma maneira de ajudar grandes projetos da Web a verificar se o número de arquivos implantados corresponde ao número de arquivos criados em um MSI (ou módulo de mesclagem). Acabei de executar a tarefa personalizada do MSBuild em nosso aplicativo principal (ainda em desenvolvimento) e ele capturou alguns arquivos ausentes, principalmente imagens, mas alguns arquivos javascript haviam passado!

Essa abordagem (espreitando a tabela File do MSI conectando-se ao destino AfterBuild do projeto WiX) pode funcionar para outros tipos de aplicativos nos quais você tem acesso a uma lista completa dos arquivos esperados.

si618
fonte
2

Executar uma reinstalação forçada quando uma instalação não permite desinstalar ou reinstalar e não reverte.

Script VBscript usado para substituir uma instalação que não está sendo desinstalada por qualquer motivo.

Dim objShell
set objShell = wscript.createObject("wscript.shell")

iReturn = objShell.Run("CMD /K MsiExec.exe /I ""C:\Users\TheUser\Documents\Visual Studio 2010\Projects\InstallationTarget\HelloInstaller\bin\Debug\HelloInstaller.msi"" REINSTALLMODE=vomus REINSTALL=ALL",,True)
Terrance
fonte
2

Crie uma interface do usuário que tenha uma ação personalizada que definirá uma variável e a interface do usuário desabilitará / habilitará o próximo botão (ou similar) com base na variável definida na ação personalizada.

Não é tão direto quanto você imagina, não é muito difícil, mas não está documentado em nenhum lugar!

Interações Wix com Condições, Propriedades e Ações Personalizadas

Daniel Powell
fonte