Bagaimana cara menambahkan tindakan khusus WiX yang hanya terjadi pada penghapusan instalasi (melalui MSI)?

160

Saya ingin memodifikasi penginstal MSI (dibuat melalui WiX ) untuk menghapus seluruh direktori saat mencopot pemasangan.

Saya mengerti opsi RemoveFiledan RemoveFolderdi WiX, tetapi ini tidak cukup kuat untuk secara rekursif menghapus seluruh folder yang memiliki konten yang dibuat setelah instalasi.

Saya perhatikan pertanyaan serupa Stack Overflow Menghapus file ketika menghapus WiX , tapi saya bertanya-tanya apakah ini bisa dilakukan lebih sederhana menggunakan panggilan ke skrip batch untuk menghapus folder.

Ini adalah pertama kalinya saya menggunakan WiX, dan saya masih terbiasa dengan tindakan khusus . Apa yang akan menjadi contoh dasar dari tindakan kustom yang akan menjalankan skrip batch saat mencopot pemasangan?

Komunitas
sumber

Jawaban:

188

EDIT : Mungkin lihat jawabannya saat ini tepat di bawah .


Topik ini sudah lama menjadi sakit kepala. Saya akhirnya menemukan jawabannya. Ada beberapa solusi online, tetapi tidak ada satupun yang benar-benar berfungsi. Dan tentu saja tidak ada dokumentasi. Jadi dalam bagan di bawah ini ada beberapa properti yang disarankan untuk digunakan dan nilai-nilai yang mereka miliki untuk berbagai skenario pemasangan:

teks alternatif

Jadi dalam kasus saya, saya menginginkan CA yang hanya akan berjalan di pencopotan pemasangan - bukan pemutakhiran, bukan perbaikan atau modifikasi. Menurut tabel di atas saya harus menggunakan

<Custom Action='CA_ID' Before='other_CA_ID'>
        (NOT UPGRADINGPRODUCTCODE) AND (REMOVE="ALL")</Custom>

Dan itu berhasil!

nelsonjchen
sumber
25
Apakah nilai dalam bagan itu benar? Mengapa Anda perlu menambahkan REMOVE = "ALL"? BUKAN UPGRADINGPRODUCTCODE hanya berlaku untuk pencopotan (menurut bagan), jadi (BUKAN UPGRADINGPRODUCTCODE) DAN (HAPUS = "SEMUA") juga hanya berlaku pada pencopotan pemasangan. REMOVE = "ALL" sepertinya tidak perlu.
Todd Ropog
2
Saya setuju dengan @ToddRopog - Contoh dan tabel kebenaran sepertinya tidak setuju. Benarkah itu benar?
Tim Long
19
Tabel kebenaran sedikit salah. BUKAN UPGRADEPRODUKTODE juga berlaku untuk pemasangan pertama
Neil
2
Kondisi umum: alekdavis.blogspot.ru/2013/05/…
KindDragon
1
Silakan konfirmasi: Diinstal dan DIINSTAL adalah hal yang berbeda, hanya Diinstal diatur oleh Penginstal Windows. Saya tidak berpikir bahwa INSTALLED berfungsi.
Micha Wiedenmann
139

Ada beberapa masalah dengan jawaban yaluna , juga nama properti peka huruf besar-kecil, Installedapakah ejaan yang benar ( INSTALLEDtidak akan berfungsi). Tabel di atas seharusnya adalah ini:

masukkan deskripsi gambar di sini

Juga dengan asumsi perbaikan penuh & menghapus nilai properti yang sebenarnya bisa:

masukkan deskripsi gambar di sini

The WiX Ekspresi Sintaks dokumentasi mengatakan:

Dalam ekspresi ini, Anda bisa menggunakan nama properti (ingat bahwa mereka peka terhadap huruf besar-kecil).

Properti didokumentasikan di Panduan Penginstal Windows (mis. Dipasang )

EDIT: Koreksi kecil ke tabel pertama; jelas "Uninstall" juga bisa terjadi hanya dengan REMOVEmakhluk True.

