2015-08-24 81 views
0

Variable ODBC DSN name in InstallShield MSI installation中提出的想法出發,我試圖找到在使用InstallShield 2015創建的多實例MSI安裝程序項目中定義註冊表項的最佳方式,他們在自己的實例未安裝後清理。爲了測試這個想法,我遵循了以下步驟:從MSI多實例InstallShield包中卸載註冊表項的最佳方法

  1. 創建一個名爲Dummy1的新基本MSI項目。
  2. 在「應用程序文件」項目助理步驟中,添加Readme.txt作爲文件傳遞到[ProgramFilesFolder] \ My Company Name \ My Product Name。
  3. 在安裝設計器,文件和文件夾中,右鍵單擊並將其設置爲密鑰文件。
  4. 在Components視圖中,添加註冊表項HKLM \ SOFTWARE \ Dummy,名稱[InstanceId],值「Installed」。
  5. 使用發行嚮導創建具有所有默認值的發行版。
  6. 產品配置1,選擇多個實例選項卡,並添加 實例1
  7. 在項目助理,申請採訪中,設置屬性爲「是」,允許指定的安裝位置。
  8. 構建setup.exe並從Windows資源管理器運行兩次,首先安裝到公司\ 1,然後安裝到公司\ 2。
  9. 驗證HKEY_LOCAL_MACHINE \ SOFTWARE \ Wow6432Node \ Dummy包含「0」和「1」 值。
  10. 從控制面板卸載Dummy1的一個實例。
  11. 觀察到其中一個README.TXT文件已被刪除,但包含它的空目錄(在我的情況下爲「1」)和註冊表項全部仍然存在。

我預料在步驟11中已創建的目錄和其中一個註冊表項已被刪除。我錯過了什麼?

編輯:當我打開很多記錄的我可以看到這樣一個消息:

MSI(S)(E0:F8)[14:30:24:540]:允許卸載共享 組件:{3AACE297-A264-4223-A0AF-C5A20D37551F}。其他客戶端 存在,但安裝到不同的位置

看來至少在IS 2015年有包含在「共享」組件中沒有類似的認可安裝到不同的路徑的註冊表項,並在相同的註冊表項組件作爲正在被刪除的文件似乎不會在刪除組件時捲起。

我可以用於去除最後一個組件安裝的日誌文件中看到:

MSI (s) (E0:08) [14:36:09:538]: Executing op: ActionStart(Name=RemoveRegistryValues,Description=Removing system registry values,Template=Key: [1], Name: [2]) 
MSI (s) (E0:08) [14:36:09:538]: Executing op: ProgressTotal(Total=1,Type=1,ByteEquivalent=13200) 
MSI (s) (E0:08) [14:36:09:538]: Executing op: RegOpenKey(Root=-2147483646,Key=SOFTWARE\Infor\0,,BinaryType=0,,) 
MSI (s) (E0:08) [14:36:09:538]: Executing op: RegRemoveValue(Name=Test,Value=One,) 

正如我在日誌中看到了的卸載它並沒有提到「允許共享組件的卸載」重複組件。而且,在卸載重複組件的日誌中,所有這4行都完全沒有,我不知道爲什麼。

回答

0

有一個優雅的解決方案需要花費相當多的精力才能找出不熟悉Windows Installer或InstallShield的人。 InstallShield's Releases頁面上的Release對象的Events選項卡有一個Precompression事件,在此期間,可以在MSI文件壓縮到Setup.exe之前修改它。我已經進入這個命令進入預壓事件:

"<ISProjectFolder>\UpdateMSI.exe" "<ISReleasePath>\<ISProductConfigName>\<ISReleaseName>\DiskImages\Disk1\Dummy.msi" 

Dummy.msi由產生的InstallShield微星它被壓縮或之前(如釋放被配置爲不壓縮的結果)。 我創建了一個名爲UpdateMSI的VB.NET程序,並將輸出結果輸入C:\InstallShield 2015 Projects\。該程序所做的實質上是提取由InstallShield生成並嵌入到MSI文件中的所有實例轉換,並更新它們以更新組件的GUID。現在我只有一個組件更新註冊表,所以我只更新一個組件,爲每個實例分配一個單獨的GUID(最多9個實例加上默認實例)。

結果是一個安裝程序通過保持它們單獨的GUID,而無需複製它們在項目的InstallShield該乾淨安裝和未安裝這些組件。

UpdateMSI的代碼如下所示。

Imports System.Runtime.InteropServices 

