すべての人に良い一日を! 前回の記事では、パッチを生成するタスクを設定し、Wix上でパッチを作成するためのテクノロジーの概要を開始しました( PatchWizを使用)。 同じ場所で、問題を完全に解決するには「何か他のもの」が必要であるという結論に達しました。 パート2へようこそ。ここでは、すべてのソースコードを使用して組織的および技術的なアプローチを説明します。
そもそも、私は私たちのアプローチ、つまり、ドグマではなく私の経験と結果を説明していると言います。
そのため、前回指摘したすべての欠点を回避するために、インストールを作成するためのルールとパッチを作成するためのユーティリティを開発する必要がありました。
問題に対する組織的な解決策
これを行うことにしました。インストールはマージモジュールから組み立てられます 。
特定の基本アセンブリ(ベースライン)が選択されます。これには、全員に必須のバイナリを含むモジュールが含まれます。 彼女は製品の新しいバージョンごとに行っています。 さらに、 ビルド番号のみが変更された場合は、ベースバージョンがビルド番号 = 0の以前のものである差分パッチが作成されます(参照アセンブリと呼びましょう)。
時間軸上の円は、作成されたアセンブリを示します。 サポートアセンブリは青でマークされ、緑は以前のサポートアセンブリに基づいて異なるパッチを作成するために使用されます。 また、さらに、以前のサポートアセンブリに基づいて、サポートアセンブリ用の差分パッチも作成されます。
したがって、クライアントが私たちに最新バージョンを要求するとき、私たちは彼が現在インストールされたバージョンを持っているかどうか、そしてその番号は何であるかをチェックします。 さらに-完全なアセンブリまたはパッチが送信されます。
同様のスキームが自動更新で機能します。msiまたはmspパッケージは、標準のmsiexecツールを使用してインストールされるサーバーから取得されます。
このメカニズムの実装を理解するには、次の解決策を検討してください。
ソリューションの概要
まず、ソリューションの一般的な構造を考慮します(もちろん、フォルダーはわかりやすいように名前が付けられています)。 その完全なソースコードはGitHubにアップロードされ 、リンクは最後にあります。
フォルダーには以下が含まれます。
1.アプリケーション自体。
2. Wixの拡張機能を含む、インストールを収集するための一般的なファイル。
3.プロジェクトアプリケーションのインストール。
4.パッチを作成するためのユーティリティ。
5.テスト用のボディーシャツ。
フォルダ番号は、必要なアクションの順序を反映しています。 始めましょう。
手順に従ってください
ステップ1.アプリケーションを作成する
私たちのケースでは、バージョンを変更する単純なコンソールアプリケーション「Hello、World!」になります。
ステップ2.インストール用の一般的なパーツを作成する
ファイルを調べます。
Deploy.Variables.wxiは、すべての企業インストールに共通の変数です。
<Include> <?define Manufacturer=""?> <?define ManufacturerUrl="http://company.ru"?> <?define Language="1049"?> <?define Codepage="1251"?> </Include>
ここではすべてが明確であるように思えます。すべてのインストールプロジェクトで使用される変数が示されています。
Deploy.Yasen.Variables.wxi-すべての製品インストール用の共通Wixファイル:
<Include> <?define ProductName="" ?> <?define YasenProductCode="{06CABA42-492E-49CE-9849-F85E87442E99}"?> <?define YasenUpgradeCode="{BA8CCE3C-4267-4291-B330-16EE510F023B}"?> </Include>
製品コードとアップデートを個別に報告するのはなぜですか? そのため、多くの場所(異なるインストールプロジェクトとパッチ記述子)でそれらが必要になるためです。
Deploy.Yasen.ProductContent.wxiは、さまざまなインストールプロパティを持つ共有ファイルです。
<Include> <Package Id="$(var.PackageId)" InstallerVersion="200" Compressed="yes" Languages="$(var.Language)" SummaryCodepage="$(var.Codepage)" Comments=" $(var.ProductName)" Keywords=", , , , , " Description="$(var.Subject)" InstallScope="perMachine" /> <!-- --> <Property Id="MSIUSEREALADMINDETECTION" Value="1" /> <MajorUpgrade DowngradeErrorMessage =" " AllowDowngrades="no" /> <Upgrade Id='$(var.UpgradeCode)' > <UpgradeVersion OnlyDetect="no" Maximum="$(var.ProductVersion)" IncludeMaximum="no" Property="OLDERVERSIONBEINGUPGRADED" MigrateFeatures="yes" /> </Upgrade> <!-- Media --> <Media Id="1" Cabinet="media1.cab" EmbedCab="yes" /> </Include>
このファイルでは、最終的に以前のファイルの変数の使用を開始します(これらは、メインのインストールファイルを作成するときにここで利用可能になります)。 必須のMedia要素が指定され、いくつかの追加要素(主に更新に関連する)が指定されています。
- MSIUSEREALADMINDETECTION- = 1の場合、実際の管理者の権利が要求されます(UACを使用)。
- MajorUpgrade要素-私たちの場合、 メジャーアップグレードの可能性を示します -新しいバージョンへの更新は許可され、古いバージョンへのロールバックは禁止されています。
- 要素のアップグレード -可能な更新の説明が含まれています。
- UpgradeVersion要素は、以前のバージョンの検出条件( UpgradeCodeによる )と、それらが検出された場合のアクションを記述します。 可能なオプション(すべてを提供するわけではありません):
最小 -このインストールで更新できる最小バージョン。
最大 -このインストールで更新できる最大バージョン。
IncludeMaximum-最大バージョンの「包括的」(デフォルト-はい)。
IncludeMinimum- 「包括的」な最小バージョン。
プロパティ -変数の名前が含まれます。 指定されたバージョンで対応する製品が見つかった場合、それらのコードは「;」を介してこの変数に書き込まれます。 この変数の値は、たとえばCustomActionを実行するときに、後で参加できます。
OnlyDetect- = ' yes 'の場合、指定されたバージョンのみが検出され、指定されたプロパティに書き込まれますが、それらはアンインストールされません。
IgnoreRemoveFailure-検出されたバージョンをアンインストールするときにエラーを無視するかどうかを指示します。
この場合、 UpgradeCodeは 、 UpgradeCode内で以前のすべてのバージョンをアップグレードできるように構成されています。
さらに進んでください: Deploy.Yasen.PatchCreation.xml-この製品のパッチ記述子。
<Include xmlns="http://schemas.microsoft.com/wix/2006/wi"> <?include Deploy.Variables.wxi?> <?include Deploy.Yasen.Variables.wxi?> <?define PatchDescription=" $(var.ProductName)"?> <PatchCreation Id="$(var.PatchId)" Codepage="$(var.Codepage)" CleanWorkingFolder="yes" OutputPath="patch.pcp" WholeFilesOnly="yes" > <PatchInformation Description="$(var.PatchDescription)" Comments="$(var.PatchDescription)" Manufacturer="$(var.Manufacturer)"/> <PatchMetadata AllowRemoval="yes" Description="$(var.PatchDescription)" ManufacturerName="$(var.Manufacturer)" TargetProductName="$(var.ProductName)" MoreInfoURL="$(var.ManufacturerUrl)" Classification="Update" DisplayName="$(var.PatchDescription) $(var.PatchVersion)"/> <Family DiskId="2" Name="$(var.Family)" SequenceStart="5000" > <UpgradeImage SourceFile="$(var.NewMsi)" Id="NewPackage" > <TargetImage SourceFile="$(var.BaseMsi)" Order="2" Id="BasePackage" IgnoreMissingFiles="no" Validation = "0x00000912" /> </UpgradeImage> </Family> <PatchSequence PatchFamily="$(var.PatchFamily)" Sequence="$(var.PatchVersion)" Supersede="yes" ProductCode="$(var.ProductCode)"/> </PatchCreation> </Include>
第1部でPatchCreation構造を十分に詳しく調べましたが、ここでは少なくとも2つの非常に重要な違いを確認できます。
- TargetImage要素の検証属性。 パッチのスキップを許可するのはこの属性です。 ここで詳細に説明されます 。 そのため、複数のバージョンを更新できるようにするには、通常、それらすべてを( TargetImage要素を使用して)指定する必要があり、これにより、パッチのサイズが大きくなります。 要件と組織的な対策(バージョンの原則)を考慮して、以前のサポートアセンブリと値912の 検証フラグのみを示すことができました。 この値は、インストールされた製品にパッチを適用するために、条件を満たす必要があることを示します: UpgradeCode 、 ProductCodeおよびパッチのmagorおよびマイナーバージョン(更新先)およびインストールされたバージョンは一致する必要があります(ただし、 ビルド番号は一致しません)。 したがって、 マイナーバージョン内の更新のスキップは許可されます。 五つください! 検証はかなり広い範囲の値をとることができ、非常に興味深い効果を得ることができます。 お楽しみください!
- PatchCreationファイルは、たとえばvar.PatchFamily 、 var.PatchVersionなど、以前に発表されていない変数も使用します。 それらの使用は、ステップ4で明らかになります。
また、ステップ2には、特定のIncom.WixExtensionsプロジェクト(フォルダー内)があります。 これはWixの拡張子を持つプロジェクトです。 ここに論理的に存在する必要があり、その使用はステップ3で明らかになります。
ステップ3.一部のお客様のインストールを収集します
最初に、前に宣言した変数にWixファイルを接続します。
<?define WixCommonPath="$(var.ProjectDir)..\"?> <?include $(var.WixCommonPath)\Deploy.Variables.wxi?> <?include $(var.WixCommonPath)\Deploy.Yasen.Variables.wxi?>
その後、ソフトウェアのバージョンと現在の製品コードおよび更新ラインを発表します。
<?define Subject=" "?> <?define ProductVersion="$(incom.FileVersion($(var.Yasen.UI.TargetPath)))"?> <?define UpgradeCode="$(var.YasenUpgradeCode)"?> <?define ProductCode="$(incom.ChangeGuid($(var.YasenProductCode),$(var.ProductVersion), 2))"?>
UpgradeCodeは 「 現状のまま 」使用され、何らかの変換が製品コードに適用されることに注意してください。
この変換を使用して、次の目標を目指して努力します: ビルド番号を変更する場合、製品コードは変更してパッチを作成できないようにし、 メジャーバージョンまたはマイナーバージョンを変更する場合は製品コードを変更する必要があります(これは前半で説明しました)。 したがって、以下を実行します。グローバル変数から製品コードを使用し、変換関数を呼び出して、最終バージョンが依存するバージョンの部分を示します。 さらに、1- メジャーのみから、2- メジャーとマイナーから、そして値4との類推によって。
上記のWix拡張機能の時間です。
この記事の目的はWixの拡張機能を作成するための技術を説明することではないため( ここで確認できます )、簡単な要約を示します: EvaluateFunctionメソッドが再定義されたプリプロセッサーの拡張機能が作成されます。 incomプレフィックスを持つ関数を使用する場合、 Wixによって呼び出されます。
このメソッドでは、2つの機能を実行します。
•ファイルバージョンの取得
•ユニバーサルガイド変更機能
関数計算のためのWix拡張コードスニペット
/// <summary> /// /// </summary> /// <param name="prefix"></param> /// <param name="function"> </param> /// <param name="args"></param> /// <returns> </returns> public override string EvaluateFunction(string prefix, string function, string[] args) { if (prefix == "incom") { switch (function.ToLower()) { case "fileversion": var ver = FileVersionInfo.GetVersionInfo(Path.GetFullPath(args[0])).FileVersion; Console.WriteLine(string.Format("Version of {0}: {1}", args[0], ver)); return ver; case "changeguid": var guid = Guid.Parse(args[0]).ToByteArray(); version = Version.Parse(args[1]); var major = BitConverter.GetBytes((Int16)((version.Major & 0xFF) ^ ((version.Major >> 16) & 0xFF))); var minor = BitConverter.GetBytes((Int16)((version.Minor & 0xFF) ^ ((version.Minor >> 16) & 0xFF))); var build = BitConverter.GetBytes((Int16)((version.Build & 0xFF) ^ ((version.Build >> 16) & 0xFF))); var revision = BitConverter.GetBytes((Int16)((version.Revision & 0xFF) ^ ((version.Revision >> 16) & 0xFF))); var len = 4; if (args.Length > 2) len = int.Parse(args[2]); if (len > 0) { guid[0] = major[0]; guid[1] = major[1]; } if (len > 1) { guid[2] = minor[0]; guid[3] = minor[1]; } if (len > 2) { guid[4] = build[0]; guid[5] = build[1]; } if (len > 3) { guid[6] = revision[0]; guid[7] = revision[1]; } return new Guid(guid).ToString(); } } return base.EvaluateFunction(prefix, function, args); }
したがって、コードは予測どおりに動作し、製品のバージョンに依存します。 コードを少し変更するとき- ビルド番号を変更する(2〜3週間に1回)、もっと深刻なこと- マイナー番号を変更する(2〜3か月に約1回)ことで参照アセンブリを作成します。 すべてが一致すると、 メジャー番号が変わります(約3〜4年に1回)。
wixファイルに戻ります。 さらに、すべてが標準です。上記で宣言した変数を使用して、 Productブロックを作成し、ファイル、コンポーネント、機能を指定し、前の手順で説明したDeploy.Yasen.ProductContentを使用します。
<Product Id="$(var.ProductCode)" Name="$(var.ProductName)" Language="$(var.Language)" Version="$(var.ProductVersion)" Manufacturer="$(var.Manufacturer)" UpgradeCode="$(var.UpgradeCode)" > <?include $(var.WixCommonPath)\Deploy.Yasen.ProductContent.wxi?> <Directory Id="TARGETDIR" Name="SourceDir"> <Directory Id="ProgramFilesFolder"> <Directory Id="APPLICATIONFOLDER" Name="$(var.ProductName)" DiskId="1" > <!-- Content --> <Component Id="Component1" Guid="{1F4A6EF3-4B65-4405-8E08-D750E5038C75}"> <File Id="File1" Name="content.txt" Source="..\..\Incom.Yasen.Content\content.txt"/> <File Id="File2" Name="Yasen.UI.exe" Source="$(var.Yasen.UI.TargetPath)"/> </Component> </Directory> </Directory> </Directory> <Feature Id="ClientSide" Title=" $(var.ProductName)" Level="1" Absent="disallow"> <ComponentRef Id="Component1"/> </Feature> </Product>
ステップ4.パッチを作成する
これで、製品のインストールを作成するために必要なすべてが揃いました。これも実行できますが、目標はパッチです。
パート1で述べたように、コマンドラインからパッチを作成するのはかなり面倒なので、このステップではこれらのステップを実行する新しいユーティリティMakeMspを作成します。 使用要件は次のとおりです。引数は、ベースアセンブリ、最終アセンブリ、パッチ記述子、および結果へのパスを示します。
Incom.MakeMsp.exe "YasenSetup1.msi" "YasenSetup1.0.1.msi" "Deploy.Yasen.PatchCreation.xml" "Patch.msp"
一般的に、アルゴリズムは次のとおりです。
1.両方のmsiを一時フォルダーにコピーします
コードピースのコピー
Task.WaitAll( Task.Run( () => { Console.WriteLine("Start copying RTM..."); File.Copy(args[0], rtmFilePath, true); Console.WriteLine("Finished copying RTM..."); }) , Task.Run( () => { Console.WriteLine("Start copying latest..."); File.Copy(args[1], latestFilePath, true); Console.WriteLine("Finished copying latest..."); }));
2.手順2でPatchCreationを作成するときに 、未知の変数を使用したことを思い出してください。 それらを識別する時間です。 このため、ユーティリティはこれらの変数の値が書き込まれるWix構造を持つ一時ファイルを作成します。
パッチを作成するための変数を含むファイルを作成するコード
var productName = MsiReader.GetMSIParameters(latestFilePath, "ProductName"); var wixPachCreationReference = string.Format( @"<?xml version=""1.0"" encoding=""utf-8""?> <Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'> <?define Family='{0}'?> <?define PatchFamily='{0}'?> <?define PatchId='{1}'?> <?define ProductCode='{2}'?> <?define PatchVersion='{3}'?> <?define BaseMsi='{4}'?> <?define NewMsi='{5}'?> <?include {6}?> </Wix>", new string(Transliterate(productName).Where(char.IsLetterOrDigit).Take(8).ToArray()), Guid.NewGuid().ToString(), MsiReader.GetMSIParameters(latestFilePath, "ProductCode"), MsiReader.GetMSIParameters(latestFilePath, "ProductVersion"), Path.Combine(rtmPath, "rtm.msi"), Path.Combine(latesPath, "last.msi"), Path.GetFullPath(args[2]) );
音訳付きの製品の名前はPatchFamilyとして使用されます 。 PatchIdは新しいGUIDです。 ProductCode 、 PatchVersion-最終的なmsi 、 BaseMsiおよびNewMsiから抽出-一時msiへのパス(一時フォルダーにコピー)。 最後に、ファイル自体にPatchCreationを含めます。
3.次に、パート1で説明したコンパイル手順が実行されます。
管理インストール:
exec("msiexec", string.Format("/a \"{0}\" /qn TARGETDIR=\"{1}\\\"", rtmFilePath, rtmPath)); exec("msiexec", string.Format("/a \"{0}\" /qn TARGETDIR=\"{1}\\\"", latestFilePath, latesPath));
パッチのコンパイルと作成:
exec("candle", string.Format("\"{0}\" -out \"{1}\\patch.wixobj\"", Path.Combine(tempDir, "desc.xml"), tempDir)); exec("light", string.Format("\"{0}\\patch.wixobj\" -out \"{0}\\patch.pcp\"", tempDir)); exec("msimsp", string.Format("-s \"{0}\\patch.pcp\" -p \"{1}\" -l \"{0}\\msimsp.log\"", tempDir, args[3]));
ステップ5.すべてのコンパイル
インストールプロジェクト、 Wixの拡張機能、パッチ記述子、パッチを作成するためのユーティリティなど、必要なものはすべて揃っています。 それをすべてまとめるときです。
これを行うために、ソリューションのルートにCompileAll.batファイルがあり、それをすべてまとめて( フレームワーク4.0が必要です)、結果をReleasesフォルダーに入れます。 これはすべてソースで確認できます。
結果を使用する
CompileAll.batを実行した後の結果。
更新パッチ
DblClickでバージョン1をインストールできます。 結果:
DblClickを使用してパッチをインストールすることもできます。
インストールされた更新を表示すると、これが表示されます。
フルパックアップデート
完全なmsiパッケージを使用してバージョンを1.0.0から1.0.1に更新する必要がある場合は、次のパラメーターでコンソールを使用する必要があります。
msiexec /i YasenSetup1.0.1.msi REINSTALL=ALL REINSTALLMODE=vomus
ここに:
REINSTALL-更新中にこのパッケージによって再インストールされる機能を示します(すべてを示します)。 このプロパティを指定しない場合、パッケージ(およびインストールされている古いバージョン)を実行しようとすると、「別のバージョンが既にインストールされています」というメッセージが表示されます。 ( 詳細 )
REINSTALLMODE-ファイルの再インストール(更新)が正確に行われる方法を示すプロパティ。 ( 詳細 )。 私たちの場合、それは次のとおりです。
v-ローカルストレージにパッケージをキャッシュする必要があります。 実際、各製品( ProductCode )について、 Windowsは製品が配信されたPackage.Idの値を記憶しています。 製品が既にインストールされており、コンテンツとバージョンが同一であるが、 Package.Idが異なるパッケージ(ソリューションを再構築するだけ)をインストールしようとすると、 Package.Idのキャッシュされた値は、新しいインストールのPackage.Idと一致せず、インストールされたという警告が発行されます別のバージョン。 vを使用すると 、 Package.Idのコンプライアンスがチェックされません。
o-現在のバージョンが新しいバージョンよりも小さいか、ファイルがない場合は、ファイルを書き換えます。
m-レジストリキーを上書きします( HKEY_LOCAL_MACHINEおよびHKEY_CLASSES_ROOT )
u-レジストリキーを上書きします( HKEY_CURRENT_USERおよびHKEY_USERS )
s-すべてのショートカットを書き換え、アイコンキャッシュを書き換えます。
この不便な更新方法を回避し、 DblClickのみを機能させるオプションがありますが、それはまったく別の話です。
まとめると
そのため、問題を解決するために、次の手順が実行されました。
1)エンドユーザー向けのWixインストールを作成するための特別な要件が開発されました。
a。 共有ファイルで可能なすべてを取り出し、 モジュールをマージし 、 includeディレクティブを使用します 。
b。 基本値に基づいてProduct.ProductCodeを生成します。
2)パッチを生成するための単一のプロジェクトを作成します。
3)パッチの作成に役立つ特別なユーティリティを作成します。
最終結果
Wixは大きな頭痛の種の解決に役立ちます。
もちろん、いくつかのことが影の中に残っていますが、最も重要で価値のあるものが読者に提示されます。
ありがとう
参照:
GitHubのすべてのソース