ahmd0
sumber
3
REMOVE juga tampaknya diatur untuk Ubah
szx
2
Kolom 'Tingkatkan', apakah selama urutan penghapusan versi lama atau urutan instalasi versi baru?
Nick Whaley
1
@NickWhaley: Saya belum melihat ke dalamnya untuk sementara waktu tetapi saya percaya bahwa opsi "Upgrade" hanya ketika menginstal versi lebih besar dari yang sudah diinstal.
ahmd0
1
@ ahmd0, tentu saja. Tetapi ada instalasi bersarang yang terjadi di dalam RemoveExistingProducts yang memiliki serangkaian properti yang sama sekali berbeda. Itulah yang ada di kolom 'Tingkatkan' Anda. Sisa dari upgrade ini identik dengan kolom 'Instal'.
Nick Whaley
1
@NickWhaley: Opsi REMOVE akan berlaku untuk Peningkatan Utama, yaitu 1.0.0 ke 2.0.0, bukan 1.0.0 ke 1.1.0, selama pelaksanaan uninstaller versi sebelumnya. Untuk menjalankan Tindakan Kustom selama Peningkatan Utama dalam menginstal versi baru Anda harus referensi ActionProperty didefinisikan dalam tabel MSI Upgrade Anda untuk upgrade versi itu. symantec.com/connect/articles/msi-upgrade-overview msdn.microsoft.com/en-us/library/aa372379%28v=vs.85%29.aspx
Chaoix
48

Anda dapat melakukan ini dengan tindakan kustom. Anda dapat menambahkan referensi untuk tindakan khusus Anda di bawah <InstallExecuteSequence>:

<InstallExecuteSequence>
...
  <Custom Action="FileCleaner" After='InstallFinalize'>
          Installed AND NOT UPGRADINGPRODUCTCODE</Custom>

Maka Anda juga harus mendefinisikan Aksi di bawah <Product>:

<Product> 
...
  <CustomAction Id='FileCleaner' BinaryKey='FileCleanerEXE' 
                ExeCommand='' Return='asyncNoWait'  />

Di mana FileCleanerEXE adalah biner (dalam kasus saya sedikit program c ++ yang melakukan tindakan kustom) yang juga didefinisikan di bawah <Product>:

<Product> 
...
  <Binary Id="FileCleanerEXE" SourceFile="path\to\fileCleaner.exe" />

Trik sebenarnya untuk ini adalah Installed AND NOT UPGRADINGPRODUCTCODEkondisi pada Tindakan Kustom, tanpa tindakan Anda akan dijalankan pada setiap pembaruan (karena upgrade benar-benar uninstall lalu instal ulang). Yang jika Anda menghapus file mungkin tidak ingin Anda inginkan selama upgrade.

Di samping catatan: Saya sarankan melalui masalah menggunakan sesuatu seperti program C ++ untuk melakukan tindakan, alih-alih skrip batch karena kekuatan dan kontrol yang disediakannya - dan Anda dapat mencegah jendela "cmd prompt" dari berkedip saat installer Anda berjalan.

csexton
sumber
3
25 meningkatkan tetapi bukan jawaban yang diterima. Selamat datang di dunia installer! :)
Christopher Painter
4
Ini tidak benar-benar berfungsi. Ketika Anda ingin menjalankan fileCleaner.exe, yang diinstal di folder instalasi Anda sendiri, ini akan menjadi masalah ayam-dan-telur: Ini CustomActionakan dieksekusi "Setelah = 'InstallFinalize'". Pada titik ini, semua file dihapus dari folder Instalasi. Juga fileCleaner.exe. Jadi, Anda tidak dapat menjalankannya melalui CustomAction. Jawaban ini benar-benar salah. Saya bertanya-tanya tentang 42 upvotes!
Simon
40

Masalah terbesar dengan skrip batch adalah menangani rollback ketika pengguna mengklik membatalkan (atau ada yang salah selama instalasi Anda). Cara yang benar untuk menangani skenario ini adalah membuat CustomAction yang menambahkan baris sementara ke tabel RemoveFiles. Dengan begitu Penginstal Windows menangani kasing rollback untuk Anda. Ini sangat sederhana ketika Anda melihat solusinya.

Pokoknya, untuk memiliki tindakan hanya menjalankan selama uninstall menambahkan elemen Kondisi dengan:

REMOVE ~= "ALL"

the ~ = mengatakan bandingkan case case (meskipun saya pikir SEMUA selalu huruf besar). Lihat dokumentasi MSI SDK tentang Sintaksis Kondisi untuk informasi lebih lanjut.