Module Main 
    Private Declare Auto Function MsiOpenDatabase Lib "msi.dll" (ByVal szDatabasePath As String, ByVal szPersist As IntPtr, <Out> ByRef phDatabase As IntPtr) As UInt32 
    Private Declare Auto Function MsiCloseHandle Lib "msi.dll" (ByVal hAny As Integer) As UInt32 
    Private Declare Auto Function MsiDatabaseOpenView Lib "msi.dll" (ByVal hDatabase As IntPtr, ByVal szQuery As String, <Out> ByRef phView As IntPtr) As UInt32 
    Private Declare Auto Function MsiViewExecute Lib "msi.dll" (ByVal hView As IntPtr, ByVal hRecord As IntPtr) As UInt32 
    Private Declare Auto Function MsiViewFetch Lib "msi.dll" (ByVal hView As IntPtr, <Out> ByRef phRecord As IntPtr) As UInt32 
    Private Declare Auto Function MsiRecordGetString Lib "msi.dll" (ByVal hRecord As IntPtr, iField As UInt32, ByVal szValueBuf As System.Text.StringBuilder, ByRef pcchValueBuf As UInt32) As UInt32 
    Private Declare Auto Function MsiViewModify Lib "msi.dll" (ByVal hView As IntPtr, eModifyMode As IntPtr, hRecord As IntPtr) As UInt32 
    Private Declare Auto Function MsiRecordSetString Lib "msi.dll" (ByVal hRecord As IntPtr, iField As UInt32, szValue As String) As UInt32 
    Private Declare Auto Function MsiDatabaseCommit Lib "msi.dll" (ByVal hDatabase As IntPtr) As UInt32 
    Private Declare Auto Function MsiViewClose Lib "msi.dll" (ByVal hView As IntPtr) As UInt32 
    Private Declare Auto Function MsiDatabaseApplyTransform Lib "msi.dll" (ByVal hDatabase As IntPtr, ByVal szTransformFile As String, ByVal iErrorConditions As Integer) As UInt32 
    Private Declare Auto Function MsiDatabaseGenerateTransform Lib "msi.dll" (ByVal hDatabase As IntPtr, ByVal hDatabaseReference As IntPtr, ByVal szTransformFile As String, ByVal iReserved As Integer, ByVal iReserved As Integer) As UInt32 
    Private Declare Auto Function MsiCreateTransformSummaryInfo Lib "msi.dll" (ByVal hDatabase As IntPtr, ByVal hDatabaseReference As IntPtr, ByVal szTransformFile As String, ByVal iErrorConditions As Integer, ByVal iValidation As Integer) As UInt32 
    Private Declare Auto Function MsiRecordSetStream Lib "msi.dll" (ByVal hRecord As IntPtr, ByVal iField As UInt32, ByVal szFilePath As String) As UInt32 

    Private Const MSIDBOPEN_READONLY As Integer = 0 
    Private Const MSIDBOPEN_TRANSACT As Integer = 1 
    Private Const MSIDBOPEN_DIRECT As Integer = 2 
    Private Const ERROR_SUCCESS As Integer = 0 
    Private Const ERROR_NO_MORE_ITEMS As Integer = 259 
    Private Const MSIMODIFY_UPDATE As Integer = 2 

    Private Enum MSITRANSFORM_VALIDATE 
     None = 0 
     Language = 1 
     Product = 2 
     MajorVersion = 8 
     MinorVersion = &H10 
     UpdateVersion = &H20 
     NewLessBaseVersion = &H40 
     NewLessEqualBaseVersion = &H80 
     NewEqualBaseVersion = &H100 
     NewGreaterEqualBaseVersion = &H200 
     NewGreaterBaseVersion = &H400 
     UpgradeCode = &H800 
    End Enum 

    Const RegComponentGuid As String = "{3AACE297-A264-4223-A0AF-C5A20D37551F}" 
    Dim RegComponentInstGuids As String() = { _ 
     "{12456A37-51F3-4F53-A19E-34DF8CB1B063}", _ 
     "{0F00D476-E1F2-4B5D-85C4-D380A33BB78E}", _ 
     "{EFC3C9D6-9C2B-43E9-84A3-837C86BE5DD8}", _ 
     "{1398A80D-681D-4726-9972-8DEC8B030B4A}", _ 
     "{FF610463-5E35-4059-A3C4-EB51A7CABA1D}", _ 
     "{C4A43C73-5791-4B17-906C-93240AAB2F03}", _ 
     "{12E37949-704F-4A92-A7D2-5B7B94554505}", _ 
     "{E9175D7C-BC4E-4AC1-A79D-6B314EAB5D45}", _ 
     "{0A1E0D5C-44CC-4199-9C4E-FE23A1136B60}"} 

    Function Main(args As String()) As Integer 
     If args.Count <> 1 Then 
     Console.Error.WriteLine(String.Format("{0} <MSI File>", System.Reflection.Assembly.GetExecutingAssembly().GetName().Name)) 
     Console.ReadLine() 
     Return 1 
     End If 
     Main = UpdateTransforms(args(0)) 
    End Function 

    Public Sub CheckResult(result As Integer, Optional ByVal failureName As String = "MSI call") 
     If result <> ERROR_SUCCESS Then 
     Throw New ApplicationException(String.Format("{0} failed with code {1}", failureName, result)) 
     End If 
    End Sub 

    Private Function UpdateTransforms(msiFile As String) 
     Dim hDB As IntPtr = IntPtr.Zero 
     Dim hDBOriginal As IntPtr = IntPtr.Zero 
     Dim hView As IntPtr = IntPtr.Zero 
     Dim mainResult As Integer = 0 
     Dim hRec As IntPtr = IntPtr.Zero 
     Dim instanceNames As IEnumerable(Of String) = Nothing 

     Try 
     instanceNames = GetEmbeddedTransforms(msiFile) 
     Console.WriteLine("Found {0} instances embedded in {1}", instanceNames.Count, msiFile) 
     If instanceNames.Count > RegComponentInstGuids.Length Then 
      Console.Error.WriteLine("More instances were found than pre-defined GUIDs") 
      Console.ReadLine() 
      Return 4 
     End If 

     ' For each embedded instance, modify the MST for that instance to also modify component GUIDs 
     Dim componentIndex As Integer = 0 
     For Each instanceName In instanceNames 
      Dim msiCopy As String = System.IO.Path.Combine(System.IO.Path.GetTempPath(), "InstanceIdTmp.msi") 
      System.IO.File.Copy(msiFile, msiCopy) 
      CheckResult(MsiOpenDatabase(msiCopy, MSIDBOPEN_TRANSACT, hDB), "MsiOpenDatabase") 
      CheckResult(MsiDatabaseApplyTransform(hDB, String.Format(":{0}", instanceName), 0), "MsiDatabaseApplyTransform") 
      CheckResult(MsiDatabaseOpenView(hDB, String.Format("UPDATE `Component` SET `Component`.`ComponentId`='{0}' WHERE `Component`.`ComponentId`='{1}'", RegComponentInstGuids(componentIndex), RegComponentGuid), hView), "MsiDatabaseOpenView") 
      CheckResult(MsiViewExecute(hView, IntPtr.Zero), "MsiViewExecute") 
      CheckResult(MsiViewClose(hView), "MsiViewClose") 
      CheckResult(MsiDatabaseCommit(hDB), "MsiDatabaseCommit") 
      CheckResult(MsiOpenDatabase(msiFile, MSIDBOPEN_TRANSACT, hDBOriginal), "MsiOpenDatabase") 
      Dim mstFile As String = System.IO.Path.Combine(System.IO.Path.GetTempPath(), instanceName) 
      CheckResult(MsiDatabaseGenerateTransform(hDB, hDBOriginal, mstFile, 0, 0), "MsiDatabaseGenerateTransform") 
      CheckResult(MsiCreateTransformSummaryInfo(hDB, hDBOriginal, mstFile, 0, MSITRANSFORM_VALIDATE.UpgradeCode), "MsiCreateTransformSummaryInfo") 
      CheckResult(MsiCloseHandle(hView), "MsiCloseHandle") 
      hView = IntPtr.Zero 
      CheckResult(MsiDatabaseOpenView(hDBOriginal, String.Format("SELECT Data FROM `_Storages` WHERE Name='{0}'", instanceName), hView), "MsiDatabaseOpenView") 
      Console.WriteLine("Updating component {0} in instance {1} to use {2}", RegComponentGuid, componentIndex, RegComponentInstGuids(componentIndex)) 
      CheckResult(MsiViewExecute(hView, IntPtr.Zero), "MsiViewExecute") 
      CheckResult(MsiViewFetch(hView, hRec), "MsiViewFetch") 
      CheckResult(MsiRecordSetStream(hRec, 1, mstFile), "MsiRecordSetStream") 
      CheckResult(MsiViewModify(hView, 2, hRec), "MsiViewModify") 
      CheckResult(MsiCloseHandle(hRec), "MsiCloseHandle") 
      hRec = IntPtr.Zero 
      CheckResult(MsiViewClose(hView), "MsiViewClose") 
      CheckResult(MsiDatabaseCommit(hDBOriginal), "MsiDatabaseCommit") 
      If System.IO.File.Exists(mstFile) Then System.IO.File.Delete(mstFile) 
      CheckResult(MsiCloseHandle(hView), "MsiCloseHandle") 
      hView = IntPtr.Zero 
      CheckResult(MsiCloseHandle(hDBOriginal), "MsiCloseHandle") 
      hDBOriginal = IntPtr.Zero 
      CheckResult(MsiCloseHandle(hDB), "MsiCloseHandle") 
      hDB = IntPtr.Zero 
      System.IO.File.Delete(msiCopy) 
      componentIndex += 1 
     Next 
     Catch ex As System.Exception 
     Console.Error.WriteLine(ex.ToString()) 
     Console.ReadLine() 
     mainResult = 2 
     Finally 
     Try 
      If hRec <> IntPtr.Zero Then CheckResult(MsiCloseHandle(hRec), "MsiCloseHandle") 
      If hDBOriginal <> IntPtr.Zero Then CheckResult(MsiCloseHandle(hDBOriginal), "MsiCloseHandle") 
      If hView <> IntPtr.Zero Then CheckResult(MsiCloseHandle(hView), "MsiCloseHandle") 
     Catch ex As Exception 
      Console.Error.WriteLine(ex.ToString()) 
      Console.ReadLine() 
      mainResult = 3 
     Finally 
      If hDB <> IntPtr.Zero Then 
       CheckResult(MsiCloseHandle(hDB), "MsiCloseHandle") 
      End If 
      Dim msiCopy As String = System.IO.Path.Combine(System.IO.Path.GetTempPath(), "InstanceIdTmp.msi") 
      If System.IO.File.Exists(msiCopy) Then System.IO.File.Delete(msiCopy) 
      If instanceNames IsNot Nothing Then 
       For Each instanceName In instanceNames 
        If System.IO.File.Exists(instanceName) Then System.IO.File.Delete(instanceName) 
       Next 
      End If 
     End Try 
     End Try 
     Return mainResult 
    End Function 

    Private Function GetEmbeddedTransforms(msiFile As String) As IEnumerable(Of String) 
     Dim hDB As IntPtr = IntPtr.Zero 
     Dim hView As IntPtr = IntPtr.Zero 
     Dim hRec As IntPtr = IntPtr.Zero 
     Dim instanceNames As New LinkedList(Of String) 
     Try 
     CheckResult(MsiOpenDatabase(msiFile, MSIDBOPEN_READONLY, hDB), "MsiOpenDatabase") 
     CheckResult(MsiDatabaseOpenView(hDB, "SELECT Name FROM `_Storages`", hView), "MsiDatabaseOpenView") 
     CheckResult(MsiViewExecute(hView, IntPtr.Zero), "MsiViewExecute") 
     Do 
      Dim fetchResult = MsiViewFetch(hView, hRec) 
      If fetchResult = ERROR_NO_MORE_ITEMS Then Exit Do 
      CheckResult(fetchResult) 
      Dim instMstName As New System.Text.StringBuilder(256) 
      CheckResult(MsiRecordGetString(hRec, 1, instMstName, instMstName.Capacity - 1), "MsiRecordGetString") 
      If instMstName.ToString() Like "InstanceId#*.mst" Then 
       instanceNames.AddLast(instMstName.ToString()) 
      End If 
      CheckResult(MsiCloseHandle(hRec), "MsiCloseHandle") 
      hRec = IntPtr.Zero 
     Loop 
     CheckResult(MsiViewClose(hView), "MsiViewClose") 
     Catch ex As System.Exception 
     Console.Error.WriteLine(ex.ToString()) 
     Console.ReadLine() 
     Finally 
     If hRec <> IntPtr.Zero Then CheckResult(MsiCloseHandle(hRec), "MsiCloseHandle") 
     If hView <> IntPtr.Zero Then CheckResult(MsiCloseHandle(hView), "MsiCloseHandle") 
     If hDB <> IntPtr.Zero Then CheckResult(MsiCloseHandle(hDB), "MsiCloseHandle") 
     End Try 
     Return instanceNames 
    End Function 
End Module 
相關問題