วิธีเพิ่มแอ็คชันที่กำหนดเองของ WiX ที่เกิดขึ้นเฉพาะในการถอนการติดตั้ง (ผ่าน MSI)


161

ฉันต้องการแก้ไขตัวติดตั้ง MSI (สร้างผ่านWiX ) เพื่อลบไดเรกทอรีทั้งหมดเมื่อถอนการติดตั้ง

ฉันเข้าใจRemoveFileและRemoveFolderตัวเลือกใน WiX แต่สิ่งเหล่านี้ไม่แข็งแกร่งพอที่จะลบโฟลเดอร์ทั้งหมดซ้ำที่มีเนื้อหาที่สร้างขึ้นหลังจากการติดตั้ง

ฉันสังเกตเห็นคำถาม Stack Overflow ที่คล้ายกันการลบไฟล์เมื่อถอนการติดตั้ง WiXแต่ฉันสงสัยว่าจะสามารถทำได้มากกว่านี้เพียงแค่ใช้การเรียกไปยังสคริปต์ชุดงานเพื่อลบโฟลเดอร์

นี่เป็นครั้งแรกของฉันโดยใช้ WiX และฉันยังคงได้รับการแขวนของการกระทำที่กำหนดเอง อะไรจะเป็นตัวอย่างพื้นฐานของการกระทำที่กำหนดเองที่จะเรียกใช้สคริปต์ชุดในการถอนการติดตั้ง?

คำตอบ:


188

แก้ไข : บางทีอาจจะมองไปที่คำตอบขณะนี้ได้ทันทีด้านล่าง


หัวข้อนี้ปวดหัวมาเป็นเวลานาน ในที่สุดฉันก็คิดออก มีวิธีแก้ไขปัญหาบางอย่างทางออนไลน์ แต่ไม่มีวิธีใดที่ใช้งานได้จริง และแน่นอนว่าไม่มีเอกสารประกอบ ดังนั้นในแผนภูมิด้านล่างมีคุณสมบัติหลายอย่างที่แนะนำให้ใช้และค่าที่มีสำหรับสถานการณ์การติดตั้งต่างๆ:

ข้อความแสดงแทน

ดังนั้นในกรณีของฉันฉันต้องการ CA ที่จะทำงานเฉพาะในการถอนการติดตั้ง - ไม่ใช่การอัพเกรดไม่ซ่อมแซมหรือแก้ไข ตามตารางข้างต้นฉันต้องใช้

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

และมันก็ใช้งานได้!


25
ค่าในแผนภูมินั้นถูกต้องหรือไม่ ทำไมคุณต้องเพิ่ม REMOVE = "ALL" ไม่ใช่ UPGRADINGPRODUCTCODE จะเป็นจริงสำหรับการถอนการติดตั้ง (ตามแผนภูมิ) ดังนั้น (ไม่ใช่ UPGRADINGPRODUCTCODE) และ (REMOVE = "ALL") จะเป็นจริงในการถอนการติดตั้งเท่านั้น REMOVE = "ALL" ดูเหมือนไม่จำเป็น
Todd Ropog

2
ฉันเห็นด้วยกับ @ToddRopog - ตัวอย่างและตารางความจริงดูเหมือนจะไม่เห็นด้วย ถูกต้องจริงเหรอ?
Tim Long

19
ตารางความจริงนั้นผิดเล็กน้อย การอัพเกรด UPGRADINGPRODUCTCODE นั้นเป็นจริงสำหรับการติดตั้งครั้งแรกเช่นกัน
Neil

2
เงื่อนไขทั่วไป: alekdavis.blogspot.ru/2013/05/ …
KindDragon

1
กรุณายืนยัน: ติดตั้งและติดตั้งเป็นสิ่งที่แตกต่างกันติดตั้งเพียงถูกตั้งค่าโดย Windows Installer ฉันไม่คิดว่าการติดตั้งใช้งานได้
Micha Wiedenmann

140

มีปัญหาหลายอย่างกับคำตอบของ yalunaรวมถึงชื่อคุณสมบัติเป็นตัวพิมพ์เล็กและตัวพิมพ์ใหญ่Installedการสะกดที่ถูกต้อง ( INSTALLEDจะไม่ทำงาน) ตารางข้างต้นควรเป็นเช่นนี้:

ป้อนคำอธิบายรูปภาพที่นี่

นอกจากนี้สมมติว่าการซ่อมแซมแบบเต็ม & ถอนการติดตั้งมูลค่าที่แท้จริงของคุณสมบัติอาจเป็น:

ป้อนคำอธิบายรูปภาพที่นี่

แสดงออกไวยากรณ์ WiXเอกสารพูดว่า:

ในนิพจน์เหล่านี้คุณสามารถใช้ชื่อคุณสมบัติ (โปรดจำไว้ว่าตัวพิมพ์เล็กและตัวพิมพ์ใหญ่)