PS: Tidak pernah ada kasus di mana saya duduk dan berpikir, "Oh, file batch akan menjadi solusi yang baik dalam paket instalasi." Sebenarnya, menemukan paket instalasi yang memiliki file batch di dalamnya hanya akan mendorong saya untuk mengembalikan produk untuk pengembalian uang.

Rob Mensching
sumber
Saya akan menggunakan skrip batch dan kemudian melihat bagian PS. Terima kasih telah menyelamatkan saya:) The Remove ~ = "ALL" bekerja untuk saya.
ArNumb
12

Berikut adalah seperangkat properti yang saya buat yang terasa lebih intuitif untuk digunakan daripada barang bawaan. Ketentuan didasarkan dari tabel kebenaran yang disediakan di atas oleh ahmd0.

<!-- truth table for installer varables (install vs uninstall vs repair vs upgrade) https://stackoverflow.com/a/17608049/1721136 -->
 <SetProperty Id="_INSTALL"   After="FindRelatedProducts" Value="1"><![CDATA[Installed="" AND PREVIOUSVERSIONSINSTALLED=""]]></SetProperty>
 <SetProperty Id="_UNINSTALL" After="FindRelatedProducts" Value="1"><![CDATA[PREVIOUSVERSIONSINSTALLED="" AND REMOVE="ALL"]]></SetProperty>
 <SetProperty Id="_CHANGE"    After="FindRelatedProducts" Value="1"><![CDATA[Installed<>"" AND REINSTALL="" AND PREVIOUSVERSIONSINSTALLED<>"" AND REMOVE=""]]></SetProperty>
 <SetProperty Id="_REPAIR"    After="FindRelatedProducts" Value="1"><![CDATA[REINSTALL<>""]]></SetProperty>
 <SetProperty Id="_UPGRADE"   After="FindRelatedProducts" Value="1"><![CDATA[PREVIOUSVERSIONSINSTALLED<>"" ]]></SetProperty>

Berikut ini beberapa contoh penggunaan:

  <Custom Action="CaptureExistingLocalSettingsValues" After="InstallInitialize">NOT _UNINSTALL</Custom>
  <Custom Action="GetConfigXmlToPersistFromCmdLineArgs" After="InstallInitialize">_INSTALL OR _UPGRADE</Custom>
  <Custom Action="ForgetProperties" Before="InstallFinalize">_UNINSTALL OR _UPGRADE</Custom>
  <Custom Action="SetInstallCustomConfigSettingsArgs" Before="InstallCustomConfigSettings">NOT _UNINSTALL</Custom>
  <Custom Action="InstallCustomConfigSettings" Before="InstallFinalize">NOT _UNINSTALL</Custom>

Masalah:

Bill Tarbell
sumber
Ini solusi hebat. Ingatlah untuk juga mempertimbangkan kondisi PATCH dan MSIPATCHREMOVE.
Garet Jax
Di tabel kebenaran Anda, apakah Anda bermaksud menggunakan PREVIOUSVERSIONSINSTALLED bukannya UPGRADINGPRODUCTCODE seperti yang digunakan oleh ahmd0? Saya tidak melihat referensi apa pun ke PREVIOUSVERSIONSINSTALLED di halaman referensi properti MSI ( docs.microsoft.com/en-us/windows/win32/msi/property-reference ).
Patrick
Beberapa predikat untuk properti Anda tidak memperhitungkan semua baris dalam tabel ahmd0 (Dipasang, REINSTALL, UPGRADINGPRODUCTCODE, dan HAPUS). Tolong jelaskan mengapa.
Patrick
0

Saya menggunakan Tindakan Kustom yang dikodekan secara terpisah dalam C ++ DLL dan menggunakan DLL untuk memanggil fungsi yang sesuai pada Penghapusan Instalasi menggunakan sintaks ini:

<CustomAction Id="Uninstall" BinaryKey="Dll_Name" 
              DllEntry="Function_Name" Execute="deferred" />

Menggunakan blok kode di atas, saya bisa menjalankan fungsi apa pun yang didefinisikan dalam C ++ DLL saat mencopot pemasangan. FYI, fungsi hapus instalan saya memiliki kode terkait Menghapus data pengguna saat ini dan entri registri.

Sid
sumber