คุณสมบัติดังกล่าวได้รับการบันทึกไว้ในคู่มือการติดตั้ง Windows (เช่นติดตั้ง )

แก้ไข: แก้ไขเล็ก ๆ ในตารางแรก; อย่างเห็นได้ชัด "ถอนการติดตั้ง" นอกจากนี้ยังสามารถเกิดขึ้นได้มีเพียงแค่ความเป็นอยู่REMOVETrue


3
REMOVE จะถูกตั้งค่าสำหรับ Change
szx

2
คอลัมน์ 'อัปเกรด' เป็นเช่นนั้นในระหว่างการถอนการติดตั้งลำดับของเวอร์ชั่นเก่าหรือติดตั้งลำดับของเวอร์ชั่นใหม่หรือไม่
Nick Whaley

1
@NickWhaley: ฉันไม่ได้ตรวจสอบมาระยะหนึ่งแล้ว แต่ฉันเชื่อว่าตัวเลือก "อัปเกรด" เป็นเพียงเมื่อติดตั้งรุ่นที่มากกว่ารุ่นที่ติดตั้งไว้แล้ว
ahmd0

1
@ ahmd0 แน่นอน แต่มีการติดตั้งแบบซ้อนที่เกิดขึ้นภายใน RemoveExistingProducts ซึ่งมีชุดคุณสมบัติที่แตกต่างกันโดยสิ้นเชิง นั่นคือสิ่งที่อยู่ในคอลัมน์ 'อัพเกรด' ของคุณ ส่วนที่เหลือของการอัปเกรดจะเหมือนกับคอลัมน์ 'ติดตั้ง'
Nick Whaley

1
@NickWhaley: ตัวเลือกการลบจะเป็นจริงสำหรับการอัปเกรดที่สำคัญเช่น 1.0.0 ถึง 2.0.0 ไม่ใช่ 1.0.0 ถึง 1.1.0 ในระหว่างการดำเนินการถอนการติดตั้งเวอร์ชันก่อนหน้านี้ ในการใช้งาน Custom Action ในระหว่างการอัพเกรดครั้งใหญ่ในการติดตั้งเวอร์ชั่นใหม่คุณจะต้องอ้างอิง ActionProperty ที่กำหนดไว้ในตารางอัพเกรด MSI ของคุณสำหรับการอัพเกรดเวอร์ชันนั้น symantec.com/connect/articles/msi-upgrade-overview msdn.microsoft.com/en-us/library/aa372379%28v=vs.85%29.aspx
Chaoix

48

คุณสามารถทำได้ด้วยการกระทำที่กำหนดเอง คุณสามารถเพิ่มการอ้างอิงไปยังการกระทำที่กำหนดเองของคุณภายใต้<InstallExecuteSequence>:

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

จากนั้นคุณจะต้องกำหนดการกระทำของคุณภายใต้<Product>:

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

โดยที่ FileCleanerEXE เป็นไบนารี (ในกรณีของฉันเป็นโปรแกรม c ++ เล็ก ๆ ที่ทำการกระทำแบบกำหนดเอง) ซึ่งถูกกำหนดไว้ภายใต้<Product>:

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

เคล็ดลับที่แท้จริงของสิ่งนี้คือInstalled AND NOT UPGRADINGPRODUCTCODEเงื่อนไขใน Custom Action โดยที่การกระทำของคุณจะได้รับการดำเนินการในทุกการอัปเกรด (เนื่องจากการอัปเกรดเป็นการถอนการติดตั้งจริง ๆ แล้วติดตั้งใหม่) ซึ่งหากคุณลบไฟล์อาจไม่ต้องการให้คุณอัปเกรด

ในหมายเหตุด้านข้าง: ฉันขอแนะนำให้คุณประสบปัญหาในการใช้งานบางอย่างเช่นโปรแกรม C ++ เพื่อดำเนินการแทนชุดสคริปต์เนื่องจากพลังงานและการควบคุมที่ให้ - และคุณสามารถป้องกันหน้าต่าง "cmd prompt" กระพริบในขณะที่ โปรแกรมติดตั้งของคุณทำงาน


3
25 upvotes แต่ไม่ใช่คำตอบที่ยอมรับ ยินดีต้อนรับสู่โลกของโปรแกรมติดตั้ง! :)
Christopher Painter

4
มันไม่ได้ผลจริงๆ เมื่อคุณต้องการดำเนินการ fileCleaner.exe ที่ติดตั้งในโฟลเดอร์การติดตั้งของคุณเองนี่จะเป็นปัญหาไก่และไข่: CustomActionจะถูกดำเนินการ "After = 'InstallFinalize'" ณ จุดนี้ไฟล์ทั้งหมดจะถูกลบออกจากโฟลเดอร์การติดตั้ง นอกจากนี้ fileCleaner.exe ดังนั้นคุณจะไม่สามารถดำเนินการผ่าน CustomAction ได้ คำตอบนี้เป็นเพียงความผิด ฉันสงสัยเกี่ยวกับ 42 upvotes!
Simon

40

ปัญหาที่ใหญ่ที่สุดของแบทช์สคริปต์คือการจัดการการย้อนกลับเมื่อผู้ใช้คลิกยกเลิก (หรือมีบางอย่างผิดปกติระหว่างการติดตั้ง) วิธีที่ถูกต้องในการจัดการสถานการณ์นี้คือการสร้าง CustomAction ที่เพิ่มแถวชั่วคราวลงในตาราง RemoveFiles ด้วยวิธีนี้ Windows Installer จะจัดการกรณีย้อนกลับให้คุณ มันง่ายกว่าที่คุณเห็นวิธีแก้ปัญหา

อย่างไรก็ตามเพื่อให้มีการดำเนินการเพียงดำเนินการระหว่างถอนการติดตั้งเพิ่มองค์ประกอบเงื่อนไขด้วย:

REMOVE ~= "ALL"

~ = พูดว่าเปรียบเทียบตัวพิมพ์เล็กและตัวพิมพ์เล็ก (แม้ว่าฉันคิดว่า ALL จะเป็นตัวพิมพ์ใหญ่เสมอ) ดูเอกสารประกอบของMSI SDK เกี่ยวกับเงื่อนไขของไวยากรณ์สำหรับข้อมูลเพิ่มเติม

PS: ไม่เคยมีกรณีที่ฉันนั่งลงและคิดว่า "โอ้ไฟล์แบทช์จะเป็นทางออกที่ดีในแพ็คเกจการติดตั้ง" ที่จริงแล้วการค้นหาแพ็คเกจการติดตั้งที่มีไฟล์แบตช์อยู่นั้นจะกระตุ้นให้ฉันส่งคืนผลิตภัณฑ์เพื่อขอรับเงินคืนเท่านั้น


ฉันกำลังจะใช้ชุดสคริปต์แล้วเห็นส่วน PS ขอบคุณที่ช่วยฉัน:) การลบ ~ = "ALL" ใช้ได้สำหรับฉัน
ArNumb

12

นี่เป็นชุดของคุณสมบัติที่ฉันทำให้รู้สึกว่าใช้งานง่ายกว่าสิ่งที่มีอยู่ภายใน เงื่อนไขเป็นไปตามตารางความจริงที่ให้ไว้ข้างต้นโดย 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>

นี่คือตัวอย่างการใช้งาน:

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

ปัญหาที่พบ:

  • UPGRADINGPRODUCTCODE ถูกตั้งค่าระหว่างการดำเนินการ RemoveExistingProducts ดังนั้นการกระทำที่กำหนดเองใด ๆ ที่คุณเรียกใช้ก่อนหน้านี้จะไม่ทราบว่าเป็นการอัปเกรดhttps://docs.microsoft.com/en-us/windows/desktop/Msi/upgradingproductcode

นี่เป็นทางออกที่ดี โปรดจำไว้ว่าให้พิจารณาเงื่อนไข PATCH และ MSIPATCHREMOVE ด้วย
Garet Jax

ในตารางความจริงของคุณคุณหมายถึงใช้ PREVIOUSVERSIONSINSTALLED แทน UPGRADINGPRODUCTCODE ตามที่ ahmd0 ใช้หรือไม่ ฉันไม่เห็นการอ้างอิงถึง PREVIOUSVERSIONSINSTALLED ใด ๆ ในหน้าการอ้างอิงคุณสมบัติของ MSI ( docs.microsoft.com/en-us/windows/win32/msi/property-reference )
Patrick

เพรดิเคตหลายรายการสำหรับคุณสมบัติของคุณไม่ได้คำนึงถึงแถวทั้งหมดในตารางของ ahmd0 (ติดตั้งติดตั้งใหม่ติดตั้ง UPGRADINGPRODUCTCODE และ REMOVE) คุณช่วยอธิบายได้ไหม
Patrick

0

ฉันใช้ Custom Action แยกรหัสใน C ++ DLL และใช้ DLL เพื่อเรียกใช้ฟังก์ชันที่เหมาะสมในการถอนการติดตั้งโดยใช้ไวยากรณ์นี้:

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

ด้วยการใช้โค้ดบล็อกข้างต้นฉันสามารถเรียกใช้ฟังก์ชันใด ๆ ที่กำหนดไว้ใน C ++ DLL เมื่อถอนการติดตั้ง FYI, ฟังก์ชั่นถอนการติดตั้งของฉันมีรหัสเกี่ยวกับการล้างข้อมูลผู้ใช้ปัจจุบันและรายการรีจิสตรี

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.