RobMen: WiX v3.7
RobMen: Convert build process to 100% MSBuild. RobMen: Add sample how to call Burn with embedded progress. RobMen: Add Bundle self-update support to Burn. WixBuild: Version 3.7.821.0 --HG-- branch : wix37
This commit is contained in:
Родитель
39b1216d43
Коммит
604635b367
|
@ -1,3 +1,2 @@
|
|||
syntax: regexp
|
||||
^.*\.snt
|
||||
^external
|
||||
^.*\.feed
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
WixBuild: Version 3.7.0.0
|
||||
|
||||
RobMen: WiX v3.7
|
||||
|
||||
RobMen: Convert build process to 100% MSBuild.
|
||||
|
||||
RobMen: Add sample how to call Burn with embedded progress.
|
||||
|
||||
RobMen: Add Bundle self-update support to Burn.
|
||||
|
||||
WixBuild: Version 3.7.0821.0
|
||||
|
|
@ -0,0 +1,699 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 11.00
|
||||
# Visual Studio 2010
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Libraries", "Libraries", "{B217BE43-6280-4320-8477-BA0CEEFC38EE}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Samples", "Samples", "{AED0B145-6A3A-40DF-89AC-E5728B9B3DAA}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{43C7C815-5DA4-419D-882E-34263D869897}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
DTF.testrunconfig = DTF.testrunconfig
|
||||
DTF.vsmdi = DTF.vsmdi
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tools", "Tools", "{9A8288B8-A634-4BD3-9CC7-0F7C1314156B}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Compression", "DTF\Libraries\Compression\Compression.csproj", "{2D62850C-9F81-4BE9-BDF3-9379389C8F7B}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Compression.Zip", "DTF\Libraries\Compression.Zip\Compression.Zip.csproj", "{261F2857-B521-42A4-A3E0-B5165F225E50}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Compression.Cab", "DTF\Libraries\Compression.Cab\Compression.Cab.csproj", "{15895FD1-DD68-407B-8717-08F6DD14F02C}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WindowsInstaller", "DTF\Libraries\WindowsInstaller\WindowsInstaller.csproj", "{24121677-0ED0-41B5-833F-1B9A18E87BF4}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WindowsInstaller.Linq", "DTF\Libraries\WindowsInstaller.Linq\WindowsInstaller.Linq.csproj", "{CD7A37D8-9D8C-41BD-B78F-DB5E0C299D2E}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WindowsInstaller.Package", "DTF\Libraries\WindowsInstaller.Package\WindowsInstaller.Package.csproj", "{1A9940A7-3E29-4428-B753-C4CC66058F1A}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "XPack", "DTF\Samples\XPack\XPack.csproj", "{03E55D95-DABE-4571-9CDA-92A44F92A465}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SfxCA", "DTF\Tools\SfxCA\SfxCA.vcxproj", "{55D5BA28-D427-4F53-80C2-FE9EF23C1553}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MakeSfxCA", "DTF\Tools\MakeSfxCA\MakeSfxCA.csproj", "{3F246CE0-153D-4AC3-B6AC-5EAD8E2AD04B}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{03E55D95-DABE-4571-9CDA-92A44F92A465} = {03E55D95-DABE-4571-9CDA-92A44F92A465}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ManagedCA", "DTF\Samples\ManagedCA\ManagedCA.csproj", "{DB9E5F02-8241-440A-9B60-980EB5B42B13}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{55D5BA28-D427-4F53-80C2-FE9EF23C1553} = {55D5BA28-D427-4F53-80C2-FE9EF23C1553}
|
||||
{3F246CE0-153D-4AC3-B6AC-5EAD8E2AD04B} = {3F246CE0-153D-4AC3-B6AC-5EAD8E2AD04B}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Resources", "DTF\Libraries\Resources\Resources.csproj", "{44931ECB-8D6F-4C12-A872-64E261B6A98E}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Inventory", "DTF\Samples\Inventory\Inventory.csproj", "{51480F8E-B80F-42DC-91E7-3542C1F12F8C}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WiFile", "DTF\Samples\WiFile\WiFile.csproj", "{AE562F7F-EE33-41D6-A962-DA488FEFBD08}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DDiff", "DTF\Samples\DDiff\DDiff.csproj", "{1CDF4242-4C00-4744-BBCD-085128978FF3}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EmbeddedUI", "DTF\Samples\EmbeddedUI\EmbeddedUI.csproj", "{864B8C50-7895-4485-AC89-900D86FD8C0D}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{55D5BA28-D427-4F53-80C2-FE9EF23C1553} = {55D5BA28-D427-4F53-80C2-FE9EF23C1553}
|
||||
{3F246CE0-153D-4AC3-B6AC-5EAD8E2AD04B} = {3F246CE0-153D-4AC3-B6AC-5EAD8E2AD04B}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{8477AF4C-B4CA-4434-A6C4-9CC57C74AF21}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CompressionTest", "DTF\Tests\Compression\CompressionTest.csproj", "{F045FFC1-05F9-4EA2-9F03-E1CBDB7BC4F9}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CabTest", "DTF\Tests\Compression.Cab\CabTest.csproj", "{4544158C-2D63-4146-85FF-62169280144E}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ZipTest", "DTF\Tests\Compression.Zip\ZipTest.csproj", "{328799BB-7B03-4B28-8180-4132211FD07D}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WindowsInstallerTest", "DTF\Tests\WindowsInstaller\WindowsInstallerTest.csproj", "{16F5202F-9276-4166-975C-C9654BAF8012}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CustomActionTest", "DTF\Tests\WindowsInstaller.CustomActions\CustomActionTest.csproj", "{137D376B-989F-4FEA-9A67-01D8D38CA0DE}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LinqTest", "DTF\Tests\WindowsInstaller.Linq\LinqTest.csproj", "{4F55F9B8-D8B6-41EB-8796-221B4CD98324}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(TestCaseManagementSettings) = postSolution
|
||||
CategoryFile = DTF.vsmdi
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Debug|arm = Debug|arm
|
||||
Debug|ia64 = Debug|ia64
|
||||
Debug|Mixed Platforms = Debug|Mixed Platforms
|
||||
Debug|Win32 = Debug|Win32
|
||||
Debug|x64 = Debug|x64
|
||||
Debug|x86 = Debug|x86
|
||||
Release|Any CPU = Release|Any CPU
|
||||
Release|arm = Release|arm
|
||||
Release|ia64 = Release|ia64
|
||||
Release|Mixed Platforms = Release|Mixed Platforms
|
||||
Release|Win32 = Release|Win32
|
||||
Release|x64 = Release|x64
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{2D62850C-9F81-4BE9-BDF3-9379389C8F7B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{2D62850C-9F81-4BE9-BDF3-9379389C8F7B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{2D62850C-9F81-4BE9-BDF3-9379389C8F7B}.Debug|arm.ActiveCfg = Debug|arm
|
||||
{2D62850C-9F81-4BE9-BDF3-9379389C8F7B}.Debug|arm.Build.0 = Debug|arm
|
||||
{2D62850C-9F81-4BE9-BDF3-9379389C8F7B}.Debug|ia64.ActiveCfg = Debug|ia64
|
||||
{2D62850C-9F81-4BE9-BDF3-9379389C8F7B}.Debug|ia64.Build.0 = Debug|ia64
|
||||
{2D62850C-9F81-4BE9-BDF3-9379389C8F7B}.Debug|Mixed Platforms.ActiveCfg = Debug|arm
|
||||
{2D62850C-9F81-4BE9-BDF3-9379389C8F7B}.Debug|Mixed Platforms.Build.0 = Debug|arm
|
||||
{2D62850C-9F81-4BE9-BDF3-9379389C8F7B}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{2D62850C-9F81-4BE9-BDF3-9379389C8F7B}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{2D62850C-9F81-4BE9-BDF3-9379389C8F7B}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{2D62850C-9F81-4BE9-BDF3-9379389C8F7B}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{2D62850C-9F81-4BE9-BDF3-9379389C8F7B}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{2D62850C-9F81-4BE9-BDF3-9379389C8F7B}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{2D62850C-9F81-4BE9-BDF3-9379389C8F7B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{2D62850C-9F81-4BE9-BDF3-9379389C8F7B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{2D62850C-9F81-4BE9-BDF3-9379389C8F7B}.Release|arm.ActiveCfg = Release|arm
|
||||
{2D62850C-9F81-4BE9-BDF3-9379389C8F7B}.Release|arm.Build.0 = Release|arm
|
||||
{2D62850C-9F81-4BE9-BDF3-9379389C8F7B}.Release|ia64.ActiveCfg = Release|ia64
|
||||
{2D62850C-9F81-4BE9-BDF3-9379389C8F7B}.Release|ia64.Build.0 = Release|ia64
|
||||
{2D62850C-9F81-4BE9-BDF3-9379389C8F7B}.Release|Mixed Platforms.ActiveCfg = Release|arm
|
||||
{2D62850C-9F81-4BE9-BDF3-9379389C8F7B}.Release|Mixed Platforms.Build.0 = Release|arm
|
||||
{2D62850C-9F81-4BE9-BDF3-9379389C8F7B}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{2D62850C-9F81-4BE9-BDF3-9379389C8F7B}.Release|Win32.Build.0 = Release|Win32
|
||||
{2D62850C-9F81-4BE9-BDF3-9379389C8F7B}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{2D62850C-9F81-4BE9-BDF3-9379389C8F7B}.Release|x64.Build.0 = Release|Any CPU
|
||||
{2D62850C-9F81-4BE9-BDF3-9379389C8F7B}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{2D62850C-9F81-4BE9-BDF3-9379389C8F7B}.Release|x86.Build.0 = Release|Any CPU
|
||||
{261F2857-B521-42A4-A3E0-B5165F225E50}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{261F2857-B521-42A4-A3E0-B5165F225E50}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{261F2857-B521-42A4-A3E0-B5165F225E50}.Debug|arm.ActiveCfg = Debug|arm
|
||||
{261F2857-B521-42A4-A3E0-B5165F225E50}.Debug|arm.Build.0 = Debug|arm
|
||||
{261F2857-B521-42A4-A3E0-B5165F225E50}.Debug|ia64.ActiveCfg = Debug|ia64
|
||||
{261F2857-B521-42A4-A3E0-B5165F225E50}.Debug|ia64.Build.0 = Debug|ia64
|
||||
{261F2857-B521-42A4-A3E0-B5165F225E50}.Debug|Mixed Platforms.ActiveCfg = Debug|arm
|
||||
{261F2857-B521-42A4-A3E0-B5165F225E50}.Debug|Mixed Platforms.Build.0 = Debug|arm
|
||||
{261F2857-B521-42A4-A3E0-B5165F225E50}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{261F2857-B521-42A4-A3E0-B5165F225E50}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{261F2857-B521-42A4-A3E0-B5165F225E50}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{261F2857-B521-42A4-A3E0-B5165F225E50}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{261F2857-B521-42A4-A3E0-B5165F225E50}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{261F2857-B521-42A4-A3E0-B5165F225E50}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{261F2857-B521-42A4-A3E0-B5165F225E50}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{261F2857-B521-42A4-A3E0-B5165F225E50}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{261F2857-B521-42A4-A3E0-B5165F225E50}.Release|arm.ActiveCfg = Release|arm
|
||||
{261F2857-B521-42A4-A3E0-B5165F225E50}.Release|arm.Build.0 = Release|arm
|
||||
{261F2857-B521-42A4-A3E0-B5165F225E50}.Release|ia64.ActiveCfg = Release|ia64
|
||||
{261F2857-B521-42A4-A3E0-B5165F225E50}.Release|ia64.Build.0 = Release|ia64
|
||||
{261F2857-B521-42A4-A3E0-B5165F225E50}.Release|Mixed Platforms.ActiveCfg = Release|arm
|
||||
{261F2857-B521-42A4-A3E0-B5165F225E50}.Release|Mixed Platforms.Build.0 = Release|arm
|
||||
{261F2857-B521-42A4-A3E0-B5165F225E50}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{261F2857-B521-42A4-A3E0-B5165F225E50}.Release|Win32.Build.0 = Release|Win32
|
||||
{261F2857-B521-42A4-A3E0-B5165F225E50}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{261F2857-B521-42A4-A3E0-B5165F225E50}.Release|x64.Build.0 = Release|Any CPU
|
||||
{261F2857-B521-42A4-A3E0-B5165F225E50}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{261F2857-B521-42A4-A3E0-B5165F225E50}.Release|x86.Build.0 = Release|Any CPU
|
||||
{15895FD1-DD68-407B-8717-08F6DD14F02C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{15895FD1-DD68-407B-8717-08F6DD14F02C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{15895FD1-DD68-407B-8717-08F6DD14F02C}.Debug|arm.ActiveCfg = Debug|arm
|
||||
{15895FD1-DD68-407B-8717-08F6DD14F02C}.Debug|arm.Build.0 = Debug|arm
|
||||
{15895FD1-DD68-407B-8717-08F6DD14F02C}.Debug|ia64.ActiveCfg = Debug|ia64
|
||||
{15895FD1-DD68-407B-8717-08F6DD14F02C}.Debug|ia64.Build.0 = Debug|ia64
|
||||
{15895FD1-DD68-407B-8717-08F6DD14F02C}.Debug|Mixed Platforms.ActiveCfg = Debug|arm
|
||||
{15895FD1-DD68-407B-8717-08F6DD14F02C}.Debug|Mixed Platforms.Build.0 = Debug|arm
|
||||
{15895FD1-DD68-407B-8717-08F6DD14F02C}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{15895FD1-DD68-407B-8717-08F6DD14F02C}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{15895FD1-DD68-407B-8717-08F6DD14F02C}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{15895FD1-DD68-407B-8717-08F6DD14F02C}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{15895FD1-DD68-407B-8717-08F6DD14F02C}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{15895FD1-DD68-407B-8717-08F6DD14F02C}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{15895FD1-DD68-407B-8717-08F6DD14F02C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{15895FD1-DD68-407B-8717-08F6DD14F02C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{15895FD1-DD68-407B-8717-08F6DD14F02C}.Release|arm.ActiveCfg = Release|arm
|
||||
{15895FD1-DD68-407B-8717-08F6DD14F02C}.Release|arm.Build.0 = Release|arm
|
||||
{15895FD1-DD68-407B-8717-08F6DD14F02C}.Release|ia64.ActiveCfg = Release|ia64
|
||||
{15895FD1-DD68-407B-8717-08F6DD14F02C}.Release|ia64.Build.0 = Release|ia64
|
||||
{15895FD1-DD68-407B-8717-08F6DD14F02C}.Release|Mixed Platforms.ActiveCfg = Release|arm
|
||||
{15895FD1-DD68-407B-8717-08F6DD14F02C}.Release|Mixed Platforms.Build.0 = Release|arm
|
||||
{15895FD1-DD68-407B-8717-08F6DD14F02C}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{15895FD1-DD68-407B-8717-08F6DD14F02C}.Release|Win32.Build.0 = Release|Win32
|
||||
{15895FD1-DD68-407B-8717-08F6DD14F02C}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{15895FD1-DD68-407B-8717-08F6DD14F02C}.Release|x64.Build.0 = Release|Any CPU
|
||||
{15895FD1-DD68-407B-8717-08F6DD14F02C}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{15895FD1-DD68-407B-8717-08F6DD14F02C}.Release|x86.Build.0 = Release|Any CPU
|
||||
{24121677-0ED0-41B5-833F-1B9A18E87BF4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{24121677-0ED0-41B5-833F-1B9A18E87BF4}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{24121677-0ED0-41B5-833F-1B9A18E87BF4}.Debug|arm.ActiveCfg = Debug|arm
|
||||
{24121677-0ED0-41B5-833F-1B9A18E87BF4}.Debug|arm.Build.0 = Debug|arm
|
||||
{24121677-0ED0-41B5-833F-1B9A18E87BF4}.Debug|ia64.ActiveCfg = Debug|ia64
|
||||
{24121677-0ED0-41B5-833F-1B9A18E87BF4}.Debug|ia64.Build.0 = Debug|ia64
|
||||
{24121677-0ED0-41B5-833F-1B9A18E87BF4}.Debug|Mixed Platforms.ActiveCfg = Debug|arm
|
||||
{24121677-0ED0-41B5-833F-1B9A18E87BF4}.Debug|Mixed Platforms.Build.0 = Debug|arm
|
||||
{24121677-0ED0-41B5-833F-1B9A18E87BF4}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{24121677-0ED0-41B5-833F-1B9A18E87BF4}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{24121677-0ED0-41B5-833F-1B9A18E87BF4}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{24121677-0ED0-41B5-833F-1B9A18E87BF4}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{24121677-0ED0-41B5-833F-1B9A18E87BF4}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{24121677-0ED0-41B5-833F-1B9A18E87BF4}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{24121677-0ED0-41B5-833F-1B9A18E87BF4}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{24121677-0ED0-41B5-833F-1B9A18E87BF4}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{24121677-0ED0-41B5-833F-1B9A18E87BF4}.Release|arm.ActiveCfg = Release|arm
|
||||
{24121677-0ED0-41B5-833F-1B9A18E87BF4}.Release|arm.Build.0 = Release|arm
|
||||
{24121677-0ED0-41B5-833F-1B9A18E87BF4}.Release|ia64.ActiveCfg = Release|ia64
|
||||
{24121677-0ED0-41B5-833F-1B9A18E87BF4}.Release|ia64.Build.0 = Release|ia64
|
||||
{24121677-0ED0-41B5-833F-1B9A18E87BF4}.Release|Mixed Platforms.ActiveCfg = Release|arm
|
||||
{24121677-0ED0-41B5-833F-1B9A18E87BF4}.Release|Mixed Platforms.Build.0 = Release|arm
|
||||
{24121677-0ED0-41B5-833F-1B9A18E87BF4}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{24121677-0ED0-41B5-833F-1B9A18E87BF4}.Release|Win32.Build.0 = Release|Win32
|
||||
{24121677-0ED0-41B5-833F-1B9A18E87BF4}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{24121677-0ED0-41B5-833F-1B9A18E87BF4}.Release|x64.Build.0 = Release|Any CPU
|
||||
{24121677-0ED0-41B5-833F-1B9A18E87BF4}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{24121677-0ED0-41B5-833F-1B9A18E87BF4}.Release|x86.Build.0 = Release|Any CPU
|
||||
{CD7A37D8-9D8C-41BD-B78F-DB5E0C299D2E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{CD7A37D8-9D8C-41BD-B78F-DB5E0C299D2E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{CD7A37D8-9D8C-41BD-B78F-DB5E0C299D2E}.Debug|arm.ActiveCfg = Debug|arm
|
||||
{CD7A37D8-9D8C-41BD-B78F-DB5E0C299D2E}.Debug|arm.Build.0 = Debug|arm
|
||||
{CD7A37D8-9D8C-41BD-B78F-DB5E0C299D2E}.Debug|ia64.ActiveCfg = Debug|ia64
|
||||
{CD7A37D8-9D8C-41BD-B78F-DB5E0C299D2E}.Debug|ia64.Build.0 = Debug|ia64
|
||||
{CD7A37D8-9D8C-41BD-B78F-DB5E0C299D2E}.Debug|Mixed Platforms.ActiveCfg = Debug|arm
|
||||
{CD7A37D8-9D8C-41BD-B78F-DB5E0C299D2E}.Debug|Mixed Platforms.Build.0 = Debug|arm
|
||||
{CD7A37D8-9D8C-41BD-B78F-DB5E0C299D2E}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{CD7A37D8-9D8C-41BD-B78F-DB5E0C299D2E}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{CD7A37D8-9D8C-41BD-B78F-DB5E0C299D2E}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{CD7A37D8-9D8C-41BD-B78F-DB5E0C299D2E}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{CD7A37D8-9D8C-41BD-B78F-DB5E0C299D2E}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{CD7A37D8-9D8C-41BD-B78F-DB5E0C299D2E}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{CD7A37D8-9D8C-41BD-B78F-DB5E0C299D2E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{CD7A37D8-9D8C-41BD-B78F-DB5E0C299D2E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{CD7A37D8-9D8C-41BD-B78F-DB5E0C299D2E}.Release|arm.ActiveCfg = Release|arm
|
||||
{CD7A37D8-9D8C-41BD-B78F-DB5E0C299D2E}.Release|arm.Build.0 = Release|arm
|
||||
{CD7A37D8-9D8C-41BD-B78F-DB5E0C299D2E}.Release|ia64.ActiveCfg = Release|ia64
|
||||
{CD7A37D8-9D8C-41BD-B78F-DB5E0C299D2E}.Release|ia64.Build.0 = Release|ia64
|
||||
{CD7A37D8-9D8C-41BD-B78F-DB5E0C299D2E}.Release|Mixed Platforms.ActiveCfg = Release|arm
|
||||
{CD7A37D8-9D8C-41BD-B78F-DB5E0C299D2E}.Release|Mixed Platforms.Build.0 = Release|arm
|
||||
{CD7A37D8-9D8C-41BD-B78F-DB5E0C299D2E}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{CD7A37D8-9D8C-41BD-B78F-DB5E0C299D2E}.Release|Win32.Build.0 = Release|Win32
|
||||
{CD7A37D8-9D8C-41BD-B78F-DB5E0C299D2E}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{CD7A37D8-9D8C-41BD-B78F-DB5E0C299D2E}.Release|x64.Build.0 = Release|Any CPU
|
||||
{CD7A37D8-9D8C-41BD-B78F-DB5E0C299D2E}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{CD7A37D8-9D8C-41BD-B78F-DB5E0C299D2E}.Release|x86.Build.0 = Release|Any CPU
|
||||
{1A9940A7-3E29-4428-B753-C4CC66058F1A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{1A9940A7-3E29-4428-B753-C4CC66058F1A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{1A9940A7-3E29-4428-B753-C4CC66058F1A}.Debug|arm.ActiveCfg = Debug|arm
|
||||
{1A9940A7-3E29-4428-B753-C4CC66058F1A}.Debug|arm.Build.0 = Debug|arm
|
||||
{1A9940A7-3E29-4428-B753-C4CC66058F1A}.Debug|ia64.ActiveCfg = Debug|ia64
|
||||
{1A9940A7-3E29-4428-B753-C4CC66058F1A}.Debug|ia64.Build.0 = Debug|ia64
|
||||
{1A9940A7-3E29-4428-B753-C4CC66058F1A}.Debug|Mixed Platforms.ActiveCfg = Debug|arm
|
||||
{1A9940A7-3E29-4428-B753-C4CC66058F1A}.Debug|Mixed Platforms.Build.0 = Debug|arm
|
||||
{1A9940A7-3E29-4428-B753-C4CC66058F1A}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{1A9940A7-3E29-4428-B753-C4CC66058F1A}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{1A9940A7-3E29-4428-B753-C4CC66058F1A}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{1A9940A7-3E29-4428-B753-C4CC66058F1A}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{1A9940A7-3E29-4428-B753-C4CC66058F1A}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{1A9940A7-3E29-4428-B753-C4CC66058F1A}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{1A9940A7-3E29-4428-B753-C4CC66058F1A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{1A9940A7-3E29-4428-B753-C4CC66058F1A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{1A9940A7-3E29-4428-B753-C4CC66058F1A}.Release|arm.ActiveCfg = Release|arm
|
||||
{1A9940A7-3E29-4428-B753-C4CC66058F1A}.Release|arm.Build.0 = Release|arm
|
||||
{1A9940A7-3E29-4428-B753-C4CC66058F1A}.Release|ia64.ActiveCfg = Release|ia64
|
||||
{1A9940A7-3E29-4428-B753-C4CC66058F1A}.Release|ia64.Build.0 = Release|ia64
|
||||
{1A9940A7-3E29-4428-B753-C4CC66058F1A}.Release|Mixed Platforms.ActiveCfg = Release|arm
|
||||
{1A9940A7-3E29-4428-B753-C4CC66058F1A}.Release|Mixed Platforms.Build.0 = Release|arm
|
||||
{1A9940A7-3E29-4428-B753-C4CC66058F1A}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{1A9940A7-3E29-4428-B753-C4CC66058F1A}.Release|Win32.Build.0 = Release|Win32
|
||||
{1A9940A7-3E29-4428-B753-C4CC66058F1A}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{1A9940A7-3E29-4428-B753-C4CC66058F1A}.Release|x64.Build.0 = Release|Any CPU
|
||||
{1A9940A7-3E29-4428-B753-C4CC66058F1A}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{1A9940A7-3E29-4428-B753-C4CC66058F1A}.Release|x86.Build.0 = Release|Any CPU
|
||||
{03E55D95-DABE-4571-9CDA-92A44F92A465}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{03E55D95-DABE-4571-9CDA-92A44F92A465}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{03E55D95-DABE-4571-9CDA-92A44F92A465}.Debug|arm.ActiveCfg = Debug|arm
|
||||
{03E55D95-DABE-4571-9CDA-92A44F92A465}.Debug|arm.Build.0 = Debug|arm
|
||||
{03E55D95-DABE-4571-9CDA-92A44F92A465}.Debug|ia64.ActiveCfg = Debug|ia64
|
||||
{03E55D95-DABE-4571-9CDA-92A44F92A465}.Debug|ia64.Build.0 = Debug|ia64
|
||||
{03E55D95-DABE-4571-9CDA-92A44F92A465}.Debug|Mixed Platforms.ActiveCfg = Debug|arm
|
||||
{03E55D95-DABE-4571-9CDA-92A44F92A465}.Debug|Mixed Platforms.Build.0 = Debug|arm
|
||||
{03E55D95-DABE-4571-9CDA-92A44F92A465}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{03E55D95-DABE-4571-9CDA-92A44F92A465}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{03E55D95-DABE-4571-9CDA-92A44F92A465}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{03E55D95-DABE-4571-9CDA-92A44F92A465}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{03E55D95-DABE-4571-9CDA-92A44F92A465}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{03E55D95-DABE-4571-9CDA-92A44F92A465}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{03E55D95-DABE-4571-9CDA-92A44F92A465}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{03E55D95-DABE-4571-9CDA-92A44F92A465}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{03E55D95-DABE-4571-9CDA-92A44F92A465}.Release|arm.ActiveCfg = Release|arm
|
||||
{03E55D95-DABE-4571-9CDA-92A44F92A465}.Release|arm.Build.0 = Release|arm
|
||||
{03E55D95-DABE-4571-9CDA-92A44F92A465}.Release|ia64.ActiveCfg = Release|ia64
|
||||
{03E55D95-DABE-4571-9CDA-92A44F92A465}.Release|ia64.Build.0 = Release|ia64
|
||||
{03E55D95-DABE-4571-9CDA-92A44F92A465}.Release|Mixed Platforms.ActiveCfg = Release|arm
|
||||
{03E55D95-DABE-4571-9CDA-92A44F92A465}.Release|Mixed Platforms.Build.0 = Release|arm
|
||||
{03E55D95-DABE-4571-9CDA-92A44F92A465}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{03E55D95-DABE-4571-9CDA-92A44F92A465}.Release|Win32.Build.0 = Release|Win32
|
||||
{03E55D95-DABE-4571-9CDA-92A44F92A465}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{03E55D95-DABE-4571-9CDA-92A44F92A465}.Release|x64.Build.0 = Release|Any CPU
|
||||
{03E55D95-DABE-4571-9CDA-92A44F92A465}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{03E55D95-DABE-4571-9CDA-92A44F92A465}.Release|x86.Build.0 = Release|Any CPU
|
||||
{55D5BA28-D427-4F53-80C2-FE9EF23C1553}.Debug|Any CPU.ActiveCfg = Debug|Win32
|
||||
{55D5BA28-D427-4F53-80C2-FE9EF23C1553}.Debug|arm.ActiveCfg = Debug|Win32
|
||||
{55D5BA28-D427-4F53-80C2-FE9EF23C1553}.Debug|ia64.ActiveCfg = Debug|Win32
|
||||
{55D5BA28-D427-4F53-80C2-FE9EF23C1553}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32
|
||||
{55D5BA28-D427-4F53-80C2-FE9EF23C1553}.Debug|Mixed Platforms.Build.0 = Debug|Win32
|
||||
{55D5BA28-D427-4F53-80C2-FE9EF23C1553}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{55D5BA28-D427-4F53-80C2-FE9EF23C1553}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{55D5BA28-D427-4F53-80C2-FE9EF23C1553}.Debug|x64.ActiveCfg = Debug|Win32
|
||||
{55D5BA28-D427-4F53-80C2-FE9EF23C1553}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{55D5BA28-D427-4F53-80C2-FE9EF23C1553}.Debug|x86.Build.0 = Debug|Win32
|
||||
{55D5BA28-D427-4F53-80C2-FE9EF23C1553}.Release|Any CPU.ActiveCfg = Release|Win32
|
||||
{55D5BA28-D427-4F53-80C2-FE9EF23C1553}.Release|arm.ActiveCfg = Release|Win32
|
||||
{55D5BA28-D427-4F53-80C2-FE9EF23C1553}.Release|ia64.ActiveCfg = Release|Win32
|
||||
{55D5BA28-D427-4F53-80C2-FE9EF23C1553}.Release|Mixed Platforms.ActiveCfg = Release|Win32
|
||||
{55D5BA28-D427-4F53-80C2-FE9EF23C1553}.Release|Mixed Platforms.Build.0 = Release|Win32
|
||||
{55D5BA28-D427-4F53-80C2-FE9EF23C1553}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{55D5BA28-D427-4F53-80C2-FE9EF23C1553}.Release|Win32.Build.0 = Release|Win32
|
||||
{55D5BA28-D427-4F53-80C2-FE9EF23C1553}.Release|x64.ActiveCfg = Release|Win32
|
||||
{55D5BA28-D427-4F53-80C2-FE9EF23C1553}.Release|x86.ActiveCfg = Release|Win32
|
||||
{55D5BA28-D427-4F53-80C2-FE9EF23C1553}.Release|x86.Build.0 = Release|Win32
|
||||
{3F246CE0-153D-4AC3-B6AC-5EAD8E2AD04B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{3F246CE0-153D-4AC3-B6AC-5EAD8E2AD04B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{3F246CE0-153D-4AC3-B6AC-5EAD8E2AD04B}.Debug|arm.ActiveCfg = Debug|arm
|
||||
{3F246CE0-153D-4AC3-B6AC-5EAD8E2AD04B}.Debug|arm.Build.0 = Debug|arm
|
||||
{3F246CE0-153D-4AC3-B6AC-5EAD8E2AD04B}.Debug|ia64.ActiveCfg = Debug|ia64
|
||||
{3F246CE0-153D-4AC3-B6AC-5EAD8E2AD04B}.Debug|ia64.Build.0 = Debug|ia64
|
||||
{3F246CE0-153D-4AC3-B6AC-5EAD8E2AD04B}.Debug|Mixed Platforms.ActiveCfg = Debug|arm
|
||||
{3F246CE0-153D-4AC3-B6AC-5EAD8E2AD04B}.Debug|Mixed Platforms.Build.0 = Debug|arm
|
||||
{3F246CE0-153D-4AC3-B6AC-5EAD8E2AD04B}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{3F246CE0-153D-4AC3-B6AC-5EAD8E2AD04B}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{3F246CE0-153D-4AC3-B6AC-5EAD8E2AD04B}.Debug|x64.ActiveCfg = Debug|x86
|
||||
{3F246CE0-153D-4AC3-B6AC-5EAD8E2AD04B}.Debug|x64.Build.0 = Debug|x86
|
||||
{3F246CE0-153D-4AC3-B6AC-5EAD8E2AD04B}.Debug|x86.ActiveCfg = Debug|x86
|
||||
{3F246CE0-153D-4AC3-B6AC-5EAD8E2AD04B}.Debug|x86.Build.0 = Debug|x86
|
||||
{3F246CE0-153D-4AC3-B6AC-5EAD8E2AD04B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{3F246CE0-153D-4AC3-B6AC-5EAD8E2AD04B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{3F246CE0-153D-4AC3-B6AC-5EAD8E2AD04B}.Release|arm.ActiveCfg = Release|arm
|
||||
{3F246CE0-153D-4AC3-B6AC-5EAD8E2AD04B}.Release|arm.Build.0 = Release|arm
|
||||
{3F246CE0-153D-4AC3-B6AC-5EAD8E2AD04B}.Release|ia64.ActiveCfg = Release|ia64
|
||||
{3F246CE0-153D-4AC3-B6AC-5EAD8E2AD04B}.Release|ia64.Build.0 = Release|ia64
|
||||
{3F246CE0-153D-4AC3-B6AC-5EAD8E2AD04B}.Release|Mixed Platforms.ActiveCfg = Release|arm
|
||||
{3F246CE0-153D-4AC3-B6AC-5EAD8E2AD04B}.Release|Mixed Platforms.Build.0 = Release|arm
|
||||
{3F246CE0-153D-4AC3-B6AC-5EAD8E2AD04B}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{3F246CE0-153D-4AC3-B6AC-5EAD8E2AD04B}.Release|Win32.Build.0 = Release|Win32
|
||||
{3F246CE0-153D-4AC3-B6AC-5EAD8E2AD04B}.Release|x64.ActiveCfg = Release|x86
|
||||
{3F246CE0-153D-4AC3-B6AC-5EAD8E2AD04B}.Release|x64.Build.0 = Release|x86
|
||||
{3F246CE0-153D-4AC3-B6AC-5EAD8E2AD04B}.Release|x86.ActiveCfg = Release|x86
|
||||
{3F246CE0-153D-4AC3-B6AC-5EAD8E2AD04B}.Release|x86.Build.0 = Release|x86
|
||||
{DB9E5F02-8241-440A-9B60-980EB5B42B13}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{DB9E5F02-8241-440A-9B60-980EB5B42B13}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{DB9E5F02-8241-440A-9B60-980EB5B42B13}.Debug|arm.ActiveCfg = Debug|arm
|
||||
{DB9E5F02-8241-440A-9B60-980EB5B42B13}.Debug|arm.Build.0 = Debug|arm
|
||||
{DB9E5F02-8241-440A-9B60-980EB5B42B13}.Debug|ia64.ActiveCfg = Debug|ia64
|
||||
{DB9E5F02-8241-440A-9B60-980EB5B42B13}.Debug|ia64.Build.0 = Debug|ia64
|
||||
{DB9E5F02-8241-440A-9B60-980EB5B42B13}.Debug|Mixed Platforms.ActiveCfg = Debug|arm
|
||||
{DB9E5F02-8241-440A-9B60-980EB5B42B13}.Debug|Mixed Platforms.Build.0 = Debug|arm
|
||||
{DB9E5F02-8241-440A-9B60-980EB5B42B13}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{DB9E5F02-8241-440A-9B60-980EB5B42B13}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{DB9E5F02-8241-440A-9B60-980EB5B42B13}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{DB9E5F02-8241-440A-9B60-980EB5B42B13}.Debug|x64.Build.0 = Debug|x64
|
||||
{DB9E5F02-8241-440A-9B60-980EB5B42B13}.Debug|x86.ActiveCfg = Debug|x86
|
||||
{DB9E5F02-8241-440A-9B60-980EB5B42B13}.Debug|x86.Build.0 = Debug|x86
|
||||
{DB9E5F02-8241-440A-9B60-980EB5B42B13}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{DB9E5F02-8241-440A-9B60-980EB5B42B13}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{DB9E5F02-8241-440A-9B60-980EB5B42B13}.Release|arm.ActiveCfg = Release|arm
|
||||
{DB9E5F02-8241-440A-9B60-980EB5B42B13}.Release|arm.Build.0 = Release|arm
|
||||
{DB9E5F02-8241-440A-9B60-980EB5B42B13}.Release|ia64.ActiveCfg = Release|ia64
|
||||
{DB9E5F02-8241-440A-9B60-980EB5B42B13}.Release|ia64.Build.0 = Release|ia64
|
||||
{DB9E5F02-8241-440A-9B60-980EB5B42B13}.Release|Mixed Platforms.ActiveCfg = Release|arm
|
||||
{DB9E5F02-8241-440A-9B60-980EB5B42B13}.Release|Mixed Platforms.Build.0 = Release|arm
|
||||
{DB9E5F02-8241-440A-9B60-980EB5B42B13}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{DB9E5F02-8241-440A-9B60-980EB5B42B13}.Release|Win32.Build.0 = Release|Win32
|
||||
{DB9E5F02-8241-440A-9B60-980EB5B42B13}.Release|x64.ActiveCfg = Release|x64
|
||||
{DB9E5F02-8241-440A-9B60-980EB5B42B13}.Release|x64.Build.0 = Release|x64
|
||||
{DB9E5F02-8241-440A-9B60-980EB5B42B13}.Release|x86.ActiveCfg = Release|x86
|
||||
{DB9E5F02-8241-440A-9B60-980EB5B42B13}.Release|x86.Build.0 = Release|x86
|
||||
{44931ECB-8D6F-4C12-A872-64E261B6A98E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{44931ECB-8D6F-4C12-A872-64E261B6A98E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{44931ECB-8D6F-4C12-A872-64E261B6A98E}.Debug|arm.ActiveCfg = Debug|arm
|
||||
{44931ECB-8D6F-4C12-A872-64E261B6A98E}.Debug|arm.Build.0 = Debug|arm
|
||||
{44931ECB-8D6F-4C12-A872-64E261B6A98E}.Debug|ia64.ActiveCfg = Debug|ia64
|
||||
{44931ECB-8D6F-4C12-A872-64E261B6A98E}.Debug|ia64.Build.0 = Debug|ia64
|
||||
{44931ECB-8D6F-4C12-A872-64E261B6A98E}.Debug|Mixed Platforms.ActiveCfg = Debug|arm
|
||||
{44931ECB-8D6F-4C12-A872-64E261B6A98E}.Debug|Mixed Platforms.Build.0 = Debug|arm
|
||||
{44931ECB-8D6F-4C12-A872-64E261B6A98E}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{44931ECB-8D6F-4C12-A872-64E261B6A98E}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{44931ECB-8D6F-4C12-A872-64E261B6A98E}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{44931ECB-8D6F-4C12-A872-64E261B6A98E}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{44931ECB-8D6F-4C12-A872-64E261B6A98E}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{44931ECB-8D6F-4C12-A872-64E261B6A98E}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{44931ECB-8D6F-4C12-A872-64E261B6A98E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{44931ECB-8D6F-4C12-A872-64E261B6A98E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{44931ECB-8D6F-4C12-A872-64E261B6A98E}.Release|arm.ActiveCfg = Release|arm
|
||||
{44931ECB-8D6F-4C12-A872-64E261B6A98E}.Release|arm.Build.0 = Release|arm
|
||||
{44931ECB-8D6F-4C12-A872-64E261B6A98E}.Release|ia64.ActiveCfg = Release|ia64
|
||||
{44931ECB-8D6F-4C12-A872-64E261B6A98E}.Release|ia64.Build.0 = Release|ia64
|
||||
{44931ECB-8D6F-4C12-A872-64E261B6A98E}.Release|Mixed Platforms.ActiveCfg = Release|arm
|
||||
{44931ECB-8D6F-4C12-A872-64E261B6A98E}.Release|Mixed Platforms.Build.0 = Release|arm
|
||||
{44931ECB-8D6F-4C12-A872-64E261B6A98E}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{44931ECB-8D6F-4C12-A872-64E261B6A98E}.Release|Win32.Build.0 = Release|Win32
|
||||
{44931ECB-8D6F-4C12-A872-64E261B6A98E}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{44931ECB-8D6F-4C12-A872-64E261B6A98E}.Release|x64.Build.0 = Release|Any CPU
|
||||
{44931ECB-8D6F-4C12-A872-64E261B6A98E}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{44931ECB-8D6F-4C12-A872-64E261B6A98E}.Release|x86.Build.0 = Release|Any CPU
|
||||
{51480F8E-B80F-42DC-91E7-3542C1F12F8C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{51480F8E-B80F-42DC-91E7-3542C1F12F8C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{51480F8E-B80F-42DC-91E7-3542C1F12F8C}.Debug|arm.ActiveCfg = Debug|arm
|
||||
{51480F8E-B80F-42DC-91E7-3542C1F12F8C}.Debug|arm.Build.0 = Debug|arm
|
||||
{51480F8E-B80F-42DC-91E7-3542C1F12F8C}.Debug|ia64.ActiveCfg = Debug|ia64
|
||||
{51480F8E-B80F-42DC-91E7-3542C1F12F8C}.Debug|ia64.Build.0 = Debug|ia64
|
||||
{51480F8E-B80F-42DC-91E7-3542C1F12F8C}.Debug|Mixed Platforms.ActiveCfg = Debug|arm
|
||||
{51480F8E-B80F-42DC-91E7-3542C1F12F8C}.Debug|Mixed Platforms.Build.0 = Debug|arm
|
||||
{51480F8E-B80F-42DC-91E7-3542C1F12F8C}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{51480F8E-B80F-42DC-91E7-3542C1F12F8C}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{51480F8E-B80F-42DC-91E7-3542C1F12F8C}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{51480F8E-B80F-42DC-91E7-3542C1F12F8C}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{51480F8E-B80F-42DC-91E7-3542C1F12F8C}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{51480F8E-B80F-42DC-91E7-3542C1F12F8C}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{51480F8E-B80F-42DC-91E7-3542C1F12F8C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{51480F8E-B80F-42DC-91E7-3542C1F12F8C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{51480F8E-B80F-42DC-91E7-3542C1F12F8C}.Release|arm.ActiveCfg = Release|arm
|
||||
{51480F8E-B80F-42DC-91E7-3542C1F12F8C}.Release|arm.Build.0 = Release|arm
|
||||
{51480F8E-B80F-42DC-91E7-3542C1F12F8C}.Release|ia64.ActiveCfg = Release|ia64
|
||||
{51480F8E-B80F-42DC-91E7-3542C1F12F8C}.Release|ia64.Build.0 = Release|ia64
|
||||
{51480F8E-B80F-42DC-91E7-3542C1F12F8C}.Release|Mixed Platforms.ActiveCfg = Release|arm
|
||||
{51480F8E-B80F-42DC-91E7-3542C1F12F8C}.Release|Mixed Platforms.Build.0 = Release|arm
|
||||
{51480F8E-B80F-42DC-91E7-3542C1F12F8C}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{51480F8E-B80F-42DC-91E7-3542C1F12F8C}.Release|Win32.Build.0 = Release|Win32
|
||||
{51480F8E-B80F-42DC-91E7-3542C1F12F8C}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{51480F8E-B80F-42DC-91E7-3542C1F12F8C}.Release|x64.Build.0 = Release|Any CPU
|
||||
{51480F8E-B80F-42DC-91E7-3542C1F12F8C}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{51480F8E-B80F-42DC-91E7-3542C1F12F8C}.Release|x86.Build.0 = Release|Any CPU
|
||||
{AE562F7F-EE33-41D6-A962-DA488FEFBD08}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{AE562F7F-EE33-41D6-A962-DA488FEFBD08}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{AE562F7F-EE33-41D6-A962-DA488FEFBD08}.Debug|arm.ActiveCfg = Debug|arm
|
||||
{AE562F7F-EE33-41D6-A962-DA488FEFBD08}.Debug|arm.Build.0 = Debug|arm
|
||||
{AE562F7F-EE33-41D6-A962-DA488FEFBD08}.Debug|ia64.ActiveCfg = Debug|ia64
|
||||
{AE562F7F-EE33-41D6-A962-DA488FEFBD08}.Debug|ia64.Build.0 = Debug|ia64
|
||||
{AE562F7F-EE33-41D6-A962-DA488FEFBD08}.Debug|Mixed Platforms.ActiveCfg = Debug|arm
|
||||
{AE562F7F-EE33-41D6-A962-DA488FEFBD08}.Debug|Mixed Platforms.Build.0 = Debug|arm
|
||||
{AE562F7F-EE33-41D6-A962-DA488FEFBD08}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{AE562F7F-EE33-41D6-A962-DA488FEFBD08}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{AE562F7F-EE33-41D6-A962-DA488FEFBD08}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{AE562F7F-EE33-41D6-A962-DA488FEFBD08}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{AE562F7F-EE33-41D6-A962-DA488FEFBD08}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{AE562F7F-EE33-41D6-A962-DA488FEFBD08}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{AE562F7F-EE33-41D6-A962-DA488FEFBD08}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{AE562F7F-EE33-41D6-A962-DA488FEFBD08}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{AE562F7F-EE33-41D6-A962-DA488FEFBD08}.Release|arm.ActiveCfg = Release|arm
|
||||
{AE562F7F-EE33-41D6-A962-DA488FEFBD08}.Release|arm.Build.0 = Release|arm
|
||||
{AE562F7F-EE33-41D6-A962-DA488FEFBD08}.Release|ia64.ActiveCfg = Release|ia64
|
||||
{AE562F7F-EE33-41D6-A962-DA488FEFBD08}.Release|ia64.Build.0 = Release|ia64
|
||||
{AE562F7F-EE33-41D6-A962-DA488FEFBD08}.Release|Mixed Platforms.ActiveCfg = Release|arm
|
||||
{AE562F7F-EE33-41D6-A962-DA488FEFBD08}.Release|Mixed Platforms.Build.0 = Release|arm
|
||||
{AE562F7F-EE33-41D6-A962-DA488FEFBD08}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{AE562F7F-EE33-41D6-A962-DA488FEFBD08}.Release|Win32.Build.0 = Release|Win32
|
||||
{AE562F7F-EE33-41D6-A962-DA488FEFBD08}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{AE562F7F-EE33-41D6-A962-DA488FEFBD08}.Release|x64.Build.0 = Release|Any CPU
|
||||
{AE562F7F-EE33-41D6-A962-DA488FEFBD08}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{AE562F7F-EE33-41D6-A962-DA488FEFBD08}.Release|x86.Build.0 = Release|Any CPU
|
||||
{1CDF4242-4C00-4744-BBCD-085128978FF3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{1CDF4242-4C00-4744-BBCD-085128978FF3}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{1CDF4242-4C00-4744-BBCD-085128978FF3}.Debug|arm.ActiveCfg = Debug|arm
|
||||
{1CDF4242-4C00-4744-BBCD-085128978FF3}.Debug|arm.Build.0 = Debug|arm
|
||||
{1CDF4242-4C00-4744-BBCD-085128978FF3}.Debug|ia64.ActiveCfg = Debug|ia64
|
||||
{1CDF4242-4C00-4744-BBCD-085128978FF3}.Debug|ia64.Build.0 = Debug|ia64
|
||||
{1CDF4242-4C00-4744-BBCD-085128978FF3}.Debug|Mixed Platforms.ActiveCfg = Debug|arm
|
||||
{1CDF4242-4C00-4744-BBCD-085128978FF3}.Debug|Mixed Platforms.Build.0 = Debug|arm
|
||||
{1CDF4242-4C00-4744-BBCD-085128978FF3}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{1CDF4242-4C00-4744-BBCD-085128978FF3}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{1CDF4242-4C00-4744-BBCD-085128978FF3}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{1CDF4242-4C00-4744-BBCD-085128978FF3}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{1CDF4242-4C00-4744-BBCD-085128978FF3}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{1CDF4242-4C00-4744-BBCD-085128978FF3}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{1CDF4242-4C00-4744-BBCD-085128978FF3}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{1CDF4242-4C00-4744-BBCD-085128978FF3}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{1CDF4242-4C00-4744-BBCD-085128978FF3}.Release|arm.ActiveCfg = Release|arm
|
||||
{1CDF4242-4C00-4744-BBCD-085128978FF3}.Release|arm.Build.0 = Release|arm
|
||||
{1CDF4242-4C00-4744-BBCD-085128978FF3}.Release|ia64.ActiveCfg = Release|ia64
|
||||
{1CDF4242-4C00-4744-BBCD-085128978FF3}.Release|ia64.Build.0 = Release|ia64
|
||||
{1CDF4242-4C00-4744-BBCD-085128978FF3}.Release|Mixed Platforms.ActiveCfg = Release|arm
|
||||
{1CDF4242-4C00-4744-BBCD-085128978FF3}.Release|Mixed Platforms.Build.0 = Release|arm
|
||||
{1CDF4242-4C00-4744-BBCD-085128978FF3}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{1CDF4242-4C00-4744-BBCD-085128978FF3}.Release|Win32.Build.0 = Release|Win32
|
||||
{1CDF4242-4C00-4744-BBCD-085128978FF3}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{1CDF4242-4C00-4744-BBCD-085128978FF3}.Release|x64.Build.0 = Release|Any CPU
|
||||
{1CDF4242-4C00-4744-BBCD-085128978FF3}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{1CDF4242-4C00-4744-BBCD-085128978FF3}.Release|x86.Build.0 = Release|Any CPU
|
||||
{864B8C50-7895-4485-AC89-900D86FD8C0D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{864B8C50-7895-4485-AC89-900D86FD8C0D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{864B8C50-7895-4485-AC89-900D86FD8C0D}.Debug|arm.ActiveCfg = Debug|arm
|
||||
{864B8C50-7895-4485-AC89-900D86FD8C0D}.Debug|arm.Build.0 = Debug|arm
|
||||
{864B8C50-7895-4485-AC89-900D86FD8C0D}.Debug|ia64.ActiveCfg = Debug|ia64
|
||||
{864B8C50-7895-4485-AC89-900D86FD8C0D}.Debug|ia64.Build.0 = Debug|ia64
|
||||
{864B8C50-7895-4485-AC89-900D86FD8C0D}.Debug|Mixed Platforms.ActiveCfg = Debug|arm
|
||||
{864B8C50-7895-4485-AC89-900D86FD8C0D}.Debug|Mixed Platforms.Build.0 = Debug|arm
|
||||
{864B8C50-7895-4485-AC89-900D86FD8C0D}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{864B8C50-7895-4485-AC89-900D86FD8C0D}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{864B8C50-7895-4485-AC89-900D86FD8C0D}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{864B8C50-7895-4485-AC89-900D86FD8C0D}.Debug|x64.Build.0 = Debug|x64
|
||||
{864B8C50-7895-4485-AC89-900D86FD8C0D}.Debug|x86.ActiveCfg = Debug|x86
|
||||
{864B8C50-7895-4485-AC89-900D86FD8C0D}.Debug|x86.Build.0 = Debug|x86
|
||||
{864B8C50-7895-4485-AC89-900D86FD8C0D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{864B8C50-7895-4485-AC89-900D86FD8C0D}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{864B8C50-7895-4485-AC89-900D86FD8C0D}.Release|arm.ActiveCfg = Release|arm
|
||||
{864B8C50-7895-4485-AC89-900D86FD8C0D}.Release|arm.Build.0 = Release|arm
|
||||
{864B8C50-7895-4485-AC89-900D86FD8C0D}.Release|ia64.ActiveCfg = Release|ia64
|
||||
{864B8C50-7895-4485-AC89-900D86FD8C0D}.Release|ia64.Build.0 = Release|ia64
|
||||
{864B8C50-7895-4485-AC89-900D86FD8C0D}.Release|Mixed Platforms.ActiveCfg = Release|arm
|
||||
{864B8C50-7895-4485-AC89-900D86FD8C0D}.Release|Mixed Platforms.Build.0 = Release|arm
|
||||
{864B8C50-7895-4485-AC89-900D86FD8C0D}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{864B8C50-7895-4485-AC89-900D86FD8C0D}.Release|Win32.Build.0 = Release|Win32
|
||||
{864B8C50-7895-4485-AC89-900D86FD8C0D}.Release|x64.ActiveCfg = Release|x64
|
||||
{864B8C50-7895-4485-AC89-900D86FD8C0D}.Release|x64.Build.0 = Release|x64
|
||||
{864B8C50-7895-4485-AC89-900D86FD8C0D}.Release|x86.ActiveCfg = Release|x86
|
||||
{864B8C50-7895-4485-AC89-900D86FD8C0D}.Release|x86.Build.0 = Release|x86
|
||||
{F045FFC1-05F9-4EA2-9F03-E1CBDB7BC4F9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{F045FFC1-05F9-4EA2-9F03-E1CBDB7BC4F9}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{F045FFC1-05F9-4EA2-9F03-E1CBDB7BC4F9}.Debug|arm.ActiveCfg = Debug|arm
|
||||
{F045FFC1-05F9-4EA2-9F03-E1CBDB7BC4F9}.Debug|arm.Build.0 = Debug|arm
|
||||
{F045FFC1-05F9-4EA2-9F03-E1CBDB7BC4F9}.Debug|ia64.ActiveCfg = Debug|ia64
|
||||
{F045FFC1-05F9-4EA2-9F03-E1CBDB7BC4F9}.Debug|ia64.Build.0 = Debug|ia64
|
||||
{F045FFC1-05F9-4EA2-9F03-E1CBDB7BC4F9}.Debug|Mixed Platforms.ActiveCfg = Debug|arm
|
||||
{F045FFC1-05F9-4EA2-9F03-E1CBDB7BC4F9}.Debug|Mixed Platforms.Build.0 = Debug|arm
|
||||
{F045FFC1-05F9-4EA2-9F03-E1CBDB7BC4F9}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{F045FFC1-05F9-4EA2-9F03-E1CBDB7BC4F9}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{F045FFC1-05F9-4EA2-9F03-E1CBDB7BC4F9}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{F045FFC1-05F9-4EA2-9F03-E1CBDB7BC4F9}.Debug|x64.Build.0 = Debug|x64
|
||||
{F045FFC1-05F9-4EA2-9F03-E1CBDB7BC4F9}.Debug|x86.ActiveCfg = Debug|x86
|
||||
{F045FFC1-05F9-4EA2-9F03-E1CBDB7BC4F9}.Debug|x86.Build.0 = Debug|x86
|
||||
{F045FFC1-05F9-4EA2-9F03-E1CBDB7BC4F9}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{F045FFC1-05F9-4EA2-9F03-E1CBDB7BC4F9}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{F045FFC1-05F9-4EA2-9F03-E1CBDB7BC4F9}.Release|arm.ActiveCfg = Release|arm
|
||||
{F045FFC1-05F9-4EA2-9F03-E1CBDB7BC4F9}.Release|arm.Build.0 = Release|arm
|
||||
{F045FFC1-05F9-4EA2-9F03-E1CBDB7BC4F9}.Release|ia64.ActiveCfg = Release|ia64
|
||||
{F045FFC1-05F9-4EA2-9F03-E1CBDB7BC4F9}.Release|ia64.Build.0 = Release|ia64
|
||||
{F045FFC1-05F9-4EA2-9F03-E1CBDB7BC4F9}.Release|Mixed Platforms.ActiveCfg = Release|arm
|
||||
{F045FFC1-05F9-4EA2-9F03-E1CBDB7BC4F9}.Release|Mixed Platforms.Build.0 = Release|arm
|
||||
{F045FFC1-05F9-4EA2-9F03-E1CBDB7BC4F9}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{F045FFC1-05F9-4EA2-9F03-E1CBDB7BC4F9}.Release|Win32.Build.0 = Release|Win32
|
||||
{F045FFC1-05F9-4EA2-9F03-E1CBDB7BC4F9}.Release|x64.ActiveCfg = Release|x64
|
||||
{F045FFC1-05F9-4EA2-9F03-E1CBDB7BC4F9}.Release|x64.Build.0 = Release|x64
|
||||
{F045FFC1-05F9-4EA2-9F03-E1CBDB7BC4F9}.Release|x86.ActiveCfg = Release|x86
|
||||
{F045FFC1-05F9-4EA2-9F03-E1CBDB7BC4F9}.Release|x86.Build.0 = Release|x86
|
||||
{4544158C-2D63-4146-85FF-62169280144E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{4544158C-2D63-4146-85FF-62169280144E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{4544158C-2D63-4146-85FF-62169280144E}.Debug|arm.ActiveCfg = Debug|arm
|
||||
{4544158C-2D63-4146-85FF-62169280144E}.Debug|arm.Build.0 = Debug|arm
|
||||
{4544158C-2D63-4146-85FF-62169280144E}.Debug|ia64.ActiveCfg = Debug|ia64
|
||||
{4544158C-2D63-4146-85FF-62169280144E}.Debug|ia64.Build.0 = Debug|ia64
|
||||
{4544158C-2D63-4146-85FF-62169280144E}.Debug|Mixed Platforms.ActiveCfg = Debug|arm
|
||||
{4544158C-2D63-4146-85FF-62169280144E}.Debug|Mixed Platforms.Build.0 = Debug|arm
|
||||
{4544158C-2D63-4146-85FF-62169280144E}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{4544158C-2D63-4146-85FF-62169280144E}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{4544158C-2D63-4146-85FF-62169280144E}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{4544158C-2D63-4146-85FF-62169280144E}.Debug|x64.Build.0 = Debug|x64
|
||||
{4544158C-2D63-4146-85FF-62169280144E}.Debug|x86.ActiveCfg = Debug|x86
|
||||
{4544158C-2D63-4146-85FF-62169280144E}.Debug|x86.Build.0 = Debug|x86
|
||||
{4544158C-2D63-4146-85FF-62169280144E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{4544158C-2D63-4146-85FF-62169280144E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{4544158C-2D63-4146-85FF-62169280144E}.Release|arm.ActiveCfg = Release|arm
|
||||
{4544158C-2D63-4146-85FF-62169280144E}.Release|arm.Build.0 = Release|arm
|
||||
{4544158C-2D63-4146-85FF-62169280144E}.Release|ia64.ActiveCfg = Release|ia64
|
||||
{4544158C-2D63-4146-85FF-62169280144E}.Release|ia64.Build.0 = Release|ia64
|
||||
{4544158C-2D63-4146-85FF-62169280144E}.Release|Mixed Platforms.ActiveCfg = Release|arm
|
||||
{4544158C-2D63-4146-85FF-62169280144E}.Release|Mixed Platforms.Build.0 = Release|arm
|
||||
{4544158C-2D63-4146-85FF-62169280144E}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{4544158C-2D63-4146-85FF-62169280144E}.Release|Win32.Build.0 = Release|Win32
|
||||
{4544158C-2D63-4146-85FF-62169280144E}.Release|x64.ActiveCfg = Release|x64
|
||||
{4544158C-2D63-4146-85FF-62169280144E}.Release|x64.Build.0 = Release|x64
|
||||
{4544158C-2D63-4146-85FF-62169280144E}.Release|x86.ActiveCfg = Release|x86
|
||||
{4544158C-2D63-4146-85FF-62169280144E}.Release|x86.Build.0 = Release|x86
|
||||
{328799BB-7B03-4B28-8180-4132211FD07D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{328799BB-7B03-4B28-8180-4132211FD07D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{328799BB-7B03-4B28-8180-4132211FD07D}.Debug|arm.ActiveCfg = Debug|arm
|
||||
{328799BB-7B03-4B28-8180-4132211FD07D}.Debug|arm.Build.0 = Debug|arm
|
||||
{328799BB-7B03-4B28-8180-4132211FD07D}.Debug|ia64.ActiveCfg = Debug|ia64
|
||||
{328799BB-7B03-4B28-8180-4132211FD07D}.Debug|ia64.Build.0 = Debug|ia64
|
||||
{328799BB-7B03-4B28-8180-4132211FD07D}.Debug|Mixed Platforms.ActiveCfg = Debug|arm
|
||||
{328799BB-7B03-4B28-8180-4132211FD07D}.Debug|Mixed Platforms.Build.0 = Debug|arm
|
||||
{328799BB-7B03-4B28-8180-4132211FD07D}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{328799BB-7B03-4B28-8180-4132211FD07D}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{328799BB-7B03-4B28-8180-4132211FD07D}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{328799BB-7B03-4B28-8180-4132211FD07D}.Debug|x64.Build.0 = Debug|x64
|
||||
{328799BB-7B03-4B28-8180-4132211FD07D}.Debug|x86.ActiveCfg = Debug|x86
|
||||
{328799BB-7B03-4B28-8180-4132211FD07D}.Debug|x86.Build.0 = Debug|x86
|
||||
{328799BB-7B03-4B28-8180-4132211FD07D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{328799BB-7B03-4B28-8180-4132211FD07D}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{328799BB-7B03-4B28-8180-4132211FD07D}.Release|arm.ActiveCfg = Release|arm
|
||||
{328799BB-7B03-4B28-8180-4132211FD07D}.Release|arm.Build.0 = Release|arm
|
||||
{328799BB-7B03-4B28-8180-4132211FD07D}.Release|ia64.ActiveCfg = Release|ia64
|
||||
{328799BB-7B03-4B28-8180-4132211FD07D}.Release|ia64.Build.0 = Release|ia64
|
||||
{328799BB-7B03-4B28-8180-4132211FD07D}.Release|Mixed Platforms.ActiveCfg = Release|arm
|
||||
{328799BB-7B03-4B28-8180-4132211FD07D}.Release|Mixed Platforms.Build.0 = Release|arm
|
||||
{328799BB-7B03-4B28-8180-4132211FD07D}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{328799BB-7B03-4B28-8180-4132211FD07D}.Release|Win32.Build.0 = Release|Win32
|
||||
{328799BB-7B03-4B28-8180-4132211FD07D}.Release|x64.ActiveCfg = Release|x64
|
||||
{328799BB-7B03-4B28-8180-4132211FD07D}.Release|x64.Build.0 = Release|x64
|
||||
{328799BB-7B03-4B28-8180-4132211FD07D}.Release|x86.ActiveCfg = Release|x86
|
||||
{328799BB-7B03-4B28-8180-4132211FD07D}.Release|x86.Build.0 = Release|x86
|
||||
{16F5202F-9276-4166-975C-C9654BAF8012}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{16F5202F-9276-4166-975C-C9654BAF8012}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{16F5202F-9276-4166-975C-C9654BAF8012}.Debug|arm.ActiveCfg = Debug|arm
|
||||
{16F5202F-9276-4166-975C-C9654BAF8012}.Debug|arm.Build.0 = Debug|arm
|
||||
{16F5202F-9276-4166-975C-C9654BAF8012}.Debug|ia64.ActiveCfg = Debug|ia64
|
||||
{16F5202F-9276-4166-975C-C9654BAF8012}.Debug|ia64.Build.0 = Debug|ia64
|
||||
{16F5202F-9276-4166-975C-C9654BAF8012}.Debug|Mixed Platforms.ActiveCfg = Debug|arm
|
||||
{16F5202F-9276-4166-975C-C9654BAF8012}.Debug|Mixed Platforms.Build.0 = Debug|arm
|
||||
{16F5202F-9276-4166-975C-C9654BAF8012}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{16F5202F-9276-4166-975C-C9654BAF8012}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{16F5202F-9276-4166-975C-C9654BAF8012}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{16F5202F-9276-4166-975C-C9654BAF8012}.Debug|x64.Build.0 = Debug|x64
|
||||
{16F5202F-9276-4166-975C-C9654BAF8012}.Debug|x86.ActiveCfg = Debug|x86
|
||||
{16F5202F-9276-4166-975C-C9654BAF8012}.Debug|x86.Build.0 = Debug|x86
|
||||
{16F5202F-9276-4166-975C-C9654BAF8012}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{16F5202F-9276-4166-975C-C9654BAF8012}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{16F5202F-9276-4166-975C-C9654BAF8012}.Release|arm.ActiveCfg = Release|arm
|
||||
{16F5202F-9276-4166-975C-C9654BAF8012}.Release|arm.Build.0 = Release|arm
|
||||
{16F5202F-9276-4166-975C-C9654BAF8012}.Release|ia64.ActiveCfg = Release|ia64
|
||||
{16F5202F-9276-4166-975C-C9654BAF8012}.Release|ia64.Build.0 = Release|ia64
|
||||
{16F5202F-9276-4166-975C-C9654BAF8012}.Release|Mixed Platforms.ActiveCfg = Release|arm
|
||||
{16F5202F-9276-4166-975C-C9654BAF8012}.Release|Mixed Platforms.Build.0 = Release|arm
|
||||
{16F5202F-9276-4166-975C-C9654BAF8012}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{16F5202F-9276-4166-975C-C9654BAF8012}.Release|Win32.Build.0 = Release|Win32
|
||||
{16F5202F-9276-4166-975C-C9654BAF8012}.Release|x64.ActiveCfg = Release|x64
|
||||
{16F5202F-9276-4166-975C-C9654BAF8012}.Release|x64.Build.0 = Release|x64
|
||||
{16F5202F-9276-4166-975C-C9654BAF8012}.Release|x86.ActiveCfg = Release|x86
|
||||
{16F5202F-9276-4166-975C-C9654BAF8012}.Release|x86.Build.0 = Release|x86
|
||||
{137D376B-989F-4FEA-9A67-01D8D38CA0DE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{137D376B-989F-4FEA-9A67-01D8D38CA0DE}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{137D376B-989F-4FEA-9A67-01D8D38CA0DE}.Debug|arm.ActiveCfg = Debug|arm
|
||||
{137D376B-989F-4FEA-9A67-01D8D38CA0DE}.Debug|arm.Build.0 = Debug|arm
|
||||
{137D376B-989F-4FEA-9A67-01D8D38CA0DE}.Debug|ia64.ActiveCfg = Debug|ia64
|
||||
{137D376B-989F-4FEA-9A67-01D8D38CA0DE}.Debug|ia64.Build.0 = Debug|ia64
|
||||
{137D376B-989F-4FEA-9A67-01D8D38CA0DE}.Debug|Mixed Platforms.ActiveCfg = Debug|arm
|
||||
{137D376B-989F-4FEA-9A67-01D8D38CA0DE}.Debug|Mixed Platforms.Build.0 = Debug|arm
|
||||
{137D376B-989F-4FEA-9A67-01D8D38CA0DE}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{137D376B-989F-4FEA-9A67-01D8D38CA0DE}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{137D376B-989F-4FEA-9A67-01D8D38CA0DE}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{137D376B-989F-4FEA-9A67-01D8D38CA0DE}.Debug|x64.Build.0 = Debug|x64
|
||||
{137D376B-989F-4FEA-9A67-01D8D38CA0DE}.Debug|x86.ActiveCfg = Debug|x86
|
||||
{137D376B-989F-4FEA-9A67-01D8D38CA0DE}.Debug|x86.Build.0 = Debug|x86
|
||||
{137D376B-989F-4FEA-9A67-01D8D38CA0DE}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{137D376B-989F-4FEA-9A67-01D8D38CA0DE}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{137D376B-989F-4FEA-9A67-01D8D38CA0DE}.Release|arm.ActiveCfg = Release|arm
|
||||
{137D376B-989F-4FEA-9A67-01D8D38CA0DE}.Release|arm.Build.0 = Release|arm
|
||||
{137D376B-989F-4FEA-9A67-01D8D38CA0DE}.Release|ia64.ActiveCfg = Release|ia64
|
||||
{137D376B-989F-4FEA-9A67-01D8D38CA0DE}.Release|ia64.Build.0 = Release|ia64
|
||||
{137D376B-989F-4FEA-9A67-01D8D38CA0DE}.Release|Mixed Platforms.ActiveCfg = Release|arm
|
||||
{137D376B-989F-4FEA-9A67-01D8D38CA0DE}.Release|Mixed Platforms.Build.0 = Release|arm
|
||||
{137D376B-989F-4FEA-9A67-01D8D38CA0DE}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{137D376B-989F-4FEA-9A67-01D8D38CA0DE}.Release|Win32.Build.0 = Release|Win32
|
||||
{137D376B-989F-4FEA-9A67-01D8D38CA0DE}.Release|x64.ActiveCfg = Release|x64
|
||||
{137D376B-989F-4FEA-9A67-01D8D38CA0DE}.Release|x64.Build.0 = Release|x64
|
||||
{137D376B-989F-4FEA-9A67-01D8D38CA0DE}.Release|x86.ActiveCfg = Release|x86
|
||||
{137D376B-989F-4FEA-9A67-01D8D38CA0DE}.Release|x86.Build.0 = Release|x86
|
||||
{4F55F9B8-D8B6-41EB-8796-221B4CD98324}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{4F55F9B8-D8B6-41EB-8796-221B4CD98324}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{4F55F9B8-D8B6-41EB-8796-221B4CD98324}.Debug|arm.ActiveCfg = Debug|arm
|
||||
{4F55F9B8-D8B6-41EB-8796-221B4CD98324}.Debug|arm.Build.0 = Debug|arm
|
||||
{4F55F9B8-D8B6-41EB-8796-221B4CD98324}.Debug|ia64.ActiveCfg = Debug|ia64
|
||||
{4F55F9B8-D8B6-41EB-8796-221B4CD98324}.Debug|ia64.Build.0 = Debug|ia64
|
||||
{4F55F9B8-D8B6-41EB-8796-221B4CD98324}.Debug|Mixed Platforms.ActiveCfg = Debug|arm
|
||||
{4F55F9B8-D8B6-41EB-8796-221B4CD98324}.Debug|Mixed Platforms.Build.0 = Debug|arm
|
||||
{4F55F9B8-D8B6-41EB-8796-221B4CD98324}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{4F55F9B8-D8B6-41EB-8796-221B4CD98324}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{4F55F9B8-D8B6-41EB-8796-221B4CD98324}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{4F55F9B8-D8B6-41EB-8796-221B4CD98324}.Debug|x64.Build.0 = Debug|x64
|
||||
{4F55F9B8-D8B6-41EB-8796-221B4CD98324}.Debug|x86.ActiveCfg = Debug|x86
|
||||
{4F55F9B8-D8B6-41EB-8796-221B4CD98324}.Debug|x86.Build.0 = Debug|x86
|
||||
{4F55F9B8-D8B6-41EB-8796-221B4CD98324}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{4F55F9B8-D8B6-41EB-8796-221B4CD98324}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{4F55F9B8-D8B6-41EB-8796-221B4CD98324}.Release|arm.ActiveCfg = Release|arm
|
||||
{4F55F9B8-D8B6-41EB-8796-221B4CD98324}.Release|arm.Build.0 = Release|arm
|
||||
{4F55F9B8-D8B6-41EB-8796-221B4CD98324}.Release|ia64.ActiveCfg = Release|ia64
|
||||
{4F55F9B8-D8B6-41EB-8796-221B4CD98324}.Release|ia64.Build.0 = Release|ia64
|
||||
{4F55F9B8-D8B6-41EB-8796-221B4CD98324}.Release|Mixed Platforms.ActiveCfg = Release|arm
|
||||
{4F55F9B8-D8B6-41EB-8796-221B4CD98324}.Release|Mixed Platforms.Build.0 = Release|arm
|
||||
{4F55F9B8-D8B6-41EB-8796-221B4CD98324}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{4F55F9B8-D8B6-41EB-8796-221B4CD98324}.Release|Win32.Build.0 = Release|Win32
|
||||
{4F55F9B8-D8B6-41EB-8796-221B4CD98324}.Release|x64.ActiveCfg = Release|x64
|
||||
{4F55F9B8-D8B6-41EB-8796-221B4CD98324}.Release|x64.Build.0 = Release|x64
|
||||
{4F55F9B8-D8B6-41EB-8796-221B4CD98324}.Release|x86.ActiveCfg = Release|x86
|
||||
{4F55F9B8-D8B6-41EB-8796-221B4CD98324}.Release|x86.Build.0 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(NestedProjects) = preSolution
|
||||
{2D62850C-9F81-4BE9-BDF3-9379389C8F7B} = {B217BE43-6280-4320-8477-BA0CEEFC38EE}
|
||||
{261F2857-B521-42A4-A3E0-B5165F225E50} = {B217BE43-6280-4320-8477-BA0CEEFC38EE}
|
||||
{15895FD1-DD68-407B-8717-08F6DD14F02C} = {B217BE43-6280-4320-8477-BA0CEEFC38EE}
|
||||
{24121677-0ED0-41B5-833F-1B9A18E87BF4} = {B217BE43-6280-4320-8477-BA0CEEFC38EE}
|
||||
{CD7A37D8-9D8C-41BD-B78F-DB5E0C299D2E} = {B217BE43-6280-4320-8477-BA0CEEFC38EE}
|
||||
{1A9940A7-3E29-4428-B753-C4CC66058F1A} = {B217BE43-6280-4320-8477-BA0CEEFC38EE}
|
||||
{44931ECB-8D6F-4C12-A872-64E261B6A98E} = {B217BE43-6280-4320-8477-BA0CEEFC38EE}
|
||||
{03E55D95-DABE-4571-9CDA-92A44F92A465} = {AED0B145-6A3A-40DF-89AC-E5728B9B3DAA}
|
||||
{DB9E5F02-8241-440A-9B60-980EB5B42B13} = {AED0B145-6A3A-40DF-89AC-E5728B9B3DAA}
|
||||
{51480F8E-B80F-42DC-91E7-3542C1F12F8C} = {AED0B145-6A3A-40DF-89AC-E5728B9B3DAA}
|
||||
{AE562F7F-EE33-41D6-A962-DA488FEFBD08} = {AED0B145-6A3A-40DF-89AC-E5728B9B3DAA}
|
||||
{1CDF4242-4C00-4744-BBCD-085128978FF3} = {AED0B145-6A3A-40DF-89AC-E5728B9B3DAA}
|
||||
{864B8C50-7895-4485-AC89-900D86FD8C0D} = {AED0B145-6A3A-40DF-89AC-E5728B9B3DAA}
|
||||
{55D5BA28-D427-4F53-80C2-FE9EF23C1553} = {9A8288B8-A634-4BD3-9CC7-0F7C1314156B}
|
||||
{3F246CE0-153D-4AC3-B6AC-5EAD8E2AD04B} = {9A8288B8-A634-4BD3-9CC7-0F7C1314156B}
|
||||
{F045FFC1-05F9-4EA2-9F03-E1CBDB7BC4F9} = {8477AF4C-B4CA-4434-A6C4-9CC57C74AF21}
|
||||
{4544158C-2D63-4146-85FF-62169280144E} = {8477AF4C-B4CA-4434-A6C4-9CC57C74AF21}
|
||||
{328799BB-7B03-4B28-8180-4132211FD07D} = {8477AF4C-B4CA-4434-A6C4-9CC57C74AF21}
|
||||
{16F5202F-9276-4166-975C-C9654BAF8012} = {8477AF4C-B4CA-4434-A6C4-9CC57C74AF21}
|
||||
{137D376B-989F-4FEA-9A67-01D8D38CA0DE} = {8477AF4C-B4CA-4434-A6C4-9CC57C74AF21}
|
||||
{4F55F9B8-D8B6-41EB-8796-221B4CD98324} = {8477AF4C-B4CA-4434-A6C4-9CC57C74AF21}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
|
@ -0,0 +1,18 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<TestRunConfiguration name="Local Test Run" id="d7cdca3d-88a0-4e00-be96-2a5ca2db8548" xmlns="http://microsoft.com/schemas/VisualStudio/TeamTest/2010">
|
||||
<Description>This is a default test run configuration for a local test run.</Description>
|
||||
<NamingScheme baseName="DTF" useDefault="false" />
|
||||
<TestTypeSpecific>
|
||||
<WebTestRunConfiguration testTypeId="4e7599fa-5ecb-43e9-a887-cd63cf72d207">
|
||||
<Browser name="Internet Explorer 7.0">
|
||||
<Headers>
|
||||
<Header name="User-Agent" value="Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)" />
|
||||
<Header name="Accept" value="*/*" />
|
||||
<Header name="Accept-Language" value="{{$IEAcceptLanguage}}" />
|
||||
<Header name="Accept-Encoding" value="GZIP" />
|
||||
</Headers>
|
||||
</Browser>
|
||||
<Network Name="LAN" BandwidthInKbps="0" />
|
||||
</WebTestRunConfiguration>
|
||||
</TestTypeSpecific>
|
||||
</TestRunConfiguration>
|
|
@ -0,0 +1,95 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<TestLists xmlns="http://microsoft.com/schemas/VisualStudio/TeamTest/2010">
|
||||
<TestList name="CA" id="687d36d6-8130-4b57-a944-cff8ee025283" parentListId="8c43106b-9dc1-4907-a29f-aa66a61bf5b6">
|
||||
<TestLinks>
|
||||
<TestLink id="16d12a90-2e64-c4f7-78d7-beefe14ec4ed" name="CustomActionTest1" storage="..\..\build\debug\x86\microsoft.deployment.test.customactions.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
<TestLink id="165a9deb-4327-a539-10ed-2c60006fd719" name="CustomActionData" storage="..\..\build\debug\x86\microsoft.deployment.test.customactions.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
</TestLinks>
|
||||
</TestList>
|
||||
<TestList name="Lists of Tests" id="8c43106b-9dc1-4907-a29f-aa66a61bf5b6">
|
||||
<RunConfiguration id="d7cdca3d-88a0-4e00-be96-2a5ca2db8548" name="Local Test Run" storage="DTF.testrunconfig" type="Microsoft.VisualStudio.TestTools.Common.TestRunConfiguration, Microsoft.VisualStudio.QualityTools.Common, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
</TestList>
|
||||
<TestList name="Linq" id="96e9fb57-2f47-4636-a194-cddc34d1b889" parentListId="8c43106b-9dc1-4907-a29f-aa66a61bf5b6">
|
||||
<TestLinks>
|
||||
<TestLink id="ee2ffbce-2ed4-5b54-58cc-dd8a8119640b" name="EnumProducts" storage="..\..\build\debug\x86\microsoft.deployment.test.linq.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
<TestLink id="9490b03f-2278-1311-6376-01046c6f3abf" name="EnumTable" storage="..\..\build\debug\x86\microsoft.deployment.test.linq.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
<TestLink id="a8ca208b-1f65-30a2-df36-7382af157385" name="LinqWhereOperators" storage="..\..\build\debug\x86\microsoft.deployment.test.linq.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
<TestLink id="e74de56a-0b83-64e2-3679-50ac37c44445" name="LinqShapeSelect" storage="..\..\build\debug\x86\microsoft.deployment.test.linq.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
<TestLink id="6ef38409-9e03-8140-bb99-dadd8a666911" name="LinqUpdateNullableString" storage="..\..\build\debug\x86\microsoft.deployment.test.linq.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
<TestLink id="94fadde8-f57f-ea1a-df6f-d3b4bad6c043" name="LinqSimple" storage="..\..\build\debug\x86\microsoft.deployment.test.linq.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
<TestLink id="d92a1daa-8677-8f6f-6a12-e46ff10b2eee" name="LinqTwoWayJoin" storage="..\..\build\debug\x86\microsoft.deployment.test.linq.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
<TestLink id="fb6c62ce-c41b-d6e8-8ccc-db7dabc8ca98" name="LinqInsertDelete" storage="..\..\build\debug\x86\microsoft.deployment.test.linq.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
<TestLink id="b2234eee-356b-0dc4-2e46-76887c2df8e7" name="LinqWhereNull" storage="..\..\build\debug\x86\microsoft.deployment.test.linq.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
<TestLink id="be389146-a45d-d020-49fa-c11fc0cc364d" name="EnumComponents" storage="..\..\build\debug\x86\microsoft.deployment.test.linq.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
<TestLink id="22487991-e127-15c0-fbb0-56059c1c3e87" name="LinqFourWayJoin" storage="..\..\build\debug\x86\microsoft.deployment.test.linq.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
<TestLink id="ba4ab21d-81f9-fdfd-e984-ecf6c3f3a98d" name="DatabaseAsQueryable" storage="..\..\build\debug\x86\microsoft.deployment.test.linq.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
<TestLink id="b3d1c4f4-6907-d08f-dc44-0c3f8e5ff7d1" name="EnumFeatures" storage="..\..\build\debug\x86\microsoft.deployment.test.linq.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
<TestLink id="35dc2cd8-35c4-96d9-441d-978f14ae0f60" name="LinqQueryQRecord" storage="..\..\build\debug\x86\microsoft.deployment.test.linq.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
<TestLink id="b2b7a3a7-61f4-9f73-8bf9-71b5205c866d" name="LinqOrderBy" storage="..\..\build\debug\x86\microsoft.deployment.test.linq.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
</TestLinks>
|
||||
</TestList>
|
||||
<TestList name="WindowsInstaller" id="a05a5433-b0eb-4727-bee5-43ddb4dab92b" parentListId="8c43106b-9dc1-4907-a29f-aa66a61bf5b6">
|
||||
<TestLinks>
|
||||
<TestLink id="76e05ce1-3152-ef40-728d-8caf1ddd7fd0" name="InstallerMessageResources" storage="..\..\build\debug\x86\microsoft.deployment.test.windowsinstaller.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
<TestLink id="54133410-e02d-9458-43aa-9ce8cc5e19be" name="InstallerTransactTwoProducts" storage="..\..\build\debug\x86\microsoft.deployment.test.windowsinstaller.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
<TestLink id="16b75d3a-3c0b-d246-de97-8f807f9647b3" name="EnumComponentQualifiers" storage="..\..\build\debug\x86\microsoft.deployment.test.windowsinstaller.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
<TestLink id="2fe64e6f-1fff-9c88-75c8-868dd92c566f" name="InstallerErrorMessages" storage="..\..\build\debug\x86\microsoft.deployment.test.windowsinstaller.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
<TestLink id="ba10b6f6-ff8e-8e5c-6616-193e1a450614" name="InstallerInstallProduct" storage="..\..\build\debug\x86\microsoft.deployment.test.windowsinstaller.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
<TestLink id="a3d56994-6796-fe9d-cca5-7270ae4fd082" name="InstallerDatabaseSchema" storage="..\..\build\debug\x86\microsoft.deployment.test.windowsinstaller.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
<TestLink id="a90a5a27-5eec-e305-81ce-018c9e4561e0" name="InstallerViewTables" storage="..\..\build\debug\x86\microsoft.deployment.test.windowsinstaller.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
</TestLinks>
|
||||
</TestList>
|
||||
<TestList name="Zip" id="c0ae4f55-ac18-4545-a2a8-d5d99bc486d0" parentListId="8c43106b-9dc1-4907-a29f-aa66a61bf5b6">
|
||||
<TestLinks>
|
||||
<TestLink id="f5d3cd03-734c-a799-70c0-718742923d39" name="ZipInfoNullParams" storage="..\..\build\debug\x86\microsoft.deployment.test.zip.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
<TestLink id="9f3ec33c-e19b-7a69-c2f5-bf512ee2ad6c" name="ZipBadUnpackStreamContexts" storage="..\..\build\debug\x86\microsoft.deployment.test.zip.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
<TestLink id="b9401275-c907-b88d-e3a7-8d41dfdb2fbb" name="ZipFileCounts" storage="..\..\build\debug\x86\microsoft.deployment.test.zip.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
<TestLink id="86f6bced-7259-ea95-c8b0-3a5355fb344b" name="ZipBadPackStreamContexts" storage="..\..\build\debug\x86\microsoft.deployment.test.zip.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
<TestLink id="ccec862a-8f0e-f3d4-4655-232e8610c530" name="ZipProgress" storage="..\..\build\debug\x86\microsoft.deployment.test.zip.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
<TestLink id="8dabaf4c-b4e5-6345-a18d-e8d1ea2ae357" name="ZipFileSizes" storage="..\..\build\debug\x86\microsoft.deployment.test.zip.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
<TestLink id="7a119869-9aac-f58d-116b-3f3082704a38" name="ZipArchiveCounts" storage="..\..\build\debug\x86\microsoft.deployment.test.zip.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
<TestLink id="77c27be9-7e2a-d92f-6d57-b73576f069ae" name="ZipTruncatedArchive" storage="..\..\build\debug\x86\microsoft.deployment.test.zip.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
<TestLink id="1a4cfad0-0518-a918-ce75-e97e888d38e5" name="ZipFileInfoNullParams" storage="..\..\build\debug\x86\microsoft.deployment.test.zip.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
<TestLink id="1a39c6e0-57c4-3d44-9f4a-1654397ab0cd" name="ZipArchiveSizes" storage="..\..\build\debug\x86\microsoft.deployment.test.zip.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
<TestLink id="758bfad5-9240-932d-62d0-7143b9f65520" name="ZipEngineNullParams" storage="..\..\build\debug\x86\microsoft.deployment.test.zip.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
<TestLink id="8934309a-f963-832e-bbe9-27116af154cb" name="ZipCompLevelParam" storage="..\..\build\debug\x86\microsoft.deployment.test.zip.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
<TestLink id="6cd000ea-a9d8-013a-c7b7-cfa353824ac7" name="ZipInfoGetFiles" storage="..\..\build\debug\x86\microsoft.deployment.test.zip.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
</TestLinks>
|
||||
</TestList>
|
||||
<TestList name="Cab" id="ca4dd201-739c-4cf9-bf02-5400a266accb" parentListId="8c43106b-9dc1-4907-a29f-aa66a61bf5b6">
|
||||
<TestLinks>
|
||||
<TestLink id="ad321d35-d261-7507-e869-b2be56cc01cb" name="CabCompLevelParam" storage="..\..\build\debug\x86\microsoft.deployment.test.cab.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
<TestLink id="fd7538ab-d8ef-587d-0b31-14c74f7ed5e1" name="CabFileInfoNullParams" storage="..\..\build\debug\x86\microsoft.deployment.test.cab.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
<TestLink id="96f42e97-11f7-bd7f-adf4-924462d699ed" name="CabinetFolders" storage="..\..\build\debug\x86\microsoft.deployment.test.cab.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
<TestLink id="2f700617-94e1-aab4-a353-6d0bf5af3df0" name="CabEngineNoTempFileTest" storage="..\..\build\debug\x86\microsoft.deployment.test.cab.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
<TestLink id="858d7183-ac3a-8994-48fc-fce87dfe58de" name="CabArchiveSizeParam" storage="..\..\build\debug\x86\microsoft.deployment.test.cab.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
<TestLink id="b89ea4de-fc4c-1ddb-947e-337588a86e7f" name="CabinetBuggyFileSizes" storage="..\..\build\debug\x86\microsoft.deployment.test.cab.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
<TestLink id="d6560a5f-7c8c-592b-3413-95043dc6f306" name="CabinetTruncateOnCreate" storage="..\..\build\debug\x86\microsoft.deployment.test.cab.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
<TestLink id="50656de7-a2f1-e0a3-c25b-5efa750d1041" name="CabInfoProperties" storage="..\..\build\debug\x86\microsoft.deployment.test.cab.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
<TestLink id="6cf7d53c-31cd-9ba3-305e-fbc0b24fe914" name="CabinetOffset" storage="..\..\build\debug\x86\microsoft.deployment.test.cab.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
<TestLink id="89c90096-5405-a8ed-41ab-ef40209f4976" name="CabinetArchiveCounts" storage="..\..\build\debug\x86\microsoft.deployment.test.cab.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
<TestLink id="ba9a99ad-d25e-861e-fdd9-442863074848" name="CabinetExtractUpdate" storage="..\..\build\debug\x86\microsoft.deployment.test.cab.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
<TestLink id="67e07a8d-0d2f-a350-11f2-7707a258b791" name="CabInfoNullParams" storage="..\..\build\debug\x86\microsoft.deployment.test.cab.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
<TestLink id="787927f7-d237-cde5-64a8-b9025a927e3b" name="CabinetProgress" storage="..\..\build\debug\x86\microsoft.deployment.test.cab.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
<TestLink id="1d6fa553-cc95-6b11-0ec8-e7784a84d693" name="CabEngineNullParams" storage="..\..\build\debug\x86\microsoft.deployment.test.cab.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
<TestLink id="b536d2e8-6522-8d56-d920-69e9b963ed08" name="CabInfoGetFiles" storage="..\..\build\debug\x86\microsoft.deployment.test.cab.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
<TestLink id="e0366263-19ae-c21a-4cfb-3be2399793fe" name="CabinetFileSizes" storage="..\..\build\debug\x86\microsoft.deployment.test.cab.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
<TestLink id="11c8910e-db9e-6c80-32ed-61a1ecced74b" name="CabFileStreamContextNullParams" storage="..\..\build\debug\x86\microsoft.deployment.test.cab.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
<TestLink id="50da9591-721b-9aaf-6122-82e722fa12b8" name="CabExtractorIsCabinet" storage="..\..\build\debug\x86\microsoft.deployment.test.cab.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
<TestLink id="8504adf0-98fa-c3b0-a43c-2d739104e077" name="CabFileInfoOpenText" storage="..\..\build\debug\x86\microsoft.deployment.test.cab.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
<TestLink id="c44b1f2f-ede1-ade9-8d7e-0375efebbba9" name="CabBadUnpackStreamContexts" storage="..\..\build\debug\x86\microsoft.deployment.test.cab.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
<TestLink id="4f8c2ce6-d7dd-7fe9-c04f-141220dbe342" name="CabFolderSizeParam" storage="..\..\build\debug\x86\microsoft.deployment.test.cab.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
<TestLink id="e3291f3b-ef47-0195-d78d-59bbbeb33880" name="CabTruncatedArchive" storage="..\..\build\debug\x86\microsoft.deployment.test.cab.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
<TestLink id="b85217ba-ce04-7d22-32ca-8f98327f2f0c" name="CabInfoCompressExtract" storage="..\..\build\debug\x86\microsoft.deployment.test.cab.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
<TestLink id="e1bd864e-aa68-eb07-75a8-3ddfd1e692fe" name="CabinetUtfPaths" storage="..\..\build\debug\x86\microsoft.deployment.test.cab.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
<TestLink id="16d3fa74-8482-5ee6-a64b-24ea9d1e6799" name="CabExtractorGetFiles" storage="..\..\build\debug\x86\microsoft.deployment.test.cab.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
<TestLink id="46d4e0aa-bb0e-5f5e-e6b5-9c369dc0fefc" name="CabExtractorExtract" storage="..\..\build\debug\x86\microsoft.deployment.test.cab.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
<TestLink id="6ba757f2-a274-7aa7-72fe-392d647e2e85" name="CabExtractorFindOffset" storage="..\..\build\debug\x86\microsoft.deployment.test.cab.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
<TestLink id="d5dc5ace-8708-276a-cde7-7975552c9362" name="CabFileInfoProperties" storage="..\..\build\debug\x86\microsoft.deployment.test.cab.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
<TestLink id="a0f2a55c-c712-8ad4-9e81-986ec0796802" name="CabinetMultithread" storage="..\..\build\debug\x86\microsoft.deployment.test.cab.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
<TestLink id="11c8c8a0-cdb6-22ba-2715-02c7fb6c04ec" name="CabBadPackStreamContexts" storage="..\..\build\debug\x86\microsoft.deployment.test.cab.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
<TestLink id="56cf2946-b9f0-1526-2e49-425fc3f699a9" name="CabInfoSerialization" storage="..\..\build\debug\x86\microsoft.deployment.test.cab.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
<TestLink id="b694e515-8bd6-5bf8-bb8a-5d66cd299e5b" name="CabinetFileCounts" storage="..\..\build\debug\x86\microsoft.deployment.test.cab.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
</TestLinks>
|
||||
</TestList>
|
||||
</TestLists>
|
|
@ -0,0 +1,59 @@
|
|||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>Deployment Tools Foundation Overview</title>
|
||||
<link rel="stylesheet" type="text/css" href="../styles/presentation.css" />
|
||||
<link rel="stylesheet" type="text/css" href="ms-help://Hx/HxRuntime/HxLink.css" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="control">
|
||||
<span class="productTitle">Deployment Tools Foundation</span><br />
|
||||
<span class="topicTitle">Deployment Tools Foundation</span><br />
|
||||
<div id="toolbar">
|
||||
<span id="chickenFeet"><span class="nolink">Overview</span></span>
|
||||
<span id="languageFilter">v4.0</span>
|
||||
</div>
|
||||
</div>
|
||||
<div id="main">
|
||||
<div id="header">
|
||||
</div>
|
||||
<div class="summary">
|
||||
<p>Deployment Tools Foundation is a rich set of .NET class libraries and
|
||||
related resources that together bring the Windows deployment platform
|
||||
technologies into the .NET world. It is designed to greatly simplify
|
||||
deployment-related development tasks while still exposing the complete
|
||||
functionality of the underlying technology.</p>
|
||||
|
||||
<p>The primary focus of DTF is to provide a foundation for development of
|
||||
various kinds of tools to support deployment throughout the product
|
||||
lifecycle, including setup authoring, building, analysis, debugging, and
|
||||
testing tools. In addition to tools, DTF can also be useful for install-time
|
||||
activities such as setup bootstrappers, external UI, and custom actions,
|
||||
and for application run-time activities that need to access the deployment
|
||||
platform.</p>
|
||||
|
||||
<p>For a description of the the latest changes, see <a
|
||||
href="whatsnew.htm">What's New</a>.</p>
|
||||
|
||||
</div>
|
||||
|
||||
<div id="footer">
|
||||
<p />
|
||||
Send comments on this topic to <a id="HT_MailLink" href="mailto:wix-users%40lists.sourceforge.net?Subject=Deployment Tools Foundation Documentation">
|
||||
wix-users@lists.sourceforge.net</a>
|
||||
|
||||
<script type="text/javascript">
|
||||
var HT_mailLink = document.getElementById("HT_MailLink");
|
||||
var HT_mailLinkText = HT_mailLink.innerHTML;
|
||||
HT_mailLink.href += ": " + document.title;
|
||||
HT_mailLink.innerHTML = HT_mailLinkText;
|
||||
</script>
|
||||
|
||||
<p />
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,94 @@
|
|||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>Building Managed Custom Actions</title>
|
||||
<link rel="stylesheet" type="text/css" href="../styles/presentation.css" />
|
||||
<link rel="stylesheet" type="text/css" href="ms-help://Hx/HxRuntime/HxLink.css" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="control">
|
||||
<span class="productTitle">Deployment Tools Foundation</span><br />
|
||||
<span class="topicTitle">Building Managed Custom Actions</span><br />
|
||||
<div id="toolbar">
|
||||
<span id="chickenFeet">
|
||||
<a href="using.htm">Development Guide</a> >
|
||||
<a href="managedcas.htm">Managed CAs</a> >
|
||||
<span class="nolink">Building</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div id="main">
|
||||
<div id="header">
|
||||
</div>
|
||||
<div class="summary">
|
||||
|
||||
<p>The build process for managed CA DLLs is a little complicated becuase of the
|
||||
proxy-wrapper and dll-export requirements. Here's an overview:</p>
|
||||
<ol>
|
||||
<li>
|
||||
<p>Compile your CA assembly, which references Microsoft.Deployment.WindowsInstaller.dll and
|
||||
marks exported custom actions with a CustomActionAttribute.</p>
|
||||
<li>
|
||||
<p>Package the CA assembly, CustomAction.config, Microsoft.Deployment.WindowsInstaller.dll,
|
||||
and any other dependencies using <b>MakeSfxCA.exe</b>. The filenames of CustomAction.config
|
||||
and Microsoft.Deployment.WindowsInstaller.dll must not be changed, since
|
||||
the custom action proxy specifically looks for those files.</p>
|
||||
</ol>
|
||||
<p><br>
|
||||
</p>
|
||||
<p><b>Compiling</b></p>
|
||||
<pre><font face="Consolas, Courier New">
|
||||
csc.exe
|
||||
/target:library
|
||||
/r:$(DTFbin)\Microsoft.Deployment.WindowsInstaller.dll
|
||||
/out:SampleCAs.dll
|
||||
*.cs
|
||||
</font></pre>
|
||||
<p><b>Wrapping</b><pre><font face="Consolas, Courier New">
|
||||
MakeSfxCA.exe
|
||||
$(OutDir)\SampleCAsPackage.dll
|
||||
$(DTFbin)\SfxCA.dll
|
||||
SampleCAs.dll
|
||||
CustomAction.config
|
||||
$(DTFbin)\Microsoft.Deployment.WindowsInstaller.dll
|
||||
</font></pre>
|
||||
</p>
|
||||
<p>Now the resulting package, SampleCAsPackage.dll, is ready to be inserted
|
||||
into the Binary table of the MSI.</p>
|
||||
<p><br/>
|
||||
</p>
|
||||
<p>For a working example of building a managed custom action package
|
||||
you can look at included sample ManagedCAs project.</p>
|
||||
<p><br/>
|
||||
</p>
|
||||
|
||||
<p><br/></p>
|
||||
<p><b>See also:</b></p>
|
||||
<ul>
|
||||
<li><a href="writingcas.htm">Writing Managed Custom Actions</a></li>
|
||||
<li><a href="caconfig.htm">Specifying the Runtime Version</a></li>
|
||||
</ul>
|
||||
<p><br/></p>
|
||||
|
||||
</div>
|
||||
|
||||
<div id="footer">
|
||||
<p />
|
||||
Send comments on this topic to <a id="HT_MailLink" href="mailto:wix-users%40lists.sourceforge.net?Subject=Deployment Tools Foundation Documentation">
|
||||
wix-users@lists.sourceforge.net</a>
|
||||
|
||||
<script type="text/javascript">
|
||||
var HT_mailLink = document.getElementById("HT_MailLink");
|
||||
var HT_mailLinkText = HT_mailLink.innerHTML;
|
||||
HT_mailLink.href += ": " + document.title;
|
||||
HT_mailLink.innerHTML = HT_mailLinkText;
|
||||
</script>
|
||||
|
||||
<p />
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,63 @@
|
|||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>Archive Pack/Unpack Tool</title>
|
||||
<link rel="stylesheet" type="text/css" href="../styles/presentation.css" />
|
||||
<link rel="stylesheet" type="text/css" href="ms-help://Hx/HxRuntime/HxLink.css" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="control">
|
||||
<span class="productTitle">Deployment Tools Foundation</span><br />
|
||||
<span class="topicTitle">Archive Pack/Unpack Tool</span><br />
|
||||
<div id="toolbar">
|
||||
<span id="chickenFeet">
|
||||
<a href="using.htm">Development Guide</a> >
|
||||
<a href="samples.htm">Samples</a> >
|
||||
<span class="nolink">XPack</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div id="main">
|
||||
<div id="header">
|
||||
</div>
|
||||
<div class="summary">
|
||||
<p><pre><font face="Consolas, Courier New">Usage: CabPack.exe <directory> <package.cab>
|
||||
Usage: XPack /P <archive.cab> <directory>
|
||||
Usage: XPack /P <archive.zip> <directory>
|
||||
|
||||
Packs all files in a directory tree into an archive,
|
||||
using either the cab or zip format. Any existing archive
|
||||
with the same name will be overwritten.
|
||||
|
||||
|
||||
Usage: XPack /U <archive.cab> <directory>
|
||||
Usage: XPack /U <archive.zip> <directory>
|
||||
|
||||
Unpacks all files from a cab or zip archive to the
|
||||
specified directory. Any existing files with the same
|
||||
names will be overwritten.</font></pre>
|
||||
</p>
|
||||
<p><br/></p>
|
||||
|
||||
</div>
|
||||
|
||||
<div id="footer">
|
||||
<p />
|
||||
Send comments on this topic to <a id="HT_MailLink" href="mailto:wix-users%40lists.sourceforge.net?Subject=Deployment Tools Foundation Documentation">
|
||||
wix-users@lists.sourceforge.net</a>
|
||||
|
||||
<script type="text/javascript">
|
||||
var HT_mailLink = document.getElementById("HT_MailLink");
|
||||
var HT_mailLinkText = HT_mailLink.innerHTML;
|
||||
HT_mailLink.href += ": " + document.title;
|
||||
HT_mailLink.innerHTML = HT_mailLinkText;
|
||||
</script>
|
||||
|
||||
<p />
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,101 @@
|
|||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>Working with Cabinet Files</title>
|
||||
<link rel="stylesheet" type="text/css" href="../styles/presentation.css" />
|
||||
<link rel="stylesheet" type="text/css" href="ms-help://Hx/HxRuntime/HxLink.css" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="control">
|
||||
<span class="productTitle">Deployment Tools Foundation</span><br />
|
||||
<span class="topicTitle">Working with Cabinet Files</span><br />
|
||||
<div id="toolbar">
|
||||
<span id="chickenFeet">
|
||||
<a href="using.htm">Development Guide</a> >
|
||||
<span class="nolink">Cabinet Files</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div id="main">
|
||||
<div id="header">
|
||||
</div>
|
||||
<div class="summary">
|
||||
|
||||
<h3>Creating a cabinet</h3>
|
||||
<pre><font face="Consolas, Courier New"> CabInfo cabInfo = <font color="blue">new</font> CabInfo(<font color="purple">"package.cab"</font>);
|
||||
cabInfo.Pack(<font color="purple">"D:\\FilesToCompress"</font>);</font></pre><br />
|
||||
<p>1. Create a <a href="DTFAPI.chm::/html/M_Microsoft_Deployment_Compression_Cab_CabInfo__ctor_1.htm">new CabInfo</a> instance referring to the (future) location of the .cab file.</p>
|
||||
<p>2. Compress files:</p><ul>
|
||||
<li>Easily compress an entire directory with the <a href="DTFAPI.chm::/html/Overload_Microsoft_Deployment_Compression_ArchiveInfo_Pack.htm">Pack</a> method.</li>
|
||||
<li>Compress a specific list of exernal and internal filenames with the <a href="DTFAPI.chm::/html/Overload_Microsoft_Deployment_Compression_ArchiveInfo_PackFiles.htm">PackFiles</a> method.</li>
|
||||
<li>Compress a dictionary mapping of internal to external filenames with the <a href="DTFAPI.chm::/html/Overload_Microsoft_Deployment_Compression_ArchiveInfo_PackFileSet.htm">PackFileSet</a> method.</li>
|
||||
</ul>
|
||||
|
||||
<p><br/></p>
|
||||
<h3>Listing a cabinet</h3>
|
||||
<pre><font face="Consolas, Courier New"> CabInfo cabInfo = <font color="blue">new</font> CabInfo(<font color="purple">"package.cab"</font>);
|
||||
<font color="blue">foreach</font> (CabFileInfo fileInfo <font color="blue">in</font> cabInfo.GetFiles())
|
||||
Console.WriteLine(fileInfo.Name + <font color="purple">"\t"</font> + fileInfo.Length);</font></pre><br />
|
||||
<p>1. Create a <a href="DTFAPI.chm::/html/M_Microsoft_Deployment_Compression_Cab_CabInfo__ctor_1.htm">new CabInfo</a> instance referring to the location of the .cab file.</p>
|
||||
<p>2. Enumerate files returned by the <a href="DTFAPI.chm::/html/Overload_Microsoft_Deployment_Compression_Cab_CabInfo_GetFiles.htm">GetFiles</a> method.</p><ul>
|
||||
<li>Each <a href="DTFAPI.chm::/html/T_Microsoft_Deployment_Compression_Cab_CabFileInfo.htm">CabFileInfo</a> instance contains metadata about one file.</li>
|
||||
</ul>
|
||||
|
||||
<p><br/></p>
|
||||
<h3>Extracting a cabinet</h3>
|
||||
<pre><font face="Consolas, Courier New"> CabInfo cabInfo = <font color="blue">new</font> CabInfo(<font color="purple">"package.cab"</font>);
|
||||
cabInfo.Unpack(<font color="purple">"D:\\ExtractedFiles"</font>);</font></pre><br />
|
||||
<p>1. Create a <a href="DTFAPI.chm::/html/M_Microsoft_Deployment_Compression_Cab_CabInfo__ctor_1.htm">new CabInfo</a> instance referring to the location of the .cab file.</p>
|
||||
<p>2. Extract files:</p><ul>
|
||||
<li>Easily extract all files to a directory with the <a href="DTFAPI.chm::/html/Overload_Microsoft_Deployment_Compression_ArchiveInfo_Unpack.htm">Unpack</a> method.</li>
|
||||
<li>Easily extract a single file with the <a href="DTFAPI.chm::/html/M_Microsoft_Deployment_Compression_ArchiveInfo_UnpackFile.htm">UnpackFile</a> method.</li>
|
||||
<li>Extract a specific list of filenames with the <a href="DTFAPI.chm::/html/Overload_Microsoft_Deployment_Compression_ArchiveInfo_UnpackFiles.htm">UnpackFiles</a> method.</li>
|
||||
<li>Extract a dictionary mapping of internal to external filenames with the <a href="DTFAPI.chm::/html/Overload_Microsoft_Deployment_Compression_ArchiveInfo_UnpackFileSet.htm">UnpackFileSet</a> method.</li>
|
||||
</ul>
|
||||
|
||||
<p><br/></p>
|
||||
<h3>Getting progress</h3>
|
||||
Most cabinet operation methods have an overload that allows you to specify a event handler
|
||||
for receiving <a href="DTFAPI.chm::/html/T_Microsoft_Deployment_Compression_ArchiveProgressEventArgs.htm">archive
|
||||
progress events</a>. The <a href="cabpack.htm">XPack sample</a>
|
||||
demonstrates use of the callback to report detailed progress to the console.
|
||||
|
||||
<p><br/></p>
|
||||
<h3>Stream-based compression</h3>
|
||||
The CabEngine class contains static methods for performing compression/decompression operations directly
|
||||
on any kind of Stream. However these methods are more difficult to use, since the caller must implement a
|
||||
<a href="DTFAPI.chm::/html/T_Microsoft_Deployment_Compression_ArchiveFileStreamContext.htm">stream context</a>
|
||||
that provides the file metadata which would otherwise have been provided by the filesystem. The CabInfo class
|
||||
uses the CabEngine class with FileStreams to provide the more traditional file-based interface.
|
||||
|
||||
<p><br/></p>
|
||||
<p><b>See also:</b></p>
|
||||
<ul>
|
||||
<li><a href="DTFAPI.chm::/html/T_Microsoft_Deployment_Compression_Cab_CabInfo.htm">CabInfo class</a></li>
|
||||
<li><a href="DTFAPI.chm::/html/T_Microsoft_Deployment_Compression_Cab_CabEngine.htm">CabEngine class</a></li>
|
||||
<li><a href="cabpack.htm">XPack Sample Tool</a></li>
|
||||
</ul>
|
||||
<p><br/></p>
|
||||
|
||||
</div>
|
||||
|
||||
<div id="footer">
|
||||
<p />
|
||||
Send comments on this topic to <a id="HT_MailLink" href="mailto:wix-users%40lists.sourceforge.net?Subject=Deployment Tools Foundation Documentation">
|
||||
wix-users@lists.sourceforge.net</a>
|
||||
|
||||
<script type="text/javascript">
|
||||
var HT_mailLink = document.getElementById("HT_MailLink");
|
||||
var HT_mailLinkText = HT_mailLink.innerHTML;
|
||||
HT_mailLink.href += ": " + document.title;
|
||||
HT_mailLink.innerHTML = HT_mailLinkText;
|
||||
</script>
|
||||
|
||||
<p />
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,63 @@
|
|||
<!doctype HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" >
|
||||
<html>
|
||||
<head>
|
||||
<title>Managed Wrapper Library for Cabinet APIs</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=Windows-1252">
|
||||
<link rel="stylesheet" type="text/css" href="MSDN.css">
|
||||
</head>
|
||||
<body id="bodyID" class="dtBODY">
|
||||
<div id="nsbanner">
|
||||
<div id="bannerrow1">
|
||||
<table class="bannerparthead" cellspacing="0" id="Table1">
|
||||
<tr id="hdr">
|
||||
<td class="runninghead">Managed Libraries for Windows Installer</td>
|
||||
<td class="product"></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div id="TitleRow">
|
||||
<h1 class="dtH1">Managed Wrapper Library for Cabinet APIs</h1>
|
||||
</div>
|
||||
</div>
|
||||
<div id="nstext">
|
||||
<p>This is a managed library that provides the ability to
|
||||
create and extract cabinet files. It uses cabinet.dll (present on all versions of Windows)
|
||||
to do the actual compression/decompression. It provides access to nearly all
|
||||
cabinet capabilities, including spanning of multiple cab files. It even has support for
|
||||
preserving directory structures and UTF8 paths.</p>
|
||||
<p>There are two ways to use the library. <a href="ms-its:MMLRef.chm::/Microsoft.Cab.CabinetInfo.html">CabinetInfo</a>
|
||||
and <a href="ms-its:MMLRef.chm::/Microsoft.Cab.CabinetFileInfo.html">CabinetFileInfo</a>
|
||||
(similar to DirectoryInfo and FileInfo respectively)
|
||||
provide high-level object-oriented methods for doing common file-based cabinet creation and
|
||||
extraction tasks. On the other hand, the <a href="ms-its:MMLRef.chm::/Microsoft.Cab.Cabinet.html">Cabinet</a>
|
||||
class provides low-level access to all
|
||||
functionality, and operates completely in terms of .NET Streams. The previous two classes use
|
||||
the Cabinet class to do all the actual work.</p>
|
||||
<p>There are also two ways to build the library.
|
||||
Compiling it normally will produce the fully functional
|
||||
library in the <a href="ms-its:MMLRef.chm::/Microsoft.Cab.html">Microsoft.Cab</a>
|
||||
namespace, while compiling it with the <tt>/D:CABMINIMAL
|
||||
/D:CABEXTRACTONLY</tt> flags will create a compact assembly with only the core extraction
|
||||
functionality, in the <a href="ms-its:MMLRef.chm::/Microsoft.Cab.MiniExtract.html">Microsoft.Cab.MiniExtract</a>
|
||||
namespace.</p>
|
||||
<p>The cabinet library interops with native cabinet APIs which use the 'cdecl'
|
||||
calling-convention. When building against .NET Framework versions before 2.0,
|
||||
this library requires a special post-build step to process the UnmanagedFunctionPointerAttribute.
|
||||
If you use this code in another assembly, don't forget to run <a href="augmentil.htm">AugmentIL</a>
|
||||
on it to fix the delegate calling-conventions, otherwise you will encounter a
|
||||
NullReferenceException when attempting to call the cabinet APIs. When building against
|
||||
.NET Framework version 2.0 or later, the UnmanagedFunctionPointerAttribute.cs source file
|
||||
should be omitted.</p>
|
||||
|
||||
<p><br/></p>
|
||||
<p><b>See also:</b></p>
|
||||
<ul>
|
||||
<li><a href="cabs.htm">Working with Cabinet Files</a></li>
|
||||
<li><a href="ms-its:MMLRef.chm::/Microsoft.Cab.CabinetInfoMethods.html">CabinetInfo Methods</a></li>
|
||||
<li><a href="ms-its:MMLRef.chm::/Microsoft.Cab.CabinetMethods.html">Cabinet Methods</a></li>
|
||||
<li><a href="cabpack.htm">CabPack Sample Tool</a></li>
|
||||
</ul>
|
||||
<p><br/></p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,83 @@
|
|||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>Specifying the Runtime Version</title>
|
||||
<link rel="stylesheet" type="text/css" href="../styles/presentation.css" />
|
||||
<link rel="stylesheet" type="text/css" href="ms-help://Hx/HxRuntime/HxLink.css" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="control">
|
||||
<span class="productTitle">Deployment Tools Foundation</span><br />
|
||||
<span class="topicTitle">Specifying the Runtime Version</span><br />
|
||||
<div id="toolbar">
|
||||
<span id="chickenFeet">
|
||||
<a href="using.htm">Development Guide</a> >
|
||||
<a href="managedcas.htm">Managed CAs</a> >
|
||||
<a href="writingcas.htm">Writing CAs</a> >
|
||||
<span class="nolink">CustomAction.config</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div id="main">
|
||||
<div id="header">
|
||||
</div>
|
||||
<div class="summary">
|
||||
|
||||
<p>Every managed custom action package should contain a CustomAction.config file, even though it is not required by the toolset.
|
||||
Here is a sample:</p><pre><font face="Consolas, Courier New">
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<configuration>
|
||||
<startup>
|
||||
<supportedRuntime version="v2.0.50727"/>
|
||||
</startup>
|
||||
</configuration></font></pre><br />
|
||||
<p>The configuration file follows the standard schema for .NET Framework
|
||||
configuration files <a target=_blank href="http://msdn2.microsoft.com/en-us/library/9w519wzk(VS.80).aspx">documented on MSDN</a>.</p>
|
||||
<p><br/></p>
|
||||
<p><b>Supported Runtime Version</b></p>
|
||||
<p>In the startup section, use <a target=_blank href="http://msdn2.microsoft.com/en-us/library/w4atty68(VS.80).aspx">supportedRuntime</a>
|
||||
tags to explicitly specify the version(s) of the .NET Framework that the custom action should run on.
|
||||
If no versions are specified, the chosen version of the .NET Framework will be
|
||||
the "best" match to what Microsoft.Deployment.WindowsInstaller.dll was built against.</p>
|
||||
<p><font color="red"><b>Warning: leaving the version unspecified is dangerous</b></font>
|
||||
as it introduces a risk of compatibility problems with future versions of the .NET Framework.
|
||||
It is highly recommended that you specify only the version(s)
|
||||
of the .NET Framework that you have tested against.</p>
|
||||
<p><br/></p>
|
||||
|
||||
<p><b>Other Configuration</b></p>
|
||||
<p>Various other kinds of configuration settings may also be added to this file, as it is a standard
|
||||
<a target=_blank href="http://msdn2.microsoft.com/en-us/library/kza1yk3a(VS.80).aspx">.NET Framework application config file</a>
|
||||
for the custom action.</p>
|
||||
<p><br/></p>
|
||||
|
||||
<p><b>See also:</b></p>
|
||||
<ul>
|
||||
<li><a href="writingcas.htm">Writing Managed Custom Actions</a></li>
|
||||
<li><a href="buildingcas.htm">Building Managed Custom Actions</a></li>
|
||||
<li><a href="caproxy.htm">Proxy for Managed Custom Actions</a></li>
|
||||
</ul>
|
||||
<p><br/></p>
|
||||
|
||||
</div>
|
||||
|
||||
<div id="footer">
|
||||
<p />
|
||||
Send comments on this topic to <a id="HT_MailLink" href="mailto:wix-users%40lists.sourceforge.net?Subject=Deployment Tools Foundation Documentation">
|
||||
wix-users@lists.sourceforge.net</a>
|
||||
|
||||
<script type="text/javascript">
|
||||
var HT_mailLink = document.getElementById("HT_MailLink");
|
||||
var HT_mailLinkText = HT_mailLink.innerHTML;
|
||||
HT_mailLink.href += ": " + document.title;
|
||||
HT_mailLink.innerHTML = HT_mailLinkText;
|
||||
</script>
|
||||
|
||||
<p />
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,74 @@
|
|||
<!doctype HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" >
|
||||
<html>
|
||||
<head>
|
||||
<title>Proxy Class for Managed Custom Actions</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=Windows-1252">
|
||||
<link rel="stylesheet" type="text/css" href="MSDN.css">
|
||||
</head>
|
||||
<body id="bodyID" class="dtBODY">
|
||||
<div id="nsbanner">
|
||||
<div id="bannerrow1">
|
||||
<table class="bannerparthead" cellspacing="0" id="Table1">
|
||||
<tr id="hdr">
|
||||
<td class="runninghead">Managed Libraries for Windows Installer</td>
|
||||
<td class="product"></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div id="TitleRow">
|
||||
<h1 class="dtH1">Proxy for Managed Custom Actions</h1>
|
||||
</div>
|
||||
</div>
|
||||
<div id="nstext">
|
||||
<p>The custom action proxy allows an MSI developer to write
|
||||
custom actions in managed code, while maintaing all the advantages of type 1
|
||||
DLL custom actions including full access to installer state, properties,
|
||||
and the session database.</p>
|
||||
<p>There are generally four problems that needed to be
|
||||
solved in order to create a type 1 custom action in managed code:</p>
|
||||
<ol>
|
||||
<li><p><strong>Exporting the CA function as a native entry point callable by
|
||||
MSI:</strong> The Windows Installer engine expects to call a LoadLibrary and
|
||||
GetProcAddress on the custom action DLL, so an unmanaged DLL needs to implement
|
||||
the function that is initially called by MSI and ultimately returns the result.
|
||||
This function acts as a proxy to relay the custom action call into the
|
||||
managed custom action assembly, and relay the result back to the caller. </p>
|
||||
<li><strong>Providing supporting assemblies without
|
||||
requiring them to be installed as files:</strong> If a DLL custom
|
||||
action runs before the product's files are installed, then it is difficult
|
||||
to provide any supporting files, because of the way the CA DLL is singly
|
||||
extracted and executed from a temp file. (This can be a problem for
|
||||
unmanaged CAs as well.) With managed custom actions we have already hit
|
||||
that problem since both the CA assembly and the MSI wrapper assembly
|
||||
need to be loaded. To solve this, the proxy DLL carries an appended
|
||||
cab package. When invoked, it will extract all contents of the
|
||||
cab package to a temporary working directory. This way the cab package can
|
||||
carry any arbitrary dependencies the custom action may require.</li>
|
||||
<li><p><strong>Hosting and configuring the Common Language Runtime:</strong>
|
||||
In order to invoke a method in a managed assembly from a previously
|
||||
unmanaged process, the CLR needs to be "hosted". This involves choosing
|
||||
the correct version of the .NET Framework to use out of the available
|
||||
version(s) on the system, binding that version to the current process, and
|
||||
configuring it to load assemblies from the temporary working directory.</p>
|
||||
<li><p><strong>Converting the integer session handle into a
|
||||
Session object:</strong> The <a href="">Session</a> class in the managed
|
||||
wrapper library has a constructor which takes an integer session handle as
|
||||
its parameter. So the proxy simply instantiates this object before
|
||||
calling the real CA function.</p>
|
||||
</ol>
|
||||
<p>The unmanaged CAPack module, when used in combination with the managed proxy in
|
||||
the
|
||||
Microsoft.WindowsInstaller assembly, accomplishes the tasks above to enable
|
||||
fully-functional managed DLL custom actions.</p>
|
||||
<p><br/></p>
|
||||
<p><b>See also:</b></p>
|
||||
<ul>
|
||||
<li><a href="writingcas.htm">Writing Managed Custom Actions</a></li>
|
||||
<li><a href="caconfig.htm">Writing the CustomAction.config file</a></li>
|
||||
<li><a href="samplecas.htm">Sample C# Custom Actions</a></li>
|
||||
<li><a href="buildingcas.htm">Building Managed Custom Actions</a></li>
|
||||
</ul>
|
||||
<p><br/></p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,120 @@
|
|||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>Working with MSI Databases</title>
|
||||
<link rel="stylesheet" type="text/css" href="../styles/presentation.css" />
|
||||
<link rel="stylesheet" type="text/css" href="ms-help://Hx/HxRuntime/HxLink.css" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="control">
|
||||
<span class="productTitle">Deployment Tools Foundation</span><br />
|
||||
<span class="topicTitle">Working with MSI Databases</span><br />
|
||||
<div id="toolbar">
|
||||
<span id="chickenFeet">
|
||||
<a href="using.htm">Development Guide</a> >
|
||||
<span class="nolink">MSI Databases</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div id="main">
|
||||
<div id="header">
|
||||
</div>
|
||||
<div class="summary">
|
||||
|
||||
<h3>Querying a database</h3>
|
||||
<pre><font face="Consolas, Courier New"> <font color=blue>using</font> (Database db = <font color=blue>new</font> Database(<font color="purple">"product.msi"</font>, DatabaseOpenMode.ReadOnly))
|
||||
{
|
||||
<font color=blue>string</font> value = (<font color=blue>string</font>) db.ExecuteScalar(
|
||||
<font color="purple">"SELECT `Value` FROM `Property` WHERE `Property` = '{0}'"</font>, propName);
|
||||
}</font></pre><br />
|
||||
<p>1. Create a <a href="DTFAPI.chm::/html/Overload_Microsoft_Deployment_WindowsInstaller_Database__ctor.htm">new Database</a>
|
||||
instance referring to the location of the .msi or .msm file.</p>
|
||||
<p>2. Execute the query:</p><ul>
|
||||
<li>The <a href="DTFAPI.chm::/html/Overload_Microsoft_Deployment_WindowsInstaller_Database_ExecuteScalar.htm">ExecuteScalar</a>
|
||||
method is a shortcut for opening a view, executing the view, and fetching a single value.</li>
|
||||
<li>The <a href="DTFAPI.chm::/html/Overload_Microsoft_Deployment_WindowsInstaller_Database_ExecuteQuery.htm">ExecuteQuery</a>
|
||||
method is a shortcut for opening a view, executing the view, and fetching all values.</li>
|
||||
<li>Or do it all manually with <a href="DTFAPI.chm::/html/M_Microsoft_Deployment_WindowsInstaller_Database_OpenView.htm">Database.OpenView</a>,
|
||||
<a href="DTFAPI.chm::/html/Overload_Microsoft_Deployment_WindowsInstaller_View_Execute.htm">View.Execute</a>, and
|
||||
<a href="DTFAPI.chm::/html/M_Microsoft_Deployment_WindowsInstaller_View_Fetch.htm">View.Fetch</a>.</li>
|
||||
</ul>
|
||||
|
||||
<p><br/></p>
|
||||
<h3>Updating a binary</h3>
|
||||
<pre><font face="Consolas, Courier New"> Database db = <font color=blue>null</font>;
|
||||
View view = <font color=blue>null</font>;
|
||||
Record rec = <font color=blue>null</font>;
|
||||
<font color=blue>try</font>
|
||||
{
|
||||
db = <font color=blue>new</font> Database(<font color="purple">"product.msi"</font>, DatabaseOpenMode.Direct);
|
||||
view = db.OpenView(<font color="purple">"UPDATE `Binary` SET `Data` = ? WHERE `Name` = '{0}'"</font>, binName))
|
||||
rec = <font color=blue>new</font> Record(1);
|
||||
rec.SetStream(1, binFile);
|
||||
view.Execute(rec);
|
||||
db.Commit();
|
||||
}
|
||||
<font color=blue>finally</font>
|
||||
{
|
||||
<font color=blue>if</font> (rec != <font color=blue>null</font>) rec.Close();
|
||||
<font color=blue>if</font> (view != <font color=blue>null</font>) view.Close();
|
||||
<font color=blue>if</font> (db != <font color=blue>null</font>) db.Close();
|
||||
}</font></pre><br />
|
||||
<p>1. Create a <a href="DTFAPI.chm::/html/Overload_Microsoft_Deployment_WindowsInstaller_Database__ctor.htm">new Database</a>
|
||||
instance referring to the location of the .msi or .msm file.</p>
|
||||
<p>2. Open a view by calling one of the <a href="DTFAPI.chm::/html/M_Microsoft_Deployment_WindowsInstaller_Database_OpenView.htm">Database.OpenView</a> overloads.</p><ul>
|
||||
<li>Parameters can be substituted in the SQL string using the String.Format syntax.</li>
|
||||
</ul>
|
||||
<p>3. Create a record with one field containing the new binary value.</p>
|
||||
<p>4. Execute the view by calling one of the <a href="DTFAPI.chm::/html/Overload_Microsoft_Deployment_WindowsInstaller_View_Execute.htm">View.Execute</a> overloads.</p><ul>
|
||||
<li>A record can be supplied for substitution of field tokens (?) in the query.</li>
|
||||
</ul>
|
||||
<p>5. <a href="DTFAPI.chm::/html/M_Microsoft_Deployment_WindowsInstaller_Database_Commit.htm">Commit</a> the Database.</p>
|
||||
<p>6. <a href="DTFAPI.chm::/html/M_Microsoft_Deployment_WindowsInstaller_InstallerHandle_Close.htm">Close</a> the handles.</p>
|
||||
|
||||
<p><br/></p>
|
||||
<h3>About handles</h3>
|
||||
<p>Handle objects (Database, View, Record, SummaryInfo, Session) will remain open until
|
||||
they are explicitly closed or until the objects are collected by the GC. So for the tightest
|
||||
code, handle objects should be explicitly closed when they are no longer needed,
|
||||
since closing them can release significant resources, and too many unnecessary
|
||||
open handles can degrade performance. This is especially important within a loop
|
||||
construct: for example when iterating over all the Records in a table, it is much cleaner
|
||||
and faster to close each Record after it is used.</p>
|
||||
<p>The handle classes in the managed library all extend the
|
||||
<a href="DTFAPI.chm::/html/T_Microsoft_Deployment_WindowsInstaller_InstallerHandle.htm">InstallerHandle</a>
|
||||
class, which implements the IDisposable interface. This makes them easily managed with C#'s
|
||||
using statement. Alternatively, they can be closed in a finally block.</p>
|
||||
<p>As a general rule, <i>methods</i> in the library return new handle objects that should be managed
|
||||
and closed by the calling code, while <i>properties</i> only return a reference to a prexisting handle
|
||||
object.</p>
|
||||
|
||||
<p><br/></p>
|
||||
<p><b>See also:</b></p>
|
||||
<ul>
|
||||
<li><a href="powerdiff.htm">MSI Diff Sample Tool</a></li>
|
||||
<li><a href="DTFAPI.chm::/html/T_Microsoft_Deployment_WindowsInstaller_Database.htm">Database Class</a></li>
|
||||
</ul>
|
||||
<p><br/></p>
|
||||
|
||||
</div>
|
||||
|
||||
<div id="footer">
|
||||
<p />
|
||||
Send comments on this topic to <a id="HT_MailLink" href="mailto:wix-users%40lists.sourceforge.net?Subject=Deployment Tools Foundation Documentation">
|
||||
wix-users@lists.sourceforge.net</a>
|
||||
|
||||
<script type="text/javascript">
|
||||
var HT_mailLink = document.getElementById("HT_MailLink");
|
||||
var HT_mailLinkText = HT_mailLink.innerHTML;
|
||||
HT_mailLink.href += ": " + document.title;
|
||||
HT_mailLink.innerHTML = HT_mailLinkText;
|
||||
</script>
|
||||
|
||||
<p />
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,66 @@
|
|||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>Debugging Managed Custom Actions</title>
|
||||
<link rel="stylesheet" type="text/css" href="../styles/presentation.css" />
|
||||
<link rel="stylesheet" type="text/css" href="ms-help://Hx/HxRuntime/HxLink.css" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="control">
|
||||
<span class="productTitle">Deployment Tools Foundation</span><br />
|
||||
<span class="topicTitle">Debugging Managed Custom Actions</span><br />
|
||||
<div id="toolbar">
|
||||
<span id="chickenFeet">
|
||||
<a href="using.htm">Development Guide</a> >
|
||||
<a href="managedcas.htm">Managed CAs</a> >
|
||||
<span class="nolink">Debugging</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div id="main">
|
||||
<div id="header">
|
||||
</div>
|
||||
<div class="summary">
|
||||
<p>There are two ways to attach a debugger to a managed custom action.</p>
|
||||
<p><b>Attach to message-box:</b> Add some temporary code to your custom action to display a
|
||||
message box. Then when the message box pops up at install time, you can attch your
|
||||
debugger to that process (usually identifiable by the title of the message box).
|
||||
Once attached, you can ensure that symbols are loaded if necessary (they will be automatically
|
||||
loaded if PDB files were embedded in the CA assembly at build time), then set breakpoints
|
||||
anywhere in the custom action code.</p>
|
||||
<p><b>MMsiBreak environment variable:</b> When debugging <i>managed</i> custom actions,
|
||||
you should use the MMsiBreak environment variable instead of MsiBreak. Set the MMsiBreak
|
||||
variable to the custom action entrypoint name. (Remember this might be different from
|
||||
the method name if it was overridden by the CustomActionAttribute.) When the CA proxy
|
||||
finds a matching name, the CLR JIT-debugging dialog
|
||||
will appear with text similar to "An exception 'Launch for user' has occurred
|
||||
in <i>YourCustomActionName</i>." The debug break occurs after the custom
|
||||
action assembly has been loaded, but just before custom action method is invoked.
|
||||
Once attached, you can ensure that symbols are loaded if necessary,
|
||||
then set breakpoints anywhere in the custom action code. Note: the MMsiBreak
|
||||
environment variable can also accept a comma-separated list of action names, any of
|
||||
which will cause a break when hit.</p>
|
||||
<p><br/></p>
|
||||
|
||||
</div>
|
||||
|
||||
<div id="footer">
|
||||
<p />
|
||||
Send comments on this topic to <a id="HT_MailLink" href="mailto:wix-users%40lists.sourceforge.net?Subject=Deployment Tools Foundation Documentation">
|
||||
wix-users@lists.sourceforge.net</a>
|
||||
|
||||
<script type="text/javascript">
|
||||
var HT_mailLink = document.getElementById("HT_MailLink");
|
||||
var HT_mailLinkText = HT_mailLink.innerHTML;
|
||||
HT_mailLink.href += ": " + document.title;
|
||||
HT_mailLink.innerHTML = HT_mailLinkText;
|
||||
</script>
|
||||
|
||||
<p />
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,88 @@
|
|||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>Dependencies</title>
|
||||
<link rel="stylesheet" type="text/css" href="../styles/presentation.css" />
|
||||
<link rel="stylesheet" type="text/css" href="ms-help://Hx/HxRuntime/HxLink.css" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="control">
|
||||
<span class="productTitle">Deployment Tools Foundation</span><br />
|
||||
<span class="topicTitle">Dependencies</span><br />
|
||||
<div id="toolbar">
|
||||
<span id="chickenFeet">
|
||||
<a href="about.htm">Overview</a> >
|
||||
<span class="nolink">Dependencies</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div id="main">
|
||||
<div id="header">
|
||||
</div>
|
||||
<div class="summary">
|
||||
|
||||
<p>This page lists all the components that the DTF project depends on, at build time and at run-time.</p>
|
||||
|
||||
<h3>Build-time Dependencies</h3>
|
||||
<ul>
|
||||
<li><p><b>Visual Studio / .NET Framework</b> - Most of DTF can be built with Visual Studio 2005 &
|
||||
.NET Framework 2.0. However, the LINQ project requires VS 2008 & .NET Framework 3.5.</p></li>
|
||||
|
||||
<li><p><b>Sandcastle</b> - .NET documentation build engine from Microsoft, used to process all the XML doc-comments
|
||||
in DTF libraries into DTFAPI.chm.
|
||||
<a href="http://www.codeplex.com/Sandcastle/" target="_blank">(official site)</a></p></li>
|
||||
|
||||
<li><p><b>Sandcastle Builder</b> - Sandcastle by itself is complex and difficult to use; this free tool
|
||||
from Codeplex provides an easy-to-use project system around it to automate the documentation build process.
|
||||
<a href="http://www.codeplex.com/SHFB/" target="_blank">(project link)</a></p></li>
|
||||
|
||||
<li><p><b>HTML Help Workshop</b> - Tools for building HTML Help 1.x (CHM files). Used to build DTF.chm.
|
||||
<a href="http://msdn2.microsoft.com/en-us/library/ms669985.aspx" target="_blank">(download link)</a></p></li>
|
||||
</ul>
|
||||
|
||||
<h3>Run-time Dependencies</h3>
|
||||
<ul>
|
||||
<li><p><b>.NET Framework</b> - Most of DTF requires .NET Framework 2.0. (.NET 1.1 is no longer supported.)
|
||||
The only exception is the LINQ assembly which requires .NET Framework 3.5.</p></li>
|
||||
|
||||
<li><p><b>Windows Installer</b> - Windows Installer introduced new APIs and capabilities with each successive
|
||||
version. Obviously, the corresponding functionality in the managed APIs is only available when the required
|
||||
version of the Windows Instaler (msi.dll) is installed on the system. Use the Installer.Version property
|
||||
to easily check the currently installed MSI version. Attempting to use an API not supported by the current
|
||||
version will result in an EntryPointNotFoundException. To check what version is required for a particular API,
|
||||
see the documentation link to the corresponding unmanaged API in MSI.chm.</p>
|
||||
<p>In some instances when a newer version of MSI provides an "Ex" alternative to a function, only the "Ex"
|
||||
function is used by the managed library. This may hide some functionality that would have otherwise been
|
||||
available on a system with an older version of MSI.</p></li>
|
||||
|
||||
<li><p><b>cabinet.dll</b> - The DTF cabinet compression library uses cabinet.dll to implement the
|
||||
low-level cabinet compression and decompression. This DLL is part of all versions of Windows,
|
||||
located in the system directory.</p></li>
|
||||
|
||||
<li><p><b>System.IO.Compression.DeflateStream</b> - The DTF zip compression library uses this class
|
||||
to implement the low-level zip compression and decompression. This class is part of .NET Framework
|
||||
2.0 and later.</p></li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
|
||||
<div id="footer">
|
||||
<p />
|
||||
Send comments on this topic to <a id="HT_MailLink" href="mailto:wix-users%40lists.sourceforge.net?Subject=Deployment Tools Foundation Documentation">
|
||||
wix-users@lists.sourceforge.net</a>
|
||||
|
||||
<script type="text/javascript">
|
||||
var HT_mailLink = document.getElementById("HT_MailLink");
|
||||
var HT_mailLinkText = HT_mailLink.innerHTML;
|
||||
HT_mailLink.href += ": " + document.title;
|
||||
HT_mailLink.innerHTML = HT_mailLinkText;
|
||||
</script>
|
||||
|
||||
<p />
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,34 @@
|
|||
<!doctype HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" >
|
||||
<html>
|
||||
<head>
|
||||
<title>Managed Wrapper for Binary File Patch APIs</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=Windows-1252">
|
||||
<link rel="stylesheet" type="text/css" href="MSDN.css">
|
||||
</head>
|
||||
<body id="bodyID" class="dtBODY">
|
||||
<div id="nsbanner">
|
||||
<div id="bannerrow1">
|
||||
<table class="bannerparthead" cellspacing="0" id="Table1">
|
||||
<tr id="hdr">
|
||||
<td class="runninghead">Managed Libraries for Windows Installer</td>
|
||||
<td class="product"></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div id="TitleRow">
|
||||
<h1 class="dtH1">Managed Wrapper for Binary File Patch APIs</h1>
|
||||
</div>
|
||||
</div>
|
||||
<div id="nstext">
|
||||
<p>The binary file patch creation and application APIs (supplied by MsPatchC.dll and
|
||||
MsPatchA.dll) are wrapped in the Microsoft.WindowsInstaller.FilePatch.dll assembly.</p>
|
||||
|
||||
<p><br/></p>
|
||||
<p><b>See also:</b></p>
|
||||
<ul>
|
||||
<li><a href="ms-its:MMLRef.chm::/Microsoft.WindowsInstaller.FilePatch.html">FilePatch Class</a></li>
|
||||
</ul>
|
||||
<p><br/></p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,437 @@
|
|||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>Change History</title>
|
||||
<link rel="stylesheet" type="text/css" href="../styles/presentation.css" />
|
||||
<link rel="stylesheet" type="text/css" href="ms-help://Hx/HxRuntime/HxLink.css" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="control">
|
||||
<span class="productTitle">Deployment Tools Foundation</span><br />
|
||||
<span class="topicTitle">Change History</span><br />
|
||||
<div id="toolbar">
|
||||
<span id="chickenFeet">
|
||||
<a href="about.htm">Overview</a> >
|
||||
<span class="nolink">Change History</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div id="main">
|
||||
<div id="header">
|
||||
</div>
|
||||
<div class="summary">
|
||||
|
||||
<h3><b>2007-07-03</b></h3>
|
||||
<i>See <a href="whatsnew.htm">What's New?</a></i><br /> <br /> <br />
|
||||
<hr size="2"/>
|
||||
<h3><b>2005-03-30</b></h3>
|
||||
|
||||
<ul>
|
||||
<li>New custom action proxy<ul>
|
||||
<li><b>Managed custom actions use an XML config file to specify the CLR version.</b></li>
|
||||
<li>New CAPack module is an unmanaged self-extracting CA DLL that can wrap both
|
||||
managed and unmanaged custom actions. (The old managed CAProxy module is obsolete.)</li>
|
||||
<li>Custom action build process is different but still complicated --
|
||||
see documentation for details.</li>
|
||||
<li>CustomActionAttribute no longer accepts the optional NativeDependencies
|
||||
parameter since it does not apply to the new proxy (all packaged files
|
||||
are always extracted and available when the CA executes). </li>
|
||||
</ul></li>
|
||||
|
||||
<li>64bit support<ul>
|
||||
<li>Various code fixes to pointer/handle types and structure alignments.</li>
|
||||
<li>Cabinet and MSI libraries tested on AMD64 CLR.</li>
|
||||
<li>Unmanaged and managed parts of custom action proxy tested on AMD64.</li>
|
||||
</ul></li>
|
||||
|
||||
<li>MSI 3.1 APIs added:<ul>
|
||||
<li>Installer.SetExternalUI(ExternalUIRecordHandler)</li>
|
||||
<li>Installer.NotifySidChange</li>
|
||||
</ul></li>
|
||||
|
||||
<li>Code builds easier with .NET Famework 2.0<ul>
|
||||
<li>AugmentIL post-build step is no longer necessary when compiling the cabinet code
|
||||
against .NET Framework 2.0, which has builtin support for cdecl delegates.</li>
|
||||
<li>All C# code compiles against .NET Framework 2.0 without obsolete warnings,
|
||||
when setting the NETFX2 preprocessor define.</li>
|
||||
<li>Same code is still compatible with .NET Framework 1.0 + AugmentIL.</li>
|
||||
</ul></li>
|
||||
|
||||
<li>Miscellaneous bugfixes/changes:<ul>
|
||||
<li>InstallPackage.ExtractFiles could fail in some cominations of
|
||||
compressed/uncompressed files - fixed.</li>
|
||||
<li>Installer.DeterminePatchSequence was broken due to an incorrect interop struct - fixed.</li>
|
||||
<li>CabinetInfo and CabinetFileInfo classes made serializable.</li>
|
||||
<li>Added Session.FormatString method to simplify formatting a string with
|
||||
property substitutions.</li>
|
||||
</ul></li>
|
||||
<li>Documentation updates:<ul>
|
||||
<li>Updated all documentation for new CA proxy.</li>
|
||||
<li>Added new topic discussing InstallUtil.</li>
|
||||
</ul></li>
|
||||
</ul>
|
||||
|
||||
<hr size="2"/>
|
||||
<h3><b>2004-04-13</b></h3>
|
||||
|
||||
<ul>
|
||||
<li>Documentation<ul>
|
||||
<li>Consolidated all documentation into a single CHM file.</li>
|
||||
<li>Added new topics about working with MSI databases & cabinet files,
|
||||
to help new users get oriented more easily.</li>
|
||||
</ul></li>
|
||||
|
||||
<li>WindowsInstaller<ul>
|
||||
<li>Removed [Beta] tags from MSI 3.0 APIs, but otherwise there
|
||||
have been no changes since 3.0 Beta 1.<ul>
|
||||
<li>Be warned these are still the least-tested parts of
|
||||
the library, so early users may encounter bugs.</li>
|
||||
</ul></li>
|
||||
</ul></li>
|
||||
|
||||
<li>InstallPackage<ul>
|
||||
<li>Fixed InstallPackage.ExtractFiles() bug when directory doesn't exist.</li>
|
||||
<li>Added ability to handle uncompressed files in a package marked as compressed.</li>
|
||||
</ul></li>
|
||||
|
||||
<li>Cabinet<ul>
|
||||
<li>Fixed improper handling of file attributes.<ul>
|
||||
<li>This bug caused some packages to not be extractable by other tools.</li>
|
||||
</ul></li>
|
||||
<li>Added support for UTF filenames.<ul>
|
||||
<li>Non-ASCII filenames will automatically be stored as UTF-8.
|
||||
(But note most other tools don't know how to extract them.)</li>
|
||||
</ul></li>
|
||||
</ul></li>
|
||||
</ul>
|
||||
|
||||
<hr size="2"/>
|
||||
<h3><b>2003-10-13</b></h3>
|
||||
|
||||
<ul>
|
||||
<li>Cab<ul>
|
||||
<li>Fixed a bug introduced in v2.4.0 that caused files to be left in the %TEMP%
|
||||
directory after creating a cab.</li>
|
||||
<li>Unsealed the CabinetInfo, CabinetFileInfo, CabinetStatus classes and made a few methods
|
||||
protected and virtual.</li>
|
||||
</ul></li>
|
||||
|
||||
<li>AugmentIL<ul>
|
||||
<li>Fixed a bug that sometimes caused a crash when specifying a relative output path
|
||||
on the command-line.</li>
|
||||
<li>Fixed a bug that sometimes caused the Win32 version to be missing from the output file.</li>
|
||||
</ul></li>
|
||||
|
||||
<li>Samples\Diff: added new sample tool<ul>
|
||||
<li>Recursively diffs directories, MSIs, MSPs, CABs, other files.</li>
|
||||
</ul></li>
|
||||
</ul>
|
||||
|
||||
<hr size="2"/>
|
||||
<h3><b>2003-09-23</b></h3>
|
||||
|
||||
<ul>
|
||||
<li>Cab<ul>
|
||||
<li>Fixed a bug that caused compressing very large files/file sets to use way too
|
||||
much memory. Performance on large inputs is now within a few % of native cab tools
|
||||
(sometimes even a little faster!) for the same compression level.</li>
|
||||
</ul></li>
|
||||
|
||||
<li>WindowsInstaller<ul>
|
||||
<li>All the new MSI 3.0 beta APIs are wrapped, resulting in the following additions:<ul>
|
||||
<li>New classes - Product, Patch (for accessing sourcelist and other config)</li>
|
||||
<li>New methods on Install class - GetProducts, GetPatches, RemovePatches,
|
||||
ApplyMultiplePatches, DetermineApplicablePatches, ExtractPatchXmlData</li>
|
||||
<li>New enumerations - InstallContext, PatchStates, SourceType</li>
|
||||
<li>Additional InstallProperty values</li>
|
||||
</ul></li>
|
||||
<li>Note, MSI 3.0 support should be considered preliminary for now,
|
||||
as APIs (both native and managed) are subject to change.</li>
|
||||
<li>For MSI 2.0 compatibility, developers should not use any classes or
|
||||
methods that are marked as [MSI 3.0 beta] in the documentation.</li>
|
||||
<li>And unrelated to 3.0, a few additional enums have been added:
|
||||
DialogAttributes, ControlAttributes, CustomActionTypes,
|
||||
IniFileAction, RegistryRoot, RemoveFileInstallMode,
|
||||
ServiceControlEvents, ServiceInstallFlags, TextStyles,
|
||||
UpgradeAttributes, LocatorType</li>
|
||||
<li>Also made a few minor non-breaking changes to keep the library FxCop-clean.</li>
|
||||
</ul></li>
|
||||
|
||||
<li>AugmentIL<ul>
|
||||
<li>Added support for strongname signing and delay-signing. AugmentIL tries to
|
||||
locate the keyfile using the AssemblyKeyFileAttribute, or you may specify the
|
||||
path with the new /key option.</li>
|
||||
<li>All "released" assemblies will now be strongname-signed
|
||||
(with an unofficial key).</li>
|
||||
</ul></li>
|
||||
|
||||
<li>CAProxy<ul>
|
||||
<li>Added support for NativeDependencies property on CustomActionAttribute. This enables
|
||||
custom actions to P/Invoke into native DLLs that are carried with them.</li>
|
||||
</ul></li>
|
||||
|
||||
<li>Samples\SampleCAs<ul>
|
||||
<li>In SampleCA2, changed MessageBox.Show("") to session.Message(User,""),
|
||||
because generally it is a bad practice for CAs to show independent UI.</li>
|
||||
<li>Added test of CustomActionAttribute.NativeDependencies functionality.</li>
|
||||
</ul></li>
|
||||
|
||||
<li>Samples\CabPack: added new sample<ul>
|
||||
<li>Demonstrates & tests the cab library by creating self-extracting packages</li>
|
||||
</ul></li>
|
||||
|
||||
<li>Samples\Inventory: added new sample<ul>
|
||||
<li>Shows a hierarchical, relational, searchable view of all of the product,
|
||||
feature, component, file, and patch data managed by MSI, for all products
|
||||
installed on the system.</li>
|
||||
</ul></li>
|
||||
</ul>
|
||||
|
||||
<hr size="2"/>
|
||||
<h3><b>2003-09-12</b></h3>
|
||||
|
||||
<ul>
|
||||
<li>Cab:<ul>
|
||||
<li>Added CabinetInfo.CompressDirectory method, capable of compressing an
|
||||
entire directory tree structure.</li>
|
||||
<li>Updated documentation of various methods concerning support of directory
|
||||
structure inside cabs.</li>
|
||||
<li>CabinetInfo case-sensitivity was inconsistent -
|
||||
now it is case-insensitive by default, though case is still preserved</li>
|
||||
<li>Separated assembly attributes into assembly.cs</li>
|
||||
</ul></li>
|
||||
<li>Msi:<ul>
|
||||
<li>InstallerException and subclasses automatically get extended error data
|
||||
from MSI's last-error-record when available. The data is stored
|
||||
in the exception and made available through the GetErrorRecord()
|
||||
method, and the exception's Message includes the formatted error
|
||||
message and data. This makes most exceptions extremely informative!</li>
|
||||
<li>Added View.GetValidationErrors() method, and supporting ValidationErrorInfo
|
||||
struct and ValidationError enum. This wrapper for the MsiViewGetError
|
||||
API had been accidentally left out.</li>
|
||||
<li>Session.Message() now supports message-box flags to specify buttons & icon</li>
|
||||
<li>Added doc remarks to various methods about closing handles.</li>
|
||||
<li>Separated assembly attributes into assembly.cs</li>
|
||||
</ul></li>
|
||||
<li>AugmentIL:<ul>
|
||||
<li>Recent builds of ildasm v2.0.* have a slightly different output format,
|
||||
which could break AugmentIL in some cases - fixed</li>
|
||||
</ul></li>
|
||||
<li>SampleCAs:<ul>
|
||||
<li>Removed 'using' clause from SampleCA1 -- there's no need to close the session's active database handle</li>
|
||||
</ul></li>
|
||||
<li>Documentation:<ul>
|
||||
<li>Added note to ReadMe about compiling the cab source into another assembly</li>
|
||||
</ul></li>
|
||||
</ul>
|
||||
|
||||
<hr size="2"/>
|
||||
<h3><b>2003-08-07</b></h3>
|
||||
|
||||
<ul>
|
||||
<li>Cab:<ul>
|
||||
<li>CabinetInfo.IsValid() usually returned false even for valid cabs - fixed</li>
|
||||
<li>Extracting cab files with null timestamps generated exception - fixed</li>
|
||||
</ul></li>
|
||||
<li>Msi:<ul>
|
||||
<li>Added InstallCanceledException, subclass of InstallerException;
|
||||
Methods which may be canceled by the user can throw this exception</li>
|
||||
<li>Added MessageResult enumeration;
|
||||
Used by Session.Message() and ExternalUIHandler delegate</li>
|
||||
<li>Installer.EnableLog() now supports extended attributes correctly:
|
||||
Append mode and flush-every-line</li>
|
||||
<li>Added Session.DoActionSequence() -
|
||||
This wrapper for the MsiSequence API had been accidentally left out</li>
|
||||
</ul></li>
|
||||
<li>CAProxy:<ul>
|
||||
<li>Catches InstallCanceledException, returns ERROR_INSTALL_USEREXIT
|
||||
so CA developer doesn't necessarily have to handle the exception</li>
|
||||
</ul></li>
|
||||
<li>Msi\Package:<ul>
|
||||
<li>Added TransformInfo class: metadata about an individual patch transform</li>
|
||||
<li>Added PatchPackage.GetTransform*() methods which return TransformInfo</li>
|
||||
</ul></li>
|
||||
<li>Documentation:<ul>
|
||||
<li>Added section to ReadMe.htm about building managed custom actions</li>
|
||||
</ul></li>
|
||||
</ul>
|
||||
|
||||
<hr size="2"/>
|
||||
<h3><b>2003-06-02</b></h3>
|
||||
|
||||
<ul>
|
||||
<li>Msi:<ul>
|
||||
<li>Validation didn't work on merge modules - fixed</li>
|
||||
</ul></li>
|
||||
<li>CAProxy:<ul>
|
||||
<li>Was broken in 2.1 - fixed</li>
|
||||
</ul></li>
|
||||
</ul>
|
||||
|
||||
<hr size="2"/>
|
||||
<h3><b>2003-05-14</b></h3>
|
||||
|
||||
<ul>
|
||||
<li>Msi:<ul>
|
||||
<li>External UI handler didn't survive a garbage collection - fixed</li>
|
||||
<li>Validation engine was completely broken - now it should work
|
||||
at least for MSIs which are already mostly valid</li>
|
||||
<li>Added DynamicLoad property to CustomActionAttribute<br />
|
||||
Usage: set DynamicLoad=false when using XmlSerialization; default is true</li>
|
||||
</ul></li>
|
||||
<li>Msi\Package:<ul>
|
||||
<li>File extraction and update methods didn't work on merge modules - fixed</li>
|
||||
<li>Made file update code slightly more robust</li>
|
||||
<li>Removed hard-reference to the FilePatch assembly - now it is only
|
||||
loaded if working with binary file patches</li>
|
||||
</ul></li>
|
||||
<li>AugmentIL:<ul>
|
||||
<li>AugmentIL would crash if some input files had read-only attr - fixed</li>
|
||||
<li>Made /verbose switch slightly more verbose</li>
|
||||
</ul></li>
|
||||
<li>CAProxy:<ul>
|
||||
<li>Added support for the DynamicLoad property of CustomActionAttribute</li>
|
||||
<li>Added MMsiBreak debugging functionality - see doc</li>
|
||||
</ul></li>
|
||||
<li>Samples\WiFile:<ul>
|
||||
<li>Added /l (list files) switch</li>
|
||||
</ul></li>
|
||||
<li>Samples\SampleCAs:<ul>
|
||||
<li>In the makefile the comments about debug builds had an error;
|
||||
Now the sample builds debug packages (correctly) by default.</li>
|
||||
</ul></li>
|
||||
<li>Documentation:<ul>
|
||||
<li>Wrote AugmentIL.htm describing the AugmentIL tool and its options.</li>
|
||||
<li>Wrote WiFile.htm describing the WiFile sample tool.</li>
|
||||
<li>Added section to ReadMe.htm about debugging managed custom actions.</li>
|
||||
</ul></li>
|
||||
</ul>
|
||||
|
||||
<hr size="2"/>
|
||||
<h3><b>2003-03-31</b></h3>
|
||||
|
||||
<ul>
|
||||
<li>Msi: Implemented the remaining APIs, also minor improvements and bugfixes<ul>
|
||||
<li>All published APIs are wrapped, with the exception of four:
|
||||
MsiGetFileSignatureInformation (because I don't know of a .NET analog
|
||||
for the returned certificate structure), and 3 APIs for previewing UI</li>
|
||||
<li>Database.OpenView and Database.Execute* now take String.Format style params</li>
|
||||
<li>Database.ApplyTransform can optionally use the error-suppression flags
|
||||
stored in the transform summary info</li>
|
||||
<li>Added a few supporting enumerations and structures for the remaining APIs</li>
|
||||
<li>InstallerException gets a descriptive message for any MSI or system error</li>
|
||||
<li>Fixed a bug in InstallerException which would usually report "error 0"</li>
|
||||
<li>Added optimization for setting a Record field to a MemoryStream</li>
|
||||
<li>Record.GetStream is capable of extracting substorages</li>
|
||||
<li>Moved InstallPath class to Microsoft.WindowsInstaller.Package assembly</li>
|
||||
</ul></li>
|
||||
<li>Msi\FilePatch: added new project<ul>
|
||||
<li>Binary file patch API wrapper</li>
|
||||
</ul></li>
|
||||
<li>Msi\Package: added new project<ul>
|
||||
<li>Helper classes for working with MSI and MSP packages</li>
|
||||
</ul></li>
|
||||
<li>Cab: some minor bugfixes<ul>
|
||||
<li>Cabinet.Extract(stream, name) threw a NullReferenceException if the file
|
||||
didn't exist in the cab -- now it returns null</li>
|
||||
<li>CabinetInfo.CompressFileSet() was broken -- fixed</li>
|
||||
<li>If a Cabinet callback throws an exception, it is propogated as the
|
||||
inner-exception of the CabinetException</li>
|
||||
</ul></li>
|
||||
<li>Samples\WiFile: added new sample<ul>
|
||||
<li>Demonstrates some features of InstallPackage class in Msi\Package project</li>
|
||||
</ul></li>
|
||||
</ul>
|
||||
|
||||
<hr size="2"/>
|
||||
<h3><b>2003-03-20</b></h3>
|
||||
|
||||
Documentation!<ul>
|
||||
<li>Msi and Cab sources include complete C# XML documentation.</li>
|
||||
<li>Msi and Cab makefiles generate XML documentation files.</li>
|
||||
<li>Reference CHM compiled from XML documentation with NDoc.</li>
|
||||
</ul>
|
||||
|
||||
<p>I am aware that exceptions are still not documented in most areas.
|
||||
Other than that, feel free to send me a note if it's still not clear
|
||||
how to use parts of the API after reading the documentation.</p>
|
||||
|
||||
<p>Version is still 1.1 because there are no code changes in this release.</p>
|
||||
|
||||
<hr size="2"/>
|
||||
<h3><b>2003-03-13</b></h3>
|
||||
|
||||
<ul>
|
||||
<li>Msi: lots of small improvements for usability and consistency<ul>
|
||||
<li>Reworked ExternalUIHandler support</li>
|
||||
<li>Added Installer properties/methods:<ul>
|
||||
<li>Components</li>
|
||||
<li>ComponentClients()</li>
|
||||
<li>ComponentState()</li>
|
||||
<li>ComponentPath()</li>
|
||||
<li>EnableLog()</li>
|
||||
</ul></li>
|
||||
<li>Added Session.EvaluateCondition() method</li>
|
||||
<li>Improved exception-handling in many methods in Installer, Database,
|
||||
& Session classes</li>
|
||||
<li>Added extensive XML doc-comments to Installer, Database, View,
|
||||
& Session classes</li>
|
||||
<li>A few breaking changes:<ul>
|
||||
<li>View.ModifyMode enumeration moved outside View and
|
||||
renamed ViewModifyMode</li>
|
||||
<li>InstallLogMode enumeration renamed to InstallLogModes
|
||||
(naming convention for bitfields)</li>
|
||||
<li>Record constructor takes arbitrary number of parameters</li>
|
||||
</ul></li>
|
||||
</ul></li>
|
||||
<li>AugmentIL: almost completely rewritten<ul>
|
||||
<li>Ildasm/ilasm steps are built-in<ul>
|
||||
<li>The round-trip can be done in one step</li>
|
||||
<li>IL source input/output is still supported</li>
|
||||
</ul></li>
|
||||
<li>Never throws an unhandled exception</li>
|
||||
<li>Organized command-line options, consistent with other .NET tools</li>
|
||||
<li>Uses a plugin architecture to allow additional augmentations</li>
|
||||
</ul></li>
|
||||
<li>CAProxy: Added AIL_CAProxy.cs - AugmentIL plugin generates CA proxy methods</li>
|
||||
|
||||
<li>SampleCAs: Updated makefile for new AugmentIL usage</li>
|
||||
</ul>
|
||||
|
||||
<hr size="2"/>
|
||||
<h3><b>2003-01-16</b></h3>
|
||||
|
||||
<ul>
|
||||
<li>ReadMe.htm: Added section on writing managed CAs</li>
|
||||
<li>SampleCAs: Added missing reference to System.Windows.Forms.dll to the makefile</li>
|
||||
<li>AugmentIL: Added specific warning messages for when CA method has wrong signature</li>
|
||||
<li>Put sources in Toolbox-hosted Source Depot.</li>
|
||||
</ul>
|
||||
|
||||
<hr size="2"/>
|
||||
<h3><b>2003-01-14</b></h3>
|
||||
Initial posting to http://toolbox
|
||||
|
||||
<p> </p>
|
||||
</div>
|
||||
|
||||
<div id="footer">
|
||||
<p />
|
||||
Send comments on this topic to <a id="HT_MailLink" href="mailto:wix-users%40lists.sourceforge.net?Subject=Deployment Tools Foundation Documentation">
|
||||
wix-users@lists.sourceforge.net</a>
|
||||
|
||||
<script type="text/javascript">
|
||||
var HT_mailLink = document.getElementById("HT_MailLink");
|
||||
var HT_mailLinkText = HT_mailLink.innerHTML;
|
||||
HT_mailLink.href += ": " + document.title;
|
||||
HT_mailLink.innerHTML = HT_mailLinkText;
|
||||
</script>
|
||||
|
||||
<p />
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,94 @@
|
|||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>About InstallUtil</title>
|
||||
<link rel="stylesheet" type="text/css" href="../styles/presentation.css" />
|
||||
<link rel="stylesheet" type="text/css" href="ms-help://Hx/HxRuntime/HxLink.css" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="control">
|
||||
<span class="productTitle">Deployment Tools Foundation</span><br />
|
||||
<span class="topicTitle">About InstallUtil</span><br />
|
||||
<div id="toolbar">
|
||||
<span id="chickenFeet">
|
||||
<a href="using.htm">Development Guide</a> >
|
||||
<a href="managedcas.htm">Managed CAs</a> >
|
||||
<span class="nolink">InstallUtil</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div id="main">
|
||||
<div id="header">
|
||||
</div>
|
||||
<div class="summary">
|
||||
<p>
|
||||
InstallUtil is often considered as another option for executing MSI custom actions
|
||||
written in managed code. But in most cases it is not the best solution, for a number
|
||||
of reasons.</p>
|
||||
<p>
|
||||
InstallUtil (in either InstallUtil.exe or InstallUtilLib.dll form) is a .NET Framework
|
||||
tool for executing the System.Configuration.Installer classes that are implemented
|
||||
in an assembly. That way the assembly can contain any special code required to install
|
||||
itself and uninstall itself. Essentially it is the .NET replacement for COM self-registration
|
||||
aka DllRegisterServer.</p>
|
||||
<p>
|
||||
Self-reg or System.Configuration.Installer is convenient for development use in
|
||||
order to test code without creating an actual setup package, or for an IDE which
|
||||
wants to generate self-installing code. But experienced setup developers and the
|
||||
<a href="MSI.chm::/setup/selfreg_table.htm">Windows Installer documentation</a>
|
||||
all agree that self-reg is a bad practice for a
|
||||
production-quality setup. The current theory of state-of-the-art setup is that it
|
||||
should be as data-driven as possible. That is, the setup package describes as fully
|
||||
as possible the desired state of the system, and then the installer engine calculates
|
||||
the necessary actions to install, uninstall, patch, etc.</p>
|
||||
<p>
|
||||
S.C.I encourages developers to write code for things such as registering services
|
||||
or registering COM classes or other things which are more appropriately done using
|
||||
built-in MSI functionality (the ServiceInstall and Registry tables). The Visual
|
||||
Studio .NET wizards also tend to generate this kind of install code. Again, that
|
||||
is nice for development but not good for real installations. You end up with similar
|
||||
but slightly different code in many places for doing the same thing. And that code
|
||||
is a black-box to the installer engine.</p>
|
||||
<p>
|
||||
An ideal MSI custom action is a logical extension of the setup engine, meaning it
|
||||
is data-driven and written in a very generic way to read from existing or custom
|
||||
tables in the MSI database, following a very similar pattern to the built-in actions.
|
||||
This makes the CA re-usable, and makes the installation more transparent. S.C.I
|
||||
custom actions invoked by InstallUtil cannot be data-driven because they don't have
|
||||
full access to the install session or database. They also cannot write to the install
|
||||
session's regular MSI log, but instead use a separate log which is bad for supportability.</p>
|
||||
<p>
|
||||
InstallUtil also requires that the assembly be installed before the CA is able to
|
||||
execute. This is a problem for CAs that need to execute during the UI phase, or
|
||||
gather information before installation. For that purpose MSI allows custom action
|
||||
binaries to be embedded as non-installed files, but InstallUtil cannot make use
|
||||
of those.</p>
|
||||
<p>
|
||||
Custom actions developed with DTF have none of the limitations of InstallUtil,
|
||||
giving a setup developer full capabilities to write well-designed custom actions,
|
||||
only now in managed code.
|
||||
</p>
|
||||
|
||||
<p> </p>
|
||||
</div>
|
||||
|
||||
<div id="footer">
|
||||
<p />
|
||||
Send comments on this topic to <a id="HT_MailLink" href="mailto:wix-users%40lists.sourceforge.net?Subject=Deployment Tools Foundation Documentation">
|
||||
wix-users@lists.sourceforge.net</a>
|
||||
|
||||
<script type="text/javascript">
|
||||
var HT_mailLink = document.getElementById("HT_MailLink");
|
||||
var HT_mailLinkText = HT_mailLink.innerHTML;
|
||||
HT_mailLink.href += ": " + document.title;
|
||||
HT_mailLink.innerHTML = HT_mailLinkText;
|
||||
</script>
|
||||
|
||||
<p />
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,78 @@
|
|||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>Windows Installer System Inventory Viewer</title>
|
||||
<link rel="stylesheet" type="text/css" href="../styles/presentation.css" />
|
||||
<link rel="stylesheet" type="text/css" href="ms-help://Hx/HxRuntime/HxLink.css" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="control">
|
||||
<span class="productTitle">Deployment Tools Foundation</span><br />
|
||||
<span class="topicTitle">Windows Installer System Inventory Viewer</span><br />
|
||||
<div id="toolbar">
|
||||
<span id="chickenFeet">
|
||||
<a href="using.htm">Development Guide</a> >
|
||||
<a href="samples.htm">Samples</a> >
|
||||
<span class="nolink">Inventory</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div id="main">
|
||||
<div id="header">
|
||||
</div>
|
||||
<div class="summary">
|
||||
<p>This application shows a hierarchical, relational, searchable
|
||||
view of all of the product, feature, component, file, and patch
|
||||
data managed by MSI, for all products installed on the system.</p>
|
||||
<p><br/></p>
|
||||
|
||||
<h4>Navigation</h4>
|
||||
<ol>
|
||||
<li><p>The tree on the left is self-explanatory.</p></li>
|
||||
<li><p>Click on a row-header (grey box on the left side of the
|
||||
grid) to jump to a table with more details about the item referred
|
||||
to by that row. For example, clicking on a row-header of a
|
||||
table that lists components will take you to a table that lists
|
||||
the files in that component. Not every table has this ability,
|
||||
but the cursor will turn to a hand shape to indicate when this is
|
||||
possible.</p></li>
|
||||
<li><p>Also you can navigate back and forward through your history
|
||||
using the buttons in the application or mouse buttons 4 and 5.</p></li>
|
||||
</ol>
|
||||
<p><br/></p>
|
||||
|
||||
<h4>Searching</h4>
|
||||
<p>The search feature is not hard to find. By default, searches
|
||||
are limited to the current table. However, if you choose to find
|
||||
"In Subtree" by checking the box, the search will include
|
||||
the current table as well as all tables under the current location in
|
||||
the tree. While this can take a long time if there is a lot of
|
||||
data under the current node, you can stop the search at any time with
|
||||
the stop button. The search pauses when a match is found, but
|
||||
clicking "Find" again will continue the same search from that
|
||||
point (unless you uncheck the "Continue" checkbox or change
|
||||
the search string).</p>
|
||||
|
||||
<p><br/></p>
|
||||
</div>
|
||||
|
||||
<div id="footer">
|
||||
<p />
|
||||
Send comments on this topic to <a id="HT_MailLink" href="mailto:wix-users%40lists.sourceforge.net?Subject=Deployment Tools Foundation Documentation">
|
||||
wix-users@lists.sourceforge.net</a>
|
||||
|
||||
<script type="text/javascript">
|
||||
var HT_mailLink = document.getElementById("HT_MailLink");
|
||||
var HT_mailLinkText = HT_mailLink.innerHTML;
|
||||
HT_mailLink.href += ": " + document.title;
|
||||
HT_mailLink.innerHTML = HT_mailLinkText;
|
||||
</script>
|
||||
|
||||
<p />
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,53 @@
|
|||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>Managed Custom Actions</title>
|
||||
<link rel="stylesheet" type="text/css" href="../styles/presentation.css" />
|
||||
<link rel="stylesheet" type="text/css" href="ms-help://Hx/HxRuntime/HxLink.css" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="control">
|
||||
<span class="productTitle">Deployment Tools Foundation</span><br />
|
||||
<span class="topicTitle">Managed Custom Actions</span><br />
|
||||
<div id="toolbar">
|
||||
<span id="chickenFeet">
|
||||
<a href="using.htm">Development Guide</a> >
|
||||
<span class="nolink">Managed CAs</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div id="main">
|
||||
<div id="header">
|
||||
</div>
|
||||
<div class="summary">
|
||||
|
||||
<ul>
|
||||
<li><a href="writingcas.htm">Writing Managed Custom Actions</a></li>
|
||||
<li><a href="caconfig.htm">Specifying the Runtime Version</a></li>
|
||||
<li><a href="buildingcas.htm">Building Managed Custom Actions</a></li>
|
||||
<li><a href="debuggingcas.htm">Debugging Managed Custom Actions</a></li>
|
||||
<li><a href="installutil.htm">About InstallUtil</a></li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
|
||||
<div id="footer">
|
||||
<p />
|
||||
Send comments on this topic to <a id="HT_MailLink" href="mailto:wix-users%40lists.sourceforge.net?Subject=Deployment Tools Foundation Documentation">
|
||||
wix-users@lists.sourceforge.net</a>
|
||||
|
||||
<script type="text/javascript">
|
||||
var HT_mailLink = document.getElementById("HT_MailLink");
|
||||
var HT_mailLinkText = HT_mailLink.innerHTML;
|
||||
HT_mailLink.href += ": " + document.title;
|
||||
HT_mailLink.innerHTML = HT_mailLinkText;
|
||||
</script>
|
||||
|
||||
<p />
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,59 @@
|
|||
<!doctype HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" >
|
||||
<html>
|
||||
<head>
|
||||
<title>Included Components</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=Windows-1252">
|
||||
<link rel="stylesheet" type="text/css" href="MSDN.css">
|
||||
</head>
|
||||
<body id="bodyID" class="dtBODY">
|
||||
<div id="nsbanner">
|
||||
<div id="bannerrow1">
|
||||
<table class="bannerparthead" cellspacing="0" id="Table1">
|
||||
<tr id="hdr">
|
||||
<td class="runninghead">Managed Libraries for Windows Installer</td>
|
||||
<td class="product"></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div id="TitleRow">
|
||||
<h1 class="dtH1">Helper Classes for Windows Installer Packages</h1>
|
||||
</div>
|
||||
</div>
|
||||
<div id="nstext">
|
||||
<p>Included are some useful helper classes for working with
|
||||
MSI and MSP packages:</p>
|
||||
<ul>
|
||||
<li><p><a href="ms-its:MMLRef.chm::/Microsoft.WindowsInstaller.InstallPackage.html"
|
||||
><strong>InstallPackage</strong></a> - extends the Database class to provide powerful
|
||||
package-based operations such as:</p>
|
||||
<ul>
|
||||
<li>direct extraction of files to uncompressed source
|
||||
path
|
||||
<li>updating files from uncompressed source path back
|
||||
into the compressed source for the package (including updating file
|
||||
metadata)
|
||||
<li>applying a patch directly to the package
|
||||
<li>consolidating a package with uncompressed source files or multiple msm-cabs
|
||||
into a package with a single compressed cabinet</li>
|
||||
</ul>
|
||||
<P></P>
|
||||
<li><p><a href="ms-its:MMLRef.chm::/Microsoft.WindowsInstaller.InstallPathMap.html"
|
||||
><strong>InstallPathMap</strong>, <a href="ms-its:MMLRef.chm::/Microsoft.WindowsInstaller.InstallPath.html"
|
||||
><strong>InstallPath</strong></a> - represent the directory structure
|
||||
of an installation package, including file, component, and directory source and target
|
||||
install paths. Accessible by file, component, or directory keys; searchable by
|
||||
filename.</p>
|
||||
<li><p><a href="ms-its:MMLRef.chm::/Microsoft.WindowsInstaller.PatchPackage.html"
|
||||
><strong>PatchPackage</strong></a> - allows convenient access to patch properties,
|
||||
and analysis and extraction of transforms</p></li>
|
||||
</ul>
|
||||
<p><br/></p>
|
||||
<p>These classes are in the Microsoft.WindowsInstaller.Package.dll assembly.</p>
|
||||
<p><br/></p>
|
||||
<p><b>See also:</b></p>
|
||||
<p>The <a href="wifile.htm">WiFile</a> sample tool demonstrates some usage of the
|
||||
InstallPackage class.</p>
|
||||
<p><br/></p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,80 @@
|
|||
<!doctype HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" >
|
||||
<html>
|
||||
<head>
|
||||
<title>Included Components</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=Windows-1252">
|
||||
<link rel="stylesheet" type="text/css" href="MSDN.css">
|
||||
</head>
|
||||
<body id="bodyID" class="dtBODY">
|
||||
<div id="nsbanner">
|
||||
<div id="bannerrow1">
|
||||
<table class="bannerparthead" cellspacing="0" id="Table1">
|
||||
<tr id="hdr">
|
||||
<td class="runninghead">Managed Libraries for Windows Installer</td>
|
||||
<td class="product"></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div id="TitleRow">
|
||||
<h1 class="dtH1">Managed wrapper library for Windows Installer APIs</h1>
|
||||
</div>
|
||||
</div>
|
||||
<div id="nstext">
|
||||
<p>Microsoft.WindowsInstaller.dll is a complete .NET wrapper for the
|
||||
Windows Installer APIs. It provides a convenient object model that is
|
||||
comfortable to .NET developers and still familiar to anyone who has used
|
||||
the MSI scripting object model.</p>
|
||||
<h3>Notes</h3>
|
||||
<ul>
|
||||
<li><p>All published MSI APIs are wrapped, with the exception of four:
|
||||
MsiGetFileSignatureInformation (because I don't know of a .NET analog for the
|
||||
returned certificate structure), and three APIs for previewing UI dialogs.
|
||||
Other than that, you should be able to do everything that you can
|
||||
do via the C APIs or the COM automation interface.</p>
|
||||
<li><p>Some parts of this code have never had a formal test
|
||||
pass, so use at your own risk. But much of the code is exercised daily, used
|
||||
by the Developer Division Sustaining Engineering team's BRIQS system to build
|
||||
and test patches. And it has been in use by many other teams for over two
|
||||
years now with only a few minor fixes, so it can be considered very stable.</p>
|
||||
<li><p>Despite its official-sounding namespace, this assembly is not officially
|
||||
sanctioned by the Windows Installer team. But currently there are not any
|
||||
plans for an official set of managed even in Longhorn, so I don't see a
|
||||
conflict for now.</p></li>
|
||||
</ul>
|
||||
<h3>Why rewrite it?</h3>
|
||||
<p>It is possible to access MSI's COM Automation interfaces via C# and VB.NET.
|
||||
So why create yet another wrapper? Here are some of my reasons:</p>
|
||||
<ul>
|
||||
<li><p>One of the primary things I wanted to be able to do
|
||||
was write custom actions in C#. The automation interface was not usable in
|
||||
that case, because there is no way to convert the integer session handle
|
||||
(received as a parameter to the type 1 custom action function) into a Session
|
||||
automation object.</p>
|
||||
<li><p>The automation interface does not provide a way to
|
||||
specify an external UI handler. Besides external UI, this is also needed
|
||||
to do validation.</p>
|
||||
<li><p>The automation interface does not provide a way to
|
||||
explicitly close handles (other than Views). I ran into this problem when I
|
||||
wanted to programmatically delete a database that I'd just finished using, but
|
||||
couldn't because it was still open!</p>
|
||||
<li><p>Finally, COM Automation is somewhat slower than invoking
|
||||
the APIs directly.</p></li>
|
||||
</ul>
|
||||
|
||||
<p><br/></p>
|
||||
<p><b>See also:</b></p>
|
||||
<ul>
|
||||
<li><a href="ms-its:MMLRef.chm::/Microsoft.WindowsInstaller.html">Microsoft.WindowsInstaller Namespace</a></li>
|
||||
<ul>
|
||||
<li><a href="ms-its:MMLRef.chm::/Microsoft.WindowsInstaller.Installer.html">Installer Class</a></li>
|
||||
<li><a href="ms-its:MMLRef.chm::/Microsoft.WindowsInstaller.Database.html">Database Class</a></li>
|
||||
<li><a href="ms-its:MMLRef.chm::/Microsoft.WindowsInstaller.Session.html">Session Class</a></li>
|
||||
</ul>
|
||||
<li><a href="msihelper.htm">Helper Classes for Windows Installer Packages</a></li>
|
||||
<li><a href="writingcas.htm">Writing Managed Custom Actions</a></li>
|
||||
<li><a href="databases.htm">Working with MSI Databases</a></li>
|
||||
</ul>
|
||||
<p><br/></p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,86 @@
|
|||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>Working with Install Packages</title>
|
||||
<link rel="stylesheet" type="text/css" href="../styles/presentation.css" />
|
||||
<link rel="stylesheet" type="text/css" href="ms-help://Hx/HxRuntime/HxLink.css" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="control">
|
||||
<span class="productTitle">Deployment Tools Foundation</span><br />
|
||||
<span class="topicTitle">Working with Install Packages</span><br />
|
||||
<div id="toolbar">
|
||||
<span id="chickenFeet">
|
||||
<a href="using.htm">Development Guide</a> >
|
||||
<span class="nolink">Install Packages</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div id="main">
|
||||
<div id="header">
|
||||
</div>
|
||||
<div class="summary">
|
||||
|
||||
<h3>Updating files in a product layout</h3>
|
||||
<p>The InstallPackage class makes it easy to work with files and cabinets
|
||||
in the context of a compressed or uncompressed product layout.</p>
|
||||
<p>This hypothetical example takes an IDictionary 'files' which maps file keys to file paths. Each
|
||||
file is to be updated in the package layout; cabinets are even recompressed if necessary to include the new files.</p>
|
||||
<pre><font face="Consolas, Courier New"> <font color=blue>using</font> (InstallPackage pkg = <font color=blue>new</font> InstallPackage(<font color=purple>"d:\builds\product.msi"</font>,
|
||||
DatabaseOpenMode.Transact))
|
||||
{
|
||||
pkg.WorkingDirectory = Path.Combine(Path.GetTempFolder(), <font color=purple>"pkgtmp"</font>);
|
||||
<font color=blue>foreach</font> (string fileKey in files.Keys)
|
||||
{
|
||||
<font color=blue>string</font> sourceFilePath = files[fileKey];
|
||||
<font color=blue>string</font> destFilePath = pkg.Files[fileKey].SourcePath;
|
||||
destFilePath = Path.Combine(pkg.WorkingDirectory, destFilePath);
|
||||
File.Copy(sourceFilePath, destFilePath, <font color=blue>true</font>);
|
||||
}
|
||||
pkg.UpdateFiles(<font color=blue>new</font> ArrayList(files.Keys));
|
||||
pkg.Commit();
|
||||
Directory.Delete(pkg.WorkingDirectory, <font color=blue>true</font>);
|
||||
}</font></pre><br />
|
||||
<p>1. Create a <a href="DTFAPI.chm::/html/Overload_Microsoft_Deployment_WindowsInstaller_Package_InstallPackage__ctor.htm">new InstallPackage</a>
|
||||
instance referring to the location of the .msi. This is actually just a specialized subclass of a Database.</p>
|
||||
<p>2. Set the <a href="DTFAPI.chm::/html/P_Microsoft_Deployment_WindowsInstaller_Package_InstallPackage_WorkingDirectory.htm">WorkingDirectory</a>.
|
||||
This is the root directory where the package expects to find the new source files.</p>
|
||||
<p>3. Copy each file to its proper location in the working directory. The
|
||||
<a href="DTFAPI.chm::/html/P_Microsoft_Deployment_WindowsInstaller_Package_InstallPackage_Files.htm">InstallPackage.Files</a>
|
||||
property is used to look up the relative source path of each file.</p>
|
||||
<p>4. Call <a href="DTFAPI.chm::/html/Overload_Microsoft_Deployment_WindowsInstaller_Package_InstallPackage_UpdateFiles.htm">InstallPackage.UpdateFiles</a>
|
||||
with the list of file keys. This will re-compress and package the files if necessary, and also update the
|
||||
following data: File.FileSize, File.Version, File.Language, MsiFileHash.HashPart*.</p>
|
||||
<p>5. Commit the database changes and cleanup the working directory.</p>
|
||||
</ul>
|
||||
|
||||
<p><br/></p>
|
||||
<p><b>See also:</b></p>
|
||||
<ul>
|
||||
<li><a href="wifile.htm">WiFile Sample Tool</a> - a more complete tool that expands on the above example.</li>
|
||||
<li><a href="DTFAPI.chm::/html/T_Microsoft_Deployment_WindowsInstaller_Package_InstallPackage.htm">InstallPackage Class</a></li>
|
||||
</ul>
|
||||
<p><br/></p>
|
||||
|
||||
</div>
|
||||
|
||||
<div id="footer">
|
||||
<p />
|
||||
Send comments on this topic to <a id="HT_MailLink" href="mailto:wix-users%40lists.sourceforge.net?Subject=Deployment Tools Foundation Documentation">
|
||||
wix-users@lists.sourceforge.net</a>
|
||||
|
||||
<script type="text/javascript">
|
||||
var HT_mailLink = document.getElementById("HT_MailLink");
|
||||
var HT_mailLinkText = HT_mailLink.innerHTML;
|
||||
HT_mailLink.href += ": " + document.title;
|
||||
HT_mailLink.innerHTML = HT_mailLinkText;
|
||||
</script>
|
||||
|
||||
<p />
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,71 @@
|
|||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>MSI, MSP, CAB Diff Tool</title>
|
||||
<link rel="stylesheet" type="text/css" href="../styles/presentation.css" />
|
||||
<link rel="stylesheet" type="text/css" href="ms-help://Hx/HxRuntime/HxLink.css" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="control">
|
||||
<span class="productTitle">Deployment Tools Foundation</span><br />
|
||||
<span class="topicTitle">MSI, MSP, CAB Diff Tool</span><br />
|
||||
<div id="toolbar">
|
||||
<span id="chickenFeet">
|
||||
<a href="using.htm">Development Guide</a> >
|
||||
<a href="samples.htm">Samples</a> >
|
||||
<span class="nolink">DDiff</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div id="main">
|
||||
<div id="header">
|
||||
</div>
|
||||
<div class="summary">
|
||||
<h2>MSI, MSP, CAB Diff Tool</h2>
|
||||
|
||||
<p><pre><font face="Consolas, Courier New">Usage: DDiff target1 target2 [options]
|
||||
Example: DDiff d:\dir1 d:\dir2
|
||||
Example: DDiff setup1.msi setup2.msi
|
||||
Example: DDiff patch1.msp patch2.msp -patchtarget target.msi
|
||||
Example: DDiff package1.cab package2.cab
|
||||
|
||||
Options:
|
||||
/o [filename] Output results to text file (UTF8)
|
||||
/p [package.msi] Diff patches relative to target MSI</font></pre>
|
||||
</p>
|
||||
<p><br/></p>
|
||||
|
||||
<p>The following types of inputs can be diffed:
|
||||
<ul>
|
||||
<li><b>Directories</b>: files and subdirectories are compared.</li>
|
||||
<li><b>Cab files</b>: internal file list and files are compared.</li>
|
||||
<li><b>MSI/MSM database files</b>: summary info, tables, and embedded binary and cab streams are compared.</li>
|
||||
<li><b>MSP files</b>: summary info and embedded file cab are compared. When a patch target MSI is provided, the MSP's tables are also compared.</li>
|
||||
<li><b>Versioned files</b>: Win32 file version is compared.</li>
|
||||
<li><b>Text files</b>: if diff.exe is in the path, it is used to get a line-by-line diff.</li>
|
||||
<li><b>Other files</b>: file size and bytes are compared.</li>
|
||||
</ul>
|
||||
All processing is done recursively. So a versioned file within a cab within an MSI within a directory will have meaningful diff results.</p>
|
||||
|
||||
<p><br/></p>
|
||||
</div>
|
||||
<div id="footer">
|
||||
<p />
|
||||
Send comments on this topic to <a id="HT_MailLink" href="mailto:wix-users%40lists.sourceforge.net?Subject=Deployment Tools Foundation Documentation">
|
||||
wix-users@lists.sourceforge.net</a>
|
||||
|
||||
<script type="text/javascript">
|
||||
var HT_mailLink = document.getElementById("HT_MailLink");
|
||||
var HT_mailLinkText = HT_mailLink.innerHTML;
|
||||
HT_mailLink.href += ": " + document.title;
|
||||
HT_mailLink.innerHTML = HT_mailLinkText;
|
||||
</script>
|
||||
|
||||
<p />
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,84 @@
|
|||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>Sample C# Custom Action</title>
|
||||
<link rel="stylesheet" type="text/css" href="../styles/presentation.css" />
|
||||
<link rel="stylesheet" type="text/css" href="ms-help://Hx/HxRuntime/HxLink.css" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="control">
|
||||
<span class="productTitle">Deployment Tools Foundation</span><br />
|
||||
<span class="topicTitle">Sample C# Custom Action</span><br />
|
||||
<div id="toolbar">
|
||||
<span id="chickenFeet">
|
||||
<a href="using.htm">Development Guide</a> >
|
||||
<a href="managedcas.htm">Managed CAs</a> >
|
||||
<a href="writingcas.htm">Writing CAs</a> >
|
||||
<span class="nolink">C# Sample</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div id="main">
|
||||
<div id="header">
|
||||
</div>
|
||||
<div class="summary">
|
||||
|
||||
<p>MSI custom actions are MUCH easier to write in C# than
|
||||
in C++!</p><pre><font face="Consolas, Courier New"> [CustomAction]
|
||||
<font color=blue>public</font> <font color=blue>static</font> ActionResult SampleCustomAction1(Session session)
|
||||
{
|
||||
session.Log(<font color="purple">"Hello from SampleCA1"</font>);
|
||||
|
||||
<font color=blue>string</font> testProp = session[<font color="purple">"SampleCATest"</font>];
|
||||
<font color=blue>string</font> testProp2;
|
||||
testProp2 = (<font color="blue">string</font>) session.Database.ExecuteScalar(
|
||||
<font color="purple">"SELECT `Value` FROM `Property` WHERE `Property` = 'SampleCATest'"</font>);
|
||||
|
||||
<font color=blue>if</font>(testProp == testProp2)
|
||||
{
|
||||
session.Log(<font color="purple">"Simple property test passed."</font>);
|
||||
<font color=blue>return</font> ActionResult.Success;
|
||||
}
|
||||
<font color=blue>else</font>
|
||||
<font color=blue>return</font> ActionResult.Failure;
|
||||
}
|
||||
</font></pre>
|
||||
<p>A sample CA project with two CAs is included in the
|
||||
Samples\ManagedCA directory. Running the CustomActionTest project will package the CA and insert
|
||||
it into a test MSI. The MSI will invoke the custom actions, but it will not install anything
|
||||
since the second sample CA returns ActionResult.UserExit.
|
||||
</p>
|
||||
|
||||
<p><br/></p>
|
||||
<p><b>See also:</b></p>
|
||||
<ul>
|
||||
<li><a href="writingcas.htm">Writing Managed Custom Actions</a></li>
|
||||
<li><a href="caconfig.htm">Specifying the Runtime Version</a></li>
|
||||
<li><a href="databases.htm">Working with MSI Databases</a></li>
|
||||
<li><a href="buildingcas.htm">Building Managed Custom Actions</a></li>
|
||||
<li><a href="debuggingcas.htm">Debugging Managed Custom Actions</a></li>
|
||||
</ul>
|
||||
<p><br/></p>
|
||||
|
||||
</div>
|
||||
|
||||
<div id="footer">
|
||||
<p />
|
||||
Send comments on this topic to <a id="HT_MailLink" href="mailto:wix-users%40lists.sourceforge.net?Subject=Deployment Tools Foundation Documentation">
|
||||
wix-users@lists.sourceforge.net</a>
|
||||
|
||||
<script type="text/javascript">
|
||||
var HT_mailLink = document.getElementById("HT_MailLink");
|
||||
var HT_mailLinkText = HT_mailLink.innerHTML;
|
||||
HT_mailLink.href += ": " + document.title;
|
||||
HT_mailLink.innerHTML = HT_mailLinkText;
|
||||
</script>
|
||||
|
||||
<p />
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,59 @@
|
|||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>Sample Applications</title>
|
||||
<link rel="stylesheet" type="text/css" href="../styles/presentation.css" />
|
||||
<link rel="stylesheet" type="text/css" href="ms-help://Hx/HxRuntime/HxLink.css" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="control">
|
||||
<span class="productTitle">Deployment Tools Foundation</span><br />
|
||||
<span class="topicTitle">Sample Applications</span><br />
|
||||
<div id="toolbar">
|
||||
<span id="chickenFeet">
|
||||
<a href="using.htm">Development Guide</a> >
|
||||
<span class="nolink">Samples</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div id="main">
|
||||
<div id="header">
|
||||
</div>
|
||||
<div class="summary">
|
||||
<p>Besides the simple managed custom action sample, there are three functional
|
||||
and useful sample tools included in this distribution:</p>
|
||||
<p><a href="Inventory.htm"><b>MSI Inventory</b></a><br/>
|
||||
Shows a hierarchical, relational, searchable view of all of the product,
|
||||
feature, component, file, and patch data managed by MSI, for all products
|
||||
installed on the system.</p>
|
||||
<p><a href="WiFile.htm"><b>WiFile</b></a><br/>
|
||||
Extracts and updates cabbed files in an MSI setup.</p>
|
||||
<p><a href="CabPack.htm"><b>CabPack</b></a><br/>
|
||||
Creates simple self-extracting cab packages. OK, so this one isn't
|
||||
especially useful as a tool, but the code should be helpful.</p>
|
||||
<p><a href="PowerDiff.htm"><b>DDiff</b></a><br/>
|
||||
Recursively diffs MSI, MSP, CAB, and other files and directories.
|
||||
Much more thorough than widiffdb.vbs.</p>
|
||||
<p><br/></p>
|
||||
</div>
|
||||
|
||||
<div id="footer">
|
||||
<p />
|
||||
Send comments on this topic to <a id="HT_MailLink" href="mailto:wix-users%40lists.sourceforge.net?Subject=Deployment Tools Foundation Documentation">
|
||||
wix-users@lists.sourceforge.net</a>
|
||||
|
||||
<script type="text/javascript">
|
||||
var HT_mailLink = document.getElementById("HT_MailLink");
|
||||
var HT_mailLinkText = HT_mailLink.innerHTML;
|
||||
HT_mailLink.href += ": " + document.title;
|
||||
HT_mailLink.innerHTML = HT_mailLinkText;
|
||||
</script>
|
||||
|
||||
<p />
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,52 @@
|
|||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>Support/Bugs</title>
|
||||
<link rel="stylesheet" type="text/css" href="../styles/presentation.css" />
|
||||
<link rel="stylesheet" type="text/css" href="ms-help://Hx/HxRuntime/HxLink.css" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="control">
|
||||
<span class="productTitle">Deployment Tools Foundation</span><br />
|
||||
<span class="topicTitle">Support/Bugs</span><br />
|
||||
<div id="toolbar">
|
||||
<span id="chickenFeet">
|
||||
<a href="about.htm">Overview</a> >
|
||||
<span class="nolink">Support/Bugs</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div id="main">
|
||||
<div id="header">
|
||||
</div>
|
||||
<div class="summary">
|
||||
<p>Please send general support questions or comments to the
|
||||
<a href="mailto:wix-users@sourceforge.net">wix-users</a> discussion list.</p>
|
||||
|
||||
<p>Bugs, suggestions, or feature requests can be submitted at the
|
||||
<a href="http://wix.sourceforge.net/">WiX project</a>
|
||||
on Sourceforge.net.</p>
|
||||
|
||||
<p><br/></p>
|
||||
</div>
|
||||
|
||||
<div id="footer">
|
||||
<p />
|
||||
Send comments on this topic to <a id="HT_MailLink" href="mailto:wix-users%40lists.sourceforge.net?Subject=Deployment Tools Foundation Documentation">
|
||||
wix-users@lists.sourceforge.net</a>
|
||||
|
||||
<script type="text/javascript">
|
||||
var HT_mailLink = document.getElementById("HT_MailLink");
|
||||
var HT_mailLinkText = HT_mailLink.innerHTML;
|
||||
HT_mailLink.href += ": " + document.title;
|
||||
HT_mailLink.innerHTML = HT_mailLinkText;
|
||||
</script>
|
||||
|
||||
<p />
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,50 @@
|
|||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>Deployment Tools Foundation Development Guide</title>
|
||||
<link rel="stylesheet" type="text/css" href="../styles/presentation.css" />
|
||||
<link rel="stylesheet" type="text/css" href="ms-help://Hx/HxRuntime/HxLink.css" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="control">
|
||||
<span class="productTitle">Deployment Tools Foundation</span><br />
|
||||
<span class="topicTitle">Deployment Tools Foundation Development Guide</span><br />
|
||||
<div id="toolbar">
|
||||
<span id="chickenFeet">
|
||||
<span class="nolink">Development Guide</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div id="main">
|
||||
<div id="header">
|
||||
</div>
|
||||
<div class="summary">
|
||||
<ul>
|
||||
<li><a href="managedcas.htm">Managed Custom Actions</a></li>
|
||||
<li><a href="databases.htm">Working with MSI Databases</a></li>
|
||||
<li><a href="cabs.htm">Working with Cabinet Files</a></li>
|
||||
<li><a href="packages.htm">Working with Install Packages</a></li>
|
||||
<li><a href="samples.htm">Sample Applications</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div id="footer">
|
||||
<p />
|
||||
Send comments on this topic to <a id="HT_MailLink" href="mailto:wix-users%40lists.sourceforge.net?Subject=Deployment Tools Foundation Documentation">
|
||||
wix-users@lists.sourceforge.net</a>
|
||||
|
||||
<script type="text/javascript">
|
||||
var HT_mailLink = document.getElementById("HT_MailLink");
|
||||
var HT_mailLinkText = HT_mailLink.innerHTML;
|
||||
HT_mailLink.href += ": " + document.title;
|
||||
HT_mailLink.innerHTML = HT_mailLinkText;
|
||||
</script>
|
||||
|
||||
<p />
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,257 @@
|
|||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>What's New?</title>
|
||||
<link rel="stylesheet" type="text/css" href="../styles/presentation.css" />
|
||||
<link rel="stylesheet" type="text/css" href="ms-help://Hx/HxRuntime/HxLink.css" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="control">
|
||||
<span class="productTitle">Deployment Tools Foundation</span><br />
|
||||
<span class="topicTitle">What's New?</span><br />
|
||||
<div id="toolbar">
|
||||
<span id="chickenFeet">
|
||||
<a href="about.htm">Overview</a> >
|
||||
<span class="nolink">What's New?</span>
|
||||
</span>
|
||||
<span id="languageFilter">2007-07-03</span>
|
||||
</div>
|
||||
</div>
|
||||
<div id="main">
|
||||
<div id="header">
|
||||
</div>
|
||||
<div class="summary">
|
||||
|
||||
<h3>Highlights</h3>
|
||||
<ul>
|
||||
<li><p>New project name name "Deployment Tools Foundation", and
|
||||
new namespaces <font face="Consolas, Courier New">Microsoft.Deployment.*</font></p></li>
|
||||
<li><p>Added ZIP compression library</p></li>
|
||||
<li><p>Added library for reading/writing Win32 resources including file versions</p></li>
|
||||
<li><p>Managed custom action improvements:</p><ul>
|
||||
<li><p>Simplified authoring and building -- new MakeSfxCA tool
|
||||
automatically maps DLL entrypoints to CA methods.</p></li>
|
||||
<li><p>Managed custom action DLLs now run in a separate process for
|
||||
better reliability with respect to CLR versions, but still have
|
||||
full access to the MSI session.</p></li>
|
||||
</ul></li>
|
||||
<li><p>Found and fixed many bugs with extensive unit test suite</p></li>
|
||||
<li><p>LINQ to MSI ! (preview)</p></li>
|
||||
</ul>
|
||||
|
||||
<p>Unfortunately, all these changes do mean that migrating tools and applications from
|
||||
the previous release can be a moderate amount of work.</p>
|
||||
|
||||
<h3>Breaking Changes</h3>
|
||||
<p>For the first time since v1.0, this release contains some major breaking
|
||||
changes, due to a significant redesign and cleanup effort that has been a
|
||||
long time coming. The overall purpose of the changes is to bring the class
|
||||
libraries much closer to ship-quality.</p>
|
||||
<ul>
|
||||
<li><p>All libraries use a new namespace hierarchy
|
||||
under <font face="Consolas, Courier New">Microsoft.Deployment</font>.
|
||||
The new namespace aligns with the new project name, gives all the various
|
||||
libraries an identity that makes them obviously related to the DTF project,
|
||||
and mostly avoids "taking over" a namespace that might be rightfully owned
|
||||
by the platform technology owner.</p></li>
|
||||
|
||||
<li><p>Assemblies are also renamed to follow namespaces.</p></li>
|
||||
|
||||
<li><p>A new unified compression framework forms the basis for the new ZIP
|
||||
library and a redesigned CAB library. Additional archive formats can
|
||||
be plugged into the framework. The stream-based compression APIs have
|
||||
been redesigned to be more object-oriented and easier to use. The file-based
|
||||
APIs are mostly unchanged from the old cabinet library, although some names
|
||||
have changed in order to fit into the new unified framework.</p></li>
|
||||
|
||||
<li><p>Large parts of the WindowsInstaller library have been redesigned
|
||||
to be more object-oriented and to better follow .NET Framework design
|
||||
guidelines. And various APIs throughout the library have naming or other
|
||||
changes for better consistency and to follow conventions and best
|
||||
pratices as enforced by FxCop.</p></li>
|
||||
|
||||
<li><p>The WindowsInstaller APIs no longer make any attempt to mimic the
|
||||
MSI COM automation interfaces. The naming and object patterns in the
|
||||
automation interfaces often conflicted with with best practices for
|
||||
.NET Framework class libraries. Since most people start using DTF
|
||||
without having ever experienced MSI scripting, there is little
|
||||
reason to match the scripting object model. Making the APIs more
|
||||
consistent with .NET conventions will make them much easier to use
|
||||
for people already experienced with the .NET Framework.</p></li>
|
||||
|
||||
<li><p>APIs in all class libraries use generics where appropriate, especially
|
||||
the generic collection interfaces. This means .NET Framework 2.0 or later
|
||||
is required.</p></li>
|
||||
|
||||
<li><p>The FilePatch library is missing from this release. An updated
|
||||
and redesigned file-delta library is in development.</p></li>
|
||||
</ul>
|
||||
|
||||
<h3>Other Changes</h3>
|
||||
<ul>
|
||||
<li><p>New MakeSfxCA tool for building managed custom action packages: In addition to
|
||||
packaging the CA DLL and dependencies, it automatically detects managed CA methods
|
||||
and generates corresponding unmanaged DLL entrypoints in the CA host DLL (SfxCA.dll),
|
||||
where they are called by MSI. Previously it was necessary to either provide this
|
||||
mapping in a CustomAction.config file, or live with the generic "ManagedCustomActionN"
|
||||
names when authoring the CustomAction table in the MSI. For more info, see the
|
||||
help topic on building managed custom actions.</p></li>
|
||||
|
||||
<li><p>Out-of-proc managed custom action DLLs:
|
||||
When a managed custom action runs, it normally requests a specific major
|
||||
version of the CLR via CustomAction.config. However in the previous implementation,
|
||||
the request could be ignored if there was already a different version of the CLR
|
||||
loaded into the MSI process, either from a previous custom action or by some other
|
||||
means. (The CLR doesn't allow side-by-side versions within the same process.)
|
||||
While there have been no reports of this issue causing setup failures in practice,
|
||||
it may be only a matter of time, as new CLR versions keep coming out.</p>
|
||||
|
||||
<p>The redesigned native host for managed custom actions, SfxCA.dll, re-launches
|
||||
itself in a separate process before loading the CLR and invoking the managed CA.
|
||||
This ensures that the desired CLR version is always loaded, assuming it is available
|
||||
on the system. It also sets up a named-pipe remoting channel between the two processes.
|
||||
All session-related MSI API calls are routed through that channel, so that the
|
||||
custom action has full access to the installer session just as if it were
|
||||
running in-process.</p></li>
|
||||
|
||||
<li><p>The new zip compression library supports nearly all features of the zip
|
||||
file format. This includes the ZIP64 extensions for archives greater than 4GB,
|
||||
as well as disk-spanning capabilities. Zip encryption is not supported. The zip
|
||||
library has been tested against a variety of third-party zip tools; please
|
||||
report any issues with incompatible packages.</p>
|
||||
|
||||
<p>Currently only the basic DEFLATE compression algorithm is supported
|
||||
(via System.IO.Compression.DeflateStream), and the compression level is not adjustable
|
||||
when packing an archive. The zip file format has a mechanism for plugging in arbitrary
|
||||
compression algorithms, and that capability is exposed: you can provide a Stream object
|
||||
capable of compressing and decompressing bytes as an alternative to DeflateStream.</p></li>
|
||||
|
||||
<li><p>Added support for the few APIs new in MSI 4.0:</p>
|
||||
<ul>
|
||||
<li><font face="Consolas, Courier New">Installer.GetPatchFileList()</font></li>
|
||||
<li><font face="Consolas, Courier New">InstallLogModes.RMFilesInUse</font></li>
|
||||
<li><font face="Consolas, Courier New">ComponentAttributes.DisableRegistryReflection</font></li>
|
||||
<li><font face="Consolas, Courier New">ControlAttributes.ElevationShield</font></li>
|
||||
</ul> <br /></li>
|
||||
|
||||
<li><p>The documentation is now built with the
|
||||
<a href="http://msdn2.microsoft.com/en-us/vstudio/bb608422.aspx" target="_blank">Sandcastle</a> doc build engine,
|
||||
with help from the <a href="http://www.codeplex.com/SHFB" target="_blank">Sandcastle
|
||||
Help File Builder</a>. (The old CHM was built with NDoc.)</p></li>
|
||||
|
||||
<li><p>The documentation includes detailed class diagrams for the
|
||||
WindowsInstaller and Compression namespaces.</p></li>
|
||||
|
||||
<li><p>WindowsInstaller API doc topics now link straight to the corresponding
|
||||
unmanaged MSI API topics in MSDN. If you know an unmanaged MSI API you want to
|
||||
use but don't know the managed equivalent, you can search for it and find what
|
||||
managed APIs link to it.</p></li>
|
||||
|
||||
<li><p>Unit tests cover about 90% of the Compression, Compression.Zip, and
|
||||
Compression.Cab assemblies -- basically everything except some rare
|
||||
error-handling cases.</p></li>
|
||||
|
||||
<li><p>Unit tests along with samples cover over 50% of the WindowsInstaller and
|
||||
WindowsInstaller.Package assemblies (including custom action functionality). More
|
||||
test cases are still being added.</p></li>
|
||||
</ul>
|
||||
|
||||
<h3>Bugfixes</h3>
|
||||
<p>In addition to the extensive cleanup due to redesigns and unit tests, the following
|
||||
reported bugs have been fixed:</p>
|
||||
<ul>
|
||||
<li><p>Managed custom actions could in rare instances fail to load with error 183
|
||||
(directory already exists)</p></li>
|
||||
<li><p>Timestamps of files in a cabinet could be incorrectly offset based on the timezone.
|
||||
(This was due to a behavior change in the DateTime class between .NET 1.1 and 2.0.)</p></li>
|
||||
<li><p>Unicode file paths for cabbed files could be handled incorrectly in some cases</p></li>
|
||||
<li><p>Installer.DetermineApplicablePatches just didn't work</p></li>
|
||||
<li><p>InstallPackage.ApplyPatch couldn't handle applying multiple patches to the same layout</p></li>
|
||||
</ul>
|
||||
|
||||
<h3>LINQ to MSI</h3>
|
||||
<p><i>You'll never want to write MSI SQL again!</i></p>
|
||||
<p>Language INtegrated Query is a new feature in .NET Framework 3.5 and C# 3.0. Through
|
||||
a combination of intuitive language syntax and powerful query operations, LINQ provides
|
||||
a whole new level of productivity for working with data in your code. While the .NET
|
||||
Framework 3.5 provides LINQ capability for SQL databases and XML data, now you
|
||||
can write LINQ queries to fetch and even update data in MSI databases!</p>
|
||||
|
||||
<p>Look at the following example:<br />
|
||||
|
||||
<pre><font face="Consolas, Courier New"> <font color="blue">var</font> actions = <font color="blue">from</font> a <font color="blue">in</font> db.InstallExecuteSequences
|
||||
<font color="blue">join</font> ca <font color="blue">in</font> db.CustomActions <font color="blue">on</font> a.Action <font color="blue">equals</font> ca.Action
|
||||
<font color="blue">where</font> ca.Type == CustomActionTypes.Dll
|
||||
<font color="blue">orderby</font> a.Sequence
|
||||
<font color="blue">select new</font> {
|
||||
Name = a.Action,
|
||||
Target = ca.Target,
|
||||
Sequence = a.Sequence };
|
||||
|
||||
<font color="blue">foreach</font> (<font color="blue">var</font> a <font color="blue">in</font> actions)
|
||||
{
|
||||
Console.WriteLine(a);
|
||||
}
|
||||
</font></pre>
|
||||
|
||||
The query above gets automatically translated to MSI SQL:</p>
|
||||
|
||||
<p><font face="Consolas, Courier New"> SELECT `InstallExecuteSequence`.`Action`,
|
||||
`CustomAction`.`Target`, `InstallExecuteSequence`.`Sequence` FROM `InstallExecuteSequence`, `CustomAction`
|
||||
WHERE `InstallExecuteSequence`.Action` = `CustomAction`.`Action` ORDER BY `InstallExecuteSequence`.`Sequence`</font></p>
|
||||
|
||||
<p>But the query is not executed until the <font face="Consolas, Courier New">foreach</font>
|
||||
enumeration. Then records are fetched from the results incrementally as the enumeration progresses.
|
||||
The objects fetched are actually of an anonymous type created there in the query with exactly
|
||||
the desired fields. So the result of this code will be to print the Action, Target, and Sequence
|
||||
of all Type 1 custom actions.</p>
|
||||
|
||||
<p>The query functionality is currently limited by the capabilities of the MSI SQL engine. For
|
||||
example, a query can't use <font face="Consolas, Courier New">where (ca.Type &
|
||||
CustomActionTypes.Dll) != 0</font> because the bitwise-and operator is not supported by
|
||||
MSI SQL. The preview version of LINQ to MSI will throw an exception for cases like that, but
|
||||
the eventual goal is to have it automatically move the data and operation outside of MSI when
|
||||
necessary, so that any arbitrary expressions are supported in the query.</p>
|
||||
|
||||
<p>Note there are no MSI handles (or <font face="Consolas, Courier New">IDisposable</font>s)
|
||||
to worry about! Handles are all managed internally and closed deterministically. Also,
|
||||
with the entity object model for common tables, the compiler will tell you if you get a
|
||||
column name wrong or misspelled. The entity objects even support easy inserting, updating,
|
||||
and deleting (not shown here).</p>
|
||||
|
||||
<p>For more examples, see the LinqTest project in the source. More documentation
|
||||
is being written.</p>
|
||||
|
||||
<p>Obviously, LINQ to MSI requires .NET Framework 3.5. Everything else
|
||||
in DTF requires only .NET Framework 2.0.</p>
|
||||
|
||||
<p><font color="red">Note: The LINQ functionality in this DTF release is of preview quality only
|
||||
and should not be used in production. While there are unit tests covering a wide
|
||||
variety of queries, using advanced queries outside what is covered by the tests
|
||||
is likely to result in unexpected exceptions, and retrieved data might possibly be
|
||||
incorrect or incomplete. An updated LINQ to MSI library is in development.</font></p>
|
||||
|
||||
<p> </p>
|
||||
|
||||
</div>
|
||||
|
||||
<div id="footer">
|
||||
<p />
|
||||
Send comments on this topic to <a id="HT_MailLink" href="mailto:wix-users%40lists.sourceforge.net?Subject=Deployment Tools Foundation Documentation">
|
||||
wix-users@lists.sourceforge.net</a>
|
||||
|
||||
<script type="text/javascript">
|
||||
var HT_mailLink = document.getElementById("HT_MailLink");
|
||||
var HT_mailLinkText = HT_mailLink.innerHTML;
|
||||
HT_mailLink.href += ": " + document.title;
|
||||
HT_mailLink.innerHTML = HT_mailLinkText;
|
||||
</script>
|
||||
|
||||
<p />
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,73 @@
|
|||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>Windows Installer Package File Manipulation Tool</title>
|
||||
<link rel="stylesheet" type="text/css" href="../styles/presentation.css" />
|
||||
<link rel="stylesheet" type="text/css" href="ms-help://Hx/HxRuntime/HxLink.css" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="control">
|
||||
<span class="productTitle">Deployment Tools Foundation</span><br />
|
||||
<span class="topicTitle">Windows Installer Package File Manipulation Tool</span><br />
|
||||
<div id="toolbar">
|
||||
<span id="chickenFeet">
|
||||
<a href="using.htm">Development Guide</a> >
|
||||
<a href="samples.htm">Samples</a> >
|
||||
<span class="nolink">WiFile</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div id="main">
|
||||
<div id="header">
|
||||
</div>
|
||||
<div class="summary">
|
||||
<p><pre><font face="Consolas, Courier New">Usage: WiFile.exe package.msi /l [filename,filename2,...]
|
||||
Usage: WiFile.exe package.msi /x [filename,filename2,...]
|
||||
Usage: WiFile.exe package.msi /u [filename,filename2,...]
|
||||
|
||||
Lists (/l), extracts (/x) or updates (/u) files in an MSI or MSM.
|
||||
Files are extracted using their source path relative to the package.
|
||||
Specified filenames do not include paths.
|
||||
Filenames may be a pattern such as *.exe or file?.dll</font></pre>
|
||||
</p>
|
||||
<p><br/></p>
|
||||
|
||||
<h4>Example</h4>
|
||||
<p>The most powerful use of WiFile.exe is to do a round-trip update of files in a
|
||||
compressed MSI/MSM package. It works like this:<ol>
|
||||
<li>Extract specific file(s) or all files from the package:
|
||||
<tt>WiFile.exe package.msi /x *</tt></li>
|
||||
<li>The files are now expanded into their directory structure. You can edit/update
|
||||
the files however you like.</li>
|
||||
<li>Update the package with the changed files: <tt>WiFile.exe package.msi /u *</tt>
|
||||
This also updates the file metadata in the MSI including the file version, size, and hash.</li>
|
||||
</ol></p>
|
||||
<p><br/></p>
|
||||
|
||||
<h4>Notes</h4>
|
||||
<ul>
|
||||
<li><p>Also works with packages that have multiple and/or external cab(s).</p></li>
|
||||
</ul>
|
||||
|
||||
<p><br/></p>
|
||||
</div>
|
||||
<div id="footer">
|
||||
<p />
|
||||
Send comments on this topic to <a id="HT_MailLink" href="mailto:wix-users%40lists.sourceforge.net?Subject=Deployment Tools Foundation Documentation">
|
||||
wix-users@lists.sourceforge.net</a>
|
||||
|
||||
<script type="text/javascript">
|
||||
var HT_mailLink = document.getElementById("HT_MailLink");
|
||||
var HT_mailLinkText = HT_mailLink.innerHTML;
|
||||
HT_mailLink.href += ": " + document.title;
|
||||
HT_mailLink.innerHTML = HT_mailLinkText;
|
||||
</script>
|
||||
|
||||
<p />
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,114 @@
|
|||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>Writing Managed Custom Actions</title>
|
||||
<link rel="stylesheet" type="text/css" href="../styles/presentation.css" />
|
||||
<link rel="stylesheet" type="text/css" href="ms-help://Hx/HxRuntime/HxLink.css" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="control">
|
||||
<span class="productTitle">Deployment Tools Foundation</span><br />
|
||||
<span class="topicTitle">Writing Managed Custom Actions</span><br />
|
||||
<div id="toolbar">
|
||||
<span id="chickenFeet">
|
||||
<a href="using.htm">Development Guide</a> >
|
||||
<a href="managedcas.htm">Managed CAs</a> >
|
||||
<span class="nolink">Writing CAs</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div id="main">
|
||||
<div id="header">
|
||||
</div>
|
||||
<div class="summary">
|
||||
<p><b>Caveats</b></p>
|
||||
<p>Before choosing to write a custom action in managed code instead of
|
||||
traditional native C++ code, you should carefully consider the following:</p>
|
||||
<ul>
|
||||
<li><p>Obviously, it introduces a dependency on the .NET Framework. Your
|
||||
MSI package should probably have a LaunchCondition to check for the presence
|
||||
of the correct version of the .NET Framework before anything else happens.</p></li>
|
||||
<li><p>If the custom action runs at uninstall time, then even the uninstall of
|
||||
your product may fail if the .NET Framework is not present. This means a
|
||||
user could run into a problem if they uninstall the .NET Framework before
|
||||
your product.</p></li>
|
||||
<li><p>A managed custom action should be configured to run against a specific
|
||||
version of the .NET Framework, and that version should match the version your
|
||||
actual product runs against. Allowing the version to "float" to the latest
|
||||
installed .NET Framework is likely to lead to compatibility problems with
|
||||
future versions. The .NET Framework provides side-by-side functionality for
|
||||
good reason -- use it.</p></li>
|
||||
|
||||
</ul>
|
||||
<p><br/></p>
|
||||
<p><b>How To</b></p>
|
||||
<ul>
|
||||
<li><p>A custom action function needs to be declared as
|
||||
<tt>public static</tt> (aka <tt>Public Shared</tt> in VB.NET). It takes one parameter which is
|
||||
a <a href="DTFAPI.chm::/html/T_Microsoft_Deployment_WindowsInstaller_Session.htm">Session</a> object, and returns a
|
||||
<a href="DTFAPI.chm::/html/T_Microsoft_Deployment_WindowsInstaller_ActionResult.htm">ActionResult</a> enumeration.</p>
|
||||
<pre><font face="Consolas, Courier New"> [CustomAction]
|
||||
<font color=blue>public</font> <font color=blue>static</font> ActionResult MyCustomAction(Session session)</font></pre><br />
|
||||
<li><p>The function must have a
|
||||
<a href="DTFAPI.chm::/html/T_Microsoft_Deployment_WindowsInstaller_CustomActionAttribute.htm"
|
||||
>CustomActionAttribute</a>, which enables it to be
|
||||
linked to a proxy function. The attribute can take an optional
|
||||
"name" parameter, which is the name of the entrypoint
|
||||
that is exported from the custom action DLL.</p>
|
||||
<li><p>Fill in MSI CustomAction table entries just like you
|
||||
would for a normal type 1 native-DLL CA. Managed CAs can also work just
|
||||
as well in deferred, rollback, and commit modes.</p>
|
||||
<li><p>If the custom action function throws any kind of
|
||||
Exception that isn't handled internally, then it will be caught by the proxy
|
||||
function. The Exception message and stack trace will be printed to the
|
||||
MSI log if logging is enabled, and the CA will return a failure code.</p>
|
||||
<li><p>To be technically correct, any MSI handles obtained should be
|
||||
closed before a custom action function exits -- otherwise a warning
|
||||
gets printed to the log. The handle classes in the managed library
|
||||
(<a href="DTFAPI.chm::/html/T_Microsoft_Deployment_WindowsInstaller_Database.htm">Database</a>,
|
||||
<a href="DTFAPI.chm::/html/T_Microsoft_Deployment_WindowsInstaller_View.htm">View</a>,
|
||||
<a href="DTFAPI.chm::/html/T_Microsoft_Deployment_WindowsInstaller_Record.htm">Record</a>,
|
||||
<a href="DTFAPI.chm::/html/T_Microsoft_Deployment_WindowsInstaller_SummaryInfo.htm">SummaryInfo</a>)
|
||||
all implement the IDisposable interface,
|
||||
which makes them easily managed with C#'s <tt>using</tt>
|
||||
statement. Alternatively, they can be closed in a finally block.
|
||||
As a general rule, <i>methods</i> return new handle objects that should be
|
||||
managed and closed by the user code, while <i>properties</i> only return a reference
|
||||
to a prexisting handle object.</p></li>
|
||||
<li><p>Don't forget to use a <a href="caconfig.htm">CustomAction.config</a> file to
|
||||
specify what version of the .NET Framework the custom action should run against.</p></li>
|
||||
</ul>
|
||||
|
||||
<p><br/></p>
|
||||
<p><b>See also:</b></p>
|
||||
<ul>
|
||||
<li><a href="samplecas.htm">Sample C# Custom Actions</a></li>
|
||||
<li><a href="caconfig.htm">Specifying the Runtime Version</a></li>
|
||||
<li><a href="databases.htm">Working with MSI Databases</a></li>
|
||||
<li><a href="buildingcas.htm">Building Managed Custom Actions</a></li>
|
||||
<li><a href="debuggingcas.htm">Debugging Managed Custom Actions</a></li>
|
||||
</ul>
|
||||
<p><br/></p>
|
||||
|
||||
</div>
|
||||
|
||||
<div id="footer">
|
||||
<p />
|
||||
Send comments on this topic to <a id="HT_MailLink" href="mailto:wix-users%40lists.sourceforge.net?Subject=Deployment Tools Foundation Documentation">
|
||||
wix-users@lists.sourceforge.net</a>
|
||||
|
||||
<script type="text/javascript">
|
||||
var HT_mailLink = document.getElementById("HT_MailLink");
|
||||
var HT_mailLinkText = HT_mailLink.innerHTML;
|
||||
HT_mailLink.href += ": " + document.title;
|
||||
HT_mailLink.innerHTML = HT_mailLinkText;
|
||||
</script>
|
||||
|
||||
<p />
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,132 @@
|
|||
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
|
||||
<HTML>
|
||||
<HEAD>
|
||||
<!-- Sitemap 1.0 -->
|
||||
</HEAD><BODY>
|
||||
<UL>
|
||||
<LI><OBJECT type="text/sitemap">
|
||||
<param name="Name" value="Deployment Tools Foundation Overview">
|
||||
<param name="Local" value="Content\about.htm">
|
||||
</OBJECT>
|
||||
<UL>
|
||||
<LI><OBJECT type="text/sitemap">
|
||||
<param name="Name" value="What's New?">
|
||||
<param name="Local" value="Content\whatsnew.htm">
|
||||
</OBJECT>
|
||||
</LI>
|
||||
<LI><OBJECT type="text/sitemap">
|
||||
<param name="Name" value="Change History">
|
||||
<param name="Local" value="Content\history.htm">
|
||||
</OBJECT>
|
||||
</LI>
|
||||
<LI><OBJECT type="text/sitemap">
|
||||
<param name="Name" value="Dependencies">
|
||||
<param name="Local" value="Content\dependencies.htm">
|
||||
</OBJECT>
|
||||
</LI>
|
||||
<LI><OBJECT type="text/sitemap">
|
||||
<param name="Name" value="Support/Bugs">
|
||||
<param name="Local" value="Content\support.htm">
|
||||
</OBJECT>
|
||||
</LI>
|
||||
</UL>
|
||||
</LI>
|
||||
<LI><OBJECT type="text/sitemap">
|
||||
<param name="Name" value="Deployment Tools Foundation Development Guide">
|
||||
<param name="Local" value="Content\using.htm">
|
||||
</OBJECT>
|
||||
<UL>
|
||||
<LI><OBJECT type="text/sitemap">
|
||||
<param name="Name" value="Managed Custom Actions">
|
||||
<param name="Local" value="Content\managedcas.htm">
|
||||
</OBJECT>
|
||||
<UL>
|
||||
<LI><OBJECT type="text/sitemap">
|
||||
<param name="Name" value="Writing Managed Custom Actions">
|
||||
<param name="Local" value="Content\writingcas.htm">
|
||||
</OBJECT>
|
||||
<UL>
|
||||
<LI><OBJECT type="text/sitemap">
|
||||
<param name="Name" value="Specifying the Runtime Version">
|
||||
<param name="Local" value="Content\caconfig.htm">
|
||||
</OBJECT>
|
||||
</LI>
|
||||
<LI><OBJECT type="text/sitemap">
|
||||
<param name="Name" value="Sample C# Custom Actions">
|
||||
<param name="Local" value="Content\samplecas.htm">
|
||||
</OBJECT>
|
||||
</LI>
|
||||
</UL>
|
||||
</LI>
|
||||
<LI><OBJECT type="text/sitemap">
|
||||
<param name="Name" value="Building Managed Custom Actions">
|
||||
<param name="Local" value="Content\buildingcas.htm">
|
||||
</OBJECT>
|
||||
</LI>
|
||||
<LI><OBJECT type="text/sitemap">
|
||||
<param name="Name" value="Debugging Managed Custom Actions">
|
||||
<param name="Local" value="Content\debuggingcas.htm">
|
||||
</OBJECT>
|
||||
</LI>
|
||||
<LI><OBJECT type="text/sitemap">
|
||||
<param name="Name" value="InstallUtil Notes">
|
||||
<param name="Local" value="Content\installutil.htm">
|
||||
</OBJECT>
|
||||
</LI>
|
||||
</UL>
|
||||
</LI>
|
||||
<LI><OBJECT type="text/sitemap">
|
||||
<param name="Name" value="Working with MSI Databases">
|
||||
<param name="Local" value="Content\databases.htm">
|
||||
</OBJECT>
|
||||
</LI>
|
||||
<LI><OBJECT type="text/sitemap">
|
||||
<param name="Name" value="Working with Cabinet Files">
|
||||
<param name="Local" value="Content\cabs.htm">
|
||||
</OBJECT>
|
||||
</LI>
|
||||
<LI><OBJECT type="text/sitemap">
|
||||
<param name="Name" value="Working with Install Packages">
|
||||
<param name="Local" value="Content\packages.htm">
|
||||
</OBJECT>
|
||||
</LI>
|
||||
<LI><OBJECT type="text/sitemap">
|
||||
<param name="Name" value="Sample Applications">
|
||||
<param name="Local" value="Content\samples.htm">
|
||||
</OBJECT>
|
||||
<UL>
|
||||
<LI><OBJECT type="text/sitemap">
|
||||
<param name="Name" value="MSI Inventory">
|
||||
<param name="Local" value="Content\inventory.htm">
|
||||
</OBJECT>
|
||||
</LI>
|
||||
<LI><OBJECT type="text/sitemap">
|
||||
<param name="Name" value="WiFile">
|
||||
<param name="Local" value="Content\wifile.htm">
|
||||
</OBJECT>
|
||||
</LI>
|
||||
<LI><OBJECT type="text/sitemap">
|
||||
<param name="Name" value="XPack">
|
||||
<param name="Local" value="Content\cabpack.htm">
|
||||
</OBJECT>
|
||||
</LI>
|
||||
<LI><OBJECT type="text/sitemap">
|
||||
<param name="Name" value="DDiff">
|
||||
<param name="Local" value="Content\powerdiff.htm">
|
||||
</OBJECT>
|
||||
</LI>
|
||||
</UL>
|
||||
</LI>
|
||||
</UL>
|
||||
</LI>
|
||||
<LI><OBJECT type="text/sitemap">
|
||||
<param name="Name" value="Deployment Tools Foundation Reference">
|
||||
<param name="Local" value="DTFAPI.chm::/html/R_Project.htm">
|
||||
</OBJECT>
|
||||
<OBJECT type="text/sitemap">
|
||||
<param name="Merge" value="DTFAPI.chm::/DTFAPI.hhc">
|
||||
</OBJECT>
|
||||
</LI>
|
||||
</UL>
|
||||
</BODY>
|
||||
</HTML>
|
|
@ -0,0 +1,126 @@
|
|||
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
|
||||
<HTML>
|
||||
<HEAD>
|
||||
<!-- Sitemap 1.0 -->
|
||||
</HEAD><BODY>
|
||||
<UL>
|
||||
<LI><OBJECT type="text/sitemap">
|
||||
<param name="Name" value="Deployment Tools Foundation Overview">
|
||||
<param name="Local" value="Content\about.htm">
|
||||
</OBJECT>
|
||||
</LI>
|
||||
<LI><OBJECT type="text/sitemap">
|
||||
<param name="Name" value="What's New?">
|
||||
<param name="Local" value="Content\whatsnew.htm">
|
||||
</OBJECT>
|
||||
</LI>
|
||||
<LI><OBJECT type="text/sitemap">
|
||||
<param name="Name" value="Dependencies">
|
||||
<param name="Local" value="Content\dependencies.htm">
|
||||
</OBJECT>
|
||||
</LI>
|
||||
<LI><OBJECT type="text/sitemap">
|
||||
<param name="Name" value="Sample Applications">
|
||||
<param name="Local" value="Content\samples.htm">
|
||||
</OBJECT>
|
||||
</LI>
|
||||
<LI><OBJECT type="text/sitemap">
|
||||
<param name="Name" value="Inventory Sample Application">
|
||||
<param name="Local" value="Content\inventory.htm">
|
||||
</OBJECT>
|
||||
</LI>
|
||||
<LI><OBJECT type="text/sitemap">
|
||||
<param name="Name" value="WiFile Sample Tool">
|
||||
<param name="Local" value="Content\wifile.htm">
|
||||
</OBJECT>
|
||||
</LI>
|
||||
<LI><OBJECT type="text/sitemap">
|
||||
<param name="Name" value="CabPack Sample Tool">
|
||||
<param name="Local" value="Content\cabpack.htm">
|
||||
</OBJECT>
|
||||
</LI>
|
||||
<LI><OBJECT type="text/sitemap">
|
||||
<param name="Name" value="DDiff Sample Tool">
|
||||
<param name="Local" value="Content\powerdiff.htm">
|
||||
</OBJECT>
|
||||
</LI>
|
||||
<LI><OBJECT type="text/sitemap">
|
||||
<param name="Name" value="Support/Bugs">
|
||||
<param name="Local" value="Content\support.htm">
|
||||
</OBJECT>
|
||||
</LI>
|
||||
<LI><OBJECT type="text/sitemap">
|
||||
<param name="Name" value="Change History">
|
||||
<param name="Local" value="Content\history.htm">
|
||||
</OBJECT>
|
||||
</LI>
|
||||
<LI><OBJECT type="text/sitemap">
|
||||
<param name="Name" value="Using Deployment Tools Foundation">
|
||||
<param name="Local" value="Content\using.htm">
|
||||
</OBJECT>
|
||||
</LI>
|
||||
<LI><OBJECT type="text/sitemap">
|
||||
<param name="Name" value="Custom Actions">
|
||||
<param name="Local" value="Content\managedcas.htm">
|
||||
</OBJECT>
|
||||
<UL>
|
||||
<LI><OBJECT type="text/sitemap">
|
||||
<param name="Name" value="Writing">
|
||||
<param name="Local" value="Content\writingcas.htm">
|
||||
</OBJECT>
|
||||
</LI>
|
||||
<LI>
|
||||
<OBJECT type="text/sitemap">
|
||||
<param name="Name" value="CustomAction.config">
|
||||
<param name="Local" value="Content\caconfig.htm">
|
||||
</OBJECT>
|
||||
</LI>
|
||||
<LI><OBJECT type="text/sitemap">
|
||||
<param name="Name" value="Building">
|
||||
<param name="Local" value="Content\buildingcas.htm">
|
||||
</OBJECT>
|
||||
</LI>
|
||||
<LI><OBJECT type="text/sitemap">
|
||||
<param name="Name" value="Debugging">
|
||||
<param name="Local" value="Content\debuggingcas.htm">
|
||||
</OBJECT>
|
||||
</LI>
|
||||
<LI><OBJECT type="text/sitemap">
|
||||
<param name="Name" value="Samples">
|
||||
<param name="Local" value="Content\samplecas.htm">
|
||||
</OBJECT>
|
||||
</LI>
|
||||
</UL>
|
||||
</LI>
|
||||
<LI><OBJECT type="text/sitemap">
|
||||
<param name="Name" value="InstallUtil">
|
||||
<param name="Local" value="Content\installutil.htm">
|
||||
</OBJECT>
|
||||
</LI>
|
||||
<LI><OBJECT type="text/sitemap">
|
||||
<param name="Name" value="CustomAction.config file">
|
||||
<param name="Local" value="Content\caconfig.htm">
|
||||
</OBJECT>
|
||||
</LI>
|
||||
<LI><OBJECT type="text/sitemap">
|
||||
<param name="Name" value="Sample C# Custom Actions">
|
||||
<param name="Local" value="Content\samplecas.htm">
|
||||
</OBJECT>
|
||||
</LI>
|
||||
<LI><OBJECT type="text/sitemap">
|
||||
<param name="Name" value="Databases, Working with">
|
||||
<param name="Local" value="Content\databases.htm">
|
||||
</OBJECT>
|
||||
</LI>
|
||||
<LI><OBJECT type="text/sitemap">
|
||||
<param name="Name" value="Cabinets, Working with">
|
||||
<param name="Local" value="Content\cabs.htm">
|
||||
</OBJECT>
|
||||
</LI>
|
||||
<LI><OBJECT type="text/sitemap">
|
||||
<param name="Name" value="Packages, Working with">
|
||||
<param name="Local" value="Content\packages.htm">
|
||||
</OBJECT>
|
||||
</LI>
|
||||
</UL>
|
||||
</BODY></HTML>
|
|
@ -0,0 +1,49 @@
|
|||
[OPTIONS]
|
||||
Auto Index=Yes
|
||||
Compatibility=1.1 or later
|
||||
Compiled file=DTF.chm
|
||||
Contents file=DTF.hhc
|
||||
Default Window=DTF
|
||||
Default topic=Content\about.htm
|
||||
Display compile progress=Yes
|
||||
Error log file=DTFhelp.log
|
||||
Full-text search=Yes
|
||||
Index file=DTF.hhk
|
||||
Language=0x409 English (United States)
|
||||
Title=Deployment Tools Foundation
|
||||
|
||||
[WINDOWS]
|
||||
DTF="Deployment Tools Foundation","DTF.hhc","DTF.hhk","Content\about.htm","Content\about.htm",,,,,0x22520,,0x384e,[143,72,937,601],,,,,,,0
|
||||
|
||||
|
||||
[FILES]
|
||||
styles\presentation.css
|
||||
Content\about.htm
|
||||
Content\whatsnew.htm
|
||||
Content\dependencies.htm
|
||||
Content\using.htm
|
||||
DTF.hhc
|
||||
DTF.hhk
|
||||
Content\samples.htm
|
||||
Content\support.htm
|
||||
Content\history.htm
|
||||
Content\inventory.htm
|
||||
Content\wifile.htm
|
||||
Content\cabpack.htm
|
||||
Content\powerdiff.htm
|
||||
Content\cabs.htm
|
||||
Content\databases.htm
|
||||
Content\packages.htm
|
||||
Content\managedcas.htm
|
||||
Content\samplecas.htm
|
||||
Content\writingcas.htm
|
||||
Content\buildingcas.htm
|
||||
Content\debuggingcas.htm
|
||||
Content\caproxy.htm
|
||||
Content\caconfig.htm
|
||||
Content\installutil.htm
|
||||
|
||||
[MERGE FILES]
|
||||
DTFAPI.chm
|
||||
|
||||
[INFOTYPES]
|
|
@ -0,0 +1,66 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<!--
|
||||
<copyright file="dtfguide.build" company="Outercurve Foundation">
|
||||
Copyright (c) 2004, Outercurve Foundation.
|
||||
This software is released under Microsoft Reciprocal License (MS-RL).
|
||||
The license and further copyright text can be found in the file LICENSE.TXT
|
||||
LICENSE.TXT at the root directory of the distribution.
|
||||
</copyright>
|
||||
-->
|
||||
<project name="dtfguide" default="dtfguide.inc" xmlns="http://nant.sf.net/release/0.85-rc3/nant.xsd">
|
||||
<description>
|
||||
dtfref.build - Builds the Deployment Tools Foundation user-guide CHM.
|
||||
</description>
|
||||
|
||||
<!--
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Properties
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
-->
|
||||
|
||||
<!-- Include the global build properties -->
|
||||
<include buildfile="..\..\..\..\wix.include" unless="${property::exists('wix.properties.defined')}" />
|
||||
|
||||
<!-- Help tools directories -->
|
||||
<property name="dir.hhw" value="${environment::get-variable('ProgramFiles(x86)')}\HTML Help Workshop" readonly="true" if="${environment::variable-exists('ProgramFiles(x86)')}" />
|
||||
<property name="dir.hhw" value="${environment::get-variable('ProgramFiles')}\HTML Help Workshop" readonly="true" unless="${environment::variable-exists('ProgramFiles(x86)')}" />
|
||||
<property name="hhw-found" value="${directory::exists(dir.hhw)}" readonly="true" />
|
||||
|
||||
<!-- dtfguide-specific properties -->
|
||||
<property name="dir.src.dtfguide" value="${dir.wixroot.src}\dtf\Documents\Guide" readonly="true" />
|
||||
<property name="dir.build.dtfguide" value="${dir.build.wix}\dtfguide" readonly="true" />
|
||||
|
||||
<!--
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Targets
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
-->
|
||||
|
||||
<!-- Build -->
|
||||
<target name="dtfguide.build" description="Peforms a full rebuild (clean then build)" depends="dtfguide.clean dtfguide.inc">
|
||||
</target>
|
||||
|
||||
<!-- Clean -->
|
||||
<target name="dtfguide.clean" description="Cleans the build">
|
||||
<delete dir="${dir.build.dtfguide}" />
|
||||
<delete file="${dir.target.wix}\DTF.chm" />
|
||||
</target>
|
||||
|
||||
<!-- Inc -->
|
||||
<target name="dtfguide.inc" description="Performs an incremental build">
|
||||
<if test="${not ship}">
|
||||
<echo message="Skipping DTF user-guide CHM build because build flavor is not 'ship'." />
|
||||
</if>
|
||||
<if test="${not hhw-found}">
|
||||
<echo message="Skipping DTF user-guide CHM build because HTML Help Workshop was not found at '${dir.hhw}'." />
|
||||
</if>
|
||||
<if test="${ship and hhw-found}">
|
||||
<exec program="${dir.src.dtfguide}\hhc.bat" workingdir="${dir.src.dtfguide}" failonerror="true">
|
||||
<arg file="${dir.hhw}\hhc.exe" />
|
||||
<arg file="${dir.src.dtfguide}\dtf.hhp" />
|
||||
</exec>
|
||||
<move file="${dir.src.dtfguide}\DTFhelp.log" todir="${dir.build.dtfguide}" overwrite="true" />
|
||||
<move file="${dir.src.dtfguide}\DTF.chm" todir="${dir.target.wix}" overwrite="true" />
|
||||
</if>
|
||||
</target>
|
||||
</project>
|
|
@ -0,0 +1 @@
|
|||
@%1 %2 & if not errorlevel 1 exit /b 1
|
|
@ -0,0 +1,394 @@
|
|||
|
||||
/* page style */
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
background-color: #FFFFFF;
|
||||
padding: 0;
|
||||
font-size: 80%;
|
||||
font-family: verdana, sans-serif;
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
table {
|
||||
/* this is a trick to force tables to inherit the body font size */
|
||||
font-size: 100%;
|
||||
}
|
||||
|
||||
/* non-scrolling (control) region style */
|
||||
|
||||
div#control {
|
||||
margin: 0;
|
||||
background-color: #D4DFFF;
|
||||
padding: 4px;
|
||||
width: 100%;
|
||||
border-bottom-color: #C8CDDE;
|
||||
border-bottom-style: solid;
|
||||
border-bottom-width: 1;
|
||||
}
|
||||
|
||||
span.productTitle {
|
||||
font-size: 80%;
|
||||
}
|
||||
|
||||
span.topicTitle {
|
||||
font-size: 140%;
|
||||
font-weight: bold;
|
||||
color: #003399;
|
||||
}
|
||||
|
||||
span#chickenFeet {
|
||||
float: left;
|
||||
}
|
||||
|
||||
span#languageFilter {
|
||||
float: right;
|
||||
}
|
||||
|
||||
/* scrolling (content) region style */
|
||||
|
||||
div#main {
|
||||
margin: 0;
|
||||
padding: 1em;
|
||||
width: 100%;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
/* sections */
|
||||
|
||||
div#header {
|
||||
font-size: 70%;
|
||||
color: #666666;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
div.section {
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
div.sectionTitle {
|
||||
display: inline;
|
||||
font-size: 120%;
|
||||
font-weight: bold;
|
||||
color: #003399;
|
||||
}
|
||||
|
||||
div.sectionContent {
|
||||
margin-top: 0.2em;
|
||||
}
|
||||
|
||||
span.subsectionTitle {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
div#footer {
|
||||
margin-top: 1em;
|
||||
border-top: thin solid #003399;
|
||||
padding-top: 0.5em;
|
||||
}
|
||||
|
||||
/* authored content (block) */
|
||||
|
||||
p {
|
||||
margin-top: 0;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
dl {
|
||||
margin-top: 0;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
div.code {
|
||||
clear: both;
|
||||
width: 100%;
|
||||
background: #F7F7FF;
|
||||
padding: 0.4em;
|
||||
font-family: "Andale Mono";
|
||||
/* font-family: "Courier New"; */
|
||||
/* font-family: "This is not a monospace font", monospace; */
|
||||
font-size: inherit;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
pre {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
table.authoredTable {
|
||||
table-layout: fixed;
|
||||
width: 100%;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
table.authoredTable th {
|
||||
border-bottom-color: #C8CDDE;
|
||||
border-bottom-style: solid;
|
||||
border-bottom-width: 1;
|
||||
background: #EFEFF7;
|
||||
padding: 0.2em;
|
||||
text-align: left;
|
||||
color: #000066;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
table.authoredTable td {
|
||||
border-bottom-style: solid;
|
||||
border-bottom-color: #C8CDDE;
|
||||
border-bottom-width: 1px;
|
||||
background: #F7F7FF;
|
||||
padding: 0.2em;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
div.alert {
|
||||
border: 1px solid #C8CDDE;
|
||||
background: #F7F7FF;
|
||||
}
|
||||
|
||||
div.media {
|
||||
text-align: center;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
|
||||
/* authored content (inline) */
|
||||
|
||||
span.keyword {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
span.code {
|
||||
font-family: "Andale Mono", "Courier New", Courier, monospace;
|
||||
}
|
||||
|
||||
/* auto-generated controls */
|
||||
|
||||
div.langTabs {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
div.langTab {
|
||||
float: left;
|
||||
width: 16%;
|
||||
border-top: 1px solid #C8CDDE;
|
||||
border-left: 1px solid #C8CDDE;
|
||||
border-right: 1px solid #C8CDDE;
|
||||
background: #F7F7FF;
|
||||
padding: 0.2em;
|
||||
text-align: left;
|
||||
color: #000066;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
div.activeLangTab {
|
||||
float: left;
|
||||
width: 16%;
|
||||
border-top: 1px solid #C8CDDE;
|
||||
border-left: 1px solid #C8CDDE;
|
||||
border-right: 1px solid #C8CDDE;
|
||||
background: #EFEFF7;
|
||||
padding: 0.2em;
|
||||
text-align: left;
|
||||
color: #000066;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
table.members {
|
||||
table-layout: fixed;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
table.members th.iconColumn {
|
||||
width: 60px;
|
||||
}
|
||||
|
||||
table.members th.nameColumn {
|
||||
width: 33%;
|
||||
}
|
||||
|
||||
table.members th.descriptionColumn {
|
||||
width: 66%;
|
||||
}
|
||||
|
||||
table.members th {
|
||||
border-bottom-color: #C8CDDE;
|
||||
border-bottom-style: solid;
|
||||
border-bottom-width: 1;
|
||||
background: #EFEFF7;
|
||||
padding: 0.2em;
|
||||
text-align: left;
|
||||
color: #000066;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
table.members td {
|
||||
border-bottom-style: solid;
|
||||
border-bottom-color: #C8CDDE;
|
||||
border-bottom-width: 1px;
|
||||
background: #F7F7FF;
|
||||
padding: 0.2em;
|
||||
vertical-align: top;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
table.exceptions {
|
||||
table-layout: fixed;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
|
||||
table.exceptions th.exceptionNameColumn {
|
||||
width: 33%;
|
||||
}
|
||||
|
||||
table.exceptions th.exceptionConditionColumn {
|
||||
width: 66%;
|
||||
}
|
||||
|
||||
table.exceptions th {
|
||||
border-bottom-color: #C8CDDE;
|
||||
border-bottom-style: solid;
|
||||
border-bottom-width: 1;
|
||||
background: #EFEFF7;
|
||||
padding: 0.2em;
|
||||
text-align: left;
|
||||
color: #000066;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
table.exceptions td {
|
||||
border-bottom-style: solid;
|
||||
border-bottom-color: #C8CDDE;
|
||||
border-bottom-width: 1px;
|
||||
background: #F7F7FF;
|
||||
padding: 0.2em;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
table.permissions {
|
||||
table-layout: fixed;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
|
||||
table.permissions th.permissionNameColumn {
|
||||
width: 33%;
|
||||
}
|
||||
|
||||
table.permissions th.permissionConditionColumn {
|
||||
width: 66%;
|
||||
}
|
||||
|
||||
table.permissions th {
|
||||
border-bottom-color: #C8CDDE;
|
||||
border-bottom-style: solid;
|
||||
border-bottom-width: 1;
|
||||
background: #EFEFF7;
|
||||
padding: 0.2em;
|
||||
text-align: left;
|
||||
color: #000066;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
table.permissions td {
|
||||
border-bottom-style: solid;
|
||||
border-bottom-color: #C8CDDE;
|
||||
border-bottom-width: 1px;
|
||||
background: #F7F7FF;
|
||||
padding: 0.2em;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
span.obsolete {
|
||||
color: red;
|
||||
}
|
||||
|
||||
span.cs {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
span.vb {
|
||||
display: none;
|
||||
}
|
||||
|
||||
span.cpp {
|
||||
display: none;
|
||||
}
|
||||
/* syntax styling */
|
||||
|
||||
div.code span.identifier {
|
||||
font-size: 120%;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
div.code span.keyword {
|
||||
color: green;
|
||||
}
|
||||
|
||||
div.code span.parameter {
|
||||
font-style: italic;
|
||||
color: purple;
|
||||
}
|
||||
|
||||
div.code span.literal {
|
||||
color: purple;
|
||||
}
|
||||
|
||||
div.code span.comment {
|
||||
color: red;
|
||||
}
|
||||
|
||||
span.foreignPhrase {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
span.placeholder {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
a {
|
||||
color: blue;
|
||||
font-weight: bold;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
MSHelp\:link {
|
||||
color: blue;
|
||||
font-weight: bold;
|
||||
hoverColor: #3366ff;
|
||||
}
|
||||
|
||||
span.nolink {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
table.filter {
|
||||
table-layout: fixed;
|
||||
}
|
||||
|
||||
tr.tabs td.tab {
|
||||
width: 10em;
|
||||
background: #F7F7FF;
|
||||
padding: 0.2em;
|
||||
text-align: left;
|
||||
color: #000066;
|
||||
font-weight: normal;
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
tr.tabs td.activeTab {
|
||||
width: 10em;
|
||||
background: #EFEFF7;
|
||||
padding: 0.2em;
|
||||
text-align: left;
|
||||
color: #000066;
|
||||
font-weight: bold;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
td.line {
|
||||
background: #EFEFF7;
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
<html>
|
||||
<head>
|
||||
<title>Class Diagram: Microsoft.Deployment.Compression</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<h3><font face="Verdana">Microsoft.Deployment.Compression Namespace</font></h3>
|
||||
|
||||
<img src="Compression1.png" width="870" height="596" border="0" />
|
||||
<img src="Compression2.png" width="870" height="596" border="0" />
|
||||
|
||||
</body>
|
||||
</html>
|
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 77 KiB |
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 67 KiB |
|
@ -0,0 +1,14 @@
|
|||
<html>
|
||||
<head>
|
||||
<title>Class Diagram: Microsoft.Deployment.WindowsInstaller</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<h3><font face="Verdana">Microsoft.Deployment.WindowsInstaller Namespace</font></h3>
|
||||
|
||||
<img src="WindowsInstaller1.png" width="1136" height="1247" border="0" />
|
||||
<img src="WindowsInstaller2.png" width="1108" height="1247" border="0" />
|
||||
<img src="WindowsInstaller3.png" width="866" height="1247" border="0" />
|
||||
|
||||
</body>
|
||||
</html>
|
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 203 KiB |
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 176 KiB |
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 118 KiB |
|
@ -0,0 +1,87 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<!--
|
||||
<copyright file="dtfref.build" company="Outercurve Foundation">
|
||||
Copyright (c) 2004, Outercurve Foundation.
|
||||
This software is released under Microsoft Reciprocal License (MS-RL).
|
||||
The license and further copyright text can be found in the file LICENSE.TXT
|
||||
LICENSE.TXT at the root directory of the distribution.
|
||||
</copyright>
|
||||
-->
|
||||
<project name="dtfref" default="dtfref.inc" xmlns="http://nant.sf.net/release/0.85-rc3/nant.xsd">
|
||||
<description>
|
||||
dtfref.build - Builds the Deployment Tools Foundation reference CHM.
|
||||
</description>
|
||||
|
||||
<!--
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Properties
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
-->
|
||||
|
||||
<!-- Include the global build properties -->
|
||||
<include buildfile="..\..\..\..\wix.include" unless="${property::exists('wix.properties.defined')}" />
|
||||
|
||||
<!-- Help tools directories -->
|
||||
<property name="dir.sandcastle" value="${dir.externalroot}\Sandcastle" readonly="true" />
|
||||
<property name="dir.sandcastlebuilder" value="${dir.externalroot}\SandcastleBuilder" readonly="true" />
|
||||
<property name="dir.hhw" value="${environment::get-variable('ProgramFiles(x86)')}\HTML Help Workshop" readonly="true" if="${environment::variable-exists('ProgramFiles(x86)')}" />
|
||||
<property name="dir.hhw" value="${environment::get-variable('ProgramFiles')}\HTML Help Workshop" readonly="true" unless="${environment::variable-exists('ProgramFiles(x86)')}" />
|
||||
|
||||
<property name="sandcastle-found" value="${directory::exists(dir.sandcastle)}" readonly="true" />
|
||||
<property name="sandcastlebuilder-found" value="${directory::exists(dir.sandcastlebuilder)}" readonly="true" />
|
||||
<property name="hhw-found" value="${directory::exists(dir.hhw)}" readonly="true" />
|
||||
|
||||
<!-- dtfref-specific properties -->
|
||||
<property name="dir.src.dtfref" value="${dir.wixroot.src}\dtf\Documents\Reference" readonly="true" />
|
||||
<property name="dir.target.dtfref" value="${dir.target.project}\dtfref" readonly="true" />
|
||||
<property name="dir.build.dtfref" value="${dir.build.project}\dtfref" readonly="true" />
|
||||
|
||||
<!--
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Targets
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
-->
|
||||
|
||||
<!-- Build -->
|
||||
<target name="dtfref.build" description="Peforms a full rebuild (clean then build)" depends="dtfref.clean dtfref.inc">
|
||||
</target>
|
||||
|
||||
<!-- Clean -->
|
||||
<target name="dtfref.clean" description="Cleans the build">
|
||||
<delete dir="${dir.build.dtfref}" />
|
||||
<delete dir="${dir.target.dtfref}" />
|
||||
<delete file="${dir.target.wix}\DTFAPI.chm" />
|
||||
</target>
|
||||
|
||||
<!-- Inc -->
|
||||
<target name="dtfref.inc" description="Performs an incremental build">
|
||||
<if test="${not ship}">
|
||||
<echo message="Skipping DTF reference CHM build because build flavor is not 'ship'." />
|
||||
</if>
|
||||
<if test="${not hhw-found}">
|
||||
<echo message="Skipping DTF reference CHM build because HTML Help Workshop was not found at '${dir.hhw}'." />
|
||||
</if>
|
||||
<if test="${not sandcastle-found}">
|
||||
<echo message="Skipping DTF reference CHM build because Sandcastle was not found at '${dir.sandcastle}'." />
|
||||
</if>
|
||||
<if test="${not sandcastlebuilder-found}">
|
||||
<echo message="Skipping DTF reference CHM build because Sandcastle Builder was not found at '${dir.sandcastlebuilder}'." />
|
||||
</if>
|
||||
<if test="${ship and hhw-found and sandcastle-found and sandcastlebuilder-found}">
|
||||
<exec program="msbuild.exe" basedir="${wix.settings.msbuildframework}" failonerror="true">
|
||||
<arg value="/property:Configuration=Debug" if="${debug}" />
|
||||
<arg value="/property:Configuration=Release" if="${ship}" />
|
||||
<arg value="/property:SHFBROOT=${dir.sandcastlebuilder}" />
|
||||
<arg value="/property:OutputPath=${dir.target.dtfref}" />
|
||||
<arg value="/property:WorkingPath=${dir.build.dtfref}" />
|
||||
<arg value="/property:SandcastlePath=${dir.sandcastle}" />
|
||||
<arg value="/property:HtmlHelp1xCompilerPath=${dir.hhw}" />
|
||||
<arg value="/property:DirTargetWix=${dir.target.wix}" />
|
||||
<arg value="/verbosity:${msbuildverbosity}" />
|
||||
<arg value="/target:Rebuild" />
|
||||
<arg file="${dir.src.dtfref}\dtfref.shfbproj" />
|
||||
</exec>
|
||||
<copy file="${dir.target.dtfref}\DTFAPI.chm" todir="${dir.target.wix}" />
|
||||
</if>
|
||||
</target>
|
||||
</project>
|
|
@ -0,0 +1,102 @@
|
|||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="3.5">
|
||||
<PropertyGroup>
|
||||
<!-- The configuration and platform will be used to determine which
|
||||
assemblies to include from solution and project documentation
|
||||
sources -->
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<ProjectGuid>{27c20359-3910-423d-8058-6403935b98c6}</ProjectGuid>
|
||||
<SHFBSchemaVersion>1.8.0.3</SHFBSchemaVersion>
|
||||
<!-- AssemblyName, Name, and RootNamespace are not used by SHFB but Visual
|
||||
Studio adds them anyway -->
|
||||
<AssemblyName>Documentation</AssemblyName>
|
||||
<RootNamespace>Documentation</RootNamespace>
|
||||
<Name>Documentation</Name>
|
||||
<!-- SHFB properties -->
|
||||
<HtmlHelpName>DTFAPI</HtmlHelpName>
|
||||
<ProjectSummary>
|
||||
</ProjectSummary>
|
||||
<MissingTags>Namespace, TypeParameter</MissingTags>
|
||||
<VisibleItems>InheritedMembers, InheritedFrameworkMembers, Protected, ProtectedInternalAsProtected, SealedProtected</VisibleItems>
|
||||
<!--
|
||||
<OutputPath>.\</OutputPath>
|
||||
<HtmlHelp1xCompilerPath>
|
||||
</HtmlHelp1xCompilerPath>
|
||||
<HtmlHelp2xCompilerPath>
|
||||
</HtmlHelp2xCompilerPath>
|
||||
<SandcastlePath>
|
||||
</SandcastlePath>
|
||||
-->
|
||||
<WorkingPath>..\obj\</WorkingPath>
|
||||
<FrameworkVersion>2.0.50727</FrameworkVersion>
|
||||
<RootNamespaceTitle>Deployment Tools Foundation Namespaces</RootNamespaceTitle>
|
||||
<HelpTitle>Deployment Tools Foundation</HelpTitle>
|
||||
<FeedbackEMailAddress>wix-users%40lists.sourceforge.net</FeedbackEMailAddress>
|
||||
<FooterText>&lt%3bscript src=&quot%3bhelplink.js&quot%3b&gt%3b&lt%3b/script&gt%3b</FooterText>
|
||||
<PresentationStyle>Prototype</PresentationStyle>
|
||||
<NamingMethod>MemberName</NamingMethod>
|
||||
<NamespaceSummaries>
|
||||
<NamespaceSummaryItem name="(global)" isDocumented="False" xmlns="" />
|
||||
<NamespaceSummaryItem name="Microsoft.Deployment.Compression" isDocumented="True" xmlns="">Framework for archive packing and unpacking.</NamespaceSummaryItem>
|
||||
<NamespaceSummaryItem name="Microsoft.Deployment.Compression.Cab" isDocumented="True" xmlns="">Implements cabinet archive packing and unpacking.</NamespaceSummaryItem>
|
||||
<NamespaceSummaryItem name="Microsoft.Deployment.Compression.Zip" isDocumented="True" xmlns="">Implements zip archive packing and unpacking.</NamespaceSummaryItem>
|
||||
<NamespaceSummaryItem name="Microsoft.Deployment.Resources" isDocumented="True" xmlns="">Classes for reading and writing resource data in executable files.</NamespaceSummaryItem>
|
||||
<NamespaceSummaryItem name="Microsoft.Deployment.WindowsInstaller" isDocumented="True" xmlns="">Complete class library for the Windows Installer APIs.</NamespaceSummaryItem>
|
||||
<NamespaceSummaryItem name="Microsoft.Deployment.WindowsInstaller.Linq" isDocumented="True" xmlns="">LINQ extensions for querying Windows Installer databases (experimental).</NamespaceSummaryItem>
|
||||
<NamespaceSummaryItem name="Microsoft.Deployment.WindowsInstaller.Linq.Entities" isDocumented="False" xmlns="" />
|
||||
<NamespaceSummaryItem name="Microsoft.Deployment.WindowsInstaller.Package" isDocumented="True" xmlns="">Extended classes for working with Windows Installer installation and patch packages.</NamespaceSummaryItem>
|
||||
</NamespaceSummaries>
|
||||
<DocumentationSources>
|
||||
<DocumentationSource sourceFile="$(DirTargetWix)\Microsoft.Deployment.Compression.dll" xmlns="" />
|
||||
<DocumentationSource sourceFile="$(DirTargetWix)\Microsoft.Deployment.Compression.xml" xmlns="" />
|
||||
<DocumentationSource sourceFile="$(DirTargetWix)\Microsoft.Deployment.Compression.Cab.dll" xmlns="" />
|
||||
<DocumentationSource sourceFile="$(DirTargetWix)\Microsoft.Deployment.Compression.Cab.xml" xmlns="" />
|
||||
<DocumentationSource sourceFile="$(DirTargetWix)\Microsoft.Deployment.Compression.Zip.dll" xmlns="" />
|
||||
<DocumentationSource sourceFile="$(DirTargetWix)\Microsoft.Deployment.Compression.Zip.xml" xmlns="" />
|
||||
<DocumentationSource sourceFile="$(DirTargetWix)\Microsoft.Deployment.Resources.dll" xmlns="" />
|
||||
<DocumentationSource sourceFile="$(DirTargetWix)\Microsoft.Deployment.Resources.xml" xmlns="" />
|
||||
<DocumentationSource sourceFile="$(DirTargetWix)\Microsoft.Deployment.WindowsInstaller.dll" xmlns="" />
|
||||
<DocumentationSource sourceFile="$(DirTargetWix)\Microsoft.Deployment.WindowsInstaller.xml" xmlns="" />
|
||||
<DocumentationSource sourceFile="$(DirTargetWix)\Microsoft.Deployment.WindowsInstaller.Package.dll" xmlns="" />
|
||||
<DocumentationSource sourceFile="$(DirTargetWix)\Microsoft.Deployment.WindowsInstaller.Package.xml" xmlns="" />
|
||||
<DocumentationSource sourceFile="$(DirTargetWix)\Microsoft.Deployment.WindowsInstaller.Linq.dll" xmlns="" />
|
||||
<DocumentationSource sourceFile="$(DirTargetWix)\Microsoft.Deployment.WindowsInstaller.Linq.xml" xmlns="" />
|
||||
</DocumentationSources>
|
||||
</PropertyGroup>
|
||||
<!-- There are no properties for these groups. AnyCPU needs to appear in
|
||||
order for Visual Studio to perform the build. The others are optional
|
||||
common platform types that may appear. -->
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x64' ">
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x64' ">
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|Win32' ">
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|Win32' ">
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="helplink.js" />
|
||||
<Content Include="Compression2.png" />
|
||||
<Content Include="Compression1.png" />
|
||||
<Content Include="Compression.htm" />
|
||||
<Content Include="WindowsInstaller.htm" />
|
||||
<Content Include="WindowsInstaller3.png" />
|
||||
<Content Include="WindowsInstaller2.png" />
|
||||
<Content Include="WindowsInstaller1.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System.Configuration, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
<Reference Include="System.Core, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
|
||||
</ItemGroup>
|
||||
<!-- Import the SHFB build targets -->
|
||||
<Import Project="$(SHFBROOT)\SandcastleHelpFileBuilder.targets" />
|
||||
</Project>
|
|
@ -0,0 +1,194 @@
|
|||
//---------------------------------------------------------------------
|
||||
// <copyright file="helplink.js" company="Outercurve Foundation">
|
||||
// Copyright (c) 2004, Outercurve Foundation.
|
||||
// This software is released under Microsoft Reciprocal License (MS-RL).
|
||||
// The license and further copyright text can be found in the file LICENSE.TXT
|
||||
// LICENSE.TXT at the root directory of the distribution.
|
||||
// </copyright>
|
||||
// <summary>
|
||||
// Fix MSDN API links in the DTF documentation pages.
|
||||
// </summary>
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
FixHelpLinks();
|
||||
|
||||
function GetHelpCode(apiName)
|
||||
{
|
||||
switch (apiName.toLowerCase())
|
||||
{
|
||||
case "msiadvertiseproduct": return 370056;
|
||||
case "msiadvertiseproductex": return 370057;
|
||||
case "msiapplymultiplepatches": return 370059;
|
||||
case "msiapplypatch": return 370060;
|
||||
case "msibegintransaction": return 736312;
|
||||
case "msiclosehandle": return 370067;
|
||||
case "msicollectuserinfo": return 370068;
|
||||
case "msiconfigurefeature": return 370069;
|
||||
case "msiconfigureproduct": return 370070;
|
||||
case "msiconfigureproductex": return 370071;
|
||||
case "msicreaterecord": return 370072;
|
||||
case "msicreatetransformsummaryinfo": return 370073;
|
||||
case "msidatabaseapplytransform": return 370074;
|
||||
case "msidatabasecommit": return 370075;
|
||||
case "msidatabaseexport": return 370076;
|
||||
case "msidatabasegeneratetransform": return 370077;
|
||||
case "msidatabasegetprimarykeys": return 370078;
|
||||
case "msidatabaseimport": return 370079;
|
||||
case "msidatabaseistablepersistent": return 370080;
|
||||
case "msidatabasemerge": return 370081;
|
||||
case "msidatabaseopenview": return 370082;
|
||||
case "msidetermineapplicablepatches": return 370084;
|
||||
case "msideterminepatchsequence": return 370085;
|
||||
case "msidoaction": return 370090;
|
||||
case "msienablelog": return 370091;
|
||||
case "msiendtransaction": return 736318;
|
||||
case "msienumclients": return 370094;
|
||||
case "msienumcomponentcosts": return 370095;
|
||||
case "msienumcomponentqualifiers": return 370096;
|
||||
case "msienumcomponents": return 370097;
|
||||
case "msienumfeatures": return 370098;
|
||||
case "msienumpatches": return 370099;
|
||||
case "msienumpatchesex": return 370100;
|
||||
case "msienumproducts": return 370101;
|
||||
case "msienumproductsex": return 370102;
|
||||
case "msienumrelatedproducts": return 370103;
|
||||
case "msievaluatecondition": return 370104;
|
||||
case "msiextractpatchxmldata": return 370105;
|
||||
case "msiformatrecord": return 370109;
|
||||
case "msigetactivedatabase": return 370110;
|
||||
case "msigetcomponentpath": return 370112;
|
||||
case "msigetcomponentstate": return 370113;
|
||||
case "msigetdatabasestate": return 370114;
|
||||
case "msigetfeaturecost": return 370115;
|
||||
case "msigetfeatureinfo": return 370116;
|
||||
case "msigetfeaturestate": return 370117;
|
||||
case "msigetfeatureusage": return 370118;
|
||||
case "msigetfeaturevalidstates": return 370119;
|
||||
case "msigetfilehash": return 370120;
|
||||
case "msigetfileversion": return 370122;
|
||||
case "msigetlanguage": return 370123;
|
||||
case "msigetlasterrorrecord": return 370124;
|
||||
case "msigetmode": return 370125;
|
||||
case "msigetpatchfilelist": return 370126;
|
||||
case "msigetpatchinfo": return 370127;
|
||||
case "msigetpatchinfoex": return 370128;
|
||||
case "msigetproductcode": return 370129;
|
||||
case "msigetproductinfo": return 370130;
|
||||
case "msigetproductinfoex": return 370131;
|
||||
case "msigetproductinfofromscript": return 370132;
|
||||
case "msigetproductproperty": return 370133;
|
||||
case "msigetproperty": return 370134;
|
||||
case "msigetshortcuttarget": return 370299;
|
||||
case "msigetsourcepath": return 370300;
|
||||
case "msigetsummaryinformation": return 370301;
|
||||
case "msigettargetpath": return 370303;
|
||||
case "msiinstallmissingcomponent": return 370311;
|
||||
case "msiinstallmissingfile": return 370313;
|
||||
case "msiinstallproduct": return 370315;
|
||||
case "msijointransaction": return 736319;
|
||||
case "msilocatecomponent": return 370320;
|
||||
case "msinotifysidchange": return 370328;
|
||||
case "msiopendatabase": return 370338;
|
||||
case "msiopenpackage": return 370339;
|
||||
case "msiopenpackageex": return 370340;
|
||||
case "msiopenproduct": return 370341;
|
||||
case "msiprocessadvertisescript": return 370353;
|
||||
case "msiprocessmessage": return 370354;
|
||||
case "msiprovideassembly": return 370355;
|
||||
case "msiprovidecomponent": return 370356;
|
||||
case "msiprovidequalifiedcomponent": return 370357;
|
||||
case "msiprovidequalifiedcomponentex":return 370358;
|
||||
case "msiquerycomponnetstate": return 370360;
|
||||
case "msiqueryfeaturestate": return 370361;
|
||||
case "msiqueryfeaturestateex": return 370362;
|
||||
case "msiqueryproductstate": return 370363;
|
||||
case "msirecordcleardata": return 370364;
|
||||
case "msirecorddatasize": return 370365;
|
||||
case "msirecordgetfieldcount": return 370366;
|
||||
case "msirecordgetinteger": return 370367;
|
||||
case "msirecordgetstring": return 370368;
|
||||
case "msirecordisnull": return 370369;
|
||||
case "msirecordreadstream": return 370370;
|
||||
case "msirecordsetinteger": return 370371;
|
||||
case "msirecordsetstream": return 370372;
|
||||
case "msirecordsetstring": return 370373;
|
||||
case "msireinstallfeature": return 370374;
|
||||
case "msireinstallproduct": return 370375;
|
||||
case "msiremovepatches": return 370376;
|
||||
case "msisequence": return 370382;
|
||||
case "msisetcomponentstate": return 370383;
|
||||
case "msisetexternalui": return 370384;
|
||||
case "msisetexternaluirecord": return 370385;
|
||||
case "msisetfeatureattributes": return 370386;
|
||||
case "msisetfeaturestate": return 370387;
|
||||
case "msisetinstalllevel": return 370388;
|
||||
case "msisetinternalui": return 370389;
|
||||
case "msisetmode": return 370390;
|
||||
case "msisetproperty": return 370391;
|
||||
case "msisettargetpath": return 370392;
|
||||
case "msisourcelistaddmediadisk": return 370394;
|
||||
case "msisourcelistaddsource": return 370395;
|
||||
case "msisourcelistaddsourceex": return 370396;
|
||||
case "msisourcelistclearall": return 370397;
|
||||
case "msisourcelistclearallex": return 370398;
|
||||
case "msisourcelistclearmediadisk": return 370399;
|
||||
case "msisourcelistclearsource": return 370401;
|
||||
case "msisourcelistenummediadisks": return 370402;
|
||||
case "msisourcelistenumsources": return 370403;
|
||||
case "msisourcelistforceresolution": return 370404;
|
||||
case "msisourcelistforceresolutionex":return 370405;
|
||||
case "msisourcelistgetinfo": return 370406;
|
||||
case "msisourcelistsetinfo": return 370407;
|
||||
case "msisummaryinfogetproperty": return 370409;
|
||||
case "msisummaryinfopersist": return 370490;
|
||||
case "msisummaryinfosetproperty": return 370491;
|
||||
case "msiusefeature": return 370502;
|
||||
case "msiusefeatureex": return 370503;
|
||||
case "msiverifydiskspace": return 370506;
|
||||
case "msiverifypackage": return 370508;
|
||||
case "msiviewexecute": return 370513;
|
||||
case "msiviewfetch": return 370514;
|
||||
case "msiviewgetcolumninfo": return 370516;
|
||||
case "msiviewgeterror": return 370518;
|
||||
case "msiviewmodify": return 370519;
|
||||
case "productid": return 370855;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
function GetHelpLink(apiName)
|
||||
{
|
||||
var helpCode = GetHelpCode(apiName);
|
||||
if (helpCode != 0)
|
||||
{
|
||||
// Found a direct link!
|
||||
var prefix = (helpCode < 500000 ? "aa" : "bb");
|
||||
return "http://msdn2.microsoft.com/en-us/library/" + prefix + helpCode + ".aspx";
|
||||
}
|
||||
else
|
||||
{
|
||||
// This link works, but goes through an annoying 5-sec redirect page.
|
||||
return "http://msdn.microsoft.com/library/en-us/msi/setup/" + apiName.toLowerCase() + ".asp";
|
||||
}
|
||||
}
|
||||
|
||||
// Change any MSI API help links from indirect MSDN references to direct references.
|
||||
function FixHelpLinks()
|
||||
{
|
||||
var msiLinkRegex = /msdn\.microsoft\.com\/library\/en-us\/msi\/setup\/([a-z]+)\.asp/i;
|
||||
var links = document.body.all.tags("a");
|
||||
var i;
|
||||
for (i = 0; i < links.length; i++)
|
||||
{
|
||||
var linkElem = links(i);
|
||||
var match = msiLinkRegex.exec(linkElem.href);
|
||||
if (match)
|
||||
{
|
||||
var apiName = match[1];
|
||||
linkElem.href = GetHelpLink(apiName);
|
||||
linkElem.target = "_blank";
|
||||
linkElem.title = "MSDN Library";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
//---------------------------------------------------------------------
|
||||
// <copyright file="AssemblyInfo.cs" company="Outercurve Foundation">
|
||||
// Copyright (c) 2004, Outercurve Foundation.
|
||||
// This software is released under Microsoft Reciprocal License (MS-RL).
|
||||
// The license and further copyright text can be found in the file LICENSE.TXT
|
||||
// LICENSE.TXT at the root directory of the distribution.
|
||||
// </copyright>
|
||||
// <summary>
|
||||
// Part of the Deployment Tools Foundation project.
|
||||
// </summary>
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
using System.Resources;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security;
|
||||
using System.Security.Permissions;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
[assembly: AssemblyDescription("Managed libraries for cabinet archive packing and unpacking")]
|
||||
[assembly: CLSCompliant(true)]
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// SECURITY: The UnmanagedCode assertions in the cabinet classes are safe, because the
|
||||
// assertions are not propogated through calls to the provided callbacks. So there
|
||||
// is no way that a partially-trusted malicious client could trick a trusted cabinet
|
||||
// class into executing its own unmanaged code.
|
||||
[assembly: SecurityPermission(SecurityAction.RequestMinimum, Assertion=true, UnmanagedCode=true)]
|
||||
|
||||
// SECURITY: Review carefully!
|
||||
// This assembly is designed so that partially trusted callers should be able to
|
||||
// do cabinet compression and extraction in a file path where they have limited
|
||||
// file I/O permission. Or they can even do in-memory compression and extraction
|
||||
// with absolutely no file I/O permission.
|
||||
[assembly: AllowPartiallyTrustedCallers]
|
||||
|
||||
[assembly: SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "Microsoft.Deployment.Compression.Cab")]
|
|
@ -0,0 +1,174 @@
|
|||
//---------------------------------------------------------------------
|
||||
// <copyright file="CabEngine.cs" company="Outercurve Foundation">
|
||||
// Copyright (c) 2004, Outercurve Foundation.
|
||||
// This software is released under Microsoft Reciprocal License (MS-RL).
|
||||
// The license and further copyright text can be found in the file LICENSE.TXT
|
||||
// LICENSE.TXT at the root directory of the distribution.
|
||||
// </copyright>
|
||||
// <summary>
|
||||
// Part of the Deployment Tools Foundation project.
|
||||
// </summary>
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Deployment.Compression.Cab
|
||||
{
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
|
||||
/// <summary>
|
||||
/// Engine capable of packing and unpacking archives in the cabinet format.
|
||||
/// </summary>
|
||||
public class CabEngine : CompressionEngine
|
||||
{
|
||||
private CabPacker packer;
|
||||
private CabUnpacker unpacker;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the cabinet engine.
|
||||
/// </summary>
|
||||
public CabEngine()
|
||||
: base()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes of resources allocated by the cabinet engine.
|
||||
/// </summary>
|
||||
/// <param name="disposing">If true, the method has been called directly
|
||||
/// or indirectly by a user's code, so managed and unmanaged resources
|
||||
/// will be disposed. If false, the method has been called by the runtime
|
||||
/// from inside the finalizer, and only unmanaged resources will be
|
||||
/// disposed.</param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
if (packer != null)
|
||||
{
|
||||
packer.Dispose();
|
||||
packer = null;
|
||||
}
|
||||
if (unpacker != null)
|
||||
{
|
||||
unpacker.Dispose();
|
||||
unpacker = null;
|
||||
}
|
||||
}
|
||||
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
private CabPacker Packer
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this.packer == null)
|
||||
{
|
||||
this.packer = new CabPacker(this);
|
||||
}
|
||||
|
||||
return this.packer;
|
||||
}
|
||||
}
|
||||
|
||||
private CabUnpacker Unpacker
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this.unpacker == null)
|
||||
{
|
||||
this.unpacker = new CabUnpacker(this);
|
||||
}
|
||||
|
||||
return this.unpacker;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a cabinet or chain of cabinets.
|
||||
/// </summary>
|
||||
/// <param name="streamContext">A context interface to handle opening
|
||||
/// and closing of cabinet and file streams.</param>
|
||||
/// <param name="files">The paths of the files in the archive (not
|
||||
/// external file paths).</param>
|
||||
/// <param name="maxArchiveSize">The maximum number of bytes for one
|
||||
/// cabinet before the contents are chained to the next cabinet, or zero
|
||||
/// for unlimited cabinet size.</param>
|
||||
/// <exception cref="ArchiveException">The cabinet could not be
|
||||
/// created.</exception>
|
||||
/// <remarks>
|
||||
/// The stream context implementation may provide a mapping from the
|
||||
/// file paths within the cabinet to the external file paths.
|
||||
/// <para>Smaller folder sizes can make it more efficient to extract
|
||||
/// individual files out of large cabinet packages.</para>
|
||||
/// </remarks>
|
||||
public override void Pack(
|
||||
IPackStreamContext streamContext,
|
||||
IEnumerable<string> files,
|
||||
long maxArchiveSize)
|
||||
{
|
||||
this.Packer.CompressionLevel = this.CompressionLevel;
|
||||
this.Packer.UseTempFiles = this.UseTempFiles;
|
||||
this.Packer.Pack(streamContext, files, maxArchiveSize);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether a Stream begins with a header that indicates
|
||||
/// it is a valid cabinet file.
|
||||
/// </summary>
|
||||
/// <param name="stream">Stream for reading the cabinet file.</param>
|
||||
/// <returns>True if the stream is a valid cabinet file
|
||||
/// (with no offset); false otherwise.</returns>
|
||||
public override bool IsArchive(Stream stream)
|
||||
{
|
||||
return this.Unpacker.IsArchive(stream);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets information about files in a cabinet or cabinet chain.
|
||||
/// </summary>
|
||||
/// <param name="streamContext">A context interface to handle opening
|
||||
/// and closing of cabinet and file streams.</param>
|
||||
/// <param name="fileFilter">A predicate that can determine
|
||||
/// which files to process, optional.</param>
|
||||
/// <returns>Information about files in the cabinet stream.</returns>
|
||||
/// <exception cref="ArchiveException">The cabinet provided
|
||||
/// by the stream context is not valid.</exception>
|
||||
/// <remarks>
|
||||
/// The <paramref name="fileFilter"/> predicate takes an internal file
|
||||
/// path and returns true to include the file or false to exclude it.
|
||||
/// </remarks>
|
||||
public override IList<ArchiveFileInfo> GetFileInfo(
|
||||
IUnpackStreamContext streamContext,
|
||||
Predicate<string> fileFilter)
|
||||
{
|
||||
return this.Unpacker.GetFileInfo(streamContext, fileFilter);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extracts files from a cabinet or cabinet chain.
|
||||
/// </summary>
|
||||
/// <param name="streamContext">A context interface to handle opening
|
||||
/// and closing of cabinet and file streams.</param>
|
||||
/// <param name="fileFilter">An optional predicate that can determine
|
||||
/// which files to process.</param>
|
||||
/// <exception cref="ArchiveException">The cabinet provided
|
||||
/// by the stream context is not valid.</exception>
|
||||
/// <remarks>
|
||||
/// The <paramref name="fileFilter"/> predicate takes an internal file
|
||||
/// path and returns true to include the file or false to exclude it.
|
||||
/// </remarks>
|
||||
public override void Unpack(
|
||||
IUnpackStreamContext streamContext,
|
||||
Predicate<string> fileFilter)
|
||||
{
|
||||
this.Unpacker.Unpack(streamContext, fileFilter);
|
||||
}
|
||||
|
||||
internal void ReportProgress(ArchiveProgressEventArgs e)
|
||||
{
|
||||
base.OnProgress(e);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,166 @@
|
|||
//---------------------------------------------------------------------
|
||||
// <copyright file="CabException.cs" company="Outercurve Foundation">
|
||||
// Copyright (c) 2004, Outercurve Foundation.
|
||||
// This software is released under Microsoft Reciprocal License (MS-RL).
|
||||
// The license and further copyright text can be found in the file LICENSE.TXT
|
||||
// LICENSE.TXT at the root directory of the distribution.
|
||||
// </copyright>
|
||||
// <summary>
|
||||
// Part of the Deployment Tools Foundation project.
|
||||
// </summary>
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Deployment.Compression.Cab
|
||||
{
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Resources;
|
||||
using System.Globalization;
|
||||
using System.Security.Permissions;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
/// <summary>
|
||||
/// Exception class for cabinet operations.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class CabException : ArchiveException
|
||||
{
|
||||
private static ResourceManager errorResources;
|
||||
private int error;
|
||||
private int errorCode;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new CabException with a specified error message and a reference to the
|
||||
/// inner exception that is the cause of this exception.
|
||||
/// </summary>
|
||||
/// <param name="message">The message that describes the error.</param>
|
||||
/// <param name="innerException">The exception that is the cause of the current exception. If the
|
||||
/// innerException parameter is not a null reference (Nothing in Visual Basic), the current exception
|
||||
/// is raised in a catch block that handles the inner exception.</param>
|
||||
public CabException(string message, Exception innerException)
|
||||
: this(0, 0, message, innerException) { }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new CabException with a specified error message.
|
||||
/// </summary>
|
||||
/// <param name="message">The message that describes the error.</param>
|
||||
public CabException(string message)
|
||||
: this(0, 0, message, null) { }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new CabException.
|
||||
/// </summary>
|
||||
public CabException()
|
||||
: this(0, 0, null, null) { }
|
||||
|
||||
internal CabException(int error, int errorCode, string message, Exception innerException)
|
||||
: base(message, innerException)
|
||||
{
|
||||
this.error = error;
|
||||
this.errorCode = errorCode;
|
||||
}
|
||||
|
||||
internal CabException(int error, int errorCode, string message)
|
||||
: this(error, errorCode, message, null) { }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the CabException class with serialized data.
|
||||
/// </summary>
|
||||
/// <param name="info">The SerializationInfo that holds the serialized object data about the exception being thrown.</param>
|
||||
/// <param name="context">The StreamingContext that contains contextual information about the source or destination.</param>
|
||||
protected CabException(SerializationInfo info, StreamingContext context) : base(info, context)
|
||||
{
|
||||
if (info == null)
|
||||
{
|
||||
throw new ArgumentNullException("info");
|
||||
}
|
||||
|
||||
this.error = info.GetInt32("cabError");
|
||||
this.errorCode = info.GetInt32("cabErrorCode");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the FCI or FDI cabinet engine error number.
|
||||
/// </summary>
|
||||
/// <value>A cabinet engine error number, or 0 if the exception was
|
||||
/// not related to a cabinet engine error number.</value>
|
||||
public int Error
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.error;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Win32 error code.
|
||||
/// </summary>
|
||||
/// <value>A Win32 error code, or 0 if the exception was
|
||||
/// not related to a Win32 error.</value>
|
||||
public int ErrorCode
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.errorCode;
|
||||
}
|
||||
}
|
||||
|
||||
internal static ResourceManager ErrorResources
|
||||
{
|
||||
get
|
||||
{
|
||||
if (errorResources == null)
|
||||
{
|
||||
errorResources = new ResourceManager(
|
||||
typeof(CabException).Namespace + ".Errors",
|
||||
typeof(CabException).Assembly);
|
||||
}
|
||||
return errorResources;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the SerializationInfo with information about the exception.
|
||||
/// </summary>
|
||||
/// <param name="info">The SerializationInfo that holds the serialized object data about the exception being thrown.</param>
|
||||
/// <param name="context">The StreamingContext that contains contextual information about the source or destination.</param>
|
||||
[SecurityPermission(SecurityAction.Demand, SerializationFormatter=true)]
|
||||
public override void GetObjectData(SerializationInfo info, StreamingContext context)
|
||||
{
|
||||
if (info == null)
|
||||
{
|
||||
throw new ArgumentNullException("info");
|
||||
}
|
||||
|
||||
info.AddValue("cabError", this.error);
|
||||
info.AddValue("cabErrorCode", this.errorCode);
|
||||
base.GetObjectData(info, context);
|
||||
}
|
||||
|
||||
internal static string GetErrorMessage(int error, int errorCode, bool extracting)
|
||||
{
|
||||
const int FCI_ERROR_RESOURCE_OFFSET = 1000;
|
||||
const int FDI_ERROR_RESOURCE_OFFSET = 2000;
|
||||
int resourceOffset = (extracting ? FDI_ERROR_RESOURCE_OFFSET : FCI_ERROR_RESOURCE_OFFSET);
|
||||
|
||||
string msg = CabException.ErrorResources.GetString(
|
||||
(resourceOffset + error).ToString(CultureInfo.InvariantCulture.NumberFormat),
|
||||
CultureInfo.CurrentCulture);
|
||||
|
||||
if (msg == null)
|
||||
{
|
||||
msg = CabException.ErrorResources.GetString(
|
||||
resourceOffset.ToString(CultureInfo.InvariantCulture.NumberFormat),
|
||||
CultureInfo.CurrentCulture);
|
||||
}
|
||||
|
||||
if (errorCode != 0)
|
||||
{
|
||||
const string GENERIC_ERROR_RESOURCE = "1";
|
||||
string msg2 = CabException.ErrorResources.GetString(GENERIC_ERROR_RESOURCE, CultureInfo.CurrentCulture);
|
||||
msg = String.Format(CultureInfo.InvariantCulture, "{0} " + msg2, msg, errorCode);
|
||||
}
|
||||
return msg;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,153 @@
|
|||
//---------------------------------------------------------------------
|
||||
// <copyright file="CabFileInfo.cs" company="Outercurve Foundation">
|
||||
// Copyright (c) 2004, Outercurve Foundation.
|
||||
// This software is released under Microsoft Reciprocal License (MS-RL).
|
||||
// The license and further copyright text can be found in the file LICENSE.TXT
|
||||
// LICENSE.TXT at the root directory of the distribution.
|
||||
// </copyright>
|
||||
// <summary>
|
||||
// Part of the Deployment Tools Foundation project.
|
||||
// </summary>
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Deployment.Compression.Cab
|
||||
{
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Security.Permissions;
|
||||
|
||||
/// <summary>
|
||||
/// Object representing a compressed file within a cabinet package; provides operations for getting
|
||||
/// the file properties and extracting the file.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class CabFileInfo : ArchiveFileInfo
|
||||
{
|
||||
private int cabFolder;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new CabinetFileInfo object representing a file within a cabinet in a specified path.
|
||||
/// </summary>
|
||||
/// <param name="cabinetInfo">An object representing the cabinet containing the file.</param>
|
||||
/// <param name="filePath">The path to the file within the cabinet. Usually, this is a simple file
|
||||
/// name, but if the cabinet contains a directory structure this may include the directory.</param>
|
||||
public CabFileInfo(CabInfo cabinetInfo, string filePath)
|
||||
: base(cabinetInfo, filePath)
|
||||
{
|
||||
if (cabinetInfo == null)
|
||||
{
|
||||
throw new ArgumentNullException("cabinetInfo");
|
||||
}
|
||||
|
||||
this.cabFolder = -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new CabinetFileInfo object with all parameters specified,
|
||||
/// used internally when reading the metadata out of a cab.
|
||||
/// </summary>
|
||||
/// <param name="filePath">The internal path and name of the file in the cab.</param>
|
||||
/// <param name="cabFolder">The folder number containing the file.</param>
|
||||
/// <param name="cabNumber">The cabinet number where the file starts.</param>
|
||||
/// <param name="attributes">The stored attributes of the file.</param>
|
||||
/// <param name="lastWriteTime">The stored last write time of the file.</param>
|
||||
/// <param name="length">The uncompressed size of the file.</param>
|
||||
internal CabFileInfo(
|
||||
string filePath,
|
||||
int cabFolder,
|
||||
int cabNumber,
|
||||
FileAttributes attributes,
|
||||
DateTime lastWriteTime,
|
||||
long length)
|
||||
: base(filePath, cabNumber, attributes, lastWriteTime, length)
|
||||
{
|
||||
this.cabFolder = cabFolder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the CabinetFileInfo class with serialized data.
|
||||
/// </summary>
|
||||
/// <param name="info">The SerializationInfo that holds the serialized object data about the exception being thrown.</param>
|
||||
/// <param name="context">The StreamingContext that contains contextual information about the source or destination.</param>
|
||||
protected CabFileInfo(SerializationInfo info, StreamingContext context)
|
||||
: base(info, context)
|
||||
{
|
||||
this.cabFolder = info.GetInt32("cabFolder");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the SerializationInfo with information about the archive.
|
||||
/// </summary>
|
||||
/// <param name="info">The SerializationInfo that holds the serialized object data.</param>
|
||||
/// <param name="context">The StreamingContext that contains contextual information
|
||||
/// about the source or destination.</param>
|
||||
[SecurityPermission(SecurityAction.Demand, SerializationFormatter = true)]
|
||||
public override void GetObjectData(SerializationInfo info, StreamingContext context)
|
||||
{
|
||||
base.GetObjectData(info, context);
|
||||
info.AddValue("cabFolder", this.cabFolder);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the cabinet that contains this file.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The CabinetInfo instance that retrieved this file information -- this
|
||||
/// may be null if the CabinetFileInfo object was returned directly from a
|
||||
/// stream.
|
||||
/// </value>
|
||||
public CabInfo Cabinet
|
||||
{
|
||||
get
|
||||
{
|
||||
return (CabInfo) this.Archive;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the full path of the cabinet that contains this file.
|
||||
/// </summary>
|
||||
/// <value>The full path of the cabinet that contains this file.</value>
|
||||
public string CabinetName
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.ArchiveName;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of the folder containing this file.
|
||||
/// </summary>
|
||||
/// <value>The number of the cabinet folder containing this file.</value>
|
||||
/// <remarks>A single folder or the first folder of a cabinet
|
||||
/// (or chain of cabinets) is numbered 0.</remarks>
|
||||
public int CabinetFolderNumber
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this.cabFolder < 0)
|
||||
{
|
||||
this.Refresh();
|
||||
}
|
||||
return this.cabFolder;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Refreshes the information in this object with new data retrieved
|
||||
/// from an archive.
|
||||
/// </summary>
|
||||
/// <param name="newFileInfo">Fresh instance for the same file just
|
||||
/// read from the archive.</param>
|
||||
/// <remarks>
|
||||
/// This implementation refreshes the <see cref="CabinetFolderNumber"/>.
|
||||
/// </remarks>
|
||||
protected override void Refresh(ArchiveFileInfo newFileInfo)
|
||||
{
|
||||
base.Refresh(newFileInfo);
|
||||
this.cabFolder = ((CabFileInfo) newFileInfo).cabFolder;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,92 @@
|
|||
//---------------------------------------------------------------------
|
||||
// <copyright file="CabInfo.cs" company="Outercurve Foundation">
|
||||
// Copyright (c) 2004, Outercurve Foundation.
|
||||
// This software is released under Microsoft Reciprocal License (MS-RL).
|
||||
// The license and further copyright text can be found in the file LICENSE.TXT
|
||||
// LICENSE.TXT at the root directory of the distribution.
|
||||
// </copyright>
|
||||
// <summary>
|
||||
// Part of the Deployment Tools Foundation project.
|
||||
// </summary>
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Deployment.Compression.Cab
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
/// <summary>
|
||||
/// Object representing a cabinet file on disk; provides access to
|
||||
/// file-based operations on the cabinet file.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Generally, the methods on this class are much easier to use than the
|
||||
/// stream-based interfaces provided by the <see cref="CabEngine"/> class.
|
||||
/// </remarks>
|
||||
[Serializable]
|
||||
public class CabInfo : ArchiveInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new CabinetInfo object representing a cabinet file in a specified path.
|
||||
/// </summary>
|
||||
/// <param name="path">The path to the cabinet file. When creating a cabinet file, this file does not
|
||||
/// necessarily exist yet.</param>
|
||||
public CabInfo(string path)
|
||||
: base(path)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the CabinetInfo class with serialized data.
|
||||
/// </summary>
|
||||
/// <param name="info">The SerializationInfo that holds the serialized object data about the exception being thrown.</param>
|
||||
/// <param name="context">The StreamingContext that contains contextual information about the source or destination.</param>
|
||||
protected CabInfo(SerializationInfo info, StreamingContext context)
|
||||
: base(info, context)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a compression engine that does the low-level work for
|
||||
/// this object.
|
||||
/// </summary>
|
||||
/// <returns>A new <see cref="CabEngine"/> instance.</returns>
|
||||
/// <remarks>
|
||||
/// Each instance will be <see cref="CompressionEngine.Dispose()"/>d
|
||||
/// immediately after use.
|
||||
/// </remarks>
|
||||
protected override CompressionEngine CreateCompressionEngine()
|
||||
{
|
||||
return new CabEngine();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets information about the files contained in the archive.
|
||||
/// </summary>
|
||||
/// <returns>A list of <see cref="CabFileInfo"/> objects, each
|
||||
/// containing information about a file in the archive.</returns>
|
||||
public new IList<CabFileInfo> GetFiles()
|
||||
{
|
||||
IList<ArchiveFileInfo> files = base.GetFiles();
|
||||
List<CabFileInfo> cabFiles = new List<CabFileInfo>(files.Count);
|
||||
foreach (CabFileInfo cabFile in files) cabFiles.Add(cabFile);
|
||||
return cabFiles.AsReadOnly();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets information about the certain files contained in the archive file.
|
||||
/// </summary>
|
||||
/// <param name="searchPattern">The search string, such as
|
||||
/// "*.txt".</param>
|
||||
/// <returns>A list of <see cref="CabFileInfo"/> objects, each containing
|
||||
/// information about a file in the archive.</returns>
|
||||
public new IList<CabFileInfo> GetFiles(string searchPattern)
|
||||
{
|
||||
IList<ArchiveFileInfo> files = base.GetFiles(searchPattern);
|
||||
List<CabFileInfo> cabFiles = new List<CabFileInfo>(files.Count);
|
||||
foreach (CabFileInfo cabFile in files) cabFiles.Add(cabFile);
|
||||
return cabFiles.AsReadOnly();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,667 @@
|
|||
//---------------------------------------------------------------------
|
||||
// <copyright file="CabPacker.cs" company="Outercurve Foundation">
|
||||
// Copyright (c) 2004, Outercurve Foundation.
|
||||
// This software is released under Microsoft Reciprocal License (MS-RL).
|
||||
// The license and further copyright text can be found in the file LICENSE.TXT
|
||||
// LICENSE.TXT at the root directory of the distribution.
|
||||
// </copyright>
|
||||
// <summary>
|
||||
// Part of the Deployment Tools Foundation project.
|
||||
// </summary>
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Deployment.Compression.Cab
|
||||
{
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Security.Permissions;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
internal class CabPacker : CabWorker
|
||||
{
|
||||
private const string TempStreamName = "%%TEMP%%";
|
||||
|
||||
private NativeMethods.FCI.Handle fciHandle;
|
||||
|
||||
// These delegates need to be saved as member variables
|
||||
// so that they don't get GC'd.
|
||||
private NativeMethods.FCI.PFNALLOC fciAllocMemHandler;
|
||||
private NativeMethods.FCI.PFNFREE fciFreeMemHandler;
|
||||
private NativeMethods.FCI.PFNOPEN fciOpenStreamHandler;
|
||||
private NativeMethods.FCI.PFNREAD fciReadStreamHandler;
|
||||
private NativeMethods.FCI.PFNWRITE fciWriteStreamHandler;
|
||||
private NativeMethods.FCI.PFNCLOSE fciCloseStreamHandler;
|
||||
private NativeMethods.FCI.PFNSEEK fciSeekStreamHandler;
|
||||
private NativeMethods.FCI.PFNFILEPLACED fciFilePlacedHandler;
|
||||
private NativeMethods.FCI.PFNDELETE fciDeleteFileHandler;
|
||||
private NativeMethods.FCI.PFNGETTEMPFILE fciGetTempFileHandler;
|
||||
|
||||
private NativeMethods.FCI.PFNGETNEXTCABINET fciGetNextCabinet;
|
||||
private NativeMethods.FCI.PFNSTATUS fciCreateStatus;
|
||||
private NativeMethods.FCI.PFNGETOPENINFO fciGetOpenInfo;
|
||||
|
||||
private IPackStreamContext context;
|
||||
|
||||
private FileAttributes fileAttributes;
|
||||
private DateTime fileLastWriteTime;
|
||||
|
||||
private int maxCabBytes;
|
||||
|
||||
private long totalFolderBytesProcessedInCurrentCab;
|
||||
|
||||
private CompressionLevel compressionLevel;
|
||||
private bool dontUseTempFiles;
|
||||
private IList<Stream> tempStreams;
|
||||
|
||||
public CabPacker(CabEngine cabEngine)
|
||||
: base(cabEngine)
|
||||
{
|
||||
this.fciAllocMemHandler = this.CabAllocMem;
|
||||
this.fciFreeMemHandler = this.CabFreeMem;
|
||||
this.fciOpenStreamHandler = this.CabOpenStreamEx;
|
||||
this.fciReadStreamHandler = this.CabReadStreamEx;
|
||||
this.fciWriteStreamHandler = this.CabWriteStreamEx;
|
||||
this.fciCloseStreamHandler = this.CabCloseStreamEx;
|
||||
this.fciSeekStreamHandler = this.CabSeekStreamEx;
|
||||
this.fciFilePlacedHandler = this.CabFilePlaced;
|
||||
this.fciDeleteFileHandler = this.CabDeleteFile;
|
||||
this.fciGetTempFileHandler = this.CabGetTempFile;
|
||||
this.fciGetNextCabinet = this.CabGetNextCabinet;
|
||||
this.fciCreateStatus = this.CabCreateStatus;
|
||||
this.fciGetOpenInfo = this.CabGetOpenInfo;
|
||||
this.tempStreams = new List<Stream>();
|
||||
this.compressionLevel = CompressionLevel.Normal;
|
||||
}
|
||||
|
||||
public bool UseTempFiles
|
||||
{
|
||||
get
|
||||
{
|
||||
return !this.dontUseTempFiles;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
this.dontUseTempFiles = !value;
|
||||
}
|
||||
}
|
||||
|
||||
public CompressionLevel CompressionLevel
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.compressionLevel;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
this.compressionLevel = value;
|
||||
}
|
||||
}
|
||||
|
||||
[SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands")]
|
||||
private void CreateFci(long maxArchiveSize)
|
||||
{
|
||||
NativeMethods.FCI.CCAB ccab = new NativeMethods.FCI.CCAB();
|
||||
if (maxArchiveSize > 0 && maxArchiveSize < ccab.cb)
|
||||
{
|
||||
ccab.cb = Math.Max(
|
||||
NativeMethods.FCI.MIN_DISK, (int) maxArchiveSize);
|
||||
}
|
||||
|
||||
object maxFolderSizeOption = this.context.GetOption(
|
||||
"maxFolderSize", null);
|
||||
if (maxFolderSizeOption != null)
|
||||
{
|
||||
long maxFolderSize = Convert.ToInt64(
|
||||
maxFolderSizeOption, CultureInfo.InvariantCulture);
|
||||
if (maxFolderSize > 0 && maxFolderSize < ccab.cbFolderThresh)
|
||||
{
|
||||
ccab.cbFolderThresh = (int) maxFolderSize;
|
||||
}
|
||||
}
|
||||
|
||||
this.maxCabBytes = ccab.cb;
|
||||
ccab.szCab = this.context.GetArchiveName(0);
|
||||
if (ccab.szCab == null)
|
||||
{
|
||||
throw new FileNotFoundException(
|
||||
"Cabinet name not provided by stream context.");
|
||||
}
|
||||
ccab.setID = (short) new Random().Next(
|
||||
Int16.MinValue, Int16.MaxValue + 1);
|
||||
this.CabNumbers[ccab.szCab] = 0;
|
||||
this.currentArchiveName = ccab.szCab;
|
||||
this.totalArchives = 1;
|
||||
this.CabStream = null;
|
||||
|
||||
this.Erf.Clear();
|
||||
this.fciHandle = NativeMethods.FCI.Create(
|
||||
this.ErfHandle.AddrOfPinnedObject(),
|
||||
this.fciFilePlacedHandler,
|
||||
this.fciAllocMemHandler,
|
||||
this.fciFreeMemHandler,
|
||||
this.fciOpenStreamHandler,
|
||||
this.fciReadStreamHandler,
|
||||
this.fciWriteStreamHandler,
|
||||
this.fciCloseStreamHandler,
|
||||
this.fciSeekStreamHandler,
|
||||
this.fciDeleteFileHandler,
|
||||
this.fciGetTempFileHandler,
|
||||
ccab,
|
||||
IntPtr.Zero);
|
||||
this.CheckError(false);
|
||||
}
|
||||
|
||||
[SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts")]
|
||||
[SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands")]
|
||||
[SecurityPermission(SecurityAction.Assert, UnmanagedCode = true)]
|
||||
public void Pack(
|
||||
IPackStreamContext streamContext,
|
||||
IEnumerable<string> files,
|
||||
long maxArchiveSize)
|
||||
{
|
||||
if (streamContext == null)
|
||||
{
|
||||
throw new ArgumentNullException("streamContext");
|
||||
}
|
||||
|
||||
if (files == null)
|
||||
{
|
||||
throw new ArgumentNullException("files");
|
||||
}
|
||||
|
||||
lock (this)
|
||||
{
|
||||
try
|
||||
{
|
||||
this.context = streamContext;
|
||||
|
||||
this.ResetProgressData();
|
||||
|
||||
this.CreateFci(maxArchiveSize);
|
||||
|
||||
foreach (string file in files)
|
||||
{
|
||||
FileAttributes attributes;
|
||||
DateTime lastWriteTime;
|
||||
Stream fileStream = this.context.OpenFileReadStream(
|
||||
file,
|
||||
out attributes,
|
||||
out lastWriteTime);
|
||||
if (fileStream != null)
|
||||
{
|
||||
this.totalFileBytes += fileStream.Length;
|
||||
this.totalFiles++;
|
||||
this.context.CloseFileReadStream(file, fileStream);
|
||||
}
|
||||
}
|
||||
|
||||
long uncompressedBytesInFolder = 0;
|
||||
this.currentFileNumber = -1;
|
||||
|
||||
foreach (string file in files)
|
||||
{
|
||||
FileAttributes attributes;
|
||||
DateTime lastWriteTime;
|
||||
Stream fileStream = this.context.OpenFileReadStream(
|
||||
file, out attributes, out lastWriteTime);
|
||||
if (fileStream == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (fileStream.Length >= (long) NativeMethods.FCI.MAX_FOLDER)
|
||||
{
|
||||
throw new NotSupportedException(String.Format(
|
||||
CultureInfo.InvariantCulture,
|
||||
"File {0} exceeds maximum file size " +
|
||||
"for cabinet format.",
|
||||
file));
|
||||
}
|
||||
|
||||
if (uncompressedBytesInFolder > 0)
|
||||
{
|
||||
// Automatically create a new folder if this file
|
||||
// won't fit in the current folder.
|
||||
bool nextFolder = uncompressedBytesInFolder
|
||||
+ fileStream.Length >= (long) NativeMethods.FCI.MAX_FOLDER;
|
||||
|
||||
// Otherwise ask the client if it wants to
|
||||
// move to the next folder.
|
||||
if (!nextFolder)
|
||||
{
|
||||
object nextFolderOption = streamContext.GetOption(
|
||||
"nextFolder",
|
||||
new object[] { file, this.currentFolderNumber });
|
||||
nextFolder = Convert.ToBoolean(
|
||||
nextFolderOption, CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
if (nextFolder)
|
||||
{
|
||||
this.FlushFolder();
|
||||
uncompressedBytesInFolder = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.currentFolderTotalBytes > 0)
|
||||
{
|
||||
this.currentFolderTotalBytes = 0;
|
||||
this.currentFolderNumber++;
|
||||
uncompressedBytesInFolder = 0;
|
||||
}
|
||||
|
||||
this.currentFileName = file;
|
||||
this.currentFileNumber++;
|
||||
|
||||
this.currentFileTotalBytes = fileStream.Length;
|
||||
this.currentFileBytesProcessed = 0;
|
||||
this.OnProgress(ArchiveProgressType.StartFile);
|
||||
|
||||
uncompressedBytesInFolder += fileStream.Length;
|
||||
|
||||
this.AddFile(
|
||||
file,
|
||||
fileStream,
|
||||
attributes,
|
||||
lastWriteTime,
|
||||
false,
|
||||
this.CompressionLevel);
|
||||
}
|
||||
|
||||
this.FlushFolder();
|
||||
this.FlushCabinet();
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (this.CabStream != null)
|
||||
{
|
||||
this.context.CloseArchiveWriteStream(
|
||||
this.currentArchiveNumber,
|
||||
this.currentArchiveName,
|
||||
this.CabStream);
|
||||
this.CabStream = null;
|
||||
}
|
||||
|
||||
if (this.FileStream != null)
|
||||
{
|
||||
this.context.CloseFileReadStream(
|
||||
this.currentFileName, this.FileStream);
|
||||
this.FileStream = null;
|
||||
}
|
||||
this.context = null;
|
||||
|
||||
if (this.fciHandle != null)
|
||||
{
|
||||
this.fciHandle.Dispose();
|
||||
this.fciHandle = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal override int CabOpenStreamEx(string path, int openFlags, int shareMode, out int err, IntPtr pv)
|
||||
{
|
||||
if (this.CabNumbers.ContainsKey(path))
|
||||
{
|
||||
Stream stream = this.CabStream;
|
||||
if (stream == null)
|
||||
{
|
||||
short cabNumber = this.CabNumbers[path];
|
||||
|
||||
this.currentFolderTotalBytes = 0;
|
||||
|
||||
stream = this.context.OpenArchiveWriteStream(cabNumber, path, true, this.CabEngine);
|
||||
if (stream == null)
|
||||
{
|
||||
throw new FileNotFoundException(
|
||||
String.Format(CultureInfo.InvariantCulture, "Cabinet {0} not provided.", cabNumber));
|
||||
}
|
||||
this.currentArchiveName = path;
|
||||
|
||||
this.currentArchiveTotalBytes = Math.Min(
|
||||
this.totalFolderBytesProcessedInCurrentCab, this.maxCabBytes);
|
||||
this.currentArchiveBytesProcessed = 0;
|
||||
|
||||
this.OnProgress(ArchiveProgressType.StartArchive);
|
||||
this.CabStream = stream;
|
||||
}
|
||||
path = CabWorker.CabStreamName;
|
||||
}
|
||||
else if (path == CabPacker.TempStreamName)
|
||||
{
|
||||
// Opening memory stream for a temp file.
|
||||
Stream stream = new MemoryStream();
|
||||
this.tempStreams.Add(stream);
|
||||
int streamHandle = this.StreamHandles.AllocHandle(stream);
|
||||
err = 0;
|
||||
return streamHandle;
|
||||
}
|
||||
else if (path != CabWorker.CabStreamName)
|
||||
{
|
||||
// Opening a file on disk for a temp file.
|
||||
path = Path.Combine(Path.GetTempPath(), path);
|
||||
Stream stream = new FileStream(path, FileMode.Open, FileAccess.ReadWrite);
|
||||
this.tempStreams.Add(stream);
|
||||
stream = new DuplicateStream(stream);
|
||||
int streamHandle = this.StreamHandles.AllocHandle(stream);
|
||||
err = 0;
|
||||
return streamHandle;
|
||||
}
|
||||
return base.CabOpenStreamEx(path, openFlags, shareMode, out err, pv);
|
||||
}
|
||||
|
||||
internal override int CabWriteStreamEx(int streamHandle, IntPtr memory, int cb, out int err, IntPtr pv)
|
||||
{
|
||||
int count = base.CabWriteStreamEx(streamHandle, memory, cb, out err, pv);
|
||||
if (count > 0 && err == 0)
|
||||
{
|
||||
Stream stream = this.StreamHandles[streamHandle];
|
||||
if (DuplicateStream.OriginalStream(stream) ==
|
||||
DuplicateStream.OriginalStream(this.CabStream))
|
||||
{
|
||||
this.currentArchiveBytesProcessed += cb;
|
||||
if (this.currentArchiveBytesProcessed > this.currentArchiveTotalBytes)
|
||||
{
|
||||
this.currentArchiveBytesProcessed = this.currentArchiveTotalBytes;
|
||||
}
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
internal override int CabCloseStreamEx(int streamHandle, out int err, IntPtr pv)
|
||||
{
|
||||
Stream stream = DuplicateStream.OriginalStream(this.StreamHandles[streamHandle]);
|
||||
|
||||
if (stream == DuplicateStream.OriginalStream(this.FileStream))
|
||||
{
|
||||
this.context.CloseFileReadStream(this.currentFileName, stream);
|
||||
this.FileStream = null;
|
||||
long remainder = this.currentFileTotalBytes - this.currentFileBytesProcessed;
|
||||
this.currentFileBytesProcessed += remainder;
|
||||
this.fileBytesProcessed += remainder;
|
||||
this.OnProgress(ArchiveProgressType.FinishFile);
|
||||
|
||||
this.currentFileTotalBytes = 0;
|
||||
this.currentFileBytesProcessed = 0;
|
||||
this.currentFileName = null;
|
||||
}
|
||||
else if (stream == DuplicateStream.OriginalStream(this.CabStream))
|
||||
{
|
||||
if (stream.CanWrite)
|
||||
{
|
||||
stream.Flush();
|
||||
}
|
||||
|
||||
this.currentArchiveBytesProcessed = this.currentArchiveTotalBytes;
|
||||
this.OnProgress(ArchiveProgressType.FinishArchive);
|
||||
this.currentArchiveNumber++;
|
||||
this.totalArchives++;
|
||||
|
||||
this.context.CloseArchiveWriteStream(
|
||||
this.currentArchiveNumber,
|
||||
this.currentArchiveName,
|
||||
stream);
|
||||
|
||||
this.currentArchiveName = this.NextCabinetName;
|
||||
this.currentArchiveBytesProcessed = this.currentArchiveTotalBytes = 0;
|
||||
this.totalFolderBytesProcessedInCurrentCab = 0;
|
||||
|
||||
this.CabStream = null;
|
||||
}
|
||||
else // Must be a temp stream
|
||||
{
|
||||
stream.Close();
|
||||
this.tempStreams.Remove(stream);
|
||||
}
|
||||
return base.CabCloseStreamEx(streamHandle, out err, pv);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes of resources allocated by the cabinet engine.
|
||||
/// </summary>
|
||||
/// <param name="disposing">If true, the method has been called directly or indirectly by a user's code,
|
||||
/// so managed and unmanaged resources will be disposed. If false, the method has been called by the
|
||||
/// runtime from inside the finalizer, and only unmanaged resources will be disposed.</param>
|
||||
[SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands")]
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
if (this.fciHandle != null)
|
||||
{
|
||||
this.fciHandle.Dispose();
|
||||
this.fciHandle = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
}
|
||||
|
||||
private static NativeMethods.FCI.TCOMP GetCompressionType(CompressionLevel compLevel)
|
||||
{
|
||||
if (compLevel < CompressionLevel.Min)
|
||||
{
|
||||
return NativeMethods.FCI.TCOMP.TYPE_NONE;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (compLevel > CompressionLevel.Max)
|
||||
{
|
||||
compLevel = CompressionLevel.Max;
|
||||
}
|
||||
|
||||
int lzxWindowMax =
|
||||
((int) NativeMethods.FCI.TCOMP.LZX_WINDOW_HI >> (int) NativeMethods.FCI.TCOMP.SHIFT_LZX_WINDOW) -
|
||||
((int) NativeMethods.FCI.TCOMP.LZX_WINDOW_LO >> (int) NativeMethods.FCI.TCOMP.SHIFT_LZX_WINDOW);
|
||||
int lzxWindow = lzxWindowMax *
|
||||
(compLevel - CompressionLevel.Min) / (CompressionLevel.Max - CompressionLevel.Min);
|
||||
|
||||
return (NativeMethods.FCI.TCOMP) ((int) NativeMethods.FCI.TCOMP.TYPE_LZX |
|
||||
((int) NativeMethods.FCI.TCOMP.LZX_WINDOW_LO +
|
||||
(lzxWindow << (int) NativeMethods.FCI.TCOMP.SHIFT_LZX_WINDOW)));
|
||||
}
|
||||
}
|
||||
|
||||
[SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands")]
|
||||
private void AddFile(
|
||||
string name,
|
||||
Stream stream,
|
||||
FileAttributes attributes,
|
||||
DateTime lastWriteTime,
|
||||
bool execute,
|
||||
CompressionLevel compLevel)
|
||||
{
|
||||
this.FileStream = stream;
|
||||
this.fileAttributes = attributes &
|
||||
(FileAttributes.Archive | FileAttributes.Hidden | FileAttributes.ReadOnly | FileAttributes.System);
|
||||
this.fileLastWriteTime = lastWriteTime;
|
||||
this.currentFileName = name;
|
||||
|
||||
NativeMethods.FCI.TCOMP tcomp = CabPacker.GetCompressionType(compLevel);
|
||||
|
||||
IntPtr namePtr = IntPtr.Zero;
|
||||
try
|
||||
{
|
||||
Encoding nameEncoding = Encoding.ASCII;
|
||||
if (Encoding.UTF8.GetByteCount(name) > name.Length)
|
||||
{
|
||||
nameEncoding = Encoding.UTF8;
|
||||
this.fileAttributes |= FileAttributes.Normal; // _A_NAME_IS_UTF
|
||||
}
|
||||
|
||||
byte[] nameBytes = nameEncoding.GetBytes(name);
|
||||
namePtr = Marshal.AllocHGlobal(nameBytes.Length + 1);
|
||||
Marshal.Copy(nameBytes, 0, namePtr, nameBytes.Length);
|
||||
Marshal.WriteByte(namePtr, nameBytes.Length, 0);
|
||||
|
||||
this.Erf.Clear();
|
||||
NativeMethods.FCI.AddFile(
|
||||
this.fciHandle,
|
||||
String.Empty,
|
||||
namePtr,
|
||||
execute,
|
||||
this.fciGetNextCabinet,
|
||||
this.fciCreateStatus,
|
||||
this.fciGetOpenInfo,
|
||||
tcomp);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (namePtr != IntPtr.Zero)
|
||||
{
|
||||
Marshal.FreeHGlobal(namePtr);
|
||||
}
|
||||
}
|
||||
|
||||
this.CheckError(false);
|
||||
this.FileStream = null;
|
||||
this.currentFileName = null;
|
||||
}
|
||||
|
||||
private void FlushFolder()
|
||||
{
|
||||
this.Erf.Clear();
|
||||
NativeMethods.FCI.FlushFolder(this.fciHandle, this.fciGetNextCabinet, this.fciCreateStatus);
|
||||
this.CheckError(false);
|
||||
}
|
||||
|
||||
private void FlushCabinet()
|
||||
{
|
||||
this.Erf.Clear();
|
||||
NativeMethods.FCI.FlushCabinet(this.fciHandle, false, this.fciGetNextCabinet, this.fciCreateStatus);
|
||||
this.CheckError(false);
|
||||
}
|
||||
|
||||
private int CabGetOpenInfo(
|
||||
string path,
|
||||
out short date,
|
||||
out short time,
|
||||
out short attribs,
|
||||
out int err,
|
||||
IntPtr pv)
|
||||
{
|
||||
CompressionEngine.DateTimeToDosDateAndTime(this.fileLastWriteTime, out date, out time);
|
||||
attribs = (short) this.fileAttributes;
|
||||
|
||||
Stream stream = this.FileStream;
|
||||
this.FileStream = new DuplicateStream(stream);
|
||||
int streamHandle = this.StreamHandles.AllocHandle(stream);
|
||||
err = 0;
|
||||
return streamHandle;
|
||||
}
|
||||
|
||||
private int CabFilePlaced(
|
||||
IntPtr pccab,
|
||||
string filePath,
|
||||
long fileSize,
|
||||
int continuation,
|
||||
IntPtr pv)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
private int CabGetNextCabinet(IntPtr pccab, uint prevCabSize, IntPtr pv)
|
||||
{
|
||||
NativeMethods.FCI.CCAB nextCcab = new NativeMethods.FCI.CCAB();
|
||||
Marshal.PtrToStructure(pccab, nextCcab);
|
||||
|
||||
nextCcab.szDisk = String.Empty;
|
||||
nextCcab.szCab = this.context.GetArchiveName(nextCcab.iCab);
|
||||
this.CabNumbers[nextCcab.szCab] = (short) nextCcab.iCab;
|
||||
this.NextCabinetName = nextCcab.szCab;
|
||||
|
||||
Marshal.StructureToPtr(nextCcab, pccab, false);
|
||||
return 1;
|
||||
}
|
||||
|
||||
private int CabCreateStatus(NativeMethods.FCI.STATUS typeStatus, uint cb1, uint cb2, IntPtr pv)
|
||||
{
|
||||
switch (typeStatus)
|
||||
{
|
||||
case NativeMethods.FCI.STATUS.FILE:
|
||||
if (cb2 > 0 && this.currentFileBytesProcessed < this.currentFileTotalBytes)
|
||||
{
|
||||
if (this.currentFileBytesProcessed + cb2 > this.currentFileTotalBytes)
|
||||
{
|
||||
cb2 = (uint) this.currentFileTotalBytes - (uint) this.currentFileBytesProcessed;
|
||||
}
|
||||
this.currentFileBytesProcessed += cb2;
|
||||
this.fileBytesProcessed += cb2;
|
||||
|
||||
this.OnProgress(ArchiveProgressType.PartialFile);
|
||||
}
|
||||
break;
|
||||
|
||||
case NativeMethods.FCI.STATUS.FOLDER:
|
||||
if (cb1 == 0)
|
||||
{
|
||||
this.currentFolderTotalBytes = cb2 - this.totalFolderBytesProcessedInCurrentCab;
|
||||
this.totalFolderBytesProcessedInCurrentCab = cb2;
|
||||
}
|
||||
else if (this.currentFolderTotalBytes == 0)
|
||||
{
|
||||
this.OnProgress(ArchiveProgressType.PartialArchive);
|
||||
}
|
||||
break;
|
||||
|
||||
case NativeMethods.FCI.STATUS.CABINET:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private int CabGetTempFile(IntPtr tempNamePtr, int tempNameSize, IntPtr pv)
|
||||
{
|
||||
string tempFileName;
|
||||
if (this.UseTempFiles)
|
||||
{
|
||||
tempFileName = Path.GetFileName(Path.GetTempFileName());
|
||||
}
|
||||
else
|
||||
{
|
||||
tempFileName = CabPacker.TempStreamName;
|
||||
}
|
||||
|
||||
byte[] tempNameBytes = Encoding.ASCII.GetBytes(tempFileName);
|
||||
if (tempNameBytes.Length >= tempNameSize)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
Marshal.Copy(tempNameBytes, 0, tempNamePtr, tempNameBytes.Length);
|
||||
Marshal.WriteByte(tempNamePtr, tempNameBytes.Length, 0); // null-terminator
|
||||
return 1;
|
||||
}
|
||||
|
||||
private int CabDeleteFile(string path, out int err, IntPtr pv)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Deleting a temp file - don't bother if it is only a memory stream.
|
||||
if (path != CabPacker.TempStreamName)
|
||||
{
|
||||
path = Path.Combine(Path.GetTempPath(), path);
|
||||
File.Delete(path);
|
||||
}
|
||||
}
|
||||
catch (IOException)
|
||||
{
|
||||
// Failure to delete a temp file is not fatal.
|
||||
}
|
||||
err = 0;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,586 @@
|
|||
//---------------------------------------------------------------------
|
||||
// <copyright file="CabUnpacker.cs" company="Outercurve Foundation">
|
||||
// Copyright (c) 2004, Outercurve Foundation.
|
||||
// This software is released under Microsoft Reciprocal License (MS-RL).
|
||||
// The license and further copyright text can be found in the file LICENSE.TXT
|
||||
// LICENSE.TXT at the root directory of the distribution.
|
||||
// </copyright>
|
||||
// <summary>
|
||||
// Part of the Deployment Tools Foundation project.
|
||||
// </summary>
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Deployment.Compression.Cab
|
||||
{
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Security.Permissions;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
internal class CabUnpacker : CabWorker
|
||||
{
|
||||
private NativeMethods.FDI.Handle fdiHandle;
|
||||
|
||||
// These delegates need to be saved as member variables
|
||||
// so that they don't get GC'd.
|
||||
private NativeMethods.FDI.PFNALLOC fdiAllocMemHandler;
|
||||
private NativeMethods.FDI.PFNFREE fdiFreeMemHandler;
|
||||
private NativeMethods.FDI.PFNOPEN fdiOpenStreamHandler;
|
||||
private NativeMethods.FDI.PFNREAD fdiReadStreamHandler;
|
||||
private NativeMethods.FDI.PFNWRITE fdiWriteStreamHandler;
|
||||
private NativeMethods.FDI.PFNCLOSE fdiCloseStreamHandler;
|
||||
private NativeMethods.FDI.PFNSEEK fdiSeekStreamHandler;
|
||||
|
||||
private IUnpackStreamContext context;
|
||||
|
||||
private List<ArchiveFileInfo> fileList;
|
||||
|
||||
private int folderId;
|
||||
|
||||
private Predicate<string> filter;
|
||||
|
||||
[SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts")]
|
||||
[SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands")]
|
||||
[SecurityPermission(SecurityAction.Assert, UnmanagedCode = true)]
|
||||
public CabUnpacker(CabEngine cabEngine)
|
||||
: base(cabEngine)
|
||||
{
|
||||
this.fdiAllocMemHandler = this.CabAllocMem;
|
||||
this.fdiFreeMemHandler = this.CabFreeMem;
|
||||
this.fdiOpenStreamHandler = this.CabOpenStream;
|
||||
this.fdiReadStreamHandler = this.CabReadStream;
|
||||
this.fdiWriteStreamHandler = this.CabWriteStream;
|
||||
this.fdiCloseStreamHandler = this.CabCloseStream;
|
||||
this.fdiSeekStreamHandler = this.CabSeekStream;
|
||||
|
||||
this.fdiHandle = NativeMethods.FDI.Create(
|
||||
this.fdiAllocMemHandler,
|
||||
this.fdiFreeMemHandler,
|
||||
this.fdiOpenStreamHandler,
|
||||
this.fdiReadStreamHandler,
|
||||
this.fdiWriteStreamHandler,
|
||||
this.fdiCloseStreamHandler,
|
||||
this.fdiSeekStreamHandler,
|
||||
NativeMethods.FDI.CPU_80386,
|
||||
this.ErfHandle.AddrOfPinnedObject());
|
||||
if (this.Erf.Error)
|
||||
{
|
||||
int error = this.Erf.Oper;
|
||||
int errorCode = this.Erf.Type;
|
||||
this.ErfHandle.Free();
|
||||
throw new CabException(
|
||||
error,
|
||||
errorCode,
|
||||
CabException.GetErrorMessage(error, errorCode, true));
|
||||
}
|
||||
}
|
||||
|
||||
[SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts")]
|
||||
[SecurityPermission(SecurityAction.Assert, UnmanagedCode = true)]
|
||||
public bool IsArchive(Stream stream)
|
||||
{
|
||||
if (stream == null)
|
||||
{
|
||||
throw new ArgumentNullException("stream");
|
||||
}
|
||||
|
||||
lock (this)
|
||||
{
|
||||
short id;
|
||||
int folderCount, fileCount;
|
||||
return this.IsCabinet(stream, out id, out folderCount, out fileCount);
|
||||
}
|
||||
}
|
||||
|
||||
[SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts")]
|
||||
[SecurityPermission(SecurityAction.Assert, UnmanagedCode = true)]
|
||||
public IList<ArchiveFileInfo> GetFileInfo(
|
||||
IUnpackStreamContext streamContext,
|
||||
Predicate<string> fileFilter)
|
||||
{
|
||||
if (streamContext == null)
|
||||
{
|
||||
throw new ArgumentNullException("streamContext");
|
||||
}
|
||||
|
||||
lock (this)
|
||||
{
|
||||
this.context = streamContext;
|
||||
this.filter = fileFilter;
|
||||
this.NextCabinetName = String.Empty;
|
||||
this.fileList = new List<ArchiveFileInfo>();
|
||||
bool tmpSuppress = this.SuppressProgressEvents;
|
||||
this.SuppressProgressEvents = true;
|
||||
try
|
||||
{
|
||||
for (short cabNumber = 0;
|
||||
this.NextCabinetName != null;
|
||||
cabNumber++)
|
||||
{
|
||||
this.Erf.Clear();
|
||||
this.CabNumbers[this.NextCabinetName] = cabNumber;
|
||||
|
||||
NativeMethods.FDI.Copy(
|
||||
this.fdiHandle,
|
||||
this.NextCabinetName,
|
||||
String.Empty,
|
||||
0,
|
||||
this.CabListNotify,
|
||||
IntPtr.Zero,
|
||||
IntPtr.Zero);
|
||||
this.CheckError(true);
|
||||
}
|
||||
|
||||
List<ArchiveFileInfo> tmpFileList = this.fileList;
|
||||
this.fileList = null;
|
||||
return tmpFileList.AsReadOnly();
|
||||
}
|
||||
finally
|
||||
{
|
||||
this.SuppressProgressEvents = tmpSuppress;
|
||||
|
||||
if (this.CabStream != null)
|
||||
{
|
||||
this.context.CloseArchiveReadStream(
|
||||
this.currentArchiveNumber,
|
||||
this.currentArchiveName,
|
||||
this.CabStream);
|
||||
this.CabStream = null;
|
||||
}
|
||||
|
||||
this.context = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts")]
|
||||
[SecurityPermission(SecurityAction.Assert, UnmanagedCode = true)]
|
||||
public void Unpack(
|
||||
IUnpackStreamContext streamContext,
|
||||
Predicate<string> fileFilter)
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
IList<ArchiveFileInfo> files =
|
||||
this.GetFileInfo(streamContext, fileFilter);
|
||||
|
||||
this.ResetProgressData();
|
||||
|
||||
if (files != null)
|
||||
{
|
||||
this.totalFiles = files.Count;
|
||||
|
||||
for (int i = 0; i < files.Count; i++)
|
||||
{
|
||||
this.totalFileBytes += files[i].Length;
|
||||
if (files[i].ArchiveNumber >= this.totalArchives)
|
||||
{
|
||||
int totalArchives = files[i].ArchiveNumber + 1;
|
||||
this.totalArchives = (short) totalArchives;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.context = streamContext;
|
||||
this.fileList = null;
|
||||
this.NextCabinetName = String.Empty;
|
||||
this.folderId = -1;
|
||||
this.currentFileNumber = -1;
|
||||
|
||||
try
|
||||
{
|
||||
for (short cabNumber = 0;
|
||||
this.NextCabinetName != null;
|
||||
cabNumber++)
|
||||
{
|
||||
this.Erf.Clear();
|
||||
this.CabNumbers[this.NextCabinetName] = cabNumber;
|
||||
|
||||
NativeMethods.FDI.Copy(
|
||||
this.fdiHandle,
|
||||
this.NextCabinetName,
|
||||
String.Empty,
|
||||
0,
|
||||
this.CabExtractNotify,
|
||||
IntPtr.Zero,
|
||||
IntPtr.Zero);
|
||||
this.CheckError(true);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (this.CabStream != null)
|
||||
{
|
||||
this.context.CloseArchiveReadStream(
|
||||
this.currentArchiveNumber,
|
||||
this.currentArchiveName,
|
||||
this.CabStream);
|
||||
this.CabStream = null;
|
||||
}
|
||||
|
||||
if (this.FileStream != null)
|
||||
{
|
||||
this.context.CloseFileWriteStream(this.currentFileName, this.FileStream, FileAttributes.Normal, DateTime.Now);
|
||||
this.FileStream = null;
|
||||
}
|
||||
|
||||
this.context = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal override int CabOpenStreamEx(string path, int openFlags, int shareMode, out int err, IntPtr pv)
|
||||
{
|
||||
if (this.CabNumbers.ContainsKey(path))
|
||||
{
|
||||
Stream stream = this.CabStream;
|
||||
if (stream == null)
|
||||
{
|
||||
short cabNumber = this.CabNumbers[path];
|
||||
|
||||
stream = this.context.OpenArchiveReadStream(cabNumber, path, this.CabEngine);
|
||||
if (stream == null)
|
||||
{
|
||||
throw new FileNotFoundException(String.Format(CultureInfo.InvariantCulture, "Cabinet {0} not provided.", cabNumber));
|
||||
}
|
||||
this.currentArchiveName = path;
|
||||
this.currentArchiveNumber = cabNumber;
|
||||
if (this.totalArchives <= this.currentArchiveNumber)
|
||||
{
|
||||
int totalArchives = this.currentArchiveNumber + 1;
|
||||
this.totalArchives = (short) totalArchives;
|
||||
}
|
||||
this.currentArchiveTotalBytes = stream.Length;
|
||||
this.currentArchiveBytesProcessed = 0;
|
||||
|
||||
if (this.folderId != -3) // -3 is a special folderId that requires re-opening the same cab
|
||||
{
|
||||
this.OnProgress(ArchiveProgressType.StartArchive);
|
||||
}
|
||||
this.CabStream = stream;
|
||||
}
|
||||
path = CabWorker.CabStreamName;
|
||||
}
|
||||
return base.CabOpenStreamEx(path, openFlags, shareMode, out err, pv);
|
||||
}
|
||||
|
||||
internal override int CabReadStreamEx(int streamHandle, IntPtr memory, int cb, out int err, IntPtr pv)
|
||||
{
|
||||
int count = base.CabReadStreamEx(streamHandle, memory, cb, out err, pv);
|
||||
if (err == 0 && this.CabStream != null)
|
||||
{
|
||||
if (this.fileList == null)
|
||||
{
|
||||
Stream stream = this.StreamHandles[streamHandle];
|
||||
if (DuplicateStream.OriginalStream(stream) ==
|
||||
DuplicateStream.OriginalStream(this.CabStream))
|
||||
{
|
||||
this.currentArchiveBytesProcessed += cb;
|
||||
if (this.currentArchiveBytesProcessed > this.currentArchiveTotalBytes)
|
||||
{
|
||||
this.currentArchiveBytesProcessed = this.currentArchiveTotalBytes;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
internal override int CabWriteStreamEx(int streamHandle, IntPtr memory, int cb, out int err, IntPtr pv)
|
||||
{
|
||||
int count = base.CabWriteStreamEx(streamHandle, memory, cb, out err, pv);
|
||||
if (count > 0 && err == 0)
|
||||
{
|
||||
this.currentFileBytesProcessed += cb;
|
||||
this.fileBytesProcessed += cb;
|
||||
this.OnProgress(ArchiveProgressType.PartialFile);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
internal override int CabCloseStreamEx(int streamHandle, out int err, IntPtr pv)
|
||||
{
|
||||
Stream stream = DuplicateStream.OriginalStream(this.StreamHandles[streamHandle]);
|
||||
|
||||
if (stream == DuplicateStream.OriginalStream(this.CabStream))
|
||||
{
|
||||
if (this.folderId != -3) // -3 is a special folderId that requires re-opening the same cab
|
||||
{
|
||||
this.OnProgress(ArchiveProgressType.FinishArchive);
|
||||
}
|
||||
|
||||
this.context.CloseArchiveReadStream(this.currentArchiveNumber, this.currentArchiveName, stream);
|
||||
|
||||
this.currentArchiveName = this.NextCabinetName;
|
||||
this.currentArchiveBytesProcessed = this.currentArchiveTotalBytes = 0;
|
||||
|
||||
this.CabStream = null;
|
||||
}
|
||||
return base.CabCloseStreamEx(streamHandle, out err, pv);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes of resources allocated by the cabinet engine.
|
||||
/// </summary>
|
||||
/// <param name="disposing">If true, the method has been called directly or indirectly by a user's code,
|
||||
/// so managed and unmanaged resources will be disposed. If false, the method has been called by the
|
||||
/// runtime from inside the finalizer, and only unmanaged resources will be disposed.</param>
|
||||
[SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands")]
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
if (this.fdiHandle != null)
|
||||
{
|
||||
this.fdiHandle.Dispose();
|
||||
this.fdiHandle = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetFileName(NativeMethods.FDI.NOTIFICATION notification)
|
||||
{
|
||||
bool utf8Name = (notification.attribs & (ushort) FileAttributes.Normal) != 0; // _A_NAME_IS_UTF
|
||||
|
||||
// Non-utf8 names should be completely ASCII. But for compatibility with
|
||||
// legacy tools, interpret them using the current (Default) ANSI codepage.
|
||||
Encoding nameEncoding = utf8Name ? Encoding.UTF8 : Encoding.Default;
|
||||
|
||||
// Find how many bytes are in the string.
|
||||
// Unfortunately there is no faster way.
|
||||
int nameBytesCount = 0;
|
||||
while (Marshal.ReadByte(notification.psz1, nameBytesCount) != 0)
|
||||
{
|
||||
nameBytesCount++;
|
||||
}
|
||||
|
||||
byte[] nameBytes = new byte[nameBytesCount];
|
||||
Marshal.Copy(notification.psz1, nameBytes, 0, nameBytesCount);
|
||||
string name = nameEncoding.GetString(nameBytes);
|
||||
if (Path.IsPathRooted(name))
|
||||
{
|
||||
name = name.Replace("" + Path.VolumeSeparatorChar, "");
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
private bool IsCabinet(Stream cabStream, out short id, out int cabFolderCount, out int fileCount)
|
||||
{
|
||||
int streamHandle = this.StreamHandles.AllocHandle(cabStream);
|
||||
try
|
||||
{
|
||||
this.Erf.Clear();
|
||||
NativeMethods.FDI.CABINFO fdici;
|
||||
bool isCabinet = 0 != NativeMethods.FDI.IsCabinet(this.fdiHandle, streamHandle, out fdici);
|
||||
|
||||
if (this.Erf.Error)
|
||||
{
|
||||
if (((NativeMethods.FDI.ERROR) this.Erf.Oper) == NativeMethods.FDI.ERROR.UNKNOWN_CABINET_VERSION)
|
||||
{
|
||||
isCabinet = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new CabException(
|
||||
this.Erf.Oper,
|
||||
this.Erf.Type,
|
||||
CabException.GetErrorMessage(this.Erf.Oper, this.Erf.Type, true));
|
||||
}
|
||||
}
|
||||
|
||||
id = fdici.setID;
|
||||
cabFolderCount = (int) fdici.cFolders;
|
||||
fileCount = (int) fdici.cFiles;
|
||||
return isCabinet;
|
||||
}
|
||||
finally
|
||||
{
|
||||
this.StreamHandles.FreeHandle(streamHandle);
|
||||
}
|
||||
}
|
||||
|
||||
private int CabListNotify(NativeMethods.FDI.NOTIFICATIONTYPE notificationType, NativeMethods.FDI.NOTIFICATION notification)
|
||||
{
|
||||
switch (notificationType)
|
||||
{
|
||||
case NativeMethods.FDI.NOTIFICATIONTYPE.CABINET_INFO:
|
||||
{
|
||||
string nextCab = Marshal.PtrToStringAnsi(notification.psz1);
|
||||
this.NextCabinetName = (nextCab.Length != 0 ? nextCab : null);
|
||||
return 0; // Continue
|
||||
}
|
||||
case NativeMethods.FDI.NOTIFICATIONTYPE.PARTIAL_FILE:
|
||||
{
|
||||
// This notification can occur when examining the contents of a non-first cab file.
|
||||
return 0; // Continue
|
||||
}
|
||||
case NativeMethods.FDI.NOTIFICATIONTYPE.COPY_FILE:
|
||||
{
|
||||
//bool execute = (notification.attribs & (ushort) FileAttributes.Device) != 0; // _A_EXEC
|
||||
|
||||
string name = CabUnpacker.GetFileName(notification);
|
||||
|
||||
if (this.filter == null || this.filter(name))
|
||||
{
|
||||
if (this.fileList != null)
|
||||
{
|
||||
FileAttributes attributes = (FileAttributes) notification.attribs &
|
||||
(FileAttributes.Archive | FileAttributes.Hidden | FileAttributes.ReadOnly | FileAttributes.System);
|
||||
if (attributes == (FileAttributes) 0)
|
||||
{
|
||||
attributes = FileAttributes.Normal;
|
||||
}
|
||||
DateTime lastWriteTime;
|
||||
CompressionEngine.DosDateAndTimeToDateTime(notification.date, notification.time, out lastWriteTime);
|
||||
long length = notification.cb;
|
||||
|
||||
CabFileInfo fileInfo = new CabFileInfo(
|
||||
name,
|
||||
notification.iFolder,
|
||||
notification.iCabinet,
|
||||
attributes,
|
||||
lastWriteTime,
|
||||
length);
|
||||
this.fileList.Add(fileInfo);
|
||||
this.currentFileNumber = this.fileList.Count - 1;
|
||||
this.fileBytesProcessed += notification.cb;
|
||||
}
|
||||
}
|
||||
|
||||
this.totalFiles++;
|
||||
this.totalFileBytes += notification.cb;
|
||||
return 0; // Continue
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private int CabExtractNotify(NativeMethods.FDI.NOTIFICATIONTYPE notificationType, NativeMethods.FDI.NOTIFICATION notification)
|
||||
{
|
||||
switch (notificationType)
|
||||
{
|
||||
case NativeMethods.FDI.NOTIFICATIONTYPE.CABINET_INFO:
|
||||
{
|
||||
if (this.NextCabinetName != null && this.NextCabinetName.StartsWith("?", StringComparison.Ordinal))
|
||||
{
|
||||
// We are just continuing the copy of a file that spanned cabinets.
|
||||
// The next cabinet name needs to be preserved.
|
||||
this.NextCabinetName = this.NextCabinetName.Substring(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
string nextCab = Marshal.PtrToStringAnsi(notification.psz1);
|
||||
this.NextCabinetName = (nextCab.Length != 0 ? nextCab : null);
|
||||
}
|
||||
return 0; // Continue
|
||||
}
|
||||
case NativeMethods.FDI.NOTIFICATIONTYPE.NEXT_CABINET:
|
||||
{
|
||||
string nextCab = Marshal.PtrToStringAnsi(notification.psz1);
|
||||
this.CabNumbers[nextCab] = (short) notification.iCabinet;
|
||||
this.NextCabinetName = "?" + this.NextCabinetName;
|
||||
return 0; // Continue
|
||||
}
|
||||
case NativeMethods.FDI.NOTIFICATIONTYPE.COPY_FILE:
|
||||
{
|
||||
return this.CabExtractCopyFile(notification);
|
||||
}
|
||||
case NativeMethods.FDI.NOTIFICATIONTYPE.CLOSE_FILE_INFO:
|
||||
{
|
||||
return this.CabExtractCloseFile(notification);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private int CabExtractCopyFile(NativeMethods.FDI.NOTIFICATION notification)
|
||||
{
|
||||
if (notification.iFolder != this.folderId)
|
||||
{
|
||||
if (notification.iFolder != -3) // -3 is a special folderId used when continuing a folder from a previous cab
|
||||
{
|
||||
if (this.folderId != -1) // -1 means we just started the extraction sequence
|
||||
{
|
||||
this.currentFolderNumber++;
|
||||
}
|
||||
}
|
||||
this.folderId = notification.iFolder;
|
||||
}
|
||||
|
||||
//bool execute = (notification.attribs & (ushort) FileAttributes.Device) != 0; // _A_EXEC
|
||||
|
||||
string name = CabUnpacker.GetFileName(notification);
|
||||
|
||||
if (this.filter == null || this.filter(name))
|
||||
{
|
||||
this.currentFileNumber++;
|
||||
this.currentFileName = name;
|
||||
|
||||
this.currentFileBytesProcessed = 0;
|
||||
this.currentFileTotalBytes = notification.cb;
|
||||
this.OnProgress(ArchiveProgressType.StartFile);
|
||||
|
||||
DateTime lastWriteTime;
|
||||
CompressionEngine.DosDateAndTimeToDateTime(notification.date, notification.time, out lastWriteTime);
|
||||
|
||||
Stream stream = this.context.OpenFileWriteStream(name, notification.cb, lastWriteTime);
|
||||
if (stream != null)
|
||||
{
|
||||
this.FileStream = stream;
|
||||
int streamHandle = this.StreamHandles.AllocHandle(stream);
|
||||
return streamHandle;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.fileBytesProcessed += notification.cb;
|
||||
this.OnProgress(ArchiveProgressType.FinishFile);
|
||||
this.currentFileName = null;
|
||||
}
|
||||
}
|
||||
return 0; // Continue
|
||||
}
|
||||
|
||||
private int CabExtractCloseFile(NativeMethods.FDI.NOTIFICATION notification)
|
||||
{
|
||||
Stream stream = this.StreamHandles[notification.hf];
|
||||
this.StreamHandles.FreeHandle(notification.hf);
|
||||
|
||||
//bool execute = (notification.attribs & (ushort) FileAttributes.Device) != 0; // _A_EXEC
|
||||
|
||||
string name = CabUnpacker.GetFileName(notification);
|
||||
|
||||
FileAttributes attributes = (FileAttributes) notification.attribs &
|
||||
(FileAttributes.Archive | FileAttributes.Hidden | FileAttributes.ReadOnly | FileAttributes.System);
|
||||
if (attributes == (FileAttributes) 0)
|
||||
{
|
||||
attributes = FileAttributes.Normal;
|
||||
}
|
||||
DateTime lastWriteTime;
|
||||
CompressionEngine.DosDateAndTimeToDateTime(notification.date, notification.time, out lastWriteTime);
|
||||
|
||||
stream.Flush();
|
||||
this.context.CloseFileWriteStream(name, stream, attributes, lastWriteTime);
|
||||
this.FileStream = null;
|
||||
|
||||
long remainder = this.currentFileTotalBytes - this.currentFileBytesProcessed;
|
||||
this.currentFileBytesProcessed += remainder;
|
||||
this.fileBytesProcessed += remainder;
|
||||
this.OnProgress(ArchiveProgressType.FinishFile);
|
||||
this.currentFileName = null;
|
||||
|
||||
return 1; // Continue
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,347 @@
|
|||
//---------------------------------------------------------------------
|
||||
// <copyright file="CabWorker.cs" company="Outercurve Foundation">
|
||||
// Copyright (c) 2004, Outercurve Foundation.
|
||||
// This software is released under Microsoft Reciprocal License (MS-RL).
|
||||
// The license and further copyright text can be found in the file LICENSE.TXT
|
||||
// LICENSE.TXT at the root directory of the distribution.
|
||||
// </copyright>
|
||||
// <summary>
|
||||
// Part of the Deployment Tools Foundation project.
|
||||
// </summary>
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Deployment.Compression.Cab
|
||||
{
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.IO.IsolatedStorage;
|
||||
using System.Text;
|
||||
using System.Security;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
internal abstract class CabWorker : IDisposable
|
||||
{
|
||||
internal const string CabStreamName = "%%CAB%%";
|
||||
|
||||
private CabEngine cabEngine;
|
||||
|
||||
private HandleManager<Stream> streamHandles;
|
||||
private Stream cabStream;
|
||||
private Stream fileStream;
|
||||
|
||||
private NativeMethods.ERF erf;
|
||||
private GCHandle erfHandle;
|
||||
|
||||
private IDictionary<string, short> cabNumbers;
|
||||
private string nextCabinetName;
|
||||
|
||||
private bool suppressProgressEvents;
|
||||
|
||||
private byte[] buf;
|
||||
|
||||
// Progress data
|
||||
protected string currentFileName;
|
||||
protected int currentFileNumber;
|
||||
protected int totalFiles;
|
||||
protected long currentFileBytesProcessed;
|
||||
protected long currentFileTotalBytes;
|
||||
protected short currentFolderNumber;
|
||||
protected long currentFolderTotalBytes;
|
||||
protected string currentArchiveName;
|
||||
protected short currentArchiveNumber;
|
||||
protected short totalArchives;
|
||||
protected long currentArchiveBytesProcessed;
|
||||
protected long currentArchiveTotalBytes;
|
||||
protected long fileBytesProcessed;
|
||||
protected long totalFileBytes;
|
||||
|
||||
[SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands")]
|
||||
protected CabWorker(CabEngine cabEngine)
|
||||
{
|
||||
this.cabEngine = cabEngine;
|
||||
this.streamHandles = new HandleManager<Stream>();
|
||||
this.erf = new NativeMethods.ERF();
|
||||
this.erfHandle = GCHandle.Alloc(this.erf, GCHandleType.Pinned);
|
||||
this.cabNumbers = new Dictionary<string, short>(1);
|
||||
|
||||
// 32K seems to be the size of the largest chunks processed by cabinet.dll.
|
||||
// But just in case, this buffer will auto-enlarge.
|
||||
this.buf = new byte[32768];
|
||||
}
|
||||
|
||||
~CabWorker()
|
||||
{
|
||||
this.Dispose(false);
|
||||
}
|
||||
|
||||
public CabEngine CabEngine
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.cabEngine;
|
||||
}
|
||||
}
|
||||
|
||||
internal NativeMethods.ERF Erf
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.erf;
|
||||
}
|
||||
}
|
||||
|
||||
internal GCHandle ErfHandle
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.erfHandle;
|
||||
}
|
||||
}
|
||||
|
||||
internal HandleManager<Stream> StreamHandles
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.streamHandles;
|
||||
}
|
||||
}
|
||||
|
||||
internal bool SuppressProgressEvents
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.suppressProgressEvents;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
this.suppressProgressEvents = value;
|
||||
}
|
||||
}
|
||||
|
||||
internal IDictionary<string, short> CabNumbers
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.cabNumbers;
|
||||
}
|
||||
}
|
||||
|
||||
internal string NextCabinetName
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.nextCabinetName;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
this.nextCabinetName = value;
|
||||
}
|
||||
}
|
||||
|
||||
internal Stream CabStream
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.cabStream;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
this.cabStream = value;
|
||||
}
|
||||
}
|
||||
|
||||
internal Stream FileStream
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.fileStream;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
this.fileStream = value;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
this.Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
protected void ResetProgressData()
|
||||
{
|
||||
this.currentFileName = null;
|
||||
this.currentFileNumber = 0;
|
||||
this.totalFiles = 0;
|
||||
this.currentFileBytesProcessed = 0;
|
||||
this.currentFileTotalBytes = 0;
|
||||
this.currentFolderNumber = 0;
|
||||
this.currentFolderTotalBytes = 0;
|
||||
this.currentArchiveName = null;
|
||||
this.currentArchiveNumber = 0;
|
||||
this.totalArchives = 0;
|
||||
this.currentArchiveBytesProcessed = 0;
|
||||
this.currentArchiveTotalBytes = 0;
|
||||
this.fileBytesProcessed = 0;
|
||||
this.totalFileBytes = 0;
|
||||
}
|
||||
|
||||
protected void OnProgress(ArchiveProgressType progressType)
|
||||
{
|
||||
if (!this.suppressProgressEvents)
|
||||
{
|
||||
ArchiveProgressEventArgs e = new ArchiveProgressEventArgs(
|
||||
progressType,
|
||||
this.currentFileName,
|
||||
this.currentFileNumber >= 0 ? this.currentFileNumber : 0,
|
||||
this.totalFiles,
|
||||
this.currentFileBytesProcessed,
|
||||
this.currentFileTotalBytes,
|
||||
this.currentArchiveName,
|
||||
this.currentArchiveNumber,
|
||||
this.totalArchives,
|
||||
this.currentArchiveBytesProcessed,
|
||||
this.currentArchiveTotalBytes,
|
||||
this.fileBytesProcessed,
|
||||
this.totalFileBytes);
|
||||
this.CabEngine.ReportProgress(e);
|
||||
}
|
||||
}
|
||||
|
||||
internal IntPtr CabAllocMem(int byteCount)
|
||||
{
|
||||
IntPtr memPointer = Marshal.AllocHGlobal((IntPtr) byteCount);
|
||||
return memPointer;
|
||||
}
|
||||
|
||||
internal void CabFreeMem(IntPtr memPointer)
|
||||
{
|
||||
Marshal.FreeHGlobal(memPointer);
|
||||
}
|
||||
|
||||
internal int CabOpenStream(string path, int openFlags, int shareMode)
|
||||
{
|
||||
int err; return this.CabOpenStreamEx(path, openFlags, shareMode, out err, IntPtr.Zero);
|
||||
}
|
||||
|
||||
internal virtual int CabOpenStreamEx(string path, int openFlags, int shareMode, out int err, IntPtr pv)
|
||||
{
|
||||
path = path.Trim();
|
||||
Stream stream = this.cabStream;
|
||||
this.cabStream = new DuplicateStream(stream);
|
||||
int streamHandle = this.streamHandles.AllocHandle(stream);
|
||||
err = 0;
|
||||
return streamHandle;
|
||||
}
|
||||
|
||||
internal int CabReadStream(int streamHandle, IntPtr memory, int cb)
|
||||
{
|
||||
int err; return this.CabReadStreamEx(streamHandle, memory, cb, out err, IntPtr.Zero);
|
||||
}
|
||||
|
||||
internal virtual int CabReadStreamEx(int streamHandle, IntPtr memory, int cb, out int err, IntPtr pv)
|
||||
{
|
||||
Stream stream = this.streamHandles[streamHandle];
|
||||
int count = (int) cb;
|
||||
if (count > this.buf.Length)
|
||||
{
|
||||
this.buf = new byte[count];
|
||||
}
|
||||
count = stream.Read(this.buf, 0, count);
|
||||
Marshal.Copy(this.buf, 0, memory, count);
|
||||
err = 0;
|
||||
return count;
|
||||
}
|
||||
|
||||
internal int CabWriteStream(int streamHandle, IntPtr memory, int cb)
|
||||
{
|
||||
int err; return this.CabWriteStreamEx(streamHandle, memory, cb, out err, IntPtr.Zero);
|
||||
}
|
||||
|
||||
internal virtual int CabWriteStreamEx(int streamHandle, IntPtr memory, int cb, out int err, IntPtr pv)
|
||||
{
|
||||
Stream stream = this.streamHandles[streamHandle];
|
||||
int count = (int) cb;
|
||||
if (count > this.buf.Length)
|
||||
{
|
||||
this.buf = new byte[count];
|
||||
}
|
||||
Marshal.Copy(memory, this.buf, 0, count);
|
||||
stream.Write(this.buf, 0, count);
|
||||
err = 0;
|
||||
return cb;
|
||||
}
|
||||
|
||||
internal int CabCloseStream(int streamHandle)
|
||||
{
|
||||
int err; return this.CabCloseStreamEx(streamHandle, out err, IntPtr.Zero);
|
||||
}
|
||||
|
||||
internal virtual int CabCloseStreamEx(int streamHandle, out int err, IntPtr pv)
|
||||
{
|
||||
this.streamHandles.FreeHandle(streamHandle);
|
||||
err = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
internal int CabSeekStream(int streamHandle, int offset, int seekOrigin)
|
||||
{
|
||||
int err; return this.CabSeekStreamEx(streamHandle, offset, seekOrigin, out err, IntPtr.Zero);
|
||||
}
|
||||
|
||||
internal virtual int CabSeekStreamEx(int streamHandle, int offset, int seekOrigin, out int err, IntPtr pv)
|
||||
{
|
||||
Stream stream = this.streamHandles[streamHandle];
|
||||
offset = (int) stream.Seek(offset, (SeekOrigin) seekOrigin);
|
||||
err = 0;
|
||||
return offset;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes of resources allocated by the cabinet engine.
|
||||
/// </summary>
|
||||
/// <param name="disposing">If true, the method has been called directly or indirectly by a user's code,
|
||||
/// so managed and unmanaged resources will be disposed. If false, the method has been called by the
|
||||
/// runtime from inside the finalizer, and only unmanaged resources will be disposed.</param>
|
||||
[SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands")]
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
if (this.cabStream != null)
|
||||
{
|
||||
this.cabStream.Close();
|
||||
this.cabStream = null;
|
||||
}
|
||||
|
||||
if (this.fileStream != null)
|
||||
{
|
||||
this.fileStream.Close();
|
||||
this.fileStream = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.erfHandle.IsAllocated)
|
||||
{
|
||||
this.erfHandle.Free();
|
||||
}
|
||||
}
|
||||
|
||||
protected void CheckError(bool extracting)
|
||||
{
|
||||
if (this.Erf.Error)
|
||||
{
|
||||
throw new CabException(
|
||||
this.Erf.Oper,
|
||||
this.Erf.Type,
|
||||
CabException.GetErrorMessage(this.Erf.Oper, this.Erf.Type, extracting));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<!--
|
||||
<copyright file="Compression.Cab.csproj" company="Outercurve Foundation">
|
||||
Copyright (c) 2004, Outercurve Foundation.
|
||||
This software is released under Microsoft Reciprocal License (MS-RL).
|
||||
The license and further copyright text can be found in the file LICENSE.TXT
|
||||
LICENSE.TXT at the root directory of the distribution.
|
||||
</copyright>
|
||||
-->
|
||||
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<ProjectGuid>{15895FD1-DD68-407B-8717-08F6DD14F02C}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>Microsoft.Deployment.Compression.Cab</RootNamespace>
|
||||
<AssemblyName>Microsoft.Deployment.Compression.Cab</AssemblyName>
|
||||
<CreateDocumentationFile>true</CreateDocumentationFile>
|
||||
<TargetFrameworkVersion>v2.0</TargetFrameworkVersion>
|
||||
<FxCopEnabled>false</FxCopEnabled>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="AssemblyInfo.cs" />
|
||||
<Compile Include="CabPacker.cs" />
|
||||
<Compile Include="CabEngine.cs" />
|
||||
<Compile Include="CabWorker.cs" />
|
||||
<Compile Include="CabException.cs" />
|
||||
<Compile Include="CabUnpacker.cs" />
|
||||
<Compile Include="CabFileInfo.cs" />
|
||||
<Compile Include="CabInfo.cs" />
|
||||
<Compile Include="HandleManager.cs" />
|
||||
<Compile Include="NativeMethods.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="Errors.txt" />
|
||||
<EmbeddedResource Include="Errors.resources" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<ProjectReference Include="..\Compression\Compression.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Properties\" />
|
||||
</ItemGroup>
|
||||
|
||||
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), wix.proj))\tools\WixBuild.targets" />
|
||||
</Project>
|
Двоичный файл не отображается.
|
@ -0,0 +1,35 @@
|
|||
;
|
||||
; Cabinet Error Messages
|
||||
;
|
||||
|
||||
; Generic error message.
|
||||
1=Error code: {1}
|
||||
|
||||
;
|
||||
; Cabinet creation messages - offset by 1000
|
||||
;
|
||||
1000=Unknown error creating cabinet.
|
||||
1001=Failure opening file to be stored in cabinet.
|
||||
1002=Failure reading file to be stored in cabinet.
|
||||
1003=Could not allocate enough memory to create cabinet.
|
||||
1004=Could not create a temporary file.
|
||||
1005=Unknown compression type.
|
||||
1006=Could not create cabinet file.
|
||||
1007=Client requested abort.
|
||||
1008=Failure compressing data.
|
||||
|
||||
;
|
||||
; Cabinet extraction messages - offset by 2000
|
||||
;
|
||||
2000=Unknown error extracting cabinet.
|
||||
2001=Cabinet not found.
|
||||
2002=Cabinet file does not have the correct format.
|
||||
2003=Cabinet file has an unknown version number.
|
||||
2004=Cabinet file is corrupt.
|
||||
2005=Could not allocate enough memory to extract cabinet.
|
||||
2006=Unknown compression type in a cabinet folder.
|
||||
2007=Failure decompressing data from a cabinet file.
|
||||
2008=Failure writing to target file.
|
||||
2009=Cabinets in a set do not have the same RESERVE sizes.
|
||||
2010=Cabinet returned on NEXT_CABINET is incorrect.
|
||||
2011=Client requested abort.
|
|
@ -0,0 +1,86 @@
|
|||
//---------------------------------------------------------------------
|
||||
// <copyright file="HandleManager.cs" company="Outercurve Foundation">
|
||||
// Copyright (c) 2004, Outercurve Foundation.
|
||||
// This software is released under Microsoft Reciprocal License (MS-RL).
|
||||
// The license and further copyright text can be found in the file LICENSE.TXT
|
||||
// LICENSE.TXT at the root directory of the distribution.
|
||||
// </copyright>
|
||||
// <summary>
|
||||
// Part of the Deployment Tools Foundation project.
|
||||
// </summary>
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Deployment.Compression.Cab
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
/// <summary>
|
||||
/// Generic class for managing allocations of integer handles
|
||||
/// for objects of a certain type.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of objects the handles refer to.</typeparam>
|
||||
internal sealed class HandleManager<T> where T : class
|
||||
{
|
||||
/// <summary>
|
||||
/// Auto-resizing list of objects for which handles have been allocated.
|
||||
/// Each handle is just an index into this list. When a handle is freed,
|
||||
/// the list item at that index is set to null.
|
||||
/// </summary>
|
||||
private List<T> handles;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new HandleManager instance.
|
||||
/// </summary>
|
||||
public HandleManager()
|
||||
{
|
||||
this.handles = new List<T>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the object of a handle, or null if the handle is invalid.
|
||||
/// </summary>
|
||||
/// <param name="handle">The integer handle previously allocated
|
||||
/// for the desired object.</param>
|
||||
/// <returns>The object for which the handle was allocated.</returns>
|
||||
public T this[int handle]
|
||||
{
|
||||
get
|
||||
{
|
||||
if (handle > 0 && handle <= this.handles.Count)
|
||||
{
|
||||
return this.handles[handle - 1];
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Allocates a new handle for an object.
|
||||
/// </summary>
|
||||
/// <param name="obj">Object that the handle will refer to.</param>
|
||||
/// <returns>New handle that can be later used to retrieve the object.</returns>
|
||||
public int AllocHandle(T obj)
|
||||
{
|
||||
this.handles.Add(obj);
|
||||
int handle = this.handles.Count;
|
||||
return handle;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Frees a handle that was previously allocated. Afterward the handle
|
||||
/// will be invalid and the object it referred to can no longer retrieved.
|
||||
/// </summary>
|
||||
/// <param name="handle">Handle to be freed.</param>
|
||||
public void FreeHandle(int handle)
|
||||
{
|
||||
if (handle > 0 && handle <= this.handles.Count)
|
||||
{
|
||||
this.handles[handle - 1] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,419 @@
|
|||
//---------------------------------------------------------------------
|
||||
// <copyright file="NativeMethods.cs" company="Outercurve Foundation">
|
||||
// Copyright (c) 2004, Outercurve Foundation.
|
||||
// This software is released under Microsoft Reciprocal License (MS-RL).
|
||||
// The license and further copyright text can be found in the file LICENSE.TXT
|
||||
// LICENSE.TXT at the root directory of the distribution.
|
||||
// </copyright>
|
||||
// <summary>
|
||||
// Part of the Deployment Tools Foundation project.
|
||||
// </summary>
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Deployment.Compression.Cab
|
||||
{
|
||||
using System;
|
||||
using System.Text;
|
||||
using System.Security;
|
||||
using System.Security.Permissions;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
/// <summary>
|
||||
/// Native DllImport methods and related structures and constants used for
|
||||
/// cabinet creation and extraction via cabinet.dll.
|
||||
/// </summary>
|
||||
internal static class NativeMethods
|
||||
{
|
||||
/// <summary>
|
||||
/// A direct import of constants, enums, structures, delegates, and functions from fci.h.
|
||||
/// Refer to comments in fci.h for documentation.
|
||||
/// </summary>
|
||||
internal static class FCI
|
||||
{
|
||||
internal const int MIN_DISK = 32768;
|
||||
internal const int MAX_DISK = Int32.MaxValue;
|
||||
internal const int MAX_FOLDER = 0x7FFF8000;
|
||||
internal const int MAX_FILENAME = 256;
|
||||
internal const int MAX_CABINET_NAME = 256;
|
||||
internal const int MAX_CAB_PATH = 256;
|
||||
internal const int MAX_DISK_NAME = 256;
|
||||
|
||||
internal const int CPU_80386 = 1;
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate IntPtr PFNALLOC(int cb);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate void PFNFREE(IntPtr pv);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate int PFNOPEN(string path, int oflag, int pmode, out int err, IntPtr pv);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate int PFNREAD(int fileHandle, IntPtr memory, int cb, out int err, IntPtr pv);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate int PFNWRITE(int fileHandle, IntPtr memory, int cb, out int err, IntPtr pv);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate int PFNCLOSE(int fileHandle, out int err, IntPtr pv);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate int PFNSEEK(int fileHandle, int dist, int seekType, out int err, IntPtr pv);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate int PFNDELETE(string path, out int err, IntPtr pv);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate int PFNGETNEXTCABINET(IntPtr pccab, uint cbPrevCab, IntPtr pv);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate int PFNFILEPLACED(IntPtr pccab, string path, long fileSize, int continuation, IntPtr pv);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate int PFNGETOPENINFO(string path, out short date, out short time, out short pattribs, out int err, IntPtr pv);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate int PFNSTATUS(STATUS typeStatus, uint cb1, uint cb2, IntPtr pv);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate int PFNGETTEMPFILE(IntPtr tempNamePtr, int tempNameSize, IntPtr pv);
|
||||
|
||||
/// <summary>
|
||||
/// Error codes that can be returned by FCI.
|
||||
/// </summary>
|
||||
internal enum ERROR : int
|
||||
{
|
||||
NONE,
|
||||
OPEN_SRC,
|
||||
READ_SRC,
|
||||
ALLOC_FAIL,
|
||||
TEMP_FILE,
|
||||
BAD_COMPR_TYPE,
|
||||
CAB_FILE,
|
||||
USER_ABORT,
|
||||
MCI_FAIL,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// FCI compression algorithm types and parameters.
|
||||
/// </summary>
|
||||
internal enum TCOMP : ushort
|
||||
{
|
||||
MASK_TYPE = 0x000F,
|
||||
TYPE_NONE = 0x0000,
|
||||
TYPE_MSZIP = 0x0001,
|
||||
TYPE_QUANTUM = 0x0002,
|
||||
TYPE_LZX = 0x0003,
|
||||
BAD = 0x000F,
|
||||
|
||||
MASK_LZX_WINDOW = 0x1F00,
|
||||
LZX_WINDOW_LO = 0x0F00,
|
||||
LZX_WINDOW_HI = 0x1500,
|
||||
SHIFT_LZX_WINDOW = 0x0008,
|
||||
|
||||
MASK_QUANTUM_LEVEL = 0x00F0,
|
||||
QUANTUM_LEVEL_LO = 0x0010,
|
||||
QUANTUM_LEVEL_HI = 0x0070,
|
||||
SHIFT_QUANTUM_LEVEL = 0x0004,
|
||||
|
||||
MASK_QUANTUM_MEM = 0x1F00,
|
||||
QUANTUM_MEM_LO = 0x0A00,
|
||||
QUANTUM_MEM_HI = 0x1500,
|
||||
SHIFT_QUANTUM_MEM = 0x0008,
|
||||
|
||||
MASK_RESERVED = 0xE000,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reason for FCI status callback.
|
||||
/// </summary>
|
||||
internal enum STATUS : uint
|
||||
{
|
||||
FILE = 0,
|
||||
FOLDER = 1,
|
||||
CABINET = 2,
|
||||
}
|
||||
|
||||
[SuppressMessage("Microsoft.Globalization", "CA2101:SpecifyMarshalingForPInvokeStringArguments")]
|
||||
[DllImport("cabinet.dll", EntryPoint = "FCICreate", CharSet = CharSet.Ansi, BestFitMapping = false, ThrowOnUnmappableChar = true, CallingConvention = CallingConvention.Cdecl)]
|
||||
internal static extern Handle Create(IntPtr perf, PFNFILEPLACED pfnfcifp, PFNALLOC pfna, PFNFREE pfnf, PFNOPEN pfnopen, PFNREAD pfnread, PFNWRITE pfnwrite, PFNCLOSE pfnclose, PFNSEEK pfnseek, PFNDELETE pfndelete, PFNGETTEMPFILE pfnfcigtf, [MarshalAs(UnmanagedType.LPStruct)] CCAB pccab, IntPtr pv);
|
||||
|
||||
[DllImport("cabinet.dll", EntryPoint = "FCIAddFile", CharSet = CharSet.Ansi, BestFitMapping = false, ThrowOnUnmappableChar = true, CallingConvention = CallingConvention.Cdecl)]
|
||||
internal static extern int AddFile(Handle hfci, string pszSourceFile, IntPtr pszFileName, [MarshalAs(UnmanagedType.Bool)] bool fExecute, PFNGETNEXTCABINET pfnfcignc, PFNSTATUS pfnfcis, PFNGETOPENINFO pfnfcigoi, TCOMP typeCompress);
|
||||
|
||||
[DllImport("cabinet.dll", EntryPoint = "FCIFlushCabinet", CharSet = CharSet.Ansi, BestFitMapping = false, ThrowOnUnmappableChar = true, CallingConvention = CallingConvention.Cdecl)]
|
||||
internal static extern int FlushCabinet(Handle hfci, [MarshalAs(UnmanagedType.Bool)] bool fGetNextCab, PFNGETNEXTCABINET pfnfcignc, PFNSTATUS pfnfcis);
|
||||
|
||||
[DllImport("cabinet.dll", EntryPoint = "FCIFlushFolder", CharSet = CharSet.Ansi, BestFitMapping = false, ThrowOnUnmappableChar = true, CallingConvention = CallingConvention.Cdecl)]
|
||||
internal static extern int FlushFolder(Handle hfci, PFNGETNEXTCABINET pfnfcignc, PFNSTATUS pfnfcis);
|
||||
|
||||
[SuppressUnmanagedCodeSecurity]
|
||||
[DllImport("cabinet.dll", EntryPoint = "FCIDestroy", CharSet = CharSet.Ansi, BestFitMapping = false, ThrowOnUnmappableChar = true, CallingConvention = CallingConvention.Cdecl)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
internal static extern bool Destroy(IntPtr hfci);
|
||||
|
||||
/// <summary>
|
||||
/// Cabinet information structure used for FCI initialization and GetNextCabinet callback.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
|
||||
internal class CCAB
|
||||
{
|
||||
internal int cb = MAX_DISK;
|
||||
internal int cbFolderThresh = MAX_FOLDER;
|
||||
internal int cbReserveCFHeader;
|
||||
internal int cbReserveCFFolder;
|
||||
internal int cbReserveCFData;
|
||||
internal int iCab;
|
||||
internal int iDisk;
|
||||
internal int fFailOnIncompressible;
|
||||
internal short setID;
|
||||
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=MAX_DISK_NAME )] internal string szDisk = String.Empty;
|
||||
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=MAX_CABINET_NAME)] internal string szCab = String.Empty;
|
||||
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=MAX_CAB_PATH )] internal string szCabPath = String.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures that the FCI handle is safely released.
|
||||
/// </summary>
|
||||
internal class Handle : SafeHandle
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new unintialized handle. The handle will be initialized
|
||||
/// when it is marshalled back from native code.
|
||||
/// </summary>
|
||||
internal Handle()
|
||||
: base(IntPtr.Zero, true)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the handle is invalid. An FCI handle is invalid when it is zero.
|
||||
/// </summary>
|
||||
public override bool IsInvalid
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.handle == IntPtr.Zero;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases the handle by calling FDIDestroy().
|
||||
/// </summary>
|
||||
/// <returns>True if the release succeeded.</returns>
|
||||
[SecurityPermission(SecurityAction.Assert, UnmanagedCode = true)]
|
||||
protected override bool ReleaseHandle()
|
||||
{
|
||||
return FCI.Destroy(this.handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A direct import of constants, enums, structures, delegates, and functions from fdi.h.
|
||||
/// Refer to comments in fdi.h for documentation.
|
||||
/// </summary>
|
||||
internal static class FDI
|
||||
{
|
||||
internal const int MAX_DISK = Int32.MaxValue;
|
||||
internal const int MAX_FILENAME = 256;
|
||||
internal const int MAX_CABINET_NAME = 256;
|
||||
internal const int MAX_CAB_PATH = 256;
|
||||
internal const int MAX_DISK_NAME = 256;
|
||||
|
||||
internal const int CPU_80386 = 1;
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate IntPtr PFNALLOC(int cb);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate void PFNFREE(IntPtr pv);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate int PFNOPEN(string path, int oflag, int pmode);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate int PFNREAD(int hf, IntPtr pv, int cb);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate int PFNWRITE(int hf, IntPtr pv, int cb);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate int PFNCLOSE(int hf);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate int PFNSEEK(int hf, int dist, int seektype);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate int PFNNOTIFY(NOTIFICATIONTYPE fdint, NOTIFICATION fdin);
|
||||
|
||||
/// <summary>
|
||||
/// Error codes that can be returned by FDI.
|
||||
/// </summary>
|
||||
internal enum ERROR : int
|
||||
{
|
||||
NONE,
|
||||
CABINET_NOT_FOUND,
|
||||
NOT_A_CABINET,
|
||||
UNKNOWN_CABINET_VERSION,
|
||||
CORRUPT_CABINET,
|
||||
ALLOC_FAIL,
|
||||
BAD_COMPR_TYPE,
|
||||
MDI_FAIL,
|
||||
TARGET_FILE,
|
||||
RESERVE_MISMATCH,
|
||||
WRONG_CABINET,
|
||||
USER_ABORT,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Type of notification message for the FDI Notify callback.
|
||||
/// </summary>
|
||||
internal enum NOTIFICATIONTYPE : int
|
||||
{
|
||||
CABINET_INFO,
|
||||
PARTIAL_FILE,
|
||||
COPY_FILE,
|
||||
CLOSE_FILE_INFO,
|
||||
NEXT_CABINET,
|
||||
ENUMERATE,
|
||||
}
|
||||
|
||||
[DllImport("cabinet.dll", EntryPoint = "FDICreate", CharSet = CharSet.Ansi, BestFitMapping = false, ThrowOnUnmappableChar = true, CallingConvention = CallingConvention.Cdecl)]
|
||||
internal static extern Handle Create([MarshalAs(UnmanagedType.FunctionPtr)] PFNALLOC pfnalloc, [MarshalAs(UnmanagedType.FunctionPtr)] PFNFREE pfnfree, PFNOPEN pfnopen, PFNREAD pfnread, PFNWRITE pfnwrite, PFNCLOSE pfnclose, PFNSEEK pfnseek, int cpuType, IntPtr perf);
|
||||
|
||||
[DllImport("cabinet.dll", EntryPoint = "FDICopy", CharSet = CharSet.Ansi, BestFitMapping = false, ThrowOnUnmappableChar = true, CallingConvention = CallingConvention.Cdecl)]
|
||||
internal static extern int Copy(Handle hfdi, string pszCabinet, string pszCabPath, int flags, PFNNOTIFY pfnfdin, IntPtr pfnfdid, IntPtr pvUser);
|
||||
|
||||
[SuppressUnmanagedCodeSecurity]
|
||||
[DllImport("cabinet.dll", EntryPoint = "FDIDestroy", CharSet = CharSet.Ansi, BestFitMapping = false, ThrowOnUnmappableChar = true, CallingConvention = CallingConvention.Cdecl)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
internal static extern bool Destroy(IntPtr hfdi);
|
||||
|
||||
[DllImport("cabinet.dll", EntryPoint = "FDIIsCabinet", CharSet = CharSet.Ansi, BestFitMapping = false, ThrowOnUnmappableChar = true, CallingConvention = CallingConvention.Cdecl)]
|
||||
[SuppressMessage("Microsoft.Portability", "CA1901:PInvokeDeclarationsShouldBePortable", Justification="FDI file handles definitely remain 4 bytes on 64bit platforms.")]
|
||||
internal static extern int IsCabinet(Handle hfdi, int hf, out CABINFO pfdici);
|
||||
|
||||
/// <summary>
|
||||
/// Cabinet information structure filled in by FDI IsCabinet.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal struct CABINFO
|
||||
{
|
||||
internal int cbCabinet;
|
||||
internal short cFolders;
|
||||
internal short cFiles;
|
||||
internal short setID;
|
||||
internal short iCabinet;
|
||||
internal int fReserve;
|
||||
internal int hasprev;
|
||||
internal int hasnext;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cabinet notification details passed to the FDI Notify callback.
|
||||
/// </summary>
|
||||
[SuppressMessage("Microsoft.Performance", "CA1812:AvoidUninstantiatedInternalClasses")]
|
||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
|
||||
internal class NOTIFICATION
|
||||
{
|
||||
internal int cb;
|
||||
internal IntPtr psz1;
|
||||
internal IntPtr psz2;
|
||||
internal IntPtr psz3;
|
||||
internal IntPtr pv;
|
||||
|
||||
internal IntPtr hf_ptr;
|
||||
|
||||
internal short date;
|
||||
internal short time;
|
||||
internal short attribs;
|
||||
internal short setID;
|
||||
internal short iCabinet;
|
||||
internal short iFolder;
|
||||
internal int fdie;
|
||||
|
||||
// Unlike all the other file handles in FCI/FDI, this one is
|
||||
// actually pointer-sized. Use a property to pretend it isn't.
|
||||
internal int hf
|
||||
{
|
||||
get { return (int) this.hf_ptr; }
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures that the FDI handle is safely released.
|
||||
/// </summary>
|
||||
internal class Handle : SafeHandle
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new unintialized handle. The handle will be initialized
|
||||
/// when it is marshalled back from native code.
|
||||
/// </summary>
|
||||
internal Handle()
|
||||
: base(IntPtr.Zero, true)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the handle is invalid. An FDI handle is invalid when it is zero.
|
||||
/// </summary>
|
||||
public override bool IsInvalid
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.handle == IntPtr.Zero;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases the handle by calling FDIDestroy().
|
||||
/// </summary>
|
||||
/// <returns>True if the release succeeded.</returns>
|
||||
protected override bool ReleaseHandle()
|
||||
{
|
||||
return FDI.Destroy(this.handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Error info structure for FCI and FDI.
|
||||
/// </summary>
|
||||
/// <remarks>Before being passed to FCI or FDI, this structure is
|
||||
/// pinned in memory via a GCHandle. The pinning is necessary
|
||||
/// to be able to read the results, since the ERF structure doesn't
|
||||
/// get marshalled back out after an error.</remarks>
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal class ERF
|
||||
{
|
||||
private int erfOper;
|
||||
private int erfType;
|
||||
private int fError;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the cabinet error code.
|
||||
/// </summary>
|
||||
internal int Oper
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.erfOper;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
this.erfOper = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Win32 error code.
|
||||
/// </summary>
|
||||
internal int Type
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.erfType;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
this.erfType = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// GCHandle doesn't like the bool type, so use an int underneath.
|
||||
/// </summary>
|
||||
internal bool Error
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.fError != 0;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
this.fError = value ? 1 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears the error information.
|
||||
/// </summary>
|
||||
internal void Clear()
|
||||
{
|
||||
this.Oper = 0;
|
||||
this.Type = 0;
|
||||
this.Error = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
//---------------------------------------------------------------------
|
||||
// <copyright file="AssemblyInfo.cs" company="Outercurve Foundation">
|
||||
// Copyright (c) 2004, Outercurve Foundation.
|
||||
// This software is released under Microsoft Reciprocal License (MS-RL).
|
||||
// The license and further copyright text can be found in the file LICENSE.TXT
|
||||
// LICENSE.TXT at the root directory of the distribution.
|
||||
// </copyright>
|
||||
// <summary>
|
||||
// Part of the Deployment Tools Foundation project.
|
||||
// </summary>
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security;
|
||||
using System.Security.Permissions;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
[assembly: AssemblyDescription("Managed libraries for zip archive packing and unpacking")]
|
||||
[assembly: CLSCompliant(true)]
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// SECURITY: The UnmanagedCode assertions in the zip classes are safe, because
|
||||
// only some very limited date-time unmanaged APIs are called.
|
||||
[assembly: SecurityPermission(SecurityAction.RequestMinimum, Assertion = true, UnmanagedCode = true)]
|
||||
|
||||
// SECURITY: Review carefully!
|
||||
// This assembly is designed so that partially trusted callers should be able to
|
||||
// do zip compression and extraction in a file path where they have limited
|
||||
// file I/O permission. Or they can even do in-memory compression and extraction
|
||||
// with absolutely no file I/O permission.
|
||||
[assembly: AllowPartiallyTrustedCallers]
|
||||
|
||||
[assembly: SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "Microsoft.Deployment.Compression.Zip")]
|
|
@ -0,0 +1,41 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<!--
|
||||
<copyright file="Compression.Zip.csproj" company="Outercurve Foundation">
|
||||
Copyright (c) 2004, Outercurve Foundation.
|
||||
This software is released under Microsoft Reciprocal License (MS-RL).
|
||||
The license and further copyright text can be found in the file LICENSE.TXT
|
||||
LICENSE.TXT at the root directory of the distribution.
|
||||
</copyright>
|
||||
-->
|
||||
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<ProjectGuid>{261F2857-B521-42A4-A3E0-B5165F225E50}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>Microsoft.Deployment.Compression.Zip</RootNamespace>
|
||||
<AssemblyName>Microsoft.Deployment.Compression.Zip</AssemblyName>
|
||||
<CreateDocumentationFile>true</CreateDocumentationFile>
|
||||
<TargetFrameworkVersion>v2.0</TargetFrameworkVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="AssemblyInfo.cs" />
|
||||
<Compile Include="ConcatStream.cs" />
|
||||
<Compile Include="CrcStream.cs" />
|
||||
<Compile Include="ZipCompressionMethod.cs" />
|
||||
<Compile Include="ZipException.cs" />
|
||||
<Compile Include="ZipEngine.cs" />
|
||||
<Compile Include="ZipFileInfo.cs" />
|
||||
<Compile Include="ZipFormat.cs" />
|
||||
<Compile Include="ZipInfo.cs" />
|
||||
<Compile Include="ZipPacker.cs" />
|
||||
<Compile Include="ZipUnpacker.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<ProjectReference Include="..\Compression\Compression.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), wix.proj))\tools\WixBuild.targets" />
|
||||
</Project>
|
|
@ -0,0 +1,167 @@
|
|||
//---------------------------------------------------------------------
|
||||
// <copyright file="ConcatStream.cs" company="Outercurve Foundation">
|
||||
// Copyright (c) 2004, Outercurve Foundation.
|
||||
// This software is released under Microsoft Reciprocal License (MS-RL).
|
||||
// The license and further copyright text can be found in the file LICENSE.TXT
|
||||
// LICENSE.TXT at the root directory of the distribution.
|
||||
// </copyright>
|
||||
// <summary>
|
||||
// Part of the Deployment Tools Foundation project.
|
||||
// </summary>
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Deployment.Compression.Zip
|
||||
{
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
/// <summary>
|
||||
/// Used to trick a DeflateStream into reading from or writing to
|
||||
/// a series of (chunked) streams instead of a single steream.
|
||||
/// </summary>
|
||||
internal class ConcatStream : Stream
|
||||
{
|
||||
private Stream source;
|
||||
private long position;
|
||||
private long length;
|
||||
private Action<ConcatStream> nextStreamHandler;
|
||||
|
||||
public ConcatStream(Action<ConcatStream> nextStreamHandler)
|
||||
{
|
||||
if (nextStreamHandler == null)
|
||||
{
|
||||
throw new ArgumentNullException("nextStreamHandler");
|
||||
}
|
||||
|
||||
this.nextStreamHandler = nextStreamHandler;
|
||||
this.length = Int64.MaxValue;
|
||||
}
|
||||
|
||||
public Stream Source
|
||||
{
|
||||
get { return this.source; }
|
||||
set { this.source = value; }
|
||||
}
|
||||
|
||||
public override bool CanRead
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
public override bool CanWrite
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
public override bool CanSeek
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
public override long Length
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.length;
|
||||
}
|
||||
}
|
||||
|
||||
public override long Position
|
||||
{
|
||||
get { return this.position; }
|
||||
set { throw new NotSupportedException(); }
|
||||
}
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
if (this.source == null)
|
||||
{
|
||||
this.nextStreamHandler(this);
|
||||
}
|
||||
|
||||
count = (int) Math.Min(count, this.length - this.position);
|
||||
|
||||
int bytesRemaining = count;
|
||||
while (bytesRemaining > 0)
|
||||
{
|
||||
if (this.source == null)
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
int partialCount = (int) Math.Min(bytesRemaining,
|
||||
this.source.Length - this.source.Position);
|
||||
|
||||
if (partialCount == 0)
|
||||
{
|
||||
this.nextStreamHandler(this);
|
||||
continue;
|
||||
}
|
||||
|
||||
partialCount = this.source.Read(
|
||||
buffer, offset + count - bytesRemaining, partialCount);
|
||||
bytesRemaining -= partialCount;
|
||||
this.position += partialCount;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
if (this.source == null)
|
||||
{
|
||||
this.nextStreamHandler(this);
|
||||
}
|
||||
|
||||
int bytesRemaining = count;
|
||||
while (bytesRemaining > 0)
|
||||
{
|
||||
if (this.source == null)
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
int partialCount = (int) Math.Min(bytesRemaining,
|
||||
Math.Max(0, this.length - this.source.Position));
|
||||
|
||||
if (partialCount == 0)
|
||||
{
|
||||
this.nextStreamHandler(this);
|
||||
continue;
|
||||
}
|
||||
|
||||
this.source.Write(
|
||||
buffer, offset + count - bytesRemaining, partialCount);
|
||||
bytesRemaining -= partialCount;
|
||||
this.position += partialCount;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Flush()
|
||||
{
|
||||
if (this.source != null)
|
||||
{
|
||||
this.source.Flush();
|
||||
}
|
||||
}
|
||||
|
||||
public override long Seek(long offset, SeekOrigin origin)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
this.length = value;
|
||||
}
|
||||
|
||||
public override void Close()
|
||||
{
|
||||
if (this.source != null)
|
||||
{
|
||||
this.source.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,261 @@
|
|||
//---------------------------------------------------------------------
|
||||
// <copyright file="CrcStream.cs" company="Outercurve Foundation">
|
||||
// Copyright (c) 2004, Outercurve Foundation.
|
||||
// This software is released under Microsoft Reciprocal License (MS-RL).
|
||||
// The license and further copyright text can be found in the file LICENSE.TXT
|
||||
// LICENSE.TXT at the root directory of the distribution.
|
||||
// </copyright>
|
||||
// <summary>
|
||||
// Part of the Deployment Tools Foundation project.
|
||||
// </summary>
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Deployment.Compression.Zip
|
||||
{
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
/// <summary>
|
||||
/// Wraps a source stream and calcaluates a CRC over all bytes that are read or written.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The CRC algorithm matches that used in the standard ZIP file format.
|
||||
/// </remarks>
|
||||
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Crc")]
|
||||
public class CrcStream : Stream
|
||||
{
|
||||
private Stream source;
|
||||
private uint crc;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new CrcStream instance from a source stream.
|
||||
/// </summary>
|
||||
/// <param name="source">Underlying stream where bytes will be read from or written to.</param>
|
||||
public CrcStream(Stream source)
|
||||
{
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current CRC over all bytes that have been read or written
|
||||
/// since this instance was created.
|
||||
/// </summary>
|
||||
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Crc")]
|
||||
[CLSCompliant(false)]
|
||||
public uint Crc
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.crc;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the underlying stream that this stream reads from or writes to.
|
||||
/// </summary>
|
||||
public Stream Source
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.source;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the source stream supports reading.
|
||||
/// </summary>
|
||||
/// <value>true if the stream supports reading; otherwise, false.</value>
|
||||
public override bool CanRead
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.source.CanRead;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the source stream supports writing.
|
||||
/// </summary>
|
||||
/// <value>true if the stream supports writing; otherwise, false.</value>
|
||||
public override bool CanWrite
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.source.CanWrite;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the source stream supports seeking.
|
||||
/// </summary>
|
||||
/// <value>true if the stream supports seeking; otherwise, false.</value>
|
||||
public override bool CanSeek
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.source.CanSeek;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the length of the source stream.
|
||||
/// </summary>
|
||||
public override long Length
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.source.Length;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the position of the source stream.
|
||||
/// </summary>
|
||||
public override long Position
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.source.Position;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
this.source.Position = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the position within the source stream.
|
||||
/// </summary>
|
||||
/// <param name="offset">A byte offset relative to the origin parameter.</param>
|
||||
/// <param name="origin">A value of type SeekOrigin indicating
|
||||
/// the reference point used to obtain the new position.</param>
|
||||
/// <returns>The new position within the source stream.</returns>
|
||||
/// <remarks>
|
||||
/// Note the CRC is only calculated over bytes that are actually read or
|
||||
/// written, so any bytes skipped by seeking will not contribute to the CRC.
|
||||
/// </remarks>
|
||||
public override long Seek(long offset, SeekOrigin origin)
|
||||
{
|
||||
return this.source.Seek(offset, origin);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the length of the source stream.
|
||||
/// </summary>
|
||||
/// <param name="value">The desired length of the
|
||||
/// stream in bytes.</param>
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
this.source.SetLength(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a sequence of bytes from the source stream and advances
|
||||
/// the position within the stream by the number of bytes read.
|
||||
/// </summary>
|
||||
/// <param name="buffer">An array of bytes. When this method returns, the buffer
|
||||
/// contains the specified byte array with the values between offset and
|
||||
/// (offset + count - 1) replaced by the bytes read from the current source.</param>
|
||||
/// <param name="offset">The zero-based byte offset in buffer at which to begin
|
||||
/// storing the data read from the current stream.</param>
|
||||
/// <param name="count">The maximum number of bytes to be read from the current stream.</param>
|
||||
/// <returns>The total number of bytes read into the buffer. This can be less
|
||||
/// than the number of bytes requested if that many bytes are not currently available,
|
||||
/// or zero (0) if the end of the stream has been reached.</returns>
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
count = this.source.Read(buffer, offset, count);
|
||||
this.UpdateCrc(buffer, offset, count);
|
||||
return count;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a sequence of bytes to the source stream and advances the
|
||||
/// current position within this stream by the number of bytes written.
|
||||
/// </summary>
|
||||
/// <param name="buffer">An array of bytes. This method copies count
|
||||
/// bytes from buffer to the current stream.</param>
|
||||
/// <param name="offset">The zero-based byte offset in buffer at which
|
||||
/// to begin copying bytes to the current stream.</param>
|
||||
/// <param name="count">The number of bytes to be written to the
|
||||
/// current stream.</param>
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
this.source.Write(buffer, offset, count);
|
||||
this.UpdateCrc(buffer, offset, count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Flushes the source stream.
|
||||
/// </summary>
|
||||
public override void Flush()
|
||||
{
|
||||
this.source.Flush();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Closes the underlying stream.
|
||||
/// </summary>
|
||||
public override void Close()
|
||||
{
|
||||
this.source.Close();
|
||||
base.Close();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the CRC with a range of bytes that were read or written.
|
||||
/// </summary>
|
||||
private void UpdateCrc(byte[] buffer, int offset, int count)
|
||||
{
|
||||
this.crc = ~this.crc;
|
||||
for( ; count > 0; count--, offset++)
|
||||
{
|
||||
this.crc = (this.crc >> 8) ^
|
||||
CrcStream.crcTable[(this.crc & 0xFF) ^ buffer[offset]];
|
||||
}
|
||||
this.crc = ~this.crc;
|
||||
}
|
||||
|
||||
private static uint[] crcTable = MakeCrcTable();
|
||||
|
||||
/// <summary>
|
||||
/// Computes a table that speeds up calculation of the CRC.
|
||||
/// </summary>
|
||||
private static uint[] MakeCrcTable()
|
||||
{
|
||||
const uint poly = 0x04C11DB7u;
|
||||
uint[] crcTable = new uint[256];
|
||||
for(uint n = 0; n < 256; n++)
|
||||
{
|
||||
uint c = CrcStream.Reflect(n, 8);
|
||||
c = c << 24;
|
||||
for(uint k = 0; k < 8; k++)
|
||||
{
|
||||
c = (c << 1) ^ ((c & 0x80000000u) != 0 ? poly : 0);
|
||||
}
|
||||
crcTable[n] = CrcStream.Reflect(c, 32);
|
||||
}
|
||||
return crcTable;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reflects the ordering of certain number of bits. For exmample when reflecting
|
||||
/// one byte, bit one is swapped with bit eight, bit two with bit seven, etc.
|
||||
/// </summary>
|
||||
private static uint Reflect(uint value, int bits)
|
||||
{
|
||||
for (int i = 0; i < bits / 2; i++)
|
||||
{
|
||||
uint leftBit = 1u << (bits - 1 - i);
|
||||
uint rightBit = 1u << i;
|
||||
if (((value & leftBit) != 0) != ((value & rightBit) != 0))
|
||||
{
|
||||
value ^= leftBit | rightBit;
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
//---------------------------------------------------------------------
|
||||
// <copyright file="ZipCompressionMethod.cs" company="Outercurve Foundation">
|
||||
// Copyright (c) 2004, Outercurve Foundation.
|
||||
// This software is released under Microsoft Reciprocal License (MS-RL).
|
||||
// The license and further copyright text can be found in the file LICENSE.TXT
|
||||
// LICENSE.TXT at the root directory of the distribution.
|
||||
// </copyright>
|
||||
// <summary>
|
||||
// Part of the Deployment Tools Foundation project.
|
||||
// </summary>
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Deployment.Compression.Zip
|
||||
{
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
/// <summary>
|
||||
/// Identifies the compression method or "algorithm"
|
||||
/// used for a single file within a zip archive.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Proprietary zip implementations may define additional compression
|
||||
/// methods outside of those included here.
|
||||
/// </remarks>
|
||||
public enum ZipCompressionMethod
|
||||
{
|
||||
/// <summary>
|
||||
/// The file is stored (no compression)
|
||||
/// </summary>
|
||||
Store = 0,
|
||||
|
||||
/// <summary>
|
||||
/// The file is Shrunk
|
||||
/// </summary>
|
||||
Shrink = 1,
|
||||
|
||||
/// <summary>
|
||||
/// The file is Reduced with compression factor 1
|
||||
/// </summary>
|
||||
Reduce1 = 2,
|
||||
|
||||
/// <summary>
|
||||
/// The file is Reduced with compression factor 2
|
||||
/// </summary>
|
||||
Reduce2 = 3,
|
||||
|
||||
/// <summary>
|
||||
/// The file is Reduced with compression factor 3
|
||||
/// </summary>
|
||||
Reduce3 = 4,
|
||||
|
||||
/// <summary>
|
||||
/// The file is Reduced with compression factor 4
|
||||
/// </summary>
|
||||
Reduce4 = 5,
|
||||
|
||||
/// <summary>
|
||||
/// The file is Imploded
|
||||
/// </summary>
|
||||
Implode = 6,
|
||||
|
||||
/// <summary>
|
||||
/// The file is Deflated;
|
||||
/// the most common and widely-compatible form of zip compression.
|
||||
/// </summary>
|
||||
Deflate = 8,
|
||||
|
||||
/// <summary>
|
||||
/// The file is Deflated using the enhanced Deflate64 method.
|
||||
/// </summary>
|
||||
Deflate64 = 9,
|
||||
|
||||
/// <summary>
|
||||
/// The file is compressed using the BZIP2 algorithm.
|
||||
/// </summary>
|
||||
BZip2 = 12,
|
||||
|
||||
/// <summary>
|
||||
/// The file is compressed using the LZMA algorithm.
|
||||
/// </summary>
|
||||
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Lzma")]
|
||||
Lzma = 14,
|
||||
|
||||
/// <summary>
|
||||
/// The file is compressed using the PPMd algorithm.
|
||||
/// </summary>
|
||||
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Ppmd")]
|
||||
Ppmd = 98
|
||||
}
|
||||
}
|
|
@ -0,0 +1,488 @@
|
|||
//---------------------------------------------------------------------
|
||||
// <copyright file="ZipEngine.cs" company="Outercurve Foundation">
|
||||
// Copyright (c) 2004, Outercurve Foundation.
|
||||
// This software is released under Microsoft Reciprocal License (MS-RL).
|
||||
// The license and further copyright text can be found in the file LICENSE.TXT
|
||||
// LICENSE.TXT at the root directory of the distribution.
|
||||
// </copyright>
|
||||
// <summary>
|
||||
// Part of the Deployment Tools Foundation project.
|
||||
// </summary>
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Deployment.Compression.Zip
|
||||
{
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
/// <summary>
|
||||
/// Engine capable of packing and unpacking archives in the zip format.
|
||||
/// </summary>
|
||||
public partial class ZipEngine : CompressionEngine
|
||||
{
|
||||
private static Dictionary<ZipCompressionMethod, Converter<Stream, Stream>>
|
||||
compressionStreamCreators;
|
||||
private static Dictionary<ZipCompressionMethod, Converter<Stream, Stream>>
|
||||
decompressionStreamCreators;
|
||||
|
||||
private static void InitCompressionStreamCreators()
|
||||
{
|
||||
if (ZipEngine.compressionStreamCreators == null)
|
||||
{
|
||||
ZipEngine.compressionStreamCreators = new
|
||||
Dictionary<ZipCompressionMethod, Converter<Stream, Stream>>();
|
||||
ZipEngine.decompressionStreamCreators = new
|
||||
Dictionary<ZipCompressionMethod, Converter<Stream, Stream>>();
|
||||
|
||||
ZipEngine.RegisterCompressionStreamCreator(
|
||||
ZipCompressionMethod.Store,
|
||||
CompressionMode.Compress,
|
||||
delegate(Stream stream) {
|
||||
return stream;
|
||||
});
|
||||
ZipEngine.RegisterCompressionStreamCreator(
|
||||
ZipCompressionMethod.Deflate,
|
||||
CompressionMode.Compress,
|
||||
delegate(Stream stream) {
|
||||
return new DeflateStream(stream, CompressionMode.Compress, true);
|
||||
});
|
||||
ZipEngine.RegisterCompressionStreamCreator(
|
||||
ZipCompressionMethod.Store,
|
||||
CompressionMode.Decompress,
|
||||
delegate(Stream stream) {
|
||||
return stream;
|
||||
});
|
||||
ZipEngine.RegisterCompressionStreamCreator(
|
||||
ZipCompressionMethod.Deflate,
|
||||
CompressionMode.Decompress,
|
||||
delegate(Stream stream) {
|
||||
return new DeflateStream(stream, CompressionMode.Decompress, true);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers a delegate that can create a warpper stream for
|
||||
/// compressing or uncompressing the data of a source stream.
|
||||
/// </summary>
|
||||
/// <param name="compressionMethod">Compression method being registered.</param>
|
||||
/// <param name="compressionMode">Indicates registration for ether
|
||||
/// compress or decompress mode.</param>
|
||||
/// <param name="creator">Delegate being registered.</param>
|
||||
/// <remarks>
|
||||
/// For compression, the delegate accepts a stream that writes to the archive
|
||||
/// and returns a wrapper stream that compresses bytes as they are written.
|
||||
/// For decompression, the delegate accepts a stream that reads from the archive
|
||||
/// and returns a wrapper stream that decompresses bytes as they are read.
|
||||
/// This wrapper stream model follows the design used by
|
||||
/// System.IO.Compression.DeflateStream, and indeed that class is used
|
||||
/// to implement the Deflate compression method by default.
|
||||
/// <para>To unregister a delegate, call this method again and pass
|
||||
/// null for the delegate parameter.</para>
|
||||
/// </remarks>
|
||||
/// <example>
|
||||
/// When the ZipEngine class is initialized, the Deflate compression method
|
||||
/// is automatically registered like this:
|
||||
/// <code>
|
||||
/// ZipEngine.RegisterCompressionStreamCreator(
|
||||
/// ZipCompressionMethod.Deflate,
|
||||
/// CompressionMode.Compress,
|
||||
/// delegate(Stream stream) {
|
||||
/// return new DeflateStream(stream, CompressionMode.Compress, true);
|
||||
/// });
|
||||
/// ZipEngine.RegisterCompressionStreamCreator(
|
||||
/// ZipCompressionMethod.Deflate,
|
||||
/// CompressionMode.Decompress,
|
||||
/// delegate(Stream stream) {
|
||||
/// return new DeflateStream(stream, CompressionMode.Decompress, true);
|
||||
/// });
|
||||
/// </code></example>
|
||||
public static void RegisterCompressionStreamCreator(
|
||||
ZipCompressionMethod compressionMethod,
|
||||
CompressionMode compressionMode,
|
||||
Converter<Stream, Stream> creator)
|
||||
{
|
||||
ZipEngine.InitCompressionStreamCreators();
|
||||
if (compressionMode == CompressionMode.Compress)
|
||||
{
|
||||
ZipEngine.compressionStreamCreators[compressionMethod] = creator;
|
||||
}
|
||||
else
|
||||
{
|
||||
ZipEngine.decompressionStreamCreators[compressionMethod] = creator;
|
||||
}
|
||||
}
|
||||
|
||||
// Progress data
|
||||
private string currentFileName;
|
||||
private int currentFileNumber;
|
||||
private int totalFiles;
|
||||
private long currentFileBytesProcessed;
|
||||
private long currentFileTotalBytes;
|
||||
private string mainArchiveName;
|
||||
private string currentArchiveName;
|
||||
private short currentArchiveNumber;
|
||||
private short totalArchives;
|
||||
private long currentArchiveBytesProcessed;
|
||||
private long currentArchiveTotalBytes;
|
||||
private long fileBytesProcessed;
|
||||
private long totalFileBytes;
|
||||
private string comment;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the zip engine.
|
||||
/// </summary>
|
||||
public ZipEngine()
|
||||
: base()
|
||||
{
|
||||
ZipEngine.InitCompressionStreamCreators();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the comment from the last-examined archive,
|
||||
/// or sets the comment to be added to any created archives.
|
||||
/// </summary>
|
||||
public string ArchiveComment
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.comment;
|
||||
}
|
||||
set
|
||||
{
|
||||
this.comment = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether a Stream begins with a header that indicates
|
||||
/// it is a valid archive file.
|
||||
/// </summary>
|
||||
/// <param name="stream">Stream for reading the archive file.</param>
|
||||
/// <returns>True if the stream is a valid zip archive
|
||||
/// (with no offset); false otherwise.</returns>
|
||||
public override bool IsArchive(Stream stream)
|
||||
{
|
||||
if (stream == null)
|
||||
{
|
||||
throw new ArgumentNullException("stream");
|
||||
}
|
||||
|
||||
if (stream.Length - stream.Position < 4)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
BinaryReader reader = new BinaryReader(stream);
|
||||
uint sig = reader.ReadUInt32();
|
||||
switch (sig)
|
||||
{
|
||||
case ZipFileHeader.LFHSIG:
|
||||
case ZipEndOfCentralDirectory.EOCDSIG:
|
||||
case ZipEndOfCentralDirectory.EOCD64SIG:
|
||||
case ZipFileHeader.SPANSIG:
|
||||
case ZipFileHeader.SPANSIG2:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the offset of an archive that is positioned 0 or more bytes
|
||||
/// from the start of the Stream.
|
||||
/// </summary>
|
||||
/// <param name="stream">A stream for reading the archive.</param>
|
||||
/// <returns>The offset in bytes of the archive,
|
||||
/// or -1 if no archive is found in the Stream.</returns>
|
||||
/// <remarks>The archive must begin on a 4-byte boundary.</remarks>
|
||||
public override long FindArchiveOffset(Stream stream)
|
||||
{
|
||||
long offset = base.FindArchiveOffset(stream);
|
||||
if (offset > 0)
|
||||
{
|
||||
// Some self-extract packages include the exe stub in file offset calculations.
|
||||
// Check the first header directory offset to decide whether the entire
|
||||
// archive needs to be offset or not.
|
||||
|
||||
ZipEndOfCentralDirectory eocd = this.GetEOCD(null, stream);
|
||||
if (eocd != null && eocd.totalEntries > 0)
|
||||
{
|
||||
stream.Seek(eocd.dirOffset, SeekOrigin.Begin);
|
||||
|
||||
ZipFileHeader header = new ZipFileHeader();
|
||||
if (header.Read(stream, true) && header.localHeaderOffset < stream.Length)
|
||||
{
|
||||
stream.Seek(header.localHeaderOffset, SeekOrigin.Begin);
|
||||
if (header.Read(stream, false))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets information about files in a zip archive or archive chain.
|
||||
/// </summary>
|
||||
/// <param name="streamContext">A context interface to handle opening
|
||||
/// and closing of archive and file streams.</param>
|
||||
/// <param name="fileFilter">A predicate that can determine
|
||||
/// which files to process, optional.</param>
|
||||
/// <returns>Information about files in the archive stream.</returns>
|
||||
/// <exception cref="ArchiveException">The archive provided
|
||||
/// by the stream context is not valid.</exception>
|
||||
/// <remarks>
|
||||
/// The <paramref name="fileFilter"/> predicate takes an internal file
|
||||
/// path and returns true to include the file or false to exclude it.
|
||||
/// </remarks>
|
||||
public override IList<ArchiveFileInfo> GetFileInfo(
|
||||
IUnpackStreamContext streamContext,
|
||||
Predicate<string> fileFilter)
|
||||
{
|
||||
if (streamContext == null)
|
||||
{
|
||||
throw new ArgumentNullException("streamContext");
|
||||
}
|
||||
|
||||
lock (this)
|
||||
{
|
||||
IList<ZipFileHeader> headers = this.GetCentralDirectory(streamContext);
|
||||
if (headers == null)
|
||||
{
|
||||
throw new ZipException("Zip central directory not found.");
|
||||
}
|
||||
|
||||
List<ArchiveFileInfo> files = new List<ArchiveFileInfo>(headers.Count);
|
||||
foreach (ZipFileHeader header in headers)
|
||||
{
|
||||
if (!header.IsDirectory &&
|
||||
(fileFilter == null || fileFilter(header.fileName)))
|
||||
{
|
||||
files.Add(header.ToZipFileInfo());
|
||||
}
|
||||
}
|
||||
|
||||
return files.AsReadOnly();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads all the file headers from the central directory in the main archive.
|
||||
/// </summary>
|
||||
private IList<ZipFileHeader> GetCentralDirectory(IUnpackStreamContext streamContext)
|
||||
{
|
||||
Stream archiveStream = null;
|
||||
this.currentArchiveNumber = 0;
|
||||
try
|
||||
{
|
||||
List<ZipFileHeader> headers = new List<ZipFileHeader>();
|
||||
archiveStream = this.OpenArchive(streamContext, 0);
|
||||
|
||||
ZipEndOfCentralDirectory eocd = this.GetEOCD(streamContext, archiveStream);
|
||||
if (eocd == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
else if (eocd.totalEntries == 0)
|
||||
{
|
||||
return headers;
|
||||
}
|
||||
|
||||
headers.Capacity = (int) eocd.totalEntries;
|
||||
|
||||
if (eocd.dirOffset > archiveStream.Length - ZipFileHeader.CFH_FIXEDSIZE)
|
||||
{
|
||||
streamContext.CloseArchiveReadStream(
|
||||
this.currentArchiveNumber, String.Empty, archiveStream);
|
||||
archiveStream = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
archiveStream.Seek(eocd.dirOffset, SeekOrigin.Begin);
|
||||
uint sig = new BinaryReader(archiveStream).ReadUInt32();
|
||||
if (sig != ZipFileHeader.CFHSIG)
|
||||
{
|
||||
streamContext.CloseArchiveReadStream(
|
||||
this.currentArchiveNumber, String.Empty, archiveStream);
|
||||
archiveStream = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (archiveStream == null)
|
||||
{
|
||||
this.currentArchiveNumber = (short) (eocd.dirStartDiskNumber + 1);
|
||||
archiveStream = streamContext.OpenArchiveReadStream(
|
||||
this.currentArchiveNumber, String.Empty, this);
|
||||
|
||||
if (archiveStream == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
archiveStream.Seek(eocd.dirOffset, SeekOrigin.Begin);
|
||||
|
||||
while (headers.Count < eocd.totalEntries)
|
||||
{
|
||||
ZipFileHeader header = new ZipFileHeader();
|
||||
if (!header.Read(archiveStream, true))
|
||||
{
|
||||
throw new ZipException(
|
||||
"Missing or invalid central directory file header");
|
||||
}
|
||||
|
||||
headers.Add(header);
|
||||
|
||||
if (headers.Count < eocd.totalEntries &&
|
||||
archiveStream.Position == archiveStream.Length)
|
||||
{
|
||||
streamContext.CloseArchiveReadStream(
|
||||
this.currentArchiveNumber, String.Empty, archiveStream);
|
||||
this.currentArchiveNumber++;
|
||||
archiveStream = streamContext.OpenArchiveReadStream(
|
||||
this.currentArchiveNumber, String.Empty, this);
|
||||
if (archiveStream == null)
|
||||
{
|
||||
this.currentArchiveNumber = 0;
|
||||
archiveStream = streamContext.OpenArchiveReadStream(
|
||||
this.currentArchiveNumber, String.Empty, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return headers;
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (archiveStream != null)
|
||||
{
|
||||
streamContext.CloseArchiveReadStream(
|
||||
this.currentArchiveNumber, String.Empty, archiveStream);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Locates and reads the end of central directory record near the
|
||||
/// end of the archive.
|
||||
/// </summary>
|
||||
[SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
|
||||
[SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "streamContext")]
|
||||
private ZipEndOfCentralDirectory GetEOCD(
|
||||
IUnpackStreamContext streamContext, Stream archiveStream)
|
||||
{
|
||||
BinaryReader reader = new BinaryReader(archiveStream);
|
||||
long offset = archiveStream.Length
|
||||
- ZipEndOfCentralDirectory.EOCD_RECORD_FIXEDSIZE;
|
||||
while (offset >= 0)
|
||||
{
|
||||
archiveStream.Seek(offset, SeekOrigin.Begin);
|
||||
|
||||
uint sig = reader.ReadUInt32();
|
||||
if (sig == ZipEndOfCentralDirectory.EOCDSIG)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
offset--;
|
||||
}
|
||||
|
||||
if (offset < 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
ZipEndOfCentralDirectory eocd = new ZipEndOfCentralDirectory();
|
||||
archiveStream.Seek(offset, SeekOrigin.Begin);
|
||||
if (!eocd.Read(archiveStream))
|
||||
{
|
||||
throw new ZipException("Invalid end of central directory record");
|
||||
}
|
||||
|
||||
if (eocd.dirOffset == (long) UInt32.MaxValue)
|
||||
{
|
||||
string saveComment = eocd.comment;
|
||||
|
||||
archiveStream.Seek(
|
||||
offset - Zip64EndOfCentralDirectoryLocator.EOCDL64_SIZE,
|
||||
SeekOrigin.Begin);
|
||||
|
||||
Zip64EndOfCentralDirectoryLocator eocdl =
|
||||
new Zip64EndOfCentralDirectoryLocator();
|
||||
if (!eocdl.Read(archiveStream))
|
||||
{
|
||||
throw new ZipException("Missing or invalid end of " +
|
||||
"central directory record locator");
|
||||
}
|
||||
|
||||
if (eocdl.dirStartDiskNumber == eocdl.totalDisks - 1)
|
||||
{
|
||||
// ZIP64 eocd is entirely in current stream.
|
||||
archiveStream.Seek(eocdl.dirOffset, SeekOrigin.Begin);
|
||||
if (!eocd.Read(archiveStream))
|
||||
{
|
||||
throw new ZipException("Missing or invalid ZIP64 end of " +
|
||||
"central directory record");
|
||||
}
|
||||
}
|
||||
else if (streamContext == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: handle EOCD64 spanning archives!
|
||||
throw new NotImplementedException("Zip implementation does not " +
|
||||
"handle end of central directory record that spans archives.");
|
||||
}
|
||||
|
||||
eocd.comment = saveComment;
|
||||
}
|
||||
|
||||
return eocd;
|
||||
}
|
||||
|
||||
private void ResetProgressData()
|
||||
{
|
||||
this.currentFileName = null;
|
||||
this.currentFileNumber = 0;
|
||||
this.totalFiles = 0;
|
||||
this.currentFileBytesProcessed = 0;
|
||||
this.currentFileTotalBytes = 0;
|
||||
this.currentArchiveName = null;
|
||||
this.currentArchiveNumber = 0;
|
||||
this.totalArchives = 0;
|
||||
this.currentArchiveBytesProcessed = 0;
|
||||
this.currentArchiveTotalBytes = 0;
|
||||
this.fileBytesProcessed = 0;
|
||||
this.totalFileBytes = 0;
|
||||
}
|
||||
|
||||
private void OnProgress(ArchiveProgressType progressType)
|
||||
{
|
||||
ArchiveProgressEventArgs e = new ArchiveProgressEventArgs(
|
||||
progressType,
|
||||
this.currentFileName,
|
||||
this.currentFileNumber >= 0 ? this.currentFileNumber : 0,
|
||||
this.totalFiles,
|
||||
this.currentFileBytesProcessed,
|
||||
this.currentFileTotalBytes,
|
||||
this.currentArchiveName,
|
||||
this.currentArchiveNumber,
|
||||
this.totalArchives,
|
||||
this.currentArchiveBytesProcessed,
|
||||
this.currentArchiveTotalBytes,
|
||||
this.fileBytesProcessed,
|
||||
this.totalFileBytes);
|
||||
this.OnProgress(e);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
//---------------------------------------------------------------------
|
||||
// <copyright file="ZipException.cs" company="Outercurve Foundation">
|
||||
// Copyright (c) 2004, Outercurve Foundation.
|
||||
// This software is released under Microsoft Reciprocal License (MS-RL).
|
||||
// The license and further copyright text can be found in the file LICENSE.TXT
|
||||
// LICENSE.TXT at the root directory of the distribution.
|
||||
// </copyright>
|
||||
// <summary>
|
||||
// Part of the Deployment Tools Foundation project.
|
||||
// </summary>
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Deployment.Compression.Zip
|
||||
{
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Resources;
|
||||
using System.Globalization;
|
||||
using System.Security.Permissions;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
/// <summary>
|
||||
/// Exception class for zip operations.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class ZipException : ArchiveException
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new ZipException with a specified error message and a reference to the
|
||||
/// inner exception that is the cause of this exception.
|
||||
/// </summary>
|
||||
/// <param name="message">The message that describes the error.</param>
|
||||
/// <param name="innerException">The exception that is the cause of the current exception. If the
|
||||
/// innerException parameter is not a null reference (Nothing in Visual Basic), the current exception
|
||||
/// is raised in a catch block that handles the inner exception.</param>
|
||||
public ZipException(string message, Exception innerException)
|
||||
: base(message, innerException) { }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new ZipException with a specified error message.
|
||||
/// </summary>
|
||||
/// <param name="message">The message that describes the error.</param>
|
||||
public ZipException(string message)
|
||||
: this(message, null) { }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new ZipException.
|
||||
/// </summary>
|
||||
public ZipException()
|
||||
: this(null, null) { }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the ZipException class with serialized data.
|
||||
/// </summary>
|
||||
/// <param name="info">The SerializationInfo that holds the serialized object data about the exception being thrown.</param>
|
||||
/// <param name="context">The StreamingContext that contains contextual information about the source or destination.</param>
|
||||
protected ZipException(SerializationInfo info, StreamingContext context) : base(info, context)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the SerializationInfo with information about the exception.
|
||||
/// </summary>
|
||||
/// <param name="info">The SerializationInfo that holds the serialized object data about the exception being thrown.</param>
|
||||
/// <param name="context">The StreamingContext that contains contextual information about the source or destination.</param>
|
||||
[SecurityPermission(SecurityAction.Demand, SerializationFormatter=true)]
|
||||
public override void GetObjectData(SerializationInfo info, StreamingContext context)
|
||||
{
|
||||
base.GetObjectData(info, context);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,116 @@
|
|||
//---------------------------------------------------------------------
|
||||
// <copyright file="ZipFileInfo.cs" company="Outercurve Foundation">
|
||||
// Copyright (c) 2004, Outercurve Foundation.
|
||||
// This software is released under Microsoft Reciprocal License (MS-RL).
|
||||
// The license and further copyright text can be found in the file LICENSE.TXT
|
||||
// LICENSE.TXT at the root directory of the distribution.
|
||||
// </copyright>
|
||||
// <summary>
|
||||
// Part of the Deployment Tools Foundation project.
|
||||
// </summary>
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Deployment.Compression.Zip
|
||||
{
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Security.Permissions;
|
||||
|
||||
/// <summary>
|
||||
/// Object representing a compressed file within a zip package; provides operations for getting
|
||||
/// the file properties and extracting the file.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class ZipFileInfo : ArchiveFileInfo
|
||||
{
|
||||
private long compressedLength;
|
||||
private ZipCompressionMethod compressionMethod;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new ZipFileInfo object representing a file within a zip in a specified path.
|
||||
/// </summary>
|
||||
/// <param name="zipInfo">An object representing the zip archive containing the file.</param>
|
||||
/// <param name="filePath">The path to the file within the zip archive. Usually, this is a simple file
|
||||
/// name, but if the zip archive contains a directory structure this may include the directory.</param>
|
||||
public ZipFileInfo(ZipInfo zipInfo, string filePath)
|
||||
: base(zipInfo, filePath)
|
||||
{
|
||||
if (zipInfo == null)
|
||||
{
|
||||
throw new ArgumentNullException("zipInfo");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new ZipFileInfo object with all parameters specified,
|
||||
/// used internally when reading the metadata out of a zip archive.
|
||||
/// </summary>
|
||||
/// <param name="filePath">The internal path and name of the file in the zip archive.</param>
|
||||
/// <param name="zipNumber">The zip archive number where the file starts.</param>
|
||||
/// <param name="attributes">The stored attributes of the file.</param>
|
||||
/// <param name="lastWriteTime">The stored last write time of the file.</param>
|
||||
/// <param name="length">The uncompressed size of the file.</param>
|
||||
/// <param name="compressedLength">The compressed size of the file.</param>
|
||||
/// <param name="compressionMethod">Compression algorithm used for this file.</param>
|
||||
internal ZipFileInfo(
|
||||
string filePath,
|
||||
int zipNumber,
|
||||
FileAttributes attributes,
|
||||
DateTime lastWriteTime,
|
||||
long length,
|
||||
long compressedLength,
|
||||
ZipCompressionMethod compressionMethod)
|
||||
: base(filePath, zipNumber, attributes, lastWriteTime, length)
|
||||
{
|
||||
this.compressedLength = compressedLength;
|
||||
this.compressionMethod = compressionMethod;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the ZipFileInfo class with serialized data.
|
||||
/// </summary>
|
||||
/// <param name="info">The SerializationInfo that holds the serialized object data about the exception being thrown.</param>
|
||||
/// <param name="context">The StreamingContext that contains contextual information about the source or destination.</param>
|
||||
protected ZipFileInfo(SerializationInfo info, StreamingContext context)
|
||||
: base(info, context)
|
||||
{
|
||||
this.compressedLength = info.GetInt64("compressedLength");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the compressed size of the file in bytes.
|
||||
/// </summary>
|
||||
public long CompressedLength
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.compressedLength;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the method used to compress this file.
|
||||
/// </summary>
|
||||
public ZipCompressionMethod CompressionMethod
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.compressionMethod;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the SerializationInfo with information about the archive.
|
||||
/// </summary>
|
||||
/// <param name="info">The SerializationInfo that holds the serialized object data.</param>
|
||||
/// <param name="context">The StreamingContext that contains contextual information
|
||||
/// about the source or destination.</param>
|
||||
[SecurityPermission(SecurityAction.Demand, SerializationFormatter = true)]
|
||||
public override void GetObjectData(SerializationInfo info, StreamingContext context)
|
||||
{
|
||||
base.GetObjectData(info, context);
|
||||
info.AddValue("compressedLength", this.compressedLength);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,707 @@
|
|||
//---------------------------------------------------------------------
|
||||
// <copyright file="ZipFormat.cs" company="Outercurve Foundation">
|
||||
// Copyright (c) 2004, Outercurve Foundation.
|
||||
// This software is released under Microsoft Reciprocal License (MS-RL).
|
||||
// The license and further copyright text can be found in the file LICENSE.TXT
|
||||
// LICENSE.TXT at the root directory of the distribution.
|
||||
// </copyright>
|
||||
// <summary>
|
||||
// Part of the Deployment Tools Foundation project.
|
||||
// </summary>
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Deployment.Compression.Zip
|
||||
{
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
[Flags]
|
||||
internal enum ZipFileFlags : ushort
|
||||
{
|
||||
None = 0x0000,
|
||||
Encrypt = 0x0001,
|
||||
CompressOption1 = 0x0002,
|
||||
CompressOption2 = 0x0004,
|
||||
DataDescriptor = 0x0008,
|
||||
StrongEncrypt = 0x0040,
|
||||
UTF8 = 0x0800
|
||||
}
|
||||
|
||||
internal enum ZipExtraFileFieldType : ushort
|
||||
{
|
||||
ZIP64 = 0x0001,
|
||||
NTFS_TIMES = 0x000A,
|
||||
NTFS_ACLS = 0x4453,
|
||||
EXTIME = 0x5455
|
||||
}
|
||||
|
||||
internal class ZipFileHeader
|
||||
{
|
||||
public const uint LFHSIG = 0x04034B50;
|
||||
public const uint CFHSIG = 0x02014B50;
|
||||
|
||||
public const uint SPANSIG = 0x08074b50;
|
||||
public const uint SPANSIG2 = 0x30304b50;
|
||||
|
||||
public const uint LFH_FIXEDSIZE = 30;
|
||||
public const uint CFH_FIXEDSIZE = 46;
|
||||
|
||||
public ushort versionMadeBy;
|
||||
public ushort versionNeeded;
|
||||
public ZipFileFlags flags;
|
||||
public ZipCompressionMethod compressionMethod;
|
||||
public short lastModTime;
|
||||
public short lastModDate;
|
||||
public uint crc32;
|
||||
public uint compressedSize;
|
||||
public uint uncompressedSize;
|
||||
public ushort diskStart;
|
||||
public ushort internalFileAttrs;
|
||||
public uint externalFileAttrs;
|
||||
public uint localHeaderOffset;
|
||||
public string fileName;
|
||||
public ZipExtraFileField[] extraFields;
|
||||
public string fileComment;
|
||||
public bool zip64;
|
||||
|
||||
public ZipFileHeader()
|
||||
{
|
||||
this.versionMadeBy = 20;
|
||||
this.versionNeeded = 20;
|
||||
}
|
||||
|
||||
public ZipFileHeader(ZipFileInfo fileInfo, bool zip64)
|
||||
: this()
|
||||
{
|
||||
this.flags = ZipFileFlags.None;
|
||||
this.compressionMethod = fileInfo.CompressionMethod;
|
||||
this.fileName = Path.Combine(fileInfo.Path, fileInfo.Name);
|
||||
CompressionEngine.DateTimeToDosDateAndTime(
|
||||
fileInfo.LastWriteTime, out this.lastModDate, out this.lastModTime);
|
||||
this.zip64 = zip64;
|
||||
|
||||
if (this.zip64)
|
||||
{
|
||||
this.compressedSize = UInt32.MaxValue;
|
||||
this.uncompressedSize = UInt32.MaxValue;
|
||||
this.diskStart = UInt16.MaxValue;
|
||||
this.versionMadeBy = 45;
|
||||
this.versionNeeded = 45;
|
||||
ZipExtraFileField field = new ZipExtraFileField();
|
||||
field.fieldType = ZipExtraFileFieldType.ZIP64;
|
||||
field.SetZip64Data(
|
||||
fileInfo.CompressedLength,
|
||||
fileInfo.Length,
|
||||
0,
|
||||
fileInfo.ArchiveNumber);
|
||||
this.extraFields = new ZipExtraFileField[] { field };
|
||||
}
|
||||
else
|
||||
{
|
||||
this.compressedSize = (uint) fileInfo.CompressedLength;
|
||||
this.uncompressedSize = (uint) fileInfo.Length;
|
||||
this.diskStart = (ushort) fileInfo.ArchiveNumber;
|
||||
}
|
||||
}
|
||||
|
||||
[SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", MessageId = "compressedSize")]
|
||||
[SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", MessageId = "uncompressedSize")]
|
||||
[SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", MessageId = "crc32")]
|
||||
[SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", MessageId = "localHeaderOffset")]
|
||||
public void Update(
|
||||
long compressedSize,
|
||||
long uncompressedSize,
|
||||
uint crc32,
|
||||
long localHeaderOffset,
|
||||
int archiveNumber)
|
||||
{
|
||||
this.crc32 = crc32;
|
||||
|
||||
if (this.zip64)
|
||||
{
|
||||
this.compressedSize = UInt32.MaxValue;
|
||||
this.uncompressedSize = UInt32.MaxValue;
|
||||
this.localHeaderOffset = UInt32.MaxValue;
|
||||
this.diskStart = UInt16.MaxValue;
|
||||
|
||||
if (this.extraFields != null)
|
||||
{
|
||||
foreach (ZipExtraFileField field in this.extraFields)
|
||||
{
|
||||
if (field.fieldType == ZipExtraFileFieldType.ZIP64)
|
||||
{
|
||||
field.SetZip64Data(
|
||||
compressedSize,
|
||||
uncompressedSize,
|
||||
localHeaderOffset,
|
||||
archiveNumber);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
this.compressedSize = (uint) compressedSize;
|
||||
this.uncompressedSize = (uint) uncompressedSize;
|
||||
this.localHeaderOffset = (uint) localHeaderOffset;
|
||||
this.diskStart = (ushort) archiveNumber;
|
||||
}
|
||||
}
|
||||
|
||||
public bool Read(Stream stream, bool central)
|
||||
{
|
||||
long startPos = stream.Position;
|
||||
|
||||
if (stream.Length - startPos <
|
||||
(central ? CFH_FIXEDSIZE : LFH_FIXEDSIZE))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
BinaryReader reader = new BinaryReader(stream);
|
||||
uint sig = reader.ReadUInt32();
|
||||
|
||||
if (sig == SPANSIG || sig == SPANSIG2)
|
||||
{
|
||||
// Spanned zip files may optionally begin with a special marker.
|
||||
// Just ignore it and move on.
|
||||
sig = reader.ReadUInt32();
|
||||
}
|
||||
|
||||
if (sig != (central ? CFHSIG : LFHSIG))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
this.versionMadeBy = (central ? reader.ReadUInt16() : (ushort) 0);
|
||||
this.versionNeeded = reader.ReadUInt16();
|
||||
this.flags = (ZipFileFlags) reader.ReadUInt16();
|
||||
this.compressionMethod = (ZipCompressionMethod) reader.ReadUInt16();
|
||||
this.lastModTime = reader.ReadInt16();
|
||||
this.lastModDate = reader.ReadInt16();
|
||||
this.crc32 = reader.ReadUInt32();
|
||||
this.compressedSize = reader.ReadUInt32();
|
||||
this.uncompressedSize = reader.ReadUInt32();
|
||||
|
||||
this.zip64 = this.uncompressedSize == UInt32.MaxValue;
|
||||
|
||||
int fileNameLength = reader.ReadUInt16();
|
||||
int extraFieldLength = reader.ReadUInt16();
|
||||
int fileCommentLength;
|
||||
|
||||
if (central)
|
||||
{
|
||||
fileCommentLength = reader.ReadUInt16();
|
||||
|
||||
this.diskStart = reader.ReadUInt16();
|
||||
this.internalFileAttrs = reader.ReadUInt16();
|
||||
this.externalFileAttrs = reader.ReadUInt32();
|
||||
this.localHeaderOffset = reader.ReadUInt32();
|
||||
}
|
||||
else
|
||||
{
|
||||
fileCommentLength = 0;
|
||||
this.diskStart = 0;
|
||||
this.internalFileAttrs = 0;
|
||||
this.externalFileAttrs = 0;
|
||||
this.localHeaderOffset = 0;
|
||||
}
|
||||
|
||||
if (stream.Length - stream.Position <
|
||||
fileNameLength + extraFieldLength + fileCommentLength)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Encoding headerEncoding = ((this.flags | ZipFileFlags.UTF8) != 0 ?
|
||||
Encoding.UTF8 : Encoding.GetEncoding(CultureInfo.CurrentCulture.TextInfo.OEMCodePage));
|
||||
|
||||
byte[] fileNameBytes = reader.ReadBytes(fileNameLength);
|
||||
this.fileName = headerEncoding.GetString(fileNameBytes).Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar);
|
||||
|
||||
List<ZipExtraFileField> fields = new List<ZipExtraFileField>();
|
||||
while (extraFieldLength > 0)
|
||||
{
|
||||
ZipExtraFileField field = new ZipExtraFileField();
|
||||
if (!field.Read(stream, ref extraFieldLength))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
fields.Add(field);
|
||||
if (field.fieldType == ZipExtraFileFieldType.ZIP64)
|
||||
{
|
||||
this.zip64 = true;
|
||||
}
|
||||
}
|
||||
this.extraFields = fields.ToArray();
|
||||
|
||||
byte[] fileCommentBytes = reader.ReadBytes(fileCommentLength);
|
||||
this.fileComment = headerEncoding.GetString(fileCommentBytes);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Write(Stream stream, bool central)
|
||||
{
|
||||
byte[] fileNameBytes = (this.fileName != null
|
||||
? Encoding.UTF8.GetBytes(this.fileName) : new byte[0]);
|
||||
byte[] fileCommentBytes = (this.fileComment != null
|
||||
? Encoding.UTF8.GetBytes(this.fileComment) : new byte[0]);
|
||||
bool useUtf8 =
|
||||
(this.fileName != null && fileNameBytes.Length > this.fileName.Length) ||
|
||||
(this.fileComment != null && fileCommentBytes.Length > this.fileComment.Length);
|
||||
if (useUtf8)
|
||||
{
|
||||
this.flags |= ZipFileFlags.UTF8;
|
||||
}
|
||||
|
||||
BinaryWriter writer = new BinaryWriter(stream);
|
||||
writer.Write(central ? CFHSIG : LFHSIG);
|
||||
if (central)
|
||||
{
|
||||
writer.Write(this.versionMadeBy);
|
||||
}
|
||||
writer.Write(this.versionNeeded);
|
||||
writer.Write((ushort) this.flags);
|
||||
writer.Write((ushort) this.compressionMethod);
|
||||
writer.Write(this.lastModTime);
|
||||
writer.Write(this.lastModDate);
|
||||
writer.Write(this.crc32);
|
||||
writer.Write(this.compressedSize);
|
||||
writer.Write(this.uncompressedSize);
|
||||
|
||||
ushort extraFieldLength = 0;
|
||||
if (this.extraFields != null)
|
||||
{
|
||||
foreach (ZipExtraFileField field in this.extraFields)
|
||||
{
|
||||
if (field.data != null)
|
||||
{
|
||||
extraFieldLength += (ushort) (4 + field.data.Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
writer.Write((ushort) fileNameBytes.Length);
|
||||
writer.Write(extraFieldLength);
|
||||
|
||||
if (central)
|
||||
{
|
||||
writer.Write((ushort) fileCommentBytes.Length);
|
||||
|
||||
writer.Write(this.diskStart);
|
||||
writer.Write(this.internalFileAttrs);
|
||||
writer.Write(this.externalFileAttrs);
|
||||
writer.Write(this.localHeaderOffset);
|
||||
}
|
||||
|
||||
writer.Write(fileNameBytes);
|
||||
|
||||
if (this.extraFields != null)
|
||||
{
|
||||
foreach (ZipExtraFileField field in this.extraFields)
|
||||
{
|
||||
if (field.data != null)
|
||||
{
|
||||
field.Write(stream);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (central)
|
||||
{
|
||||
writer.Write(fileCommentBytes);
|
||||
}
|
||||
}
|
||||
|
||||
[SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", MessageId = "compressedSize")]
|
||||
[SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", MessageId = "uncompressedSize")]
|
||||
[SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", MessageId = "crc32")]
|
||||
[SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", MessageId = "localHeaderOffset")]
|
||||
public void GetZip64Fields(
|
||||
out long compressedSize,
|
||||
out long uncompressedSize,
|
||||
out long localHeaderOffset,
|
||||
out int archiveNumber,
|
||||
out uint crc)
|
||||
{
|
||||
compressedSize = this.compressedSize;
|
||||
uncompressedSize = this.uncompressedSize;
|
||||
localHeaderOffset = this.localHeaderOffset;
|
||||
archiveNumber = this.diskStart;
|
||||
crc = this.crc32;
|
||||
|
||||
foreach (ZipExtraFileField field in this.extraFields)
|
||||
{
|
||||
if (field.fieldType == ZipExtraFileFieldType.ZIP64)
|
||||
{
|
||||
field.GetZip64Data(
|
||||
out compressedSize,
|
||||
out uncompressedSize,
|
||||
out localHeaderOffset,
|
||||
out archiveNumber);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ZipFileInfo ToZipFileInfo()
|
||||
{
|
||||
string name = this.fileName;
|
||||
|
||||
long compressedSizeL;
|
||||
long uncompressedSizeL;
|
||||
long localHeaderOffsetL;
|
||||
int archiveNumberL;
|
||||
uint crc;
|
||||
this.GetZip64Fields(
|
||||
out compressedSizeL,
|
||||
out uncompressedSizeL,
|
||||
out localHeaderOffsetL,
|
||||
out archiveNumberL,
|
||||
out crc);
|
||||
|
||||
DateTime dateTime;
|
||||
CompressionEngine.DosDateAndTimeToDateTime(
|
||||
this.lastModDate,
|
||||
this.lastModTime,
|
||||
out dateTime);
|
||||
FileAttributes attrs = FileAttributes.Normal;
|
||||
// TODO: look for attrs or times in extra fields
|
||||
|
||||
return new ZipFileInfo(name, archiveNumberL, attrs, dateTime,
|
||||
uncompressedSizeL, compressedSizeL, this.compressionMethod);
|
||||
}
|
||||
|
||||
public bool IsDirectory
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.fileName != null &&
|
||||
(this.fileName.EndsWith("/", StringComparison.Ordinal) ||
|
||||
this.fileName.EndsWith("\\", StringComparison.Ordinal));
|
||||
}
|
||||
}
|
||||
|
||||
public int GetSize(bool central)
|
||||
{
|
||||
int size = 30;
|
||||
|
||||
int fileNameSize = (this.fileName != null
|
||||
? Encoding.UTF8.GetByteCount(this.fileName) : 0);
|
||||
size += fileNameSize;
|
||||
|
||||
if (this.extraFields != null)
|
||||
{
|
||||
foreach (ZipExtraFileField field in this.extraFields)
|
||||
{
|
||||
if (field.data != null)
|
||||
{
|
||||
size += 4 + field.data.Length;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (central)
|
||||
{
|
||||
size += 16;
|
||||
|
||||
int fileCommentSize = (this.fileComment != null
|
||||
? Encoding.UTF8.GetByteCount(this.fileComment) : 0);
|
||||
size += fileCommentSize;
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
}
|
||||
|
||||
internal class ZipExtraFileField
|
||||
{
|
||||
public ZipExtraFileFieldType fieldType;
|
||||
public byte[] data;
|
||||
|
||||
public bool Read(Stream stream, ref int bytesRemaining)
|
||||
{
|
||||
if (bytesRemaining < 4)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
BinaryReader reader = new BinaryReader(stream);
|
||||
|
||||
this.fieldType = (ZipExtraFileFieldType) reader.ReadUInt16();
|
||||
ushort dataSize = reader.ReadUInt16();
|
||||
bytesRemaining -= 4;
|
||||
|
||||
if (bytesRemaining < dataSize)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
this.data = reader.ReadBytes(dataSize);
|
||||
bytesRemaining -= dataSize;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Write(Stream stream)
|
||||
{
|
||||
BinaryWriter writer = new BinaryWriter(stream);
|
||||
writer.Write((ushort) this.fieldType);
|
||||
|
||||
byte[] dataBytes = (this.data != null ? this.data : new byte[0]);
|
||||
writer.Write((ushort) dataBytes.Length);
|
||||
writer.Write(dataBytes);
|
||||
}
|
||||
|
||||
public bool GetZip64Data(
|
||||
out long compressedSize,
|
||||
out long uncompressedSize,
|
||||
out long localHeaderOffset,
|
||||
out int diskStart)
|
||||
{
|
||||
uncompressedSize = 0;
|
||||
compressedSize = 0;
|
||||
localHeaderOffset = 0;
|
||||
diskStart = 0;
|
||||
|
||||
if (this.fieldType != ZipExtraFileFieldType.ZIP64 ||
|
||||
this.data == null || this.data.Length != 28)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
using (MemoryStream dataStream = new MemoryStream(this.data))
|
||||
{
|
||||
BinaryReader reader = new BinaryReader(dataStream);
|
||||
uncompressedSize = reader.ReadInt64();
|
||||
compressedSize = reader.ReadInt64();
|
||||
localHeaderOffset = reader.ReadInt64();
|
||||
diskStart = reader.ReadInt32();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool SetZip64Data(
|
||||
long compressedSize,
|
||||
long uncompressedSize,
|
||||
long localHeaderOffset,
|
||||
int diskStart)
|
||||
{
|
||||
if (this.fieldType != ZipExtraFileFieldType.ZIP64)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
using (MemoryStream dataStream = new MemoryStream())
|
||||
{
|
||||
BinaryWriter writer = new BinaryWriter(dataStream);
|
||||
writer.Write(uncompressedSize);
|
||||
writer.Write(compressedSize);
|
||||
writer.Write(localHeaderOffset);
|
||||
writer.Write(diskStart);
|
||||
this.data = dataStream.ToArray();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
internal class ZipEndOfCentralDirectory
|
||||
{
|
||||
public const uint EOCDSIG = 0x06054B50;
|
||||
public const uint EOCD64SIG = 0x06064B50;
|
||||
|
||||
public const uint EOCD_RECORD_FIXEDSIZE = 22;
|
||||
public const uint EOCD64_RECORD_FIXEDSIZE = 56;
|
||||
|
||||
public ushort versionMadeBy;
|
||||
public ushort versionNeeded;
|
||||
public uint diskNumber;
|
||||
public uint dirStartDiskNumber;
|
||||
public long entriesOnDisk;
|
||||
public long totalEntries;
|
||||
public long dirSize;
|
||||
public long dirOffset;
|
||||
public string comment;
|
||||
public bool zip64;
|
||||
|
||||
public ZipEndOfCentralDirectory()
|
||||
{
|
||||
this.versionMadeBy = 20;
|
||||
this.versionNeeded = 20;
|
||||
}
|
||||
|
||||
public bool Read(Stream stream)
|
||||
{
|
||||
long startPos = stream.Position;
|
||||
|
||||
if (stream.Length - startPos < EOCD_RECORD_FIXEDSIZE)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
BinaryReader reader = new BinaryReader(stream);
|
||||
uint sig = reader.ReadUInt32();
|
||||
|
||||
this.zip64 = false;
|
||||
if (sig != EOCDSIG)
|
||||
{
|
||||
if (sig == EOCD64SIG)
|
||||
{
|
||||
this.zip64 = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.zip64)
|
||||
{
|
||||
if (stream.Length - startPos < EOCD64_RECORD_FIXEDSIZE)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
long recordSize = reader.ReadInt64();
|
||||
this.versionMadeBy = reader.ReadUInt16();
|
||||
this.versionNeeded = reader.ReadUInt16();
|
||||
this.diskNumber = reader.ReadUInt32();
|
||||
this.dirStartDiskNumber = reader.ReadUInt32();
|
||||
this.entriesOnDisk = reader.ReadInt64();
|
||||
this.totalEntries = reader.ReadInt64();
|
||||
this.dirSize = reader.ReadInt64();
|
||||
this.dirOffset = reader.ReadInt64();
|
||||
|
||||
// Ignore any extended zip64 eocd data.
|
||||
long exDataSize = recordSize + 12 - EOCD64_RECORD_FIXEDSIZE;
|
||||
|
||||
if (stream.Length - stream.Position < exDataSize)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
stream.Seek(exDataSize, SeekOrigin.Current);
|
||||
|
||||
this.comment = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.diskNumber = reader.ReadUInt16();
|
||||
this.dirStartDiskNumber = reader.ReadUInt16();
|
||||
this.entriesOnDisk = reader.ReadUInt16();
|
||||
this.totalEntries = reader.ReadUInt16();
|
||||
this.dirSize = reader.ReadUInt32();
|
||||
this.dirOffset = reader.ReadUInt32();
|
||||
|
||||
int commentLength = reader.ReadUInt16();
|
||||
|
||||
if (stream.Length - stream.Position < commentLength)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
byte[] commentBytes = reader.ReadBytes(commentLength);
|
||||
this.comment = Encoding.UTF8.GetString(commentBytes);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Write(Stream stream)
|
||||
{
|
||||
BinaryWriter writer = new BinaryWriter(stream);
|
||||
|
||||
if (this.zip64)
|
||||
{
|
||||
writer.Write(EOCD64SIG);
|
||||
writer.Write((long) EOCD64_RECORD_FIXEDSIZE);
|
||||
writer.Write(this.versionMadeBy);
|
||||
writer.Write(this.versionNeeded);
|
||||
writer.Write(this.diskNumber);
|
||||
writer.Write(this.dirStartDiskNumber);
|
||||
writer.Write(this.entriesOnDisk);
|
||||
writer.Write(this.totalEntries);
|
||||
writer.Write(this.dirSize);
|
||||
writer.Write(this.dirOffset);
|
||||
}
|
||||
else
|
||||
{
|
||||
writer.Write(EOCDSIG);
|
||||
writer.Write((ushort) Math.Min((int) UInt16.MaxValue, this.diskNumber));
|
||||
writer.Write((ushort) Math.Min((int) UInt16.MaxValue, this.dirStartDiskNumber));
|
||||
writer.Write((ushort) Math.Min((int) UInt16.MaxValue, this.entriesOnDisk));
|
||||
writer.Write((ushort) Math.Min((int) UInt16.MaxValue, this.totalEntries));
|
||||
writer.Write((uint) Math.Min((long) UInt32.MaxValue, this.dirSize));
|
||||
writer.Write((uint) Math.Min((long) UInt32.MaxValue, this.dirOffset));
|
||||
|
||||
byte[] commentBytes = (this.comment != null
|
||||
? Encoding.UTF8.GetBytes(this.comment) : new byte[0]);
|
||||
writer.Write((ushort) commentBytes.Length);
|
||||
writer.Write(commentBytes);
|
||||
}
|
||||
}
|
||||
|
||||
public int GetSize(bool zip64Size)
|
||||
{
|
||||
if (zip64Size)
|
||||
{
|
||||
return 56;
|
||||
}
|
||||
else
|
||||
{
|
||||
int commentSize = (this.comment != null
|
||||
? Encoding.UTF8.GetByteCount(this.comment) : 0);
|
||||
return 22 + commentSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class Zip64EndOfCentralDirectoryLocator
|
||||
{
|
||||
public const uint EOCDL64SIG = 0x07064B50;
|
||||
|
||||
public const uint EOCDL64_SIZE = 20;
|
||||
|
||||
public uint dirStartDiskNumber;
|
||||
public long dirOffset;
|
||||
public uint totalDisks;
|
||||
|
||||
public bool Read(Stream stream)
|
||||
{
|
||||
long startPos = stream.Position;
|
||||
if (stream.Length - startPos < EOCDL64_SIZE)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
BinaryReader reader = new BinaryReader(stream);
|
||||
uint sig = reader.ReadUInt32();
|
||||
|
||||
if (sig != EOCDL64SIG)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
this.dirStartDiskNumber = reader.ReadUInt32();
|
||||
this.dirOffset = reader.ReadInt64();
|
||||
this.totalDisks = reader.ReadUInt32();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Write(Stream stream)
|
||||
{
|
||||
BinaryWriter writer = new BinaryWriter(stream);
|
||||
writer.Write(EOCDL64SIG);
|
||||
writer.Write(this.dirStartDiskNumber);
|
||||
writer.Write(this.dirOffset);
|
||||
writer.Write(this.totalDisks);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,92 @@
|
|||
//---------------------------------------------------------------------
|
||||
// <copyright file="ZipInfo.cs" company="Outercurve Foundation">
|
||||
// Copyright (c) 2004, Outercurve Foundation.
|
||||
// This software is released under Microsoft Reciprocal License (MS-RL).
|
||||
// The license and further copyright text can be found in the file LICENSE.TXT
|
||||
// LICENSE.TXT at the root directory of the distribution.
|
||||
// </copyright>
|
||||
// <summary>
|
||||
// Part of the Deployment Tools Foundation project.
|
||||
// </summary>
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Deployment.Compression.Zip
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
/// <summary>
|
||||
/// Object representing a zip file on disk; provides access to
|
||||
/// file-based operations on the zip file.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Generally, the methods on this class are much easier to use than the
|
||||
/// stream-based interfaces provided by the <see cref="ZipEngine"/> class.
|
||||
/// </remarks>
|
||||
[Serializable]
|
||||
public class ZipInfo : ArchiveInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new CabinetInfo object representing a zip file in a specified path.
|
||||
/// </summary>
|
||||
/// <param name="path">The path to the zip file. When creating a zip file, this file does not
|
||||
/// necessarily exist yet.</param>
|
||||
public ZipInfo(string path)
|
||||
: base(path)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the CabinetInfo class with serialized data.
|
||||
/// </summary>
|
||||
/// <param name="info">The SerializationInfo that holds the serialized object data about the exception being thrown.</param>
|
||||
/// <param name="context">The StreamingContext that contains contextual information about the source or destination.</param>
|
||||
protected ZipInfo(SerializationInfo info, StreamingContext context)
|
||||
: base(info, context)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a compression engine that does the low-level work for
|
||||
/// this object.
|
||||
/// </summary>
|
||||
/// <returns>A new <see cref="ZipEngine"/> instance.</returns>
|
||||
/// <remarks>
|
||||
/// Each instance will be <see cref="CompressionEngine.Dispose()"/>d
|
||||
/// immediately after use.
|
||||
/// </remarks>
|
||||
protected override CompressionEngine CreateCompressionEngine()
|
||||
{
|
||||
return new ZipEngine();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets information about the files contained in the archive.
|
||||
/// </summary>
|
||||
/// <returns>A list of <see cref="ZipFileInfo"/> objects, each
|
||||
/// containing information about a file in the archive.</returns>
|
||||
public new IList<ZipFileInfo> GetFiles()
|
||||
{
|
||||
IList<ArchiveFileInfo> files = base.GetFiles();
|
||||
List<ZipFileInfo> zipFiles = new List<ZipFileInfo>(files.Count);
|
||||
foreach (ZipFileInfo zipFile in files) zipFiles.Add(zipFile);
|
||||
return zipFiles.AsReadOnly();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets information about the certain files contained in the archive file.
|
||||
/// </summary>
|
||||
/// <param name="searchPattern">The search string, such as
|
||||
/// "*.txt".</param>
|
||||
/// <returns>A list of <see cref="ZipFileInfo"/> objects, each containing
|
||||
/// information about a file in the archive.</returns>
|
||||
public new IList<ZipFileInfo> GetFiles(string searchPattern)
|
||||
{
|
||||
IList<ArchiveFileInfo> files = base.GetFiles(searchPattern);
|
||||
List<ZipFileInfo> zipFiles = new List<ZipFileInfo>(files.Count);
|
||||
foreach (ZipFileInfo zipFile in files) zipFiles.Add(zipFile);
|
||||
return zipFiles.AsReadOnly();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,499 @@
|
|||
//---------------------------------------------------------------------
|
||||
// <copyright file="ZipPacker.cs" company="Outercurve Foundation">
|
||||
// Copyright (c) 2004, Outercurve Foundation.
|
||||
// This software is released under Microsoft Reciprocal License (MS-RL).
|
||||
// The license and further copyright text can be found in the file LICENSE.TXT
|
||||
// LICENSE.TXT at the root directory of the distribution.
|
||||
// </copyright>
|
||||
// <summary>
|
||||
// Part of the Deployment Tools Foundation project.
|
||||
// </summary>
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Deployment.Compression.Zip
|
||||
{
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
|
||||
public partial class ZipEngine
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a zip archive or chain of zip archives.
|
||||
/// </summary>
|
||||
/// <param name="streamContext">A context interface to handle opening
|
||||
/// and closing of archive and file streams.</param>
|
||||
/// <param name="files">An array of file lists. Each list is
|
||||
/// compressed into one stream in the archive.</param>
|
||||
/// <param name="maxArchiveSize">The maximum number of bytes for one archive
|
||||
/// before the contents are chained to the next archive, or zero for unlimited
|
||||
/// archive size.</param>
|
||||
/// <exception cref="ArchiveException">The archive could not be
|
||||
/// created.</exception>
|
||||
/// <remarks>
|
||||
/// The stream context implementation may provide a mapping from the file
|
||||
/// paths within the archive to the external file paths.
|
||||
/// </remarks>
|
||||
public override void Pack(
|
||||
IPackStreamContext streamContext,
|
||||
IEnumerable<string> files,
|
||||
long maxArchiveSize)
|
||||
{
|
||||
if (streamContext == null)
|
||||
{
|
||||
throw new ArgumentNullException("streamContext");
|
||||
}
|
||||
|
||||
if (files == null)
|
||||
{
|
||||
throw new ArgumentNullException("files");
|
||||
}
|
||||
|
||||
lock (this)
|
||||
{
|
||||
Stream archiveStream = null;
|
||||
try
|
||||
{
|
||||
this.ResetProgressData();
|
||||
this.totalArchives = 1;
|
||||
|
||||
object forceZip64Value = streamContext.GetOption("forceZip64", null);
|
||||
bool forceZip64 = Convert.ToBoolean(
|
||||
forceZip64Value, CultureInfo.InvariantCulture);
|
||||
|
||||
// Count the total number of files and bytes to be compressed.
|
||||
foreach (string file in files)
|
||||
{
|
||||
FileAttributes attributes;
|
||||
DateTime lastWriteTime;
|
||||
Stream fileStream = streamContext.OpenFileReadStream(
|
||||
file,
|
||||
out attributes,
|
||||
out lastWriteTime);
|
||||
if (fileStream != null)
|
||||
{
|
||||
this.totalFileBytes += fileStream.Length;
|
||||
this.totalFiles++;
|
||||
streamContext.CloseFileReadStream(file, fileStream);
|
||||
}
|
||||
}
|
||||
|
||||
List<ZipFileHeader> fileHeaders = new List<ZipFileHeader>();
|
||||
this.currentFileNumber = -1;
|
||||
|
||||
if (this.currentArchiveName == null)
|
||||
{
|
||||
this.mainArchiveName = streamContext.GetArchiveName(0);
|
||||
this.currentArchiveName = this.mainArchiveName;
|
||||
|
||||
if (String.IsNullOrEmpty(this.currentArchiveName))
|
||||
{
|
||||
throw new FileNotFoundException("No name provided for archive.");
|
||||
}
|
||||
}
|
||||
|
||||
this.OnProgress(ArchiveProgressType.StartArchive);
|
||||
|
||||
// Compress files one by one, saving header info for each.
|
||||
foreach (string file in files)
|
||||
{
|
||||
ZipFileHeader fileHeader = this.PackOneFile(
|
||||
streamContext,
|
||||
file,
|
||||
maxArchiveSize,
|
||||
forceZip64,
|
||||
ref archiveStream);
|
||||
|
||||
if (fileHeader != null)
|
||||
{
|
||||
fileHeaders.Add(fileHeader);
|
||||
}
|
||||
|
||||
this.currentArchiveTotalBytes = (archiveStream != null ?
|
||||
archiveStream.Position : 0);
|
||||
this.currentArchiveBytesProcessed = this.currentArchiveTotalBytes;
|
||||
}
|
||||
|
||||
bool zip64 = forceZip64 || this.totalFiles > UInt16.MaxValue;
|
||||
|
||||
// Write the central directory composed of all the file headers.
|
||||
uint centralDirStartArchiveNumber = 0;
|
||||
long centralDirStartPosition = 0;
|
||||
long centralDirSize = 0;
|
||||
for (int i = 0; i < fileHeaders.Count; i++)
|
||||
{
|
||||
ZipFileHeader fileHeader = fileHeaders[i];
|
||||
|
||||
int headerSize = fileHeader.GetSize(true);
|
||||
centralDirSize += headerSize;
|
||||
|
||||
this.CheckArchiveWriteStream(
|
||||
streamContext,
|
||||
maxArchiveSize,
|
||||
headerSize,
|
||||
ref archiveStream);
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
centralDirStartArchiveNumber = (uint) this.currentArchiveNumber;
|
||||
centralDirStartPosition = archiveStream.Position;
|
||||
}
|
||||
|
||||
fileHeader.Write(archiveStream, true);
|
||||
if (fileHeader.zip64)
|
||||
{
|
||||
zip64 = true;
|
||||
}
|
||||
}
|
||||
|
||||
this.currentArchiveTotalBytes =
|
||||
(archiveStream != null ? archiveStream.Position : 0);
|
||||
this.currentArchiveBytesProcessed = this.currentArchiveTotalBytes;
|
||||
|
||||
ZipEndOfCentralDirectory eocd = new ZipEndOfCentralDirectory();
|
||||
eocd.dirStartDiskNumber = centralDirStartArchiveNumber;
|
||||
eocd.entriesOnDisk = fileHeaders.Count;
|
||||
eocd.totalEntries = fileHeaders.Count;
|
||||
eocd.dirSize = centralDirSize;
|
||||
eocd.dirOffset = centralDirStartPosition;
|
||||
eocd.comment = this.comment;
|
||||
|
||||
Zip64EndOfCentralDirectoryLocator eocdl =
|
||||
new Zip64EndOfCentralDirectoryLocator();
|
||||
|
||||
int maxFooterSize = eocd.GetSize(false);
|
||||
if (archiveStream != null && (zip64 || archiveStream.Position >
|
||||
((long) UInt32.MaxValue) - eocd.GetSize(false)))
|
||||
{
|
||||
maxFooterSize += eocd.GetSize(true) + (int)
|
||||
Zip64EndOfCentralDirectoryLocator.EOCDL64_SIZE;
|
||||
zip64 = true;
|
||||
}
|
||||
|
||||
this.CheckArchiveWriteStream(
|
||||
streamContext,
|
||||
maxArchiveSize,
|
||||
maxFooterSize,
|
||||
ref archiveStream);
|
||||
eocd.diskNumber = (uint) this.currentArchiveNumber;
|
||||
|
||||
if (zip64)
|
||||
{
|
||||
eocd.versionMadeBy = 45;
|
||||
eocd.versionNeeded = 45;
|
||||
eocd.zip64 = true;
|
||||
eocdl.dirOffset = archiveStream.Position;
|
||||
eocdl.dirStartDiskNumber = (uint) this.currentArchiveNumber;
|
||||
eocdl.totalDisks = (uint) this.currentArchiveNumber + 1;
|
||||
eocd.Write(archiveStream);
|
||||
eocdl.Write(archiveStream);
|
||||
|
||||
eocd.dirOffset = UInt32.MaxValue;
|
||||
eocd.dirStartDiskNumber = UInt16.MaxValue;
|
||||
}
|
||||
|
||||
eocd.zip64 = false;
|
||||
eocd.Write(archiveStream);
|
||||
|
||||
this.currentArchiveTotalBytes = archiveStream.Position;
|
||||
this.currentArchiveBytesProcessed = this.currentArchiveTotalBytes;
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (archiveStream != null)
|
||||
{
|
||||
streamContext.CloseArchiveWriteStream(
|
||||
this.currentArchiveNumber, this.mainArchiveName, archiveStream);
|
||||
this.OnProgress(ArchiveProgressType.FinishArchive);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Moves to the next archive in the sequence if necessary.
|
||||
/// </summary>
|
||||
private void CheckArchiveWriteStream(
|
||||
IPackStreamContext streamContext,
|
||||
long maxArchiveSize,
|
||||
long requiredSize,
|
||||
ref Stream archiveStream)
|
||||
{
|
||||
if (archiveStream != null &&
|
||||
archiveStream.Length > 0 && maxArchiveSize > 0)
|
||||
{
|
||||
long sizeRemaining = maxArchiveSize - archiveStream.Length;
|
||||
if (sizeRemaining < requiredSize)
|
||||
{
|
||||
string nextArchiveName = streamContext.GetArchiveName(
|
||||
this.currentArchiveNumber + 1);
|
||||
|
||||
if (String.IsNullOrEmpty(nextArchiveName))
|
||||
{
|
||||
throw new FileNotFoundException("No name provided for archive #" +
|
||||
this.currentArchiveNumber + 1);
|
||||
}
|
||||
|
||||
this.currentArchiveTotalBytes = archiveStream.Position;
|
||||
this.currentArchiveBytesProcessed = this.currentArchiveTotalBytes;
|
||||
|
||||
streamContext.CloseArchiveWriteStream(
|
||||
this.currentArchiveNumber,
|
||||
nextArchiveName,
|
||||
archiveStream);
|
||||
archiveStream = null;
|
||||
|
||||
this.OnProgress(ArchiveProgressType.FinishArchive);
|
||||
|
||||
this.currentArchiveNumber++;
|
||||
this.totalArchives++;
|
||||
this.currentArchiveBytesProcessed = 0;
|
||||
this.currentArchiveTotalBytes = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (archiveStream == null)
|
||||
{
|
||||
if (this.currentArchiveNumber > 0)
|
||||
{
|
||||
this.OnProgress(ArchiveProgressType.StartArchive);
|
||||
}
|
||||
|
||||
archiveStream = streamContext.OpenArchiveWriteStream(
|
||||
this.currentArchiveNumber, this.mainArchiveName, true, this);
|
||||
|
||||
if (archiveStream == null)
|
||||
{
|
||||
throw new FileNotFoundException("Stream not provided for archive #" +
|
||||
this.currentArchiveNumber);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds one file to a zip archive in the process of being created.
|
||||
/// </summary>
|
||||
private ZipFileHeader PackOneFile(
|
||||
IPackStreamContext streamContext,
|
||||
string file,
|
||||
long maxArchiveSize,
|
||||
bool forceZip64,
|
||||
ref Stream archiveStream)
|
||||
{
|
||||
Stream fileStream = null;
|
||||
int headerArchiveNumber = 0;
|
||||
try
|
||||
{
|
||||
// TODO: call GetOption to get compression method for the specific file
|
||||
ZipCompressionMethod compressionMethod = ZipCompressionMethod.Deflate;
|
||||
if (this.CompressionLevel == CompressionLevel.None)
|
||||
{
|
||||
compressionMethod = ZipCompressionMethod.Store;
|
||||
}
|
||||
|
||||
Converter<Stream, Stream> compressionStreamCreator;
|
||||
if (!ZipEngine.compressionStreamCreators.TryGetValue(
|
||||
compressionMethod, out compressionStreamCreator))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
FileAttributes attributes;
|
||||
DateTime lastWriteTime;
|
||||
fileStream = streamContext.OpenFileReadStream(
|
||||
file, out attributes, out lastWriteTime);
|
||||
if (fileStream == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
this.currentFileName = file;
|
||||
this.currentFileNumber++;
|
||||
|
||||
this.currentFileTotalBytes = fileStream.Length;
|
||||
this.currentFileBytesProcessed = 0;
|
||||
this.OnProgress(ArchiveProgressType.StartFile);
|
||||
|
||||
ZipFileInfo fileInfo = new ZipFileInfo(
|
||||
file,
|
||||
this.currentArchiveNumber,
|
||||
attributes,
|
||||
lastWriteTime,
|
||||
fileStream.Length,
|
||||
0,
|
||||
compressionMethod);
|
||||
|
||||
bool zip64 = forceZip64 || fileStream.Length >= (long) UInt32.MaxValue;
|
||||
ZipFileHeader fileHeader = new ZipFileHeader(fileInfo, zip64);
|
||||
|
||||
this.CheckArchiveWriteStream(
|
||||
streamContext,
|
||||
maxArchiveSize,
|
||||
fileHeader.GetSize(false),
|
||||
ref archiveStream);
|
||||
|
||||
long headerPosition = archiveStream.Position;
|
||||
fileHeader.Write(archiveStream, false);
|
||||
headerArchiveNumber = this.currentArchiveNumber;
|
||||
|
||||
uint crc;
|
||||
long bytesWritten = this.PackFileBytes(
|
||||
streamContext,
|
||||
fileStream,
|
||||
maxArchiveSize,
|
||||
compressionStreamCreator,
|
||||
ref archiveStream,
|
||||
out crc);
|
||||
|
||||
fileHeader.Update(
|
||||
bytesWritten,
|
||||
fileStream.Length,
|
||||
crc,
|
||||
headerPosition,
|
||||
headerArchiveNumber);
|
||||
|
||||
streamContext.CloseFileReadStream(file, fileStream);
|
||||
fileStream = null;
|
||||
|
||||
// Go back and rewrite the updated file header.
|
||||
if (this.currentArchiveNumber == headerArchiveNumber)
|
||||
{
|
||||
long fileEndPosition = archiveStream.Position;
|
||||
archiveStream.Seek(headerPosition, SeekOrigin.Begin);
|
||||
fileHeader.Write(archiveStream, false);
|
||||
archiveStream.Seek(fileEndPosition, SeekOrigin.Begin);
|
||||
}
|
||||
else
|
||||
{
|
||||
// The file spanned archives, so temporarily reopen
|
||||
// the archive where it started.
|
||||
string headerArchiveName = streamContext.GetArchiveName(
|
||||
headerArchiveNumber + 1);
|
||||
Stream headerStream = null;
|
||||
try
|
||||
{
|
||||
headerStream = streamContext.OpenArchiveWriteStream(
|
||||
headerArchiveNumber, headerArchiveName, false, this);
|
||||
headerStream.Seek(headerPosition, SeekOrigin.Begin);
|
||||
fileHeader.Write(headerStream, false);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (headerStream != null)
|
||||
{
|
||||
streamContext.CloseArchiveWriteStream(
|
||||
headerArchiveNumber, headerArchiveName, headerStream);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.OnProgress(ArchiveProgressType.FinishFile);
|
||||
|
||||
return fileHeader;
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (fileStream != null)
|
||||
{
|
||||
streamContext.CloseFileReadStream(
|
||||
this.currentFileName, fileStream);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes compressed bytes of one file to the archive,
|
||||
/// keeping track of the CRC and number of bytes written.
|
||||
/// </summary>
|
||||
private long PackFileBytes(
|
||||
IPackStreamContext streamContext,
|
||||
Stream fileStream,
|
||||
long maxArchiveSize,
|
||||
Converter<Stream, Stream> compressionStreamCreator,
|
||||
ref Stream archiveStream,
|
||||
out uint crc)
|
||||
{
|
||||
long writeStartPosition = archiveStream.Position;
|
||||
long bytesWritten = 0;
|
||||
CrcStream fileCrcStream = new CrcStream(fileStream);
|
||||
|
||||
ConcatStream concatStream = new ConcatStream(
|
||||
delegate(ConcatStream s)
|
||||
{
|
||||
Stream sourceStream = s.Source;
|
||||
bytesWritten += sourceStream.Position - writeStartPosition;
|
||||
|
||||
this.CheckArchiveWriteStream(
|
||||
streamContext,
|
||||
maxArchiveSize,
|
||||
1,
|
||||
ref sourceStream);
|
||||
|
||||
writeStartPosition = sourceStream.Position;
|
||||
s.Source = sourceStream;
|
||||
});
|
||||
|
||||
concatStream.Source = archiveStream;
|
||||
|
||||
if (maxArchiveSize > 0)
|
||||
{
|
||||
concatStream.SetLength(maxArchiveSize);
|
||||
}
|
||||
|
||||
Stream compressionStream = compressionStreamCreator(concatStream);
|
||||
|
||||
try
|
||||
{
|
||||
byte[] buf = new byte[4096];
|
||||
long bytesRemaining = fileStream.Length;
|
||||
int counter = 0;
|
||||
while (bytesRemaining > 0)
|
||||
{
|
||||
int count = (int) Math.Min(
|
||||
bytesRemaining, (long) buf.Length);
|
||||
|
||||
count = fileCrcStream.Read(buf, 0, count);
|
||||
if (count <= 0)
|
||||
{
|
||||
throw new ZipException(
|
||||
"Failed to read file: " + this.currentFileName);
|
||||
}
|
||||
|
||||
compressionStream.Write(buf, 0, count);
|
||||
bytesRemaining -= count;
|
||||
|
||||
this.fileBytesProcessed += count;
|
||||
this.currentFileBytesProcessed += count;
|
||||
this.currentArchiveTotalBytes = concatStream.Source.Position;
|
||||
this.currentArchiveBytesProcessed = this.currentArchiveTotalBytes;
|
||||
|
||||
if (++counter % 16 == 0) // Report every 64K
|
||||
{
|
||||
this.OnProgress(ArchiveProgressType.PartialFile);
|
||||
}
|
||||
}
|
||||
|
||||
if (compressionStream is DeflateStream)
|
||||
{
|
||||
compressionStream.Close();
|
||||
}
|
||||
else
|
||||
{
|
||||
compressionStream.Flush();
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
archiveStream = concatStream.Source;
|
||||
}
|
||||
|
||||
bytesWritten += archiveStream.Position - writeStartPosition;
|
||||
|
||||
crc = fileCrcStream.Crc;
|
||||
|
||||
return bytesWritten;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,346 @@
|
|||
//---------------------------------------------------------------------
|
||||
// <copyright file="ZipUnpacker.cs" company="Outercurve Foundation">
|
||||
// Copyright (c) 2004, Outercurve Foundation.
|
||||
// This software is released under Microsoft Reciprocal License (MS-RL).
|
||||
// The license and further copyright text can be found in the file LICENSE.TXT
|
||||
// LICENSE.TXT at the root directory of the distribution.
|
||||
// </copyright>
|
||||
// <summary>
|
||||
// Part of the Deployment Tools Foundation project.
|
||||
// </summary>
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Deployment.Compression.Zip
|
||||
{
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Collections.Generic;
|
||||
|
||||
public partial class ZipEngine
|
||||
{
|
||||
/// <summary>
|
||||
/// Extracts files from a zip archive or archive chain.
|
||||
/// </summary>
|
||||
/// <param name="streamContext">A context interface to handle opening
|
||||
/// and closing of archive and file streams.</param>
|
||||
/// <param name="fileFilter">An optional predicate that can determine
|
||||
/// which files to process.</param>
|
||||
/// <exception cref="ArchiveException">The archive provided
|
||||
/// by the stream context is not valid.</exception>
|
||||
/// <remarks>
|
||||
/// The <paramref name="fileFilter"/> predicate takes an internal file
|
||||
/// path and returns true to include the file or false to exclude it.
|
||||
/// </remarks>
|
||||
public override void Unpack(
|
||||
IUnpackStreamContext streamContext,
|
||||
Predicate<string> fileFilter)
|
||||
{
|
||||
if (streamContext == null)
|
||||
{
|
||||
throw new ArgumentNullException("streamContext");
|
||||
}
|
||||
|
||||
lock (this)
|
||||
{
|
||||
IList<ZipFileHeader> allHeaders = this.GetCentralDirectory(streamContext);
|
||||
if (allHeaders == null)
|
||||
{
|
||||
throw new ZipException("Zip central directory not found.");
|
||||
}
|
||||
|
||||
IList<ZipFileHeader> headers = new List<ZipFileHeader>(allHeaders.Count);
|
||||
foreach (ZipFileHeader header in allHeaders)
|
||||
{
|
||||
if (!header.IsDirectory &&
|
||||
(fileFilter == null || fileFilter(header.fileName)))
|
||||
{
|
||||
headers.Add(header);
|
||||
}
|
||||
}
|
||||
|
||||
this.ResetProgressData();
|
||||
|
||||
// Count the total number of files and bytes to be compressed.
|
||||
this.totalFiles = headers.Count;
|
||||
foreach (ZipFileHeader header in headers)
|
||||
{
|
||||
long compressedSize;
|
||||
long uncompressedSize;
|
||||
long localHeaderOffset;
|
||||
int archiveNumber;
|
||||
uint crc;
|
||||
header.GetZip64Fields(
|
||||
out compressedSize,
|
||||
out uncompressedSize,
|
||||
out localHeaderOffset,
|
||||
out archiveNumber,
|
||||
out crc);
|
||||
|
||||
this.totalFileBytes += uncompressedSize;
|
||||
if (archiveNumber >= this.totalArchives)
|
||||
{
|
||||
this.totalArchives = (short) (archiveNumber + 1);
|
||||
}
|
||||
}
|
||||
|
||||
this.currentArchiveNumber = -1;
|
||||
this.currentFileNumber = -1;
|
||||
Stream archiveStream = null;
|
||||
try
|
||||
{
|
||||
foreach (ZipFileHeader header in headers)
|
||||
{
|
||||
this.currentFileNumber++;
|
||||
this.UnpackOneFile(streamContext, header, ref archiveStream);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (archiveStream != null)
|
||||
{
|
||||
streamContext.CloseArchiveReadStream(
|
||||
0, String.Empty, archiveStream);
|
||||
this.currentArchiveNumber--;
|
||||
this.OnProgress(ArchiveProgressType.FinishArchive);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unpacks a single file from an archive or archive chain.
|
||||
/// </summary>
|
||||
private void UnpackOneFile(
|
||||
IUnpackStreamContext streamContext,
|
||||
ZipFileHeader header,
|
||||
ref Stream archiveStream)
|
||||
{
|
||||
ZipFileInfo fileInfo = null;
|
||||
Stream fileStream = null;
|
||||
try
|
||||
{
|
||||
Converter<Stream, Stream> compressionStreamCreator;
|
||||
if (!ZipEngine.decompressionStreamCreators.TryGetValue(
|
||||
header.compressionMethod, out compressionStreamCreator))
|
||||
{
|
||||
// Silently skip files of an unsupported compression method.
|
||||
return;
|
||||
}
|
||||
|
||||
long compressedSize;
|
||||
long uncompressedSize;
|
||||
long localHeaderOffset;
|
||||
int archiveNumber;
|
||||
uint crc;
|
||||
header.GetZip64Fields(
|
||||
out compressedSize,
|
||||
out uncompressedSize,
|
||||
out localHeaderOffset,
|
||||
out archiveNumber,
|
||||
out crc);
|
||||
|
||||
if (this.currentArchiveNumber != archiveNumber + 1)
|
||||
{
|
||||
if (archiveStream != null)
|
||||
{
|
||||
streamContext.CloseArchiveReadStream(
|
||||
this.currentArchiveNumber,
|
||||
String.Empty,
|
||||
archiveStream);
|
||||
archiveStream = null;
|
||||
|
||||
this.OnProgress(ArchiveProgressType.FinishArchive);
|
||||
this.currentArchiveName = null;
|
||||
}
|
||||
|
||||
this.currentArchiveNumber = (short) (archiveNumber + 1);
|
||||
this.currentArchiveBytesProcessed = 0;
|
||||
this.currentArchiveTotalBytes = 0;
|
||||
|
||||
archiveStream = this.OpenArchive(
|
||||
streamContext, this.currentArchiveNumber);
|
||||
|
||||
FileStream archiveFileStream = archiveStream as FileStream;
|
||||
this.currentArchiveName = (archiveFileStream != null ?
|
||||
Path.GetFileName(archiveFileStream.Name) : null);
|
||||
|
||||
this.currentArchiveTotalBytes = archiveStream.Length;
|
||||
this.currentArchiveNumber--;
|
||||
this.OnProgress(ArchiveProgressType.StartArchive);
|
||||
this.currentArchiveNumber++;
|
||||
}
|
||||
|
||||
archiveStream.Seek(localHeaderOffset, SeekOrigin.Begin);
|
||||
|
||||
ZipFileHeader localHeader = new ZipFileHeader();
|
||||
if (!localHeader.Read(archiveStream, false) ||
|
||||
!ZipEngine.AreFilePathsEqual(localHeader.fileName, header.fileName))
|
||||
{
|
||||
string msg = "Could not read file: " + header.fileName;
|
||||
throw new ZipException(msg);
|
||||
}
|
||||
|
||||
fileInfo = header.ToZipFileInfo();
|
||||
|
||||
fileStream = streamContext.OpenFileWriteStream(
|
||||
fileInfo.FullName,
|
||||
fileInfo.Length,
|
||||
fileInfo.LastWriteTime);
|
||||
|
||||
if (fileStream != null)
|
||||
{
|
||||
this.currentFileName = header.fileName;
|
||||
this.currentFileBytesProcessed = 0;
|
||||
this.currentFileTotalBytes = fileInfo.Length;
|
||||
this.currentArchiveNumber--;
|
||||
this.OnProgress(ArchiveProgressType.StartFile);
|
||||
this.currentArchiveNumber++;
|
||||
|
||||
this.UnpackFileBytes(
|
||||
streamContext,
|
||||
fileInfo.FullName,
|
||||
fileInfo.CompressedLength,
|
||||
fileInfo.Length,
|
||||
header.crc32,
|
||||
fileStream,
|
||||
compressionStreamCreator,
|
||||
ref archiveStream);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (fileStream != null)
|
||||
{
|
||||
streamContext.CloseFileWriteStream(
|
||||
fileInfo.FullName,
|
||||
fileStream,
|
||||
fileInfo.Attributes,
|
||||
fileInfo.LastWriteTime);
|
||||
|
||||
this.currentArchiveNumber--;
|
||||
this.OnProgress(ArchiveProgressType.FinishFile);
|
||||
this.currentArchiveNumber++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares two internal file paths while ignoring case and slash differences.
|
||||
/// </summary>
|
||||
/// <param name="path1">The first path to compare.</param>
|
||||
/// <param name="path2">The second path to compare.</param>
|
||||
/// <returns>True if the paths are equivalent.</returns>
|
||||
private static bool AreFilePathsEqual(string path1, string path2)
|
||||
{
|
||||
path1 = path1.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar);
|
||||
path2 = path2.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar);
|
||||
return String.Compare(path1, path2, StringComparison.OrdinalIgnoreCase) == 0;
|
||||
}
|
||||
|
||||
private Stream OpenArchive(IUnpackStreamContext streamContext, int archiveNumber)
|
||||
{
|
||||
Stream archiveStream = streamContext.OpenArchiveReadStream(
|
||||
archiveNumber, String.Empty, this);
|
||||
if (archiveStream == null && archiveNumber != 0)
|
||||
{
|
||||
archiveStream = streamContext.OpenArchiveReadStream(
|
||||
0, String.Empty, this);
|
||||
}
|
||||
|
||||
if (archiveStream == null)
|
||||
{
|
||||
throw new FileNotFoundException("Archive stream not provided.");
|
||||
}
|
||||
|
||||
return archiveStream;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decompresses bytes for one file from an archive or archive chain,
|
||||
/// checking the crc at the end.
|
||||
/// </summary>
|
||||
private void UnpackFileBytes(
|
||||
IUnpackStreamContext streamContext,
|
||||
string fileName,
|
||||
long compressedSize,
|
||||
long uncompressedSize,
|
||||
uint crc,
|
||||
Stream fileStream,
|
||||
Converter<Stream, Stream> compressionStreamCreator,
|
||||
ref Stream archiveStream)
|
||||
{
|
||||
CrcStream crcStream = new CrcStream(fileStream);
|
||||
|
||||
ConcatStream concatStream = new ConcatStream(
|
||||
delegate(ConcatStream s)
|
||||
{
|
||||
this.currentArchiveBytesProcessed = s.Source.Position;
|
||||
streamContext.CloseArchiveReadStream(
|
||||
this.currentArchiveNumber,
|
||||
String.Empty,
|
||||
s.Source);
|
||||
|
||||
this.currentArchiveNumber--;
|
||||
this.OnProgress(ArchiveProgressType.FinishArchive);
|
||||
this.currentArchiveNumber += 2;
|
||||
this.currentArchiveName = null;
|
||||
this.currentArchiveBytesProcessed = 0;
|
||||
this.currentArchiveTotalBytes = 0;
|
||||
|
||||
s.Source = this.OpenArchive(streamContext, this.currentArchiveNumber);
|
||||
|
||||
FileStream archiveFileStream = s.Source as FileStream;
|
||||
this.currentArchiveName = (archiveFileStream != null ?
|
||||
Path.GetFileName(archiveFileStream.Name) : null);
|
||||
|
||||
this.currentArchiveTotalBytes = s.Source.Length;
|
||||
this.currentArchiveNumber--;
|
||||
this.OnProgress(ArchiveProgressType.StartArchive);
|
||||
this.currentArchiveNumber++;
|
||||
});
|
||||
|
||||
concatStream.Source = archiveStream;
|
||||
concatStream.SetLength(compressedSize);
|
||||
|
||||
Stream decompressionStream = compressionStreamCreator(concatStream);
|
||||
|
||||
try
|
||||
{
|
||||
byte[] buf = new byte[4096];
|
||||
long bytesRemaining = uncompressedSize;
|
||||
int counter = 0;
|
||||
while (bytesRemaining > 0)
|
||||
{
|
||||
int count = (int) Math.Min(buf.Length, bytesRemaining);
|
||||
count = decompressionStream.Read(buf, 0, count);
|
||||
crcStream.Write(buf, 0, count);
|
||||
bytesRemaining -= count;
|
||||
|
||||
this.fileBytesProcessed += count;
|
||||
this.currentFileBytesProcessed += count;
|
||||
this.currentArchiveBytesProcessed = concatStream.Source.Position;
|
||||
|
||||
if (++counter % 16 == 0) // Report every 64K
|
||||
{
|
||||
this.currentArchiveNumber--;
|
||||
this.OnProgress(ArchiveProgressType.PartialFile);
|
||||
this.currentArchiveNumber++;
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
archiveStream = concatStream.Source;
|
||||
}
|
||||
|
||||
crcStream.Flush();
|
||||
|
||||
if (crcStream.Crc != crc)
|
||||
{
|
||||
throw new ZipException("CRC check failed for file: " + fileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
//---------------------------------------------------------------------
|
||||
// <copyright file="ArchiveException.cs" company="Outercurve Foundation">
|
||||
// Copyright (c) 2004, Outercurve Foundation.
|
||||
// This software is released under Microsoft Reciprocal License (MS-RL).
|
||||
// The license and further copyright text can be found in the file LICENSE.TXT
|
||||
// LICENSE.TXT at the root directory of the distribution.
|
||||
// </copyright>
|
||||
// <summary>
|
||||
// Part of the Deployment Tools Foundation project.
|
||||
// </summary>
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Deployment.Compression
|
||||
{
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
/// <summary>
|
||||
/// Base exception class for compression operations. Compression libraries should
|
||||
/// derive subclass exceptions with more specific error information relevent to the
|
||||
/// file format.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class ArchiveException : IOException
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new ArchiveException with a specified error message and a reference to the
|
||||
/// inner exception that is the cause of this exception.
|
||||
/// </summary>
|
||||
/// <param name="message">The message that describes the error.</param>
|
||||
/// <param name="innerException">The exception that is the cause of the current exception. If the
|
||||
/// innerException parameter is not a null reference (Nothing in Visual Basic), the current exception
|
||||
/// is raised in a catch block that handles the inner exception.</param>
|
||||
public ArchiveException(string message, Exception innerException)
|
||||
: base(message, innerException)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new ArchiveException with a specified error message.
|
||||
/// </summary>
|
||||
/// <param name="message">The message that describes the error.</param>
|
||||
public ArchiveException(string message)
|
||||
: this(message, null)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new ArchiveException.
|
||||
/// </summary>
|
||||
public ArchiveException()
|
||||
: this(null, null)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the ArchiveException class with serialized data.
|
||||
/// </summary>
|
||||
/// <param name="info">The SerializationInfo that holds the serialized object data about the exception being thrown.</param>
|
||||
/// <param name="context">The StreamingContext that contains contextual information about the source or destination.</param>
|
||||
protected ArchiveException(SerializationInfo info, StreamingContext context)
|
||||
: base(info, context)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,442 @@
|
|||
//---------------------------------------------------------------------
|
||||
// <copyright file="ArchiveFileInfo.cs" company="Outercurve Foundation">
|
||||
// Copyright (c) 2004, Outercurve Foundation.
|
||||
// This software is released under Microsoft Reciprocal License (MS-RL).
|
||||
// The license and further copyright text can be found in the file LICENSE.TXT
|
||||
// LICENSE.TXT at the root directory of the distribution.
|
||||
// </copyright>
|
||||
// <summary>
|
||||
// Part of the Deployment Tools Foundation project.
|
||||
// </summary>
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Deployment.Compression
|
||||
{
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Security.Permissions;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
/// <summary>
|
||||
/// Abstract object representing a compressed file within an archive;
|
||||
/// provides operations for getting the file properties and unpacking
|
||||
/// the file.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public abstract class ArchiveFileInfo : FileSystemInfo
|
||||
{
|
||||
private ArchiveInfo archiveInfo;
|
||||
private string name;
|
||||
private string path;
|
||||
|
||||
private bool initialized;
|
||||
private bool exists;
|
||||
private int archiveNumber;
|
||||
private FileAttributes attributes;
|
||||
private DateTime lastWriteTime;
|
||||
private long length;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new ArchiveFileInfo object representing a file within
|
||||
/// an archive in a specified path.
|
||||
/// </summary>
|
||||
/// <param name="archiveInfo">An object representing the archive
|
||||
/// containing the file.</param>
|
||||
/// <param name="filePath">The path to the file within the archive.
|
||||
/// Usually, this is a simple file name, but if the archive contains
|
||||
/// a directory structure this may include the directory.</param>
|
||||
protected ArchiveFileInfo(ArchiveInfo archiveInfo, string filePath)
|
||||
: base()
|
||||
{
|
||||
if (filePath == null)
|
||||
{
|
||||
throw new ArgumentNullException("filePath");
|
||||
}
|
||||
|
||||
this.Archive = archiveInfo;
|
||||
|
||||
this.name = System.IO.Path.GetFileName(filePath);
|
||||
this.path = System.IO.Path.GetDirectoryName(filePath);
|
||||
|
||||
this.attributes = FileAttributes.Normal;
|
||||
this.lastWriteTime = DateTime.MinValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new ArchiveFileInfo object with all parameters specified;
|
||||
/// used by subclasses when reading the metadata out of an archive.
|
||||
/// </summary>
|
||||
/// <param name="filePath">The internal path and name of the file in
|
||||
/// the archive.</param>
|
||||
/// <param name="archiveNumber">The archive number where the file
|
||||
/// starts.</param>
|
||||
/// <param name="attributes">The stored attributes of the file.</param>
|
||||
/// <param name="lastWriteTime">The stored last write time of the
|
||||
/// file.</param>
|
||||
/// <param name="length">The uncompressed size of the file.</param>
|
||||
protected ArchiveFileInfo(
|
||||
string filePath,
|
||||
int archiveNumber,
|
||||
FileAttributes attributes,
|
||||
DateTime lastWriteTime,
|
||||
long length)
|
||||
: this(null, filePath)
|
||||
{
|
||||
this.exists = true;
|
||||
this.archiveNumber = archiveNumber;
|
||||
this.attributes = attributes;
|
||||
this.lastWriteTime = lastWriteTime;
|
||||
this.length = length;
|
||||
this.initialized = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the ArchiveFileInfo class with
|
||||
/// serialized data.
|
||||
/// </summary>
|
||||
/// <param name="info">The SerializationInfo that holds the serialized
|
||||
/// object data about the exception being thrown.</param>
|
||||
/// <param name="context">The StreamingContext that contains contextual
|
||||
/// information about the source or destination.</param>
|
||||
protected ArchiveFileInfo(SerializationInfo info, StreamingContext context)
|
||||
: base(info, context)
|
||||
{
|
||||
this.archiveInfo = (ArchiveInfo) info.GetValue(
|
||||
"archiveInfo", typeof(ArchiveInfo));
|
||||
this.name = info.GetString("name");
|
||||
this.path = info.GetString("path");
|
||||
this.initialized = info.GetBoolean("initialized");
|
||||
this.exists = info.GetBoolean("exists");
|
||||
this.archiveNumber = info.GetInt32("archiveNumber");
|
||||
this.attributes = (FileAttributes) info.GetValue(
|
||||
"attributes", typeof(FileAttributes));
|
||||
this.lastWriteTime = info.GetDateTime("lastWriteTime");
|
||||
this.length = info.GetInt64("length");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the file.
|
||||
/// </summary>
|
||||
/// <value>The name of the file, not including any path.</value>
|
||||
public override string Name
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.name;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the internal path of the file in the archive.
|
||||
/// </summary>
|
||||
/// <value>The internal path of the file in the archive, not including
|
||||
/// the file name.</value>
|
||||
public string Path
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.path;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the full path to the file.
|
||||
/// </summary>
|
||||
/// <value>The full path to the file, including the full path to the
|
||||
/// archive, the internal path in the archive, and the file name.</value>
|
||||
/// <remarks>
|
||||
/// For example, the path <c>"C:\archive.cab\file.txt"</c> refers to
|
||||
/// a file "file.txt" inside the archive "archive.cab".
|
||||
/// </remarks>
|
||||
public override string FullName
|
||||
{
|
||||
get
|
||||
{
|
||||
string fullName = System.IO.Path.Combine(this.Path, this.Name);
|
||||
|
||||
if (this.Archive != null)
|
||||
{
|
||||
fullName = System.IO.Path.Combine(this.ArchiveName, fullName);
|
||||
}
|
||||
|
||||
return fullName;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the archive that contains this file.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The ArchiveInfo instance that retrieved this file information -- this
|
||||
/// may be null if the ArchiveFileInfo object was returned directly from
|
||||
/// a stream.
|
||||
/// </value>
|
||||
public ArchiveInfo Archive
|
||||
{
|
||||
get
|
||||
{
|
||||
return (ArchiveInfo) this.archiveInfo;
|
||||
}
|
||||
|
||||
internal set
|
||||
{
|
||||
this.archiveInfo = value;
|
||||
|
||||
// protected instance members inherited from FileSystemInfo:
|
||||
this.OriginalPath = (value != null ? value.FullName : null);
|
||||
this.FullPath = this.OriginalPath;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the full path of the archive that contains this file.
|
||||
/// </summary>
|
||||
/// <value>The full path of the archive that contains this file.</value>
|
||||
public string ArchiveName
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.Archive != null ? this.Archive.FullName : null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of the archive where this file starts.
|
||||
/// </summary>
|
||||
/// <value>The number of the archive where this file starts.</value>
|
||||
/// <remarks>A single archive or the first archive in a chain is
|
||||
/// numbered 0.</remarks>
|
||||
public int ArchiveNumber
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.archiveNumber;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the file exists within the archive.
|
||||
/// </summary>
|
||||
/// <value>True if the file exists, false otherwise.</value>
|
||||
public override bool Exists
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!this.initialized)
|
||||
{
|
||||
this.Refresh();
|
||||
}
|
||||
|
||||
return this.exists;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the uncompressed size of the file.
|
||||
/// </summary>
|
||||
/// <value>The uncompressed size of the file in bytes.</value>
|
||||
public long Length
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!this.initialized)
|
||||
{
|
||||
this.Refresh();
|
||||
}
|
||||
|
||||
return this.length;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the attributes of the file.
|
||||
/// </summary>
|
||||
/// <value>The attributes of the file as stored in the archive.</value>
|
||||
public new FileAttributes Attributes
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!this.initialized)
|
||||
{
|
||||
this.Refresh();
|
||||
}
|
||||
|
||||
return this.attributes;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the last modification time of the file.
|
||||
/// </summary>
|
||||
/// <value>The last modification time of the file as stored in the
|
||||
/// archive.</value>
|
||||
public new DateTime LastWriteTime
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!this.initialized)
|
||||
{
|
||||
this.Refresh();
|
||||
}
|
||||
|
||||
return this.lastWriteTime;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the SerializationInfo with information about the archive.
|
||||
/// </summary>
|
||||
/// <param name="info">The SerializationInfo that holds the serialized
|
||||
/// object data.</param>
|
||||
/// <param name="context">The StreamingContext that contains contextual
|
||||
/// information about the source or destination.</param>
|
||||
[SecurityPermission(SecurityAction.Demand, SerializationFormatter = true)]
|
||||
public override void GetObjectData(
|
||||
SerializationInfo info, StreamingContext context)
|
||||
{
|
||||
base.GetObjectData(info, context);
|
||||
info.AddValue("archiveInfo", this.archiveInfo);
|
||||
info.AddValue("name", this.name);
|
||||
info.AddValue("path", this.path);
|
||||
info.AddValue("initialized", this.initialized);
|
||||
info.AddValue("exists", this.exists);
|
||||
info.AddValue("archiveNumber", this.archiveNumber);
|
||||
info.AddValue("attributes", this.attributes);
|
||||
info.AddValue("lastWriteTime", this.lastWriteTime);
|
||||
info.AddValue("length", this.length);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the full path to the file.
|
||||
/// </summary>
|
||||
/// <returns>The same as <see cref="FullName"/></returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return this.FullName;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes the file. NOT SUPPORTED.
|
||||
/// </summary>
|
||||
/// <exception cref="NotSupportedException">Files cannot be deleted
|
||||
/// from an existing archive.</exception>
|
||||
public override void Delete()
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Refreshes the attributes and other cached information about the file,
|
||||
/// by re-reading the information from the archive.
|
||||
/// </summary>
|
||||
public new void Refresh()
|
||||
{
|
||||
base.Refresh();
|
||||
|
||||
if (this.Archive != null)
|
||||
{
|
||||
string filePath = System.IO.Path.Combine(this.Path, this.Name);
|
||||
ArchiveFileInfo updatedFile = this.Archive.GetFile(filePath);
|
||||
if (updatedFile == null)
|
||||
{
|
||||
throw new FileNotFoundException(
|
||||
"File not found in archive.", filePath);
|
||||
}
|
||||
|
||||
this.Refresh(updatedFile);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extracts the file.
|
||||
/// </summary>
|
||||
/// <param name="destFileName">The destination path where the file
|
||||
/// will be extracted.</param>
|
||||
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "dest")]
|
||||
public void CopyTo(string destFileName)
|
||||
{
|
||||
this.CopyTo(destFileName, false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extracts the file, optionally overwriting any existing file.
|
||||
/// </summary>
|
||||
/// <param name="destFileName">The destination path where the file
|
||||
/// will be extracted.</param>
|
||||
/// <param name="overwrite">If true, <paramref name="destFileName"/>
|
||||
/// will be overwritten if it exists.</param>
|
||||
/// <exception cref="IOException"><paramref name="overwrite"/> is false
|
||||
/// and <paramref name="destFileName"/> exists.</exception>
|
||||
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "dest")]
|
||||
public void CopyTo(string destFileName, bool overwrite)
|
||||
{
|
||||
if (destFileName == null)
|
||||
{
|
||||
throw new ArgumentNullException("destFileName");
|
||||
}
|
||||
|
||||
if (!overwrite && File.Exists(destFileName))
|
||||
{
|
||||
throw new IOException();
|
||||
}
|
||||
|
||||
if (this.Archive == null)
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
this.Archive.UnpackFile(
|
||||
System.IO.Path.Combine(this.Path, this.Name), destFileName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens the archive file for reading without actually extracting the
|
||||
/// file to disk.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A stream for reading directly from the packed file. Like any stream
|
||||
/// this should be closed/disposed as soon as it is no longer needed.
|
||||
/// </returns>
|
||||
public Stream OpenRead()
|
||||
{
|
||||
return this.Archive.OpenRead(System.IO.Path.Combine(this.Path, this.Name));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens the archive file reading text with UTF-8 encoding without
|
||||
/// actually extracting the file to disk.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A reader for reading text directly from the packed file. Like any reader
|
||||
/// this should be closed/disposed as soon as it is no longer needed.
|
||||
/// </returns>
|
||||
/// <remarks>
|
||||
/// To open an archived text file with different encoding, use the
|
||||
/// <see cref="OpenRead" /> method and pass the returned stream to one of
|
||||
/// the <see cref="StreamReader" /> constructor overloads.
|
||||
/// </remarks>
|
||||
public StreamReader OpenText()
|
||||
{
|
||||
return this.Archive.OpenText(System.IO.Path.Combine(this.Path, this.Name));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Refreshes the information in this object with new data retrieved
|
||||
/// from an archive.
|
||||
/// </summary>
|
||||
/// <param name="newFileInfo">Fresh instance for the same file just
|
||||
/// read from the archive.</param>
|
||||
/// <remarks>
|
||||
/// Subclasses may override this method to refresh sublcass fields.
|
||||
/// However they should always call the base implementation first.
|
||||
/// </remarks>
|
||||
protected virtual void Refresh(ArchiveFileInfo newFileInfo)
|
||||
{
|
||||
this.exists = newFileInfo.exists;
|
||||
this.length = newFileInfo.length;
|
||||
this.attributes = newFileInfo.attributes;
|
||||
this.lastWriteTime = newFileInfo.lastWriteTime;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,662 @@
|
|||
//---------------------------------------------------------------------
|
||||
// <copyright file="ArchiveFileStreamContext.cs" company="Outercurve Foundation">
|
||||
// Copyright (c) 2004, Outercurve Foundation.
|
||||
// This software is released under Microsoft Reciprocal License (MS-RL).
|
||||
// The license and further copyright text can be found in the file LICENSE.TXT
|
||||
// LICENSE.TXT at the root directory of the distribution.
|
||||
// </copyright>
|
||||
// <summary>
|
||||
// Part of the Deployment Tools Foundation project.
|
||||
// </summary>
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Deployment.Compression
|
||||
{
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
|
||||
/// <summary>
|
||||
/// Provides a basic implementation of the archive pack and unpack stream context
|
||||
/// interfaces, based on a list of archive files, a default directory, and an
|
||||
/// optional mapping from internal to external file paths.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This class can also handle creating or extracting chained archive packages.
|
||||
/// </remarks>
|
||||
public class ArchiveFileStreamContext
|
||||
: IPackStreamContext, IUnpackStreamContext
|
||||
{
|
||||
private IList<string> archiveFiles;
|
||||
private string directory;
|
||||
private IDictionary<string, string> files;
|
||||
private bool extractOnlyNewerFiles;
|
||||
private bool enableOffsetOpen;
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new ArchiveFileStreamContext with a archive file and
|
||||
/// no default directory or file mapping.
|
||||
/// </summary>
|
||||
/// <param name="archiveFile">The path to a archive file that will be
|
||||
/// created or extracted.</param>
|
||||
public ArchiveFileStreamContext(string archiveFile)
|
||||
: this(archiveFile, null, null)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new ArchiveFileStreamContext with a archive file, default
|
||||
/// directory and mapping from internal to external file paths.
|
||||
/// </summary>
|
||||
/// <param name="archiveFile">The path to a archive file that will be
|
||||
/// created or extracted.</param>
|
||||
/// <param name="directory">The default root directory where files will be
|
||||
/// located, optional.</param>
|
||||
/// <param name="files">A mapping from internal file paths to external file
|
||||
/// paths, optional.</param>
|
||||
/// <remarks>
|
||||
/// If the mapping is not null and a file is not included in the mapping,
|
||||
/// the file will be skipped.
|
||||
/// <para>If the external path in the mapping is a simple file name or
|
||||
/// relative file path, it will be concatenated onto the default directory,
|
||||
/// if one was specified.</para>
|
||||
/// <para>For more about how the default directory and files mapping are
|
||||
/// used, see <see cref="OpenFileReadStream"/> and
|
||||
/// <see cref="OpenFileWriteStream"/>.</para>
|
||||
/// </remarks>
|
||||
public ArchiveFileStreamContext(
|
||||
string archiveFile,
|
||||
string directory,
|
||||
IDictionary<string, string> files)
|
||||
: this(new string[] { archiveFile }, directory, files)
|
||||
{
|
||||
if (archiveFile == null)
|
||||
{
|
||||
throw new ArgumentNullException("archiveFile");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new ArchiveFileStreamContext with a list of archive files,
|
||||
/// a default directory and a mapping from internal to external file paths.
|
||||
/// </summary>
|
||||
/// <param name="archiveFiles">A list of paths to archive files that will be
|
||||
/// created or extracted.</param>
|
||||
/// <param name="directory">The default root directory where files will be
|
||||
/// located, optional.</param>
|
||||
/// <param name="files">A mapping from internal file paths to external file
|
||||
/// paths, optional.</param>
|
||||
/// <remarks>
|
||||
/// When creating chained archives, the <paramref name="archiveFiles"/> list
|
||||
/// should include at least enough archives to handle the entire set of
|
||||
/// input files, based on the maximum archive size that is passed to the
|
||||
/// <see cref="CompressionEngine"/>.<see
|
||||
/// cref="CompressionEngine.Pack(IPackStreamContext,IEnumerable<string>,long)"/>.
|
||||
/// <para>If the mapping is not null and a file is not included in the mapping,
|
||||
/// the file will be skipped.</para>
|
||||
/// <para>If the external path in the mapping is a simple file name or
|
||||
/// relative file path, it will be concatenated onto the default directory,
|
||||
/// if one was specified.</para>
|
||||
/// <para>For more about how the default directory and files mapping are used,
|
||||
/// see <see cref="OpenFileReadStream"/> and
|
||||
/// <see cref="OpenFileWriteStream"/>.</para>
|
||||
/// </remarks>
|
||||
public ArchiveFileStreamContext(
|
||||
IList<string> archiveFiles,
|
||||
string directory,
|
||||
IDictionary<string, string> files)
|
||||
{
|
||||
if (archiveFiles == null || archiveFiles.Count == 0)
|
||||
{
|
||||
throw new ArgumentNullException("archiveFiles");
|
||||
}
|
||||
|
||||
this.archiveFiles = archiveFiles;
|
||||
this.directory = directory;
|
||||
this.files = files;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the list of archive files that are created or extracted.
|
||||
/// </summary>
|
||||
/// <value>The list of archive files that are created or extracted.</value>
|
||||
public IList<string> ArchiveFiles
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.archiveFiles;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the default root directory where files are located.
|
||||
/// </summary>
|
||||
/// <value>The default root directory where files are located.</value>
|
||||
/// <remarks>
|
||||
/// For details about how the default directory is used,
|
||||
/// see <see cref="OpenFileReadStream"/> and <see cref="OpenFileWriteStream"/>.
|
||||
/// </remarks>
|
||||
public string Directory
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.directory;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the mapping from internal file paths to external file paths.
|
||||
/// </summary>
|
||||
/// <value>A mapping from internal file paths to external file paths.</value>
|
||||
/// <remarks>
|
||||
/// For details about how the files mapping is used,
|
||||
/// see <see cref="OpenFileReadStream"/> and <see cref="OpenFileWriteStream"/>.
|
||||
/// </remarks>
|
||||
public IDictionary<string, string> Files
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.files;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a flag that can prevent extracted files from overwriting
|
||||
/// newer files that already exist.
|
||||
/// </summary>
|
||||
/// <value>True to prevent overwriting newer files that already exist
|
||||
/// during extraction; false to always extract from the archive regardless
|
||||
/// of existing files.</value>
|
||||
public bool ExtractOnlyNewerFiles
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.extractOnlyNewerFiles;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
this.extractOnlyNewerFiles = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a flag that enables creating or extracting an archive
|
||||
/// at an offset within an existing file. (This is typically used to open
|
||||
/// archive-based self-extracting packages.)
|
||||
/// </summary>
|
||||
/// <value>True to search an existing package file for an archive offset
|
||||
/// or the end of the file;/ false to always create or open a plain
|
||||
/// archive file.</value>
|
||||
public bool EnableOffsetOpen
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.enableOffsetOpen;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
this.enableOffsetOpen = value;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IPackStreamContext Members
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the archive with a specified number.
|
||||
/// </summary>
|
||||
/// <param name="archiveNumber">The 0-based index of the archive within
|
||||
/// the chain.</param>
|
||||
/// <returns>The name of the requested archive. May be an empty string
|
||||
/// for non-chained archives, but may never be null.</returns>
|
||||
/// <remarks>This method returns the file name of the archive from the
|
||||
/// <see cref="archiveFiles"/> list with the specified index, or an empty
|
||||
/// string if the archive number is outside the bounds of the list. The
|
||||
/// file name should not include any directory path.</remarks>
|
||||
public virtual string GetArchiveName(int archiveNumber)
|
||||
{
|
||||
if (archiveNumber < this.archiveFiles.Count)
|
||||
{
|
||||
return Path.GetFileName(this.archiveFiles[archiveNumber]);
|
||||
}
|
||||
|
||||
return String.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens a stream for writing an archive.
|
||||
/// </summary>
|
||||
/// <param name="archiveNumber">The 0-based index of the archive within
|
||||
/// the chain.</param>
|
||||
/// <param name="archiveName">The name of the archive that was returned
|
||||
/// by <see cref="GetArchiveName"/>.</param>
|
||||
/// <param name="truncate">True if the stream should be truncated when
|
||||
/// opened (if it already exists); false if an existing stream is being
|
||||
/// re-opened for writing additional data.</param>
|
||||
/// <param name="compressionEngine">Instance of the compression engine
|
||||
/// doing the operations.</param>
|
||||
/// <returns>A writable Stream where the compressed archive bytes will be
|
||||
/// written, or null to cancel the archive creation.</returns>
|
||||
/// <remarks>
|
||||
/// This method opens the file from the <see cref="ArchiveFiles"/> list
|
||||
/// with the specified index. If the archive number is outside the bounds
|
||||
/// of the list, this method returns null.
|
||||
/// <para>If the <see cref="EnableOffsetOpen"/> flag is set, this method
|
||||
/// will seek to the start of any existing archive in the file, or to the
|
||||
/// end of the file if the existing file is not an archive.</para>
|
||||
/// </remarks>
|
||||
public virtual Stream OpenArchiveWriteStream(
|
||||
int archiveNumber,
|
||||
string archiveName,
|
||||
bool truncate,
|
||||
CompressionEngine compressionEngine)
|
||||
{
|
||||
if (archiveNumber >= this.archiveFiles.Count)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (String.IsNullOrEmpty(archiveName))
|
||||
{
|
||||
throw new ArgumentNullException("archiveName");
|
||||
}
|
||||
|
||||
// All archives must be in the same directory,
|
||||
// so always use the directory from the first archive.
|
||||
string archiveFile = Path.Combine(
|
||||
Path.GetDirectoryName(this.archiveFiles[0]), archiveName);
|
||||
Stream stream = File.Open(
|
||||
archiveFile,
|
||||
(truncate ? FileMode.OpenOrCreate : FileMode.Open),
|
||||
FileAccess.ReadWrite);
|
||||
|
||||
if (this.enableOffsetOpen)
|
||||
{
|
||||
long offset = compressionEngine.FindArchiveOffset(
|
||||
new DuplicateStream(stream));
|
||||
|
||||
// If this is not an archive file, append the archive to it.
|
||||
if (offset < 0)
|
||||
{
|
||||
offset = stream.Length;
|
||||
}
|
||||
|
||||
if (offset > 0)
|
||||
{
|
||||
stream = new OffsetStream(stream, offset);
|
||||
}
|
||||
|
||||
stream.Seek(0, SeekOrigin.Begin);
|
||||
}
|
||||
|
||||
if (truncate)
|
||||
{
|
||||
// Truncate the stream, in case a larger old archive starts here.
|
||||
stream.SetLength(0);
|
||||
}
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Closes a stream where an archive package was written.
|
||||
/// </summary>
|
||||
/// <param name="archiveNumber">The 0-based index of the archive within
|
||||
/// the chain.</param>
|
||||
/// <param name="archiveName">The name of the archive that was previously
|
||||
/// returned by <see cref="GetArchiveName"/>.</param>
|
||||
/// <param name="stream">A stream that was previously returned by
|
||||
/// <see cref="OpenArchiveWriteStream"/> and is now ready to be closed.</param>
|
||||
public virtual void CloseArchiveWriteStream(
|
||||
int archiveNumber,
|
||||
string archiveName,
|
||||
Stream stream)
|
||||
{
|
||||
if (stream != null)
|
||||
{
|
||||
stream.Close();
|
||||
|
||||
FileStream fileStream = stream as FileStream;
|
||||
if (fileStream != null)
|
||||
{
|
||||
string streamFile = fileStream.Name;
|
||||
if (!String.IsNullOrEmpty(archiveName) &&
|
||||
archiveName != Path.GetFileName(streamFile))
|
||||
{
|
||||
string archiveFile = Path.Combine(
|
||||
Path.GetDirectoryName(this.archiveFiles[0]), archiveName);
|
||||
if (File.Exists(archiveFile))
|
||||
{
|
||||
File.Delete(archiveFile);
|
||||
}
|
||||
File.Move(streamFile, archiveFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens a stream to read a file that is to be included in an archive.
|
||||
/// </summary>
|
||||
/// <param name="path">The path of the file within the archive.</param>
|
||||
/// <param name="attributes">The returned attributes of the opened file,
|
||||
/// to be stored in the archive.</param>
|
||||
/// <param name="lastWriteTime">The returned last-modified time of the
|
||||
/// opened file, to be stored in the archive.</param>
|
||||
/// <returns>A readable Stream where the file bytes will be read from
|
||||
/// before they are compressed, or null to skip inclusion of the file and
|
||||
/// continue to the next file.</returns>
|
||||
/// <remarks>
|
||||
/// This method opens a file using the following logic:
|
||||
/// <list>
|
||||
/// <item>If the <see cref="Directory"/> and the <see cref="Files"/> mapping
|
||||
/// are both null, the path is treated as relative to the current directory,
|
||||
/// and that file is opened.</item>
|
||||
/// <item>If the <see cref="Directory"/> is not null but the <see cref="Files"/>
|
||||
/// mapping is null, the path is treated as relative to that directory, and
|
||||
/// that file is opened.</item>
|
||||
/// <item>If the <see cref="Directory"/> is null but the <see cref="Files"/>
|
||||
/// mapping is not null, the path parameter is used as a key into the mapping,
|
||||
/// and the resulting value is the file path that is opened, relative to the
|
||||
/// current directory (or it may be an absolute path). If no mapping exists,
|
||||
/// the file is skipped.</item>
|
||||
/// <item>If both the <see cref="Directory"/> and the <see cref="Files"/>
|
||||
/// mapping are specified, the path parameter is used as a key into the
|
||||
/// mapping, and the resulting value is the file path that is opened, relative
|
||||
/// to the specified directory (or it may be an absolute path). If no mapping
|
||||
/// exists, the file is skipped.</item>
|
||||
/// </list>
|
||||
/// </remarks>
|
||||
public virtual Stream OpenFileReadStream(
|
||||
string path, out FileAttributes attributes, out DateTime lastWriteTime)
|
||||
{
|
||||
string filePath = this.TranslateFilePath(path);
|
||||
|
||||
if (filePath == null)
|
||||
{
|
||||
attributes = FileAttributes.Normal;
|
||||
lastWriteTime = DateTime.Now;
|
||||
return null;
|
||||
}
|
||||
|
||||
attributes = File.GetAttributes(filePath);
|
||||
lastWriteTime = File.GetLastWriteTime(filePath);
|
||||
return File.Open(filePath, FileMode.Open, FileAccess.Read, FileShare.Read);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Closes a stream that has been used to read a file.
|
||||
/// </summary>
|
||||
/// <param name="path">The path of the file within the archive; the same as
|
||||
/// the path provided when the stream was opened.</param>
|
||||
/// <param name="stream">A stream that was previously returned by
|
||||
/// <see cref="OpenFileReadStream"/> and is now ready to be closed.</param>
|
||||
public virtual void CloseFileReadStream(string path, Stream stream)
|
||||
{
|
||||
if (stream != null)
|
||||
{
|
||||
stream.Close();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets extended parameter information specific to the compression format
|
||||
/// being used.
|
||||
/// </summary>
|
||||
/// <param name="optionName">Name of the option being requested.</param>
|
||||
/// <param name="parameters">Parameters for the option; for per-file options,
|
||||
/// the first parameter is typically the internal file path.</param>
|
||||
/// <returns>Option value, or null to use the default behavior.</returns>
|
||||
/// <remarks>
|
||||
/// This implementation does not handle any options. Subclasses may override
|
||||
/// this method to allow for non-default behavior.
|
||||
/// </remarks>
|
||||
public virtual object GetOption(string optionName, object[] parameters)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IUnpackStreamContext Members
|
||||
|
||||
/// <summary>
|
||||
/// Opens the archive stream for reading.
|
||||
/// </summary>
|
||||
/// <param name="archiveNumber">The zero-based index of the archive to
|
||||
/// open.</param>
|
||||
/// <param name="archiveName">The name of the archive being opened.</param>
|
||||
/// <param name="compressionEngine">Instance of the compression engine
|
||||
/// doing the operations.</param>
|
||||
/// <returns>A stream from which archive bytes are read, or null to cancel
|
||||
/// extraction of the archive.</returns>
|
||||
/// <remarks>
|
||||
/// This method opens the file from the <see cref="ArchiveFiles"/> list with
|
||||
/// the specified index. If the archive number is outside the bounds of the
|
||||
/// list, this method returns null.
|
||||
/// <para>If the <see cref="EnableOffsetOpen"/> flag is set, this method will
|
||||
/// seek to the start of any existing archive in the file, or to the end of
|
||||
/// the file if the existing file is not an archive.</para>
|
||||
/// </remarks>
|
||||
public virtual Stream OpenArchiveReadStream(
|
||||
int archiveNumber, string archiveName, CompressionEngine compressionEngine)
|
||||
{
|
||||
if (archiveNumber >= this.archiveFiles.Count)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
string archiveFile = this.archiveFiles[archiveNumber];
|
||||
Stream stream = File.Open(
|
||||
archiveFile, FileMode.Open, FileAccess.Read, FileShare.Read);
|
||||
|
||||
if (this.enableOffsetOpen)
|
||||
{
|
||||
long offset = compressionEngine.FindArchiveOffset(
|
||||
new DuplicateStream(stream));
|
||||
if (offset > 0)
|
||||
{
|
||||
stream = new OffsetStream(stream, offset);
|
||||
}
|
||||
else
|
||||
{
|
||||
stream.Seek(0, SeekOrigin.Begin);
|
||||
}
|
||||
}
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Closes a stream where an archive was read.
|
||||
/// </summary>
|
||||
/// <param name="archiveNumber">The archive number of the stream
|
||||
/// to close.</param>
|
||||
/// <param name="archiveName">The name of the archive being closed.</param>
|
||||
/// <param name="stream">The stream that was previously returned by
|
||||
/// <see cref="OpenArchiveReadStream"/> and is now ready to be closed.</param>
|
||||
public virtual void CloseArchiveReadStream(
|
||||
int archiveNumber, string archiveName, Stream stream)
|
||||
{
|
||||
if (stream != null)
|
||||
{
|
||||
stream.Close();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens a stream for writing extracted file bytes.
|
||||
/// </summary>
|
||||
/// <param name="path">The path of the file within the archive.</param>
|
||||
/// <param name="fileSize">The uncompressed size of the file to be
|
||||
/// extracted.</param>
|
||||
/// <param name="lastWriteTime">The last write time of the file to be
|
||||
/// extracted.</param>
|
||||
/// <returns>A stream where extracted file bytes are to be written, or null
|
||||
/// to skip extraction of the file and continue to the next file.</returns>
|
||||
/// <remarks>
|
||||
/// This method opens a file using the following logic:
|
||||
/// <list>
|
||||
/// <item>If the <see cref="Directory"/> and the <see cref="Files"/> mapping
|
||||
/// are both null, the path is treated as relative to the current directory,
|
||||
/// and that file is opened.</item>
|
||||
/// <item>If the <see cref="Directory"/> is not null but the <see cref="Files"/>
|
||||
/// mapping is null, the path is treated as relative to that directory, and
|
||||
/// that file is opened.</item>
|
||||
/// <item>If the <see cref="Directory"/> is null but the <see cref="Files"/>
|
||||
/// mapping is not null, the path parameter is used as a key into the mapping,
|
||||
/// and the resulting value is the file path that is opened, relative to the
|
||||
/// current directory (or it may be an absolute path). If no mapping exists,
|
||||
/// the file is skipped.</item>
|
||||
/// <item>If both the <see cref="Directory"/> and the <see cref="Files"/>
|
||||
/// mapping are specified, the path parameter is used as a key into the
|
||||
/// mapping, and the resulting value is the file path that is opened,
|
||||
/// relative to the specified directory (or it may be an absolute path).
|
||||
/// If no mapping exists, the file is skipped.</item>
|
||||
/// </list>
|
||||
/// <para>If the <see cref="ExtractOnlyNewerFiles"/> flag is set, the file
|
||||
/// is skipped if a file currently exists in the same path with an equal
|
||||
/// or newer write time.</para>
|
||||
/// </remarks>
|
||||
public virtual Stream OpenFileWriteStream(
|
||||
string path,
|
||||
long fileSize,
|
||||
DateTime lastWriteTime)
|
||||
{
|
||||
string filePath = this.TranslateFilePath(path);
|
||||
|
||||
if (filePath == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
FileInfo file = new FileInfo(filePath);
|
||||
if (file.Exists)
|
||||
{
|
||||
if (this.extractOnlyNewerFiles && lastWriteTime != DateTime.MinValue)
|
||||
{
|
||||
if (file.LastWriteTime >= lastWriteTime)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Clear attributes that will prevent overwriting the file.
|
||||
// (The final attributes will be set after the file is unpacked.)
|
||||
FileAttributes attributesToClear =
|
||||
FileAttributes.ReadOnly | FileAttributes.Hidden | FileAttributes.System;
|
||||
if ((file.Attributes & attributesToClear) != 0)
|
||||
{
|
||||
file.Attributes &= ~attributesToClear;
|
||||
}
|
||||
}
|
||||
|
||||
if (!file.Directory.Exists)
|
||||
{
|
||||
file.Directory.Create();
|
||||
}
|
||||
|
||||
return File.Open(
|
||||
filePath, FileMode.Create, FileAccess.Write, FileShare.None);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Closes a stream where an extracted file was written.
|
||||
/// </summary>
|
||||
/// <param name="path">The path of the file within the archive.</param>
|
||||
/// <param name="stream">The stream that was previously returned by
|
||||
/// <see cref="OpenFileWriteStream"/> and is now ready to be closed.</param>
|
||||
/// <param name="attributes">The attributes of the extracted file.</param>
|
||||
/// <param name="lastWriteTime">The last write time of the file.</param>
|
||||
/// <remarks>
|
||||
/// After closing the extracted file stream, this method applies the date
|
||||
/// and attributes to that file.
|
||||
/// </remarks>
|
||||
public virtual void CloseFileWriteStream(
|
||||
string path,
|
||||
Stream stream,
|
||||
FileAttributes attributes,
|
||||
DateTime lastWriteTime)
|
||||
{
|
||||
if (stream != null)
|
||||
{
|
||||
stream.Close();
|
||||
}
|
||||
|
||||
string filePath = this.TranslateFilePath(path);
|
||||
if (filePath != null)
|
||||
{
|
||||
FileInfo file = new FileInfo(filePath);
|
||||
|
||||
if (lastWriteTime != DateTime.MinValue)
|
||||
{
|
||||
try
|
||||
{
|
||||
file.LastWriteTime = lastWriteTime;
|
||||
}
|
||||
catch (ArgumentException)
|
||||
{
|
||||
}
|
||||
catch (IOException)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
file.Attributes = attributes;
|
||||
}
|
||||
catch (IOException)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private utility methods
|
||||
|
||||
/// <summary>
|
||||
/// Translates an internal file path to an external file path using the
|
||||
/// <see cref="Directory"/> and the <see cref="Files"/> mapping, according to
|
||||
/// rules documented in <see cref="OpenFileReadStream"/> and
|
||||
/// <see cref="OpenFileWriteStream"/>.
|
||||
/// </summary>
|
||||
/// <param name="path">The path of the file with the archive.</param>
|
||||
/// <returns>The external path of the file, or null if there is no
|
||||
/// valid translation.</returns>
|
||||
private string TranslateFilePath(string path)
|
||||
{
|
||||
string filePath;
|
||||
if (this.files != null)
|
||||
{
|
||||
filePath = this.files[path];
|
||||
}
|
||||
else
|
||||
{
|
||||
filePath = path;
|
||||
}
|
||||
|
||||
if (filePath != null)
|
||||
{
|
||||
if (this.directory != null)
|
||||
{
|
||||
filePath = Path.Combine(this.directory, filePath);
|
||||
}
|
||||
}
|
||||
|
||||
return filePath;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -0,0 +1,792 @@
|
|||
//---------------------------------------------------------------------
|
||||
// <copyright file="ArchiveInfo.cs" company="Outercurve Foundation">
|
||||
// Copyright (c) 2004, Outercurve Foundation.
|
||||
// This software is released under Microsoft Reciprocal License (MS-RL).
|
||||
// The license and further copyright text can be found in the file LICENSE.TXT
|
||||
// LICENSE.TXT at the root directory of the distribution.
|
||||
// </copyright>
|
||||
// <summary>
|
||||
// Part of the Deployment Tools Foundation project.
|
||||
// </summary>
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Deployment.Compression
|
||||
{
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Security.Permissions;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
/// <summary>
|
||||
/// Abstract object representing a compressed archive on disk;
|
||||
/// provides access to file-based operations on the archive.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public abstract class ArchiveInfo : FileSystemInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new ArchiveInfo object representing an archive in a
|
||||
/// specified path.
|
||||
/// </summary>
|
||||
/// <param name="path">The path to the archive. When creating an archive,
|
||||
/// this file does not necessarily exist yet.</param>
|
||||
protected ArchiveInfo(string path) : base()
|
||||
{
|
||||
if (path == null)
|
||||
{
|
||||
throw new ArgumentNullException("path");
|
||||
}
|
||||
|
||||
// protected instance members inherited from FileSystemInfo:
|
||||
this.OriginalPath = path;
|
||||
this.FullPath = Path.GetFullPath(path);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the ArchiveInfo class with serialized data.
|
||||
/// </summary>
|
||||
/// <param name="info">The SerializationInfo that holds the serialized object
|
||||
/// data about the exception being thrown.</param>
|
||||
/// <param name="context">The StreamingContext that contains contextual
|
||||
/// information about the source or destination.</param>
|
||||
protected ArchiveInfo(SerializationInfo info, StreamingContext context)
|
||||
: base(info, context)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the directory that contains the archive.
|
||||
/// </summary>
|
||||
/// <value>A DirectoryInfo object representing the parent directory of the
|
||||
/// archive.</value>
|
||||
public DirectoryInfo Directory
|
||||
{
|
||||
get
|
||||
{
|
||||
return new DirectoryInfo(Path.GetDirectoryName(this.FullName));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the full path of the directory that contains the archive.
|
||||
/// </summary>
|
||||
/// <value>The full path of the directory that contains the archive.</value>
|
||||
public string DirectoryName
|
||||
{
|
||||
get
|
||||
{
|
||||
return Path.GetDirectoryName(this.FullName);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the size of the archive.
|
||||
/// </summary>
|
||||
/// <value>The size of the archive in bytes.</value>
|
||||
public long Length
|
||||
{
|
||||
get
|
||||
{
|
||||
return new FileInfo(this.FullName).Length;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the file name of the archive.
|
||||
/// </summary>
|
||||
/// <value>The file name of the archive, not including any path.</value>
|
||||
public override string Name
|
||||
{
|
||||
get
|
||||
{
|
||||
return Path.GetFileName(this.FullName);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the archive exists.
|
||||
/// </summary>
|
||||
/// <value>True if the archive exists; else false.</value>
|
||||
public override bool Exists
|
||||
{
|
||||
get
|
||||
{
|
||||
return File.Exists(this.FullName);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the full path of the archive.
|
||||
/// </summary>
|
||||
/// <returns>The full path of the archive.</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return this.FullName;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes the archive.
|
||||
/// </summary>
|
||||
public override void Delete()
|
||||
{
|
||||
File.Delete(this.FullName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies an existing archive to another location.
|
||||
/// </summary>
|
||||
/// <param name="destFileName">The destination file path.</param>
|
||||
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "dest")]
|
||||
public void CopyTo(string destFileName)
|
||||
{
|
||||
File.Copy(this.FullName, destFileName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies an existing archive to another location, optionally
|
||||
/// overwriting the destination file.
|
||||
/// </summary>
|
||||
/// <param name="destFileName">The destination file path.</param>
|
||||
/// <param name="overwrite">If true, the destination file will be
|
||||
/// overwritten if it exists.</param>
|
||||
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "dest")]
|
||||
public void CopyTo(string destFileName, bool overwrite)
|
||||
{
|
||||
File.Copy(this.FullName, destFileName, overwrite);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Moves an existing archive to another location.
|
||||
/// </summary>
|
||||
/// <param name="destFileName">The destination file path.</param>
|
||||
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "dest")]
|
||||
public void MoveTo(string destFileName)
|
||||
{
|
||||
File.Move(this.FullName, destFileName);
|
||||
this.FullPath = Path.GetFullPath(destFileName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the archive contains a valid archive header.
|
||||
/// </summary>
|
||||
/// <returns>True if the file is a valid archive; false otherwise.</returns>
|
||||
public bool IsValid()
|
||||
{
|
||||
using (Stream stream = File.OpenRead(this.FullName))
|
||||
{
|
||||
using (CompressionEngine compressionEngine = this.CreateCompressionEngine())
|
||||
{
|
||||
return compressionEngine.FindArchiveOffset(stream) >= 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets information about the files contained in the archive.
|
||||
/// </summary>
|
||||
/// <returns>A list of <see cref="ArchiveFileInfo"/> objects, each
|
||||
/// containing information about a file in the archive.</returns>
|
||||
public IList<ArchiveFileInfo> GetFiles()
|
||||
{
|
||||
return this.InternalGetFiles((Predicate<string>) null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets information about the certain files contained in the archive file.
|
||||
/// </summary>
|
||||
/// <param name="searchPattern">The search string, such as
|
||||
/// "*.txt".</param>
|
||||
/// <returns>A list of <see cref="ArchiveFileInfo"/> objects, each containing
|
||||
/// information about a file in the archive.</returns>
|
||||
public IList<ArchiveFileInfo> GetFiles(string searchPattern)
|
||||
{
|
||||
if (searchPattern == null)
|
||||
{
|
||||
throw new ArgumentNullException("searchPattern");
|
||||
}
|
||||
|
||||
string regexPattern = String.Format(
|
||||
CultureInfo.InvariantCulture,
|
||||
"^{0}$",
|
||||
Regex.Escape(searchPattern).Replace("\\*", ".*").Replace("\\?", "."));
|
||||
Regex regex = new Regex(
|
||||
regexPattern,
|
||||
RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
|
||||
|
||||
return this.InternalGetFiles(
|
||||
delegate(string match)
|
||||
{
|
||||
return regex.IsMatch(match);
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extracts all files from an archive to a destination directory.
|
||||
/// </summary>
|
||||
/// <param name="destDirectory">Directory where the files are to be
|
||||
/// extracted.</param>
|
||||
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "dest")]
|
||||
public void Unpack(string destDirectory)
|
||||
{
|
||||
this.Unpack(destDirectory, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extracts all files from an archive to a destination directory,
|
||||
/// optionally extracting only newer files.
|
||||
/// </summary>
|
||||
/// <param name="destDirectory">Directory where the files are to be
|
||||
/// extracted.</param>
|
||||
/// <param name="progressHandler">Handler for receiving progress
|
||||
/// information; this may be null if progress is not desired.</param>
|
||||
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "dest")]
|
||||
public void Unpack(
|
||||
string destDirectory,
|
||||
EventHandler<ArchiveProgressEventArgs> progressHandler)
|
||||
{
|
||||
using (CompressionEngine compressionEngine = this.CreateCompressionEngine())
|
||||
{
|
||||
compressionEngine.Progress += progressHandler;
|
||||
ArchiveFileStreamContext streamContext =
|
||||
new ArchiveFileStreamContext(this.FullName, destDirectory, null);
|
||||
streamContext.EnableOffsetOpen = true;
|
||||
compressionEngine.Unpack(streamContext, null);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extracts a single file from the archive.
|
||||
/// </summary>
|
||||
/// <param name="fileName">The name of the file in the archive. Also
|
||||
/// includes the internal path of the file, if any. File name matching
|
||||
/// is case-insensitive.</param>
|
||||
/// <param name="destFileName">The path where the file is to be
|
||||
/// extracted on disk.</param>
|
||||
/// <remarks>If <paramref name="destFileName"/> already exists,
|
||||
/// it will be overwritten.</remarks>
|
||||
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "dest")]
|
||||
public void UnpackFile(string fileName, string destFileName)
|
||||
{
|
||||
if (fileName == null)
|
||||
{
|
||||
throw new ArgumentNullException("fileName");
|
||||
}
|
||||
|
||||
if (destFileName == null)
|
||||
{
|
||||
throw new ArgumentNullException("destFileName");
|
||||
}
|
||||
|
||||
this.UnpackFiles(
|
||||
new string[] { fileName },
|
||||
null,
|
||||
new string[] { destFileName });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extracts multiple files from the archive.
|
||||
/// </summary>
|
||||
/// <param name="fileNames">The names of the files in the archive.
|
||||
/// Each name includes the internal path of the file, if any. File name
|
||||
/// matching is case-insensitive.</param>
|
||||
/// <param name="destDirectory">This parameter may be null, but if
|
||||
/// specified it is the root directory for any relative paths in
|
||||
/// <paramref name="destFileNames"/>.</param>
|
||||
/// <param name="destFileNames">The paths where the files are to be
|
||||
/// extracted on disk. If this parameter is null, the files will be
|
||||
/// extracted with the names from the archive.</param>
|
||||
/// <remarks>
|
||||
/// If any extracted files already exist on disk, they will be overwritten.
|
||||
/// <p>The <paramref name="destDirectory"/> and
|
||||
/// <paramref name="destFileNames"/> parameters cannot both be null.</p>
|
||||
/// </remarks>
|
||||
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "dest")]
|
||||
public void UnpackFiles(
|
||||
IList<string> fileNames,
|
||||
string destDirectory,
|
||||
IList<string> destFileNames)
|
||||
{
|
||||
this.UnpackFiles(fileNames, destDirectory, destFileNames, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extracts multiple files from the archive, optionally extracting
|
||||
/// only newer files.
|
||||
/// </summary>
|
||||
/// <param name="fileNames">The names of the files in the archive.
|
||||
/// Each name includes the internal path of the file, if any. File name
|
||||
/// matching is case-insensitive.</param>
|
||||
/// <param name="destDirectory">This parameter may be null, but if
|
||||
/// specified it is the root directory for any relative paths in
|
||||
/// <paramref name="destFileNames"/>.</param>
|
||||
/// <param name="destFileNames">The paths where the files are to be
|
||||
/// extracted on disk. If this parameter is null, the files will be
|
||||
/// extracted with the names from the archive.</param>
|
||||
/// <param name="progressHandler">Handler for receiving progress information;
|
||||
/// this may be null if progress is not desired.</param>
|
||||
/// <remarks>
|
||||
/// If any extracted files already exist on disk, they will be overwritten.
|
||||
/// <p>The <paramref name="destDirectory"/> and
|
||||
/// <paramref name="destFileNames"/> parameters cannot both be null.</p>
|
||||
/// </remarks>
|
||||
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "dest")]
|
||||
public void UnpackFiles(
|
||||
IList<string> fileNames,
|
||||
string destDirectory,
|
||||
IList<string> destFileNames,
|
||||
EventHandler<ArchiveProgressEventArgs> progressHandler)
|
||||
{
|
||||
if (fileNames == null)
|
||||
{
|
||||
throw new ArgumentNullException("fileNames");
|
||||
}
|
||||
|
||||
if (destFileNames == null)
|
||||
{
|
||||
if (destDirectory == null)
|
||||
{
|
||||
throw new ArgumentNullException("destFileNames");
|
||||
}
|
||||
|
||||
destFileNames = fileNames;
|
||||
}
|
||||
|
||||
if (destFileNames.Count != fileNames.Count)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("destFileNames");
|
||||
}
|
||||
|
||||
IDictionary<string, string> files =
|
||||
ArchiveInfo.CreateStringDictionary(fileNames, destFileNames);
|
||||
this.UnpackFileSet(files, destDirectory, progressHandler);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extracts multiple files from the archive.
|
||||
/// </summary>
|
||||
/// <param name="fileNames">A mapping from internal file paths to
|
||||
/// external file paths. Case-senstivity when matching internal paths
|
||||
/// depends on the IDictionary implementation.</param>
|
||||
/// <param name="destDirectory">This parameter may be null, but if
|
||||
/// specified it is the root directory for any relative external paths
|
||||
/// in <paramref name="fileNames"/>.</param>
|
||||
/// <remarks>
|
||||
/// If any extracted files already exist on disk, they will be overwritten.
|
||||
/// </remarks>
|
||||
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "dest")]
|
||||
public void UnpackFileSet(
|
||||
IDictionary<string, string> fileNames,
|
||||
string destDirectory)
|
||||
{
|
||||
this.UnpackFileSet(fileNames, destDirectory, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extracts multiple files from the archive.
|
||||
/// </summary>
|
||||
/// <param name="fileNames">A mapping from internal file paths to
|
||||
/// external file paths. Case-senstivity when matching internal
|
||||
/// paths depends on the IDictionary implementation.</param>
|
||||
/// <param name="destDirectory">This parameter may be null, but if
|
||||
/// specified it is the root directory for any relative external
|
||||
/// paths in <paramref name="fileNames"/>.</param>
|
||||
/// <param name="progressHandler">Handler for receiving progress
|
||||
/// information; this may be null if progress is not desired.</param>
|
||||
/// <remarks>
|
||||
/// If any extracted files already exist on disk, they will be overwritten.
|
||||
/// </remarks>
|
||||
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "dest")]
|
||||
public void UnpackFileSet(
|
||||
IDictionary<string, string> fileNames,
|
||||
string destDirectory,
|
||||
EventHandler<ArchiveProgressEventArgs> progressHandler)
|
||||
{
|
||||
if (fileNames == null)
|
||||
{
|
||||
throw new ArgumentNullException("fileNames");
|
||||
}
|
||||
|
||||
using (CompressionEngine compressionEngine = this.CreateCompressionEngine())
|
||||
{
|
||||
compressionEngine.Progress += progressHandler;
|
||||
ArchiveFileStreamContext streamContext =
|
||||
new ArchiveFileStreamContext(this.FullName, destDirectory, fileNames);
|
||||
streamContext.EnableOffsetOpen = true;
|
||||
compressionEngine.Unpack(
|
||||
streamContext,
|
||||
delegate(string match)
|
||||
{
|
||||
return fileNames.ContainsKey(match);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens a file inside the archive for reading without actually
|
||||
/// extracting the file to disk.
|
||||
/// </summary>
|
||||
/// <param name="fileName">The name of the file in the archive. Also
|
||||
/// includes the internal path of the file, if any. File name matching
|
||||
/// is case-insensitive.</param>
|
||||
/// <returns>
|
||||
/// A stream for reading directly from the packed file. Like any stream
|
||||
/// this should be closed/disposed as soon as it is no longer needed.
|
||||
/// </returns>
|
||||
public Stream OpenRead(string fileName)
|
||||
{
|
||||
Stream archiveStream = File.OpenRead(this.FullName);
|
||||
CompressionEngine compressionEngine = this.CreateCompressionEngine();
|
||||
Stream fileStream = compressionEngine.Unpack(archiveStream, fileName);
|
||||
|
||||
// Attach the archiveStream and compressionEngine to the
|
||||
// fileStream so they get disposed when the fileStream is disposed.
|
||||
return new CargoStream(fileStream, archiveStream, compressionEngine);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens a file inside the archive for reading text with UTF-8 encoding
|
||||
/// without actually extracting the file to disk.
|
||||
/// </summary>
|
||||
/// <param name="fileName">The name of the file in the archive. Also
|
||||
/// includes the internal path of the file, if any. File name matching
|
||||
/// is case-insensitive.</param>
|
||||
/// <returns>
|
||||
/// A reader for reading text directly from the packed file. Like any reader
|
||||
/// this should be closed/disposed as soon as it is no longer needed.
|
||||
/// </returns>
|
||||
/// <remarks>
|
||||
/// To open an archived text file with different encoding, use the
|
||||
/// <see cref="OpenRead" /> method and pass the returned stream to one of
|
||||
/// the <see cref="StreamReader" /> constructor overloads.
|
||||
/// </remarks>
|
||||
public StreamReader OpenText(string fileName)
|
||||
{
|
||||
return new StreamReader(this.OpenRead(fileName));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compresses all files in a directory into the archive.
|
||||
/// Does not include subdirectories.
|
||||
/// </summary>
|
||||
/// <param name="sourceDirectory">The directory containing the
|
||||
/// files to be included.</param>
|
||||
/// <remarks>
|
||||
/// Uses maximum compression level.
|
||||
/// </remarks>
|
||||
public void Pack(string sourceDirectory)
|
||||
{
|
||||
this.Pack(sourceDirectory, false, CompressionLevel.Max, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compresses all files in a directory into the archive, optionally
|
||||
/// including subdirectories.
|
||||
/// </summary>
|
||||
/// <param name="sourceDirectory">This is the root directory
|
||||
/// for to pack all files.</param>
|
||||
/// <param name="includeSubdirectories">If true, recursively include
|
||||
/// files in subdirectories.</param>
|
||||
/// <param name="compLevel">The compression level used when creating
|
||||
/// the archive.</param>
|
||||
/// <param name="progressHandler">Handler for receiving progress information;
|
||||
/// this may be null if progress is not desired.</param>
|
||||
/// <remarks>
|
||||
/// The files are stored in the archive using their relative file paths in
|
||||
/// the directory tree, if supported by the archive file format.
|
||||
/// </remarks>
|
||||
public void Pack(
|
||||
string sourceDirectory,
|
||||
bool includeSubdirectories,
|
||||
CompressionLevel compLevel,
|
||||
EventHandler<ArchiveProgressEventArgs> progressHandler)
|
||||
{
|
||||
IList<string> files = this.GetRelativeFilePathsInDirectoryTree(
|
||||
sourceDirectory, includeSubdirectories);
|
||||
this.PackFiles(sourceDirectory, files, files, compLevel, progressHandler);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compresses files into the archive, specifying the names used to
|
||||
/// store the files in the archive.
|
||||
/// </summary>
|
||||
/// <param name="sourceDirectory">This parameter may be null, but
|
||||
/// if specified it is the root directory
|
||||
/// for any relative paths in <paramref name="sourceFileNames"/>.</param>
|
||||
/// <param name="sourceFileNames">The list of files to be included in
|
||||
/// the archive.</param>
|
||||
/// <param name="fileNames">The names of the files as they are stored
|
||||
/// in the archive. Each name
|
||||
/// includes the internal path of the file, if any. This parameter may
|
||||
/// be null, in which case the files are stored in the archive with their
|
||||
/// source file names and no path information.</param>
|
||||
/// <remarks>
|
||||
/// Uses maximum compression level.
|
||||
/// <p>Duplicate items in the <paramref name="fileNames"/> array will cause
|
||||
/// an <see cref="ArchiveException"/>.</p>
|
||||
/// </remarks>
|
||||
public void PackFiles(
|
||||
string sourceDirectory,
|
||||
IList<string> sourceFileNames,
|
||||
IList<string> fileNames)
|
||||
{
|
||||
this.PackFiles(
|
||||
sourceDirectory,
|
||||
sourceFileNames,
|
||||
fileNames,
|
||||
CompressionLevel.Max,
|
||||
null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compresses files into the archive, specifying the names used to
|
||||
/// store the files in the archive.
|
||||
/// </summary>
|
||||
/// <param name="sourceDirectory">This parameter may be null, but if
|
||||
/// specified it is the root directory
|
||||
/// for any relative paths in <paramref name="sourceFileNames"/>.</param>
|
||||
/// <param name="sourceFileNames">The list of files to be included in
|
||||
/// the archive.</param>
|
||||
/// <param name="fileNames">The names of the files as they are stored in
|
||||
/// the archive. Each name includes the internal path of the file, if any.
|
||||
/// This parameter may be null, in which case the files are stored in the
|
||||
/// archive with their source file names and no path information.</param>
|
||||
/// <param name="compLevel">The compression level used when creating the
|
||||
/// archive.</param>
|
||||
/// <param name="progressHandler">Handler for receiving progress information;
|
||||
/// this may be null if progress is not desired.</param>
|
||||
/// <remarks>
|
||||
/// Duplicate items in the <paramref name="fileNames"/> array will cause
|
||||
/// an <see cref="ArchiveException"/>.
|
||||
/// </remarks>
|
||||
public void PackFiles(
|
||||
string sourceDirectory,
|
||||
IList<string> sourceFileNames,
|
||||
IList<string> fileNames,
|
||||
CompressionLevel compLevel,
|
||||
EventHandler<ArchiveProgressEventArgs> progressHandler)
|
||||
{
|
||||
if (sourceFileNames == null)
|
||||
{
|
||||
throw new ArgumentNullException("sourceFileNames");
|
||||
}
|
||||
|
||||
if (fileNames == null)
|
||||
{
|
||||
string[] fileNamesArray = new string[sourceFileNames.Count];
|
||||
for (int i = 0; i < sourceFileNames.Count; i++)
|
||||
{
|
||||
fileNamesArray[i] = Path.GetFileName(sourceFileNames[i]);
|
||||
}
|
||||
|
||||
fileNames = fileNamesArray;
|
||||
}
|
||||
else if (fileNames.Count != sourceFileNames.Count)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("fileNames");
|
||||
}
|
||||
|
||||
using (CompressionEngine compressionEngine = this.CreateCompressionEngine())
|
||||
{
|
||||
compressionEngine.Progress += progressHandler;
|
||||
IDictionary<string, string> contextFiles =
|
||||
ArchiveInfo.CreateStringDictionary(fileNames, sourceFileNames);
|
||||
ArchiveFileStreamContext streamContext = new ArchiveFileStreamContext(
|
||||
this.FullName, sourceDirectory, contextFiles);
|
||||
streamContext.EnableOffsetOpen = true;
|
||||
compressionEngine.CompressionLevel = compLevel;
|
||||
compressionEngine.Pack(streamContext, fileNames);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compresses files into the archive, specifying the names used
|
||||
/// to store the files in the archive.
|
||||
/// </summary>
|
||||
/// <param name="sourceDirectory">This parameter may be null, but if
|
||||
/// specified it is the root directory
|
||||
/// for any relative paths in <paramref name="fileNames"/>.</param>
|
||||
/// <param name="fileNames">A mapping from internal file paths to
|
||||
/// external file paths.</param>
|
||||
/// <remarks>
|
||||
/// Uses maximum compression level.
|
||||
/// </remarks>
|
||||
public void PackFileSet(
|
||||
string sourceDirectory,
|
||||
IDictionary<string, string> fileNames)
|
||||
{
|
||||
this.PackFileSet(sourceDirectory, fileNames, CompressionLevel.Max, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compresses files into the archive, specifying the names used to
|
||||
/// store the files in the archive.
|
||||
/// </summary>
|
||||
/// <param name="sourceDirectory">This parameter may be null, but if
|
||||
/// specified it is the root directory
|
||||
/// for any relative paths in <paramref name="fileNames"/>.</param>
|
||||
/// <param name="fileNames">A mapping from internal file paths to
|
||||
/// external file paths.</param>
|
||||
/// <param name="compLevel">The compression level used when creating
|
||||
/// the archive.</param>
|
||||
/// <param name="progressHandler">Handler for receiving progress information;
|
||||
/// this may be null if progress is not desired.</param>
|
||||
public void PackFileSet(
|
||||
string sourceDirectory,
|
||||
IDictionary<string, string> fileNames,
|
||||
CompressionLevel compLevel,
|
||||
EventHandler<ArchiveProgressEventArgs> progressHandler)
|
||||
{
|
||||
if (fileNames == null)
|
||||
{
|
||||
throw new ArgumentNullException("fileNames");
|
||||
}
|
||||
|
||||
string[] fileNamesArray = new string[fileNames.Count];
|
||||
fileNames.Keys.CopyTo(fileNamesArray, 0);
|
||||
|
||||
using (CompressionEngine compressionEngine = this.CreateCompressionEngine())
|
||||
{
|
||||
compressionEngine.Progress += progressHandler;
|
||||
ArchiveFileStreamContext streamContext = new ArchiveFileStreamContext(
|
||||
this.FullName, sourceDirectory, fileNames);
|
||||
streamContext.EnableOffsetOpen = true;
|
||||
compressionEngine.CompressionLevel = compLevel;
|
||||
compressionEngine.Pack(streamContext, fileNamesArray);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Given a directory, gets the relative paths of all files in the
|
||||
/// directory, optionally including all subdirectories.
|
||||
/// </summary>
|
||||
/// <param name="dir">The directory to search.</param>
|
||||
/// <param name="includeSubdirectories">True to include subdirectories
|
||||
/// in the search.</param>
|
||||
/// <returns>A list of file paths relative to the directory.</returns>
|
||||
internal IList<string> GetRelativeFilePathsInDirectoryTree(
|
||||
string dir, bool includeSubdirectories)
|
||||
{
|
||||
IList<string> fileList = new List<string>();
|
||||
this.RecursiveGetRelativeFilePathsInDirectoryTree(
|
||||
dir, String.Empty, includeSubdirectories, fileList);
|
||||
return fileList;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves information about one file from this archive.
|
||||
/// </summary>
|
||||
/// <param name="path">Path of the file in the archive.</param>
|
||||
/// <returns>File information, or null if the file was not found
|
||||
/// in the archive.</returns>
|
||||
internal ArchiveFileInfo GetFile(string path)
|
||||
{
|
||||
IList<ArchiveFileInfo> files = this.InternalGetFiles(
|
||||
delegate(string match)
|
||||
{
|
||||
return String.Compare(
|
||||
match, path, true, CultureInfo.InvariantCulture) == 0;
|
||||
});
|
||||
return (files != null && files.Count > 0 ? files[0] : null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a compression engine that does the low-level work for
|
||||
/// this object.
|
||||
/// </summary>
|
||||
/// <returns>A new compression engine instance that matches the specific
|
||||
/// subclass of archive.</returns>
|
||||
/// <remarks>
|
||||
/// Each instance will be <see cref="CompressionEngine.Dispose()"/>d
|
||||
/// immediately after use.
|
||||
/// </remarks>
|
||||
protected abstract CompressionEngine CreateCompressionEngine();
|
||||
|
||||
/// <summary>
|
||||
/// Creates a case-insensitive dictionary mapping from one list of
|
||||
/// strings to the other.
|
||||
/// </summary>
|
||||
/// <param name="keys">List of keys.</param>
|
||||
/// <param name="values">List of values that are mapped 1-to-1 to
|
||||
/// the keys.</param>
|
||||
/// <returns>A filled dictionary of the strings.</returns>
|
||||
private static IDictionary<string, string> CreateStringDictionary(
|
||||
IList<string> keys, IList<string> values)
|
||||
{
|
||||
IDictionary<string, string> stringDict =
|
||||
new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||
for (int i = 0; i < keys.Count; i++)
|
||||
{
|
||||
stringDict.Add(keys[i], values[i]);
|
||||
}
|
||||
|
||||
return stringDict;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Recursive-descent helper function for
|
||||
/// GetRelativeFilePathsInDirectoryTree.
|
||||
/// </summary>
|
||||
/// <param name="dir">The root directory of the search.</param>
|
||||
/// <param name="relativeDir">The relative directory to be
|
||||
/// processed now.</param>
|
||||
/// <param name="includeSubdirectories">True to descend into
|
||||
/// subdirectories.</param>
|
||||
/// <param name="fileList">List of files found so far.</param>
|
||||
private void RecursiveGetRelativeFilePathsInDirectoryTree(
|
||||
string dir,
|
||||
string relativeDir,
|
||||
bool includeSubdirectories,
|
||||
IList<string> fileList)
|
||||
{
|
||||
foreach (string file in System.IO.Directory.GetFiles(dir))
|
||||
{
|
||||
string fileName = Path.GetFileName(file);
|
||||
fileList.Add(Path.Combine(relativeDir, fileName));
|
||||
}
|
||||
|
||||
if (includeSubdirectories)
|
||||
{
|
||||
foreach (string subDir in System.IO.Directory.GetDirectories(dir))
|
||||
{
|
||||
string subDirName = Path.GetFileName(subDir);
|
||||
this.RecursiveGetRelativeFilePathsInDirectoryTree(
|
||||
Path.Combine(dir, subDirName),
|
||||
Path.Combine(relativeDir, subDirName),
|
||||
includeSubdirectories,
|
||||
fileList);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Uses a CompressionEngine to get ArchiveFileInfo objects from this
|
||||
/// archive, and then associates them with this ArchiveInfo instance.
|
||||
/// </summary>
|
||||
/// <param name="fileFilter">Optional predicate that can determine
|
||||
/// which files to process.</param>
|
||||
/// <returns>A list of <see cref="ArchiveFileInfo"/> objects, each
|
||||
/// containing information about a file in the archive.</returns>
|
||||
private IList<ArchiveFileInfo> InternalGetFiles(Predicate<string> fileFilter)
|
||||
{
|
||||
using (CompressionEngine compressionEngine = this.CreateCompressionEngine())
|
||||
{
|
||||
ArchiveFileStreamContext streamContext =
|
||||
new ArchiveFileStreamContext(this.FullName, null, null);
|
||||
streamContext.EnableOffsetOpen = true;
|
||||
IList<ArchiveFileInfo> files =
|
||||
compressionEngine.GetFileInfo(streamContext, fileFilter);
|
||||
for (int i = 0; i < files.Count; i++)
|
||||
{
|
||||
files[i].Archive = this;
|
||||
}
|
||||
|
||||
return files;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,317 @@
|
|||
//---------------------------------------------------------------------
|
||||
// <copyright file="ArchiveProgressEventArgs.cs" company="Outercurve Foundation">
|
||||
// Copyright (c) 2004, Outercurve Foundation.
|
||||
// This software is released under Microsoft Reciprocal License (MS-RL).
|
||||
// The license and further copyright text can be found in the file LICENSE.TXT
|
||||
// LICENSE.TXT at the root directory of the distribution.
|
||||
// </copyright>
|
||||
// <summary>
|
||||
// Part of the Deployment Tools Foundation project.
|
||||
// </summary>
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Deployment.Compression
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
/// <summary>
|
||||
/// Contains the data reported in an archive progress event.
|
||||
/// </summary>
|
||||
public class ArchiveProgressEventArgs : EventArgs
|
||||
{
|
||||
private ArchiveProgressType progressType;
|
||||
|
||||
private string currentFileName;
|
||||
private int currentFileNumber;
|
||||
private int totalFiles;
|
||||
private long currentFileBytesProcessed;
|
||||
private long currentFileTotalBytes;
|
||||
|
||||
private string currentArchiveName;
|
||||
private short currentArchiveNumber;
|
||||
private short totalArchives;
|
||||
private long currentArchiveBytesProcessed;
|
||||
private long currentArchiveTotalBytes;
|
||||
|
||||
private long fileBytesProcessed;
|
||||
private long totalFileBytes;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new ArchiveProgressEventArgs object from specified event parameters.
|
||||
/// </summary>
|
||||
/// <param name="progressType">type of status message</param>
|
||||
/// <param name="currentFileName">name of the file being processed</param>
|
||||
/// <param name="currentFileNumber">number of the current file being processed</param>
|
||||
/// <param name="totalFiles">total number of files to be processed</param>
|
||||
/// <param name="currentFileBytesProcessed">number of bytes processed so far when compressing or extracting a file</param>
|
||||
/// <param name="currentFileTotalBytes">total number of bytes in the current file</param>
|
||||
/// <param name="currentArchiveName">name of the current Archive</param>
|
||||
/// <param name="currentArchiveNumber">current Archive number, when processing a chained set of Archives</param>
|
||||
/// <param name="totalArchives">total number of Archives in a chained set</param>
|
||||
/// <param name="currentArchiveBytesProcessed">number of compressed bytes processed so far during an extraction</param>
|
||||
/// <param name="currentArchiveTotalBytes">total number of compressed bytes to be processed during an extraction</param>
|
||||
/// <param name="fileBytesProcessed">number of uncompressed file bytes processed so far</param>
|
||||
/// <param name="totalFileBytes">total number of uncompressed file bytes to be processed</param>
|
||||
public ArchiveProgressEventArgs(
|
||||
ArchiveProgressType progressType,
|
||||
string currentFileName,
|
||||
int currentFileNumber,
|
||||
int totalFiles,
|
||||
long currentFileBytesProcessed,
|
||||
long currentFileTotalBytes,
|
||||
string currentArchiveName,
|
||||
int currentArchiveNumber,
|
||||
int totalArchives,
|
||||
long currentArchiveBytesProcessed,
|
||||
long currentArchiveTotalBytes,
|
||||
long fileBytesProcessed,
|
||||
long totalFileBytes)
|
||||
{
|
||||
this.progressType = progressType;
|
||||
this.currentFileName = currentFileName;
|
||||
this.currentFileNumber = currentFileNumber;
|
||||
this.totalFiles = totalFiles;
|
||||
this.currentFileBytesProcessed = currentFileBytesProcessed;
|
||||
this.currentFileTotalBytes = currentFileTotalBytes;
|
||||
this.currentArchiveName = currentArchiveName;
|
||||
this.currentArchiveNumber = (short) currentArchiveNumber;
|
||||
this.totalArchives = (short) totalArchives;
|
||||
this.currentArchiveBytesProcessed = currentArchiveBytesProcessed;
|
||||
this.currentArchiveTotalBytes = currentArchiveTotalBytes;
|
||||
this.fileBytesProcessed = fileBytesProcessed;
|
||||
this.totalFileBytes = totalFileBytes;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type of status message.
|
||||
/// </summary>
|
||||
/// <value>A <see cref="ArchiveProgressType"/> value indicating what type of progress event occurred.</value>
|
||||
/// <remarks>
|
||||
/// The handler may choose to ignore some types of progress events.
|
||||
/// For example, if the handler will only list each file as it is
|
||||
/// compressed/extracted, it can ignore events that
|
||||
/// are not of type <see cref="ArchiveProgressType.FinishFile"/>.
|
||||
/// </remarks>
|
||||
public ArchiveProgressType ProgressType
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.progressType;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the file being processed. (The name of the file within the Archive; not the external
|
||||
/// file path.) Also includes the internal path of the file, if any. Valid for
|
||||
/// <see cref="ArchiveProgressType.StartFile"/>, <see cref="ArchiveProgressType.PartialFile"/>,
|
||||
/// and <see cref="ArchiveProgressType.FinishFile"/> messages.
|
||||
/// </summary>
|
||||
/// <value>The name of the file currently being processed, or null if processing
|
||||
/// is currently at the stream or archive level.</value>
|
||||
public string CurrentFileName
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.currentFileName;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of the current file being processed. The first file is number 0, and the last file
|
||||
/// is <see cref="TotalFiles"/>-1. Valid for <see cref="ArchiveProgressType.StartFile"/>,
|
||||
/// <see cref="ArchiveProgressType.PartialFile"/>, and <see cref="ArchiveProgressType.FinishFile"/> messages.
|
||||
/// </summary>
|
||||
/// <value>The number of the file currently being processed, or the most recent
|
||||
/// file processed if processing is currently at the stream or archive level.</value>
|
||||
public int CurrentFileNumber
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.currentFileNumber;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the total number of files to be processed. Valid for all message types.
|
||||
/// </summary>
|
||||
/// <value>The total number of files to be processed that are known so far.</value>
|
||||
public int TotalFiles
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.totalFiles;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of bytes processed so far when compressing or extracting a file. Valid for
|
||||
/// <see cref="ArchiveProgressType.StartFile"/>, <see cref="ArchiveProgressType.PartialFile"/>,
|
||||
/// and <see cref="ArchiveProgressType.FinishFile"/> messages.
|
||||
/// </summary>
|
||||
/// <value>The number of uncompressed bytes processed so far for the current file,
|
||||
/// or 0 if processing is currently at the stream or archive level.</value>
|
||||
public long CurrentFileBytesProcessed
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.currentFileBytesProcessed;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the total number of bytes in the current file. Valid for <see cref="ArchiveProgressType.StartFile"/>,
|
||||
/// <see cref="ArchiveProgressType.PartialFile"/>, and <see cref="ArchiveProgressType.FinishFile"/> messages.
|
||||
/// </summary>
|
||||
/// <value>The uncompressed size of the current file being processed,
|
||||
/// or 0 if processing is currently at the stream or archive level.</value>
|
||||
public long CurrentFileTotalBytes
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.currentFileTotalBytes;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the current archive. Not necessarily the name of the archive on disk.
|
||||
/// Valid for all message types.
|
||||
/// </summary>
|
||||
/// <value>The name of the current archive, or an empty string if no name was specified.</value>
|
||||
public string CurrentArchiveName
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.currentArchiveName;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current archive number, when processing a chained set of archives. Valid for all message types.
|
||||
/// </summary>
|
||||
/// <value>The number of the current archive.</value>
|
||||
/// <remarks>The first archive is number 0, and the last archive is
|
||||
/// <see cref="TotalArchives"/>-1.</remarks>
|
||||
public int CurrentArchiveNumber
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.currentArchiveNumber;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the total number of known archives in a chained set. Valid for all message types.
|
||||
/// </summary>
|
||||
/// <value>The total number of known archives in a chained set.</value>
|
||||
/// <remarks>
|
||||
/// When using the compression option to auto-split into multiple archives based on data size,
|
||||
/// this value will not be accurate until the end.
|
||||
/// </remarks>
|
||||
public int TotalArchives
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.totalArchives;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of compressed bytes processed so far during extraction
|
||||
/// of the current archive. Valid for all extraction messages.
|
||||
/// </summary>
|
||||
/// <value>The number of compressed bytes processed so far during extraction
|
||||
/// of the current archive.</value>
|
||||
public long CurrentArchiveBytesProcessed
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.currentArchiveBytesProcessed;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the total number of compressed bytes to be processed during extraction
|
||||
/// of the current archive. Valid for all extraction messages.
|
||||
/// </summary>
|
||||
/// <value>The total number of compressed bytes to be processed during extraction
|
||||
/// of the current archive.</value>
|
||||
public long CurrentArchiveTotalBytes
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.currentArchiveTotalBytes;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of uncompressed bytes processed so far among all files. Valid for all message types.
|
||||
/// </summary>
|
||||
/// <value>The number of uncompressed file bytes processed so far among all files.</value>
|
||||
/// <remarks>
|
||||
/// When compared to <see cref="TotalFileBytes"/>, this can be used as a measure of overall progress.
|
||||
/// </remarks>
|
||||
public long FileBytesProcessed
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.fileBytesProcessed;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the total number of uncompressed file bytes to be processed. Valid for all message types.
|
||||
/// </summary>
|
||||
/// <value>The total number of uncompressed bytes to be processed among all files.</value>
|
||||
public long TotalFileBytes
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.totalFileBytes;
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
|
||||
/// <summary>
|
||||
/// Creates a string representation of the progress event.
|
||||
/// </summary>
|
||||
/// <returns>a listing of all event parameters and values</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
string formatString =
|
||||
"{0}\n" +
|
||||
"\t CurrentFileName = {1}\n" +
|
||||
"\t CurrentFileNumber = {2}\n" +
|
||||
"\t TotalFiles = {3}\n" +
|
||||
"\t CurrentFileBytesProcessed = {4}\n" +
|
||||
"\t CurrentFileTotalBytes = {5}\n" +
|
||||
"\t CurrentArchiveName = {6}\n" +
|
||||
"\t CurrentArchiveNumber = {7}\n" +
|
||||
"\t TotalArchives = {8}\n" +
|
||||
"\t CurrentArchiveBytesProcessed = {9}\n" +
|
||||
"\t CurrentArchiveTotalBytes = {10}\n" +
|
||||
"\t FileBytesProcessed = {11}\n" +
|
||||
"\t TotalFileBytes = {12}\n";
|
||||
return String.Format(
|
||||
System.Globalization.CultureInfo.InvariantCulture,
|
||||
formatString,
|
||||
this.ProgressType,
|
||||
this.CurrentFileName,
|
||||
this.CurrentFileNumber,
|
||||
this.TotalFiles,
|
||||
this.CurrentFileBytesProcessed,
|
||||
this.CurrentFileTotalBytes,
|
||||
this.CurrentArchiveName,
|
||||
this.CurrentArchiveNumber,
|
||||
this.TotalArchives,
|
||||
this.CurrentArchiveBytesProcessed,
|
||||
this.CurrentArchiveTotalBytes,
|
||||
this.FileBytesProcessed,
|
||||
this.TotalFileBytes);
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
//---------------------------------------------------------------------
|
||||
// <copyright file="ArchiveProgressType.cs" company="Outercurve Foundation">
|
||||
// Copyright (c) 2004, Outercurve Foundation.
|
||||
// This software is released under Microsoft Reciprocal License (MS-RL).
|
||||
// The license and further copyright text can be found in the file LICENSE.TXT
|
||||
// LICENSE.TXT at the root directory of the distribution.
|
||||
// </copyright>
|
||||
// <summary>
|
||||
// Part of the Deployment Tools Foundation project.
|
||||
// </summary>
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Deployment.Compression
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
/// <summary>
|
||||
/// The type of progress event.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <p>PACKING EXAMPLE: The following sequence of events might be received when
|
||||
/// extracting a simple archive file with 2 files.</p>
|
||||
/// <list type="table">
|
||||
/// <listheader><term>Message Type</term><description>Description</description></listheader>
|
||||
/// <item><term>StartArchive</term> <description>Begin extracting archive</description></item>
|
||||
/// <item><term>StartFile</term> <description>Begin extracting first file</description></item>
|
||||
/// <item><term>PartialFile</term> <description>Extracting first file</description></item>
|
||||
/// <item><term>PartialFile</term> <description>Extracting first file</description></item>
|
||||
/// <item><term>FinishFile</term> <description>Finished extracting first file</description></item>
|
||||
/// <item><term>StartFile</term> <description>Begin extracting second file</description></item>
|
||||
/// <item><term>PartialFile</term> <description>Extracting second file</description></item>
|
||||
/// <item><term>FinishFile</term> <description>Finished extracting second file</description></item>
|
||||
/// <item><term>FinishArchive</term><description>Finished extracting archive</description></item>
|
||||
/// </list>
|
||||
/// <p></p>
|
||||
/// <p>UNPACKING EXAMPLE: Packing 3 files into 2 archive chunks, where the second file is
|
||||
/// continued to the second archive chunk.</p>
|
||||
/// <list type="table">
|
||||
/// <listheader><term>Message Type</term><description>Description</description></listheader>
|
||||
/// <item><term>StartFile</term> <description>Begin compressing first file</description></item>
|
||||
/// <item><term>FinishFile</term> <description>Finished compressing first file</description></item>
|
||||
/// <item><term>StartFile</term> <description>Begin compressing second file</description></item>
|
||||
/// <item><term>PartialFile</term> <description>Compressing second file</description></item>
|
||||
/// <item><term>PartialFile</term> <description>Compressing second file</description></item>
|
||||
/// <item><term>FinishFile</term> <description>Finished compressing second file</description></item>
|
||||
/// <item><term>StartArchive</term> <description>Begin writing first archive</description></item>
|
||||
/// <item><term>PartialArchive</term><description>Writing first archive</description></item>
|
||||
/// <item><term>FinishArchive</term> <description>Finished writing first archive</description></item>
|
||||
/// <item><term>StartFile</term> <description>Begin compressing third file</description></item>
|
||||
/// <item><term>PartialFile</term> <description>Compressing third file</description></item>
|
||||
/// <item><term>FinishFile</term> <description>Finished compressing third file</description></item>
|
||||
/// <item><term>StartArchive</term> <description>Begin writing second archive</description></item>
|
||||
/// <item><term>PartialArchive</term><description>Writing second archive</description></item>
|
||||
/// <item><term>FinishArchive</term> <description>Finished writing second archive</description></item>
|
||||
/// </list>
|
||||
/// </remarks>
|
||||
public enum ArchiveProgressType : int
|
||||
{
|
||||
/// <summary>Status message before beginning the packing or unpacking an individual file.</summary>
|
||||
StartFile,
|
||||
|
||||
/// <summary>Status message (possibly reported multiple times) during the process of packing or unpacking a file.</summary>
|
||||
PartialFile,
|
||||
|
||||
/// <summary>Status message after completion of the packing or unpacking an individual file.</summary>
|
||||
FinishFile,
|
||||
|
||||
/// <summary>Status message before beginning the packing or unpacking an archive.</summary>
|
||||
StartArchive,
|
||||
|
||||
/// <summary>Status message (possibly reported multiple times) during the process of packing or unpacking an archiv.</summary>
|
||||
PartialArchive,
|
||||
|
||||
/// <summary>Status message after completion of the packing or unpacking of an archive.</summary>
|
||||
FinishArchive,
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
//---------------------------------------------------------------------
|
||||
// <copyright file="AssemblyInfo.cs" company="Outercurve Foundation">
|
||||
// Copyright (c) 2004, Outercurve Foundation.
|
||||
// This software is released under Microsoft Reciprocal License (MS-RL).
|
||||
// The license and further copyright text can be found in the file LICENSE.TXT
|
||||
// LICENSE.TXT at the root directory of the distribution.
|
||||
// </copyright>
|
||||
// <summary>
|
||||
// Part of the Deployment Tools Foundation project.
|
||||
// </summary>
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security;
|
||||
using System.Security.Permissions;
|
||||
|
||||
[assembly: AssemblyDescription("Abstract base libraries for archive packing and unpacking")]
|
||||
[assembly: CLSCompliant(true)]
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
[assembly: SecurityPermission(SecurityAction.RequestMinimum, Unrestricted = true)]
|
||||
|
||||
// SECURITY: Review carefully!
|
||||
// This assembly is designed so that partially trusted callers should be able to
|
||||
// do compression and extraction in a file path where they have limited
|
||||
// file I/O permission. Or they can even do in-memory compression and extraction
|
||||
// with absolutely no file I/O permission.
|
||||
[assembly: AllowPartiallyTrustedCallers]
|
|
@ -0,0 +1,100 @@
|
|||
//---------------------------------------------------------------------
|
||||
// <copyright file="BasicUnpackStreamContext.cs" company="Outercurve Foundation">
|
||||
// Copyright (c) 2004, Outercurve Foundation.
|
||||
// This software is released under Microsoft Reciprocal License (MS-RL).
|
||||
// The license and further copyright text can be found in the file LICENSE.TXT
|
||||
// LICENSE.TXT at the root directory of the distribution.
|
||||
// </copyright>
|
||||
// <summary>
|
||||
// Part of the Deployment Tools Foundation project.
|
||||
// </summary>
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Deployment.Compression
|
||||
{
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
/// <summary>
|
||||
/// Stream context used to extract a single file from an archive into a memory stream.
|
||||
/// </summary>
|
||||
[SuppressMessage("Microsoft.Design", "CA1001:TypesThatOwnDisposableFieldsShouldBeDisposable")]
|
||||
public class BasicUnpackStreamContext : IUnpackStreamContext
|
||||
{
|
||||
private Stream archiveStream;
|
||||
private Stream fileStream;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new BasicExtractStreamContext that reads from the specified archive stream.
|
||||
/// </summary>
|
||||
/// <param name="archiveStream">Archive stream to read from.</param>
|
||||
public BasicUnpackStreamContext(Stream archiveStream)
|
||||
{
|
||||
this.archiveStream = archiveStream;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the stream for the extracted file, or null if no file was extracted.
|
||||
/// </summary>
|
||||
public Stream FileStream
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.fileStream;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens the archive stream for reading. Returns a DuplicateStream instance,
|
||||
/// so the stream may be virtually opened multiple times.
|
||||
/// </summary>
|
||||
/// <param name="archiveNumber">The archive number to open (ignored; 0 is assumed).</param>
|
||||
/// <param name="archiveName">The name of the archive being opened.</param>
|
||||
/// <param name="compressionEngine">Instance of the compression engine doing the operations.</param>
|
||||
/// <returns>A stream from which archive bytes are read.</returns>
|
||||
public Stream OpenArchiveReadStream(int archiveNumber, string archiveName, CompressionEngine compressionEngine)
|
||||
{
|
||||
return new DuplicateStream(this.archiveStream);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Does *not* close the stream. The archive stream should be managed by
|
||||
/// the code that invokes the archive extraction.
|
||||
/// </summary>
|
||||
/// <param name="archiveNumber">The archive number of the stream to close.</param>
|
||||
/// <param name="archiveName">The name of the archive being closed.</param>
|
||||
/// <param name="stream">The stream being closed.</param>
|
||||
public void CloseArchiveReadStream(int archiveNumber, string archiveName, Stream stream)
|
||||
{
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens a stream for writing extracted file bytes. The returned stream is a MemoryStream
|
||||
/// instance, so the file is extracted straight into memory.
|
||||
/// </summary>
|
||||
/// <param name="path">Path of the file within the archive.</param>
|
||||
/// <param name="fileSize">The uncompressed size of the file to be extracted.</param>
|
||||
/// <param name="lastWriteTime">The last write time of the file.</param>
|
||||
/// <returns>A stream where extracted file bytes are to be written.</returns>
|
||||
public Stream OpenFileWriteStream(string path, long fileSize, DateTime lastWriteTime)
|
||||
{
|
||||
this.fileStream = new MemoryStream(new byte[fileSize], 0, (int) fileSize, true, true);
|
||||
return this.fileStream;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Does *not* close the file stream. The file stream is saved in memory so it can
|
||||
/// be read later.
|
||||
/// </summary>
|
||||
/// <param name="path">Path of the file within the archive.</param>
|
||||
/// <param name="stream">The file stream to be closed.</param>
|
||||
/// <param name="attributes">The attributes of the extracted file.</param>
|
||||
/// <param name="lastWriteTime">The last write time of the file.</param>
|
||||
public void CloseFileWriteStream(string path, Stream stream, FileAttributes attributes, DateTime lastWriteTime)
|
||||
{
|
||||
// Do nothing.
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,202 @@
|
|||
//---------------------------------------------------------------------
|
||||
// <copyright file="CargoStream.cs" company="Outercurve Foundation">
|
||||
// Copyright (c) 2004, Outercurve Foundation.
|
||||
// This software is released under Microsoft Reciprocal License (MS-RL).
|
||||
// The license and further copyright text can be found in the file LICENSE.TXT
|
||||
// LICENSE.TXT at the root directory of the distribution.
|
||||
// </copyright>
|
||||
// <summary>
|
||||
// Part of the Deployment Tools Foundation project.
|
||||
// </summary>
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Deployment.Compression
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
/// <summary>
|
||||
/// Wraps a source stream and carries additional items that are disposed when the stream is closed.
|
||||
/// </summary>
|
||||
public class CargoStream : Stream
|
||||
{
|
||||
private Stream source;
|
||||
private List<IDisposable> cargo;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new a cargo stream.
|
||||
/// </summary>
|
||||
/// <param name="source">source of the stream</param>
|
||||
/// <param name="cargo">List of additional items that are disposed when the stream is closed.
|
||||
/// The order of the list is the order in which the items are disposed.</param>
|
||||
public CargoStream(Stream source, params IDisposable[] cargo)
|
||||
{
|
||||
if (source == null)
|
||||
{
|
||||
throw new ArgumentNullException("source");
|
||||
}
|
||||
|
||||
this.source = source;
|
||||
this.cargo = new List<IDisposable>(cargo);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the source stream of the cargo stream.
|
||||
/// </summary>
|
||||
public Stream Source
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.source;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the list of additional items that are disposed when the stream is closed.
|
||||
/// The order of the list is the order in which the items are disposed. The contents can be modified any time.
|
||||
/// </summary>
|
||||
public IList<IDisposable> Cargo
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.cargo;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the source stream supports reading.
|
||||
/// </summary>
|
||||
/// <value>true if the stream supports reading; otherwise, false.</value>
|
||||
public override bool CanRead
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.source.CanRead;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the source stream supports writing.
|
||||
/// </summary>
|
||||
/// <value>true if the stream supports writing; otherwise, false.</value>
|
||||
public override bool CanWrite
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.source.CanWrite;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the source stream supports seeking.
|
||||
/// </summary>
|
||||
/// <value>true if the stream supports seeking; otherwise, false.</value>
|
||||
public override bool CanSeek
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.source.CanSeek;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the length of the source stream.
|
||||
/// </summary>
|
||||
public override long Length
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.source.Length;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the position of the source stream.
|
||||
/// </summary>
|
||||
public override long Position
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.source.Position;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
this.source.Position = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Flushes the source stream.
|
||||
/// </summary>
|
||||
public override void Flush()
|
||||
{
|
||||
this.source.Flush();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the length of the source stream.
|
||||
/// </summary>
|
||||
/// <param name="value">The desired length of the stream in bytes.</param>
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
this.source.SetLength(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Closes the source stream and also closes the additional objects that are carried.
|
||||
/// </summary>
|
||||
public override void Close()
|
||||
{
|
||||
this.source.Close();
|
||||
|
||||
foreach (IDisposable cargoObject in this.cargo)
|
||||
{
|
||||
cargoObject.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads from the source stream.
|
||||
/// </summary>
|
||||
/// <param name="buffer">An array of bytes. When this method returns, the buffer
|
||||
/// contains the specified byte array with the values between offset and
|
||||
/// (offset + count - 1) replaced by the bytes read from the source.</param>
|
||||
/// <param name="offset">The zero-based byte offset in buffer at which to begin
|
||||
/// storing the data read from the stream.</param>
|
||||
/// <param name="count">The maximum number of bytes to be read from the stream.</param>
|
||||
/// <returns>The total number of bytes read into the buffer. This can be less
|
||||
/// than the number of bytes requested if that many bytes are not currently available,
|
||||
/// or zero (0) if the end of the stream has been reached.</returns>
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
return this.source.Read(buffer, offset, count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes to the source stream.
|
||||
/// </summary>
|
||||
/// <param name="buffer">An array of bytes. This method copies count
|
||||
/// bytes from buffer to the stream.</param>
|
||||
/// <param name="offset">The zero-based byte offset in buffer at which
|
||||
/// to begin copying bytes to the stream.</param>
|
||||
/// <param name="count">The number of bytes to be written to the stream.</param>
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
this.source.Write(buffer, offset, count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Changes the position of the source stream.
|
||||
/// </summary>
|
||||
/// <param name="offset">A byte offset relative to the origin parameter.</param>
|
||||
/// <param name="origin">A value of type SeekOrigin indicating the reference
|
||||
/// point used to obtain the new position.</param>
|
||||
/// <returns>The new position within the stream.</returns>
|
||||
public override long Seek(long offset, SeekOrigin origin)
|
||||
{
|
||||
return this.source.Seek(offset, origin);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,175 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ClassDiagram MajorVersion="1" MinorVersion="1">
|
||||
<Comment CommentText="File-based classes">
|
||||
<Position X="2.35" Y="1.442" Height="0.408" Width="0.783" />
|
||||
</Comment>
|
||||
<Comment CommentText="Stream-based classes">
|
||||
<Position X="9.649" Y="1.317" Height="0.4" Width="0.996" />
|
||||
</Comment>
|
||||
<Class Name="Microsoft.Deployment.Compression.ArchiveException" Collapsed="true">
|
||||
<Position X="3" Y="4.25" Width="2" />
|
||||
<TypeIdentifier>
|
||||
<HashCode>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=</HashCode>
|
||||
<FileName>ArchiveException.cs</FileName>
|
||||
</TypeIdentifier>
|
||||
</Class>
|
||||
<Class Name="Microsoft.Deployment.Compression.ArchiveFileInfo">
|
||||
<Position X="3" Y="0.5" Width="2" />
|
||||
<Members>
|
||||
<Method Name="ArchiveFileInfo" Hidden="true" />
|
||||
<Field Name="archiveInfo" Hidden="true" />
|
||||
<Field Name="archiveNumber" Hidden="true" />
|
||||
<Field Name="attributes" Hidden="true" />
|
||||
<Field Name="exists" Hidden="true" />
|
||||
<Method Name="GetObjectData" Hidden="true" />
|
||||
<Field Name="initialized" Hidden="true" />
|
||||
<Field Name="lastWriteTime" Hidden="true" />
|
||||
<Field Name="length" Hidden="true" />
|
||||
<Field Name="name" Hidden="true" />
|
||||
<Field Name="path" Hidden="true" />
|
||||
</Members>
|
||||
<TypeIdentifier>
|
||||
<HashCode>AAAgAAAAIRJAAIMEAEACgARwAAEEEAAAASAAAAEAIAA=</HashCode>
|
||||
<FileName>ArchiveFileInfo.cs</FileName>
|
||||
</TypeIdentifier>
|
||||
</Class>
|
||||
<Class Name="Microsoft.Deployment.Compression.ArchiveInfo">
|
||||
<Position X="0.5" Y="0.5" Width="2.25" />
|
||||
<Members>
|
||||
<Method Name="ArchiveInfo" Hidden="true" />
|
||||
<Method Name="CreateStringDictionary" Hidden="true" />
|
||||
<Method Name="GetFile" Hidden="true" />
|
||||
<Method Name="GetRelativeFilePathsInDirectoryTree" Hidden="true" />
|
||||
<Method Name="InternalGetFiles" Hidden="true" />
|
||||
<Method Name="RecursiveGetRelativeFilePathsInDirectoryTree" Hidden="true" />
|
||||
</Members>
|
||||
<TypeIdentifier>
|
||||
<HashCode>AAEAABAAIAAAAgQEAAgBAARAHAEJACAAAABEAAkAMAI=</HashCode>
|
||||
<FileName>ArchiveInfo.cs</FileName>
|
||||
</TypeIdentifier>
|
||||
</Class>
|
||||
<Class Name="Microsoft.Deployment.Compression.ArchiveFileStreamContext">
|
||||
<Position X="12.75" Y="0.75" Width="2.25" />
|
||||
<Members>
|
||||
<Field Name="archiveFiles" Hidden="true" />
|
||||
<Method Name="ArchiveFileStreamContext" Hidden="true" />
|
||||
<Method Name="CloseArchiveReadStream" Hidden="true" />
|
||||
<Method Name="CloseArchiveWriteStream" Hidden="true" />
|
||||
<Method Name="CloseFileReadStream" Hidden="true" />
|
||||
<Method Name="CloseFileWriteStream" Hidden="true" />
|
||||
<Field Name="directory" Hidden="true" />
|
||||
<Field Name="enableOffsetOpen" Hidden="true" />
|
||||
<Field Name="extractOnlyNewerFiles" Hidden="true" />
|
||||
<Field Name="files" Hidden="true" />
|
||||
<Method Name="GetArchiveName" Hidden="true" />
|
||||
<Method Name="GetOption" Hidden="true" />
|
||||
<Method Name="OpenArchiveReadStream" Hidden="true" />
|
||||
<Method Name="OpenArchiveWriteStream" Hidden="true" />
|
||||
<Method Name="OpenFileReadStream" Hidden="true" />
|
||||
<Method Name="OpenFileWriteStream" Hidden="true" />
|
||||
<Method Name="TranslateFilePath" Hidden="true" />
|
||||
</Members>
|
||||
<TypeIdentifier>
|
||||
<HashCode>AEQAABgAAACQAACACACAAgAQAAIgAAAAACAMgAAEAKA=</HashCode>
|
||||
<FileName>ArchiveFileStreamContext.cs</FileName>
|
||||
</TypeIdentifier>
|
||||
<Lollipop Position="0.2" />
|
||||
</Class>
|
||||
<Class Name="Microsoft.Deployment.Compression.ArchiveProgressEventArgs">
|
||||
<Position X="5.25" Y="0.5" Width="2.25" />
|
||||
<Members>
|
||||
<Method Name="ArchiveProgressEventArgs" Hidden="true" />
|
||||
<Field Name="currentArchiveBytesProcessed" Hidden="true" />
|
||||
<Field Name="currentArchiveName" Hidden="true" />
|
||||
<Field Name="currentArchiveNumber" Hidden="true" />
|
||||
<Field Name="currentArchiveTotalBytes" Hidden="true" />
|
||||
<Field Name="currentFileBytesProcessed" Hidden="true" />
|
||||
<Field Name="currentFileName" Hidden="true" />
|
||||
<Field Name="currentFileNumber" Hidden="true" />
|
||||
<Field Name="currentFileTotalBytes" Hidden="true" />
|
||||
<Field Name="fileBytesProcessed" Hidden="true" />
|
||||
<Field Name="progressType" Hidden="true" />
|
||||
<Field Name="totalArchives" Hidden="true" />
|
||||
<Field Name="totalFileBytes" Hidden="true" />
|
||||
<Field Name="totalFiles" Hidden="true" />
|
||||
</Members>
|
||||
<TypeIdentifier>
|
||||
<HashCode>AAMCAQASACAAABBBAAASUAAAQBAAAMAAAAGQAAgBEAA=</HashCode>
|
||||
<FileName>ArchiveProgressEventArgs.cs</FileName>
|
||||
</TypeIdentifier>
|
||||
</Class>
|
||||
<Class Name="Microsoft.Deployment.Compression.BasicUnpackStreamContext">
|
||||
<Position X="12.75" Y="3" Width="2.25" />
|
||||
<Members>
|
||||
<Field Name="archiveStream" Hidden="true" />
|
||||
<Method Name="BasicUnpackStreamContext" Hidden="true" />
|
||||
<Method Name="CloseArchiveReadStream" Hidden="true" />
|
||||
<Method Name="CloseFileWriteStream" Hidden="true" />
|
||||
<Field Name="fileStream" Hidden="true" />
|
||||
<Method Name="OpenArchiveReadStream" Hidden="true" />
|
||||
<Method Name="OpenFileWriteStream" Hidden="true" />
|
||||
</Members>
|
||||
<TypeIdentifier>
|
||||
<HashCode>AAAAAAgAAACEAAAAAAAAAAAAAAAgAAAAIAAMAAAAAAA=</HashCode>
|
||||
<FileName>BasicUnpackStreamContext.cs</FileName>
|
||||
</TypeIdentifier>
|
||||
<Lollipop Position="0.2" />
|
||||
</Class>
|
||||
<Class Name="Microsoft.Deployment.Compression.CompressionEngine">
|
||||
<Position X="8" Y="0.5" Width="2.25" />
|
||||
<Members>
|
||||
<Method Name="~CompressionEngine" Hidden="true" />
|
||||
<Method Name="CompressionEngine" Hidden="true" />
|
||||
<Field Name="compressionLevel" Hidden="true" />
|
||||
<Field Name="dontUseTempFiles" Hidden="true" />
|
||||
</Members>
|
||||
<TypeIdentifier>
|
||||
<HashCode>AAAEAAAABCBAACRgAAAAAAQAAEAAAAAAQAEAAAiAAAI=</HashCode>
|
||||
<FileName>CompressionEngine.cs</FileName>
|
||||
</TypeIdentifier>
|
||||
<Lollipop Position="0.2" />
|
||||
</Class>
|
||||
<Class Name="Microsoft.Deployment.Compression.DuplicateStream" Collapsed="true">
|
||||
<Position X="10.5" Y="4.25" Width="2" />
|
||||
<TypeIdentifier>
|
||||
<HashCode>AAAAAEAAAgAAQAIgGAAAIABgAAAAAAAAAAAAAAGIACA=</HashCode>
|
||||
<FileName>DuplicateStream.cs</FileName>
|
||||
</TypeIdentifier>
|
||||
</Class>
|
||||
<Class Name="Microsoft.Deployment.Compression.OffsetStream" Collapsed="true">
|
||||
<Position X="8" Y="4.25" Width="2" />
|
||||
<TypeIdentifier>
|
||||
<HashCode>AAAAAAAAAgAAQAIgGAAAAABgAAAAAEAgAAAAAAGIwCA=</HashCode>
|
||||
<FileName>OffsetStream.cs</FileName>
|
||||
</TypeIdentifier>
|
||||
</Class>
|
||||
<Interface Name="Microsoft.Deployment.Compression.IPackStreamContext">
|
||||
<Position X="10.5" Y="0.5" Width="2" />
|
||||
<TypeIdentifier>
|
||||
<HashCode>AAAAAAAAAAAAAACAAACAAAAQAAAgAAAAACAIAAAAAAA=</HashCode>
|
||||
<FileName>IPackStreamContext.cs</FileName>
|
||||
</TypeIdentifier>
|
||||
</Interface>
|
||||
<Interface Name="Microsoft.Deployment.Compression.IUnpackStreamContext">
|
||||
<Position X="10.5" Y="2.5" Width="2" />
|
||||
<TypeIdentifier>
|
||||
<HashCode>AAAAAAgAAACAAAAAAAAAAAAAAAAAAAAAAAAMAAAAAAA=</HashCode>
|
||||
<FileName>IUnpackStreamContext.cs</FileName>
|
||||
</TypeIdentifier>
|
||||
</Interface>
|
||||
<Enum Name="Microsoft.Deployment.Compression.ArchiveProgressType" Collapsed="true">
|
||||
<Position X="5.25" Y="3.75" Width="2" />
|
||||
<TypeIdentifier>
|
||||
<HashCode>QAAAAAAAAAAAAIAAgAAAAAAAAAQAAAAACIAAAAAAAAA=</HashCode>
|
||||
<FileName>ArchiveProgressType.cs</FileName>
|
||||
</TypeIdentifier>
|
||||
</Enum>
|
||||
<Enum Name="Microsoft.Deployment.Compression.CompressionLevel" Collapsed="true">
|
||||
<Position X="5.25" Y="4.5" Width="2" />
|
||||
<TypeIdentifier>
|
||||
<HashCode>AAAAAAAAABAAAAAAEAAAAAAAAAAIAAAAAAAAAAEAAAA=</HashCode>
|
||||
<FileName>CompressionLevel.cs</FileName>
|
||||
</TypeIdentifier>
|
||||
</Enum>
|
||||
<Font Name="Verdana" Size="8" />
|
||||
</ClassDiagram>
|
|
@ -0,0 +1,50 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<!--
|
||||
<copyright file="Compression.csproj" company="Outercurve Foundation">
|
||||
Copyright (c) 2004, Outercurve Foundation.
|
||||
This software is released under Microsoft Reciprocal License (MS-RL).
|
||||
The license and further copyright text can be found in the file LICENSE.TXT
|
||||
LICENSE.TXT at the root directory of the distribution.
|
||||
</copyright>
|
||||
-->
|
||||
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<ProjectGuid>{2D62850C-9F81-4BE9-BDF3-9379389C8F7B}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>Microsoft.Deployment.Compression</RootNamespace>
|
||||
<AssemblyName>Microsoft.Deployment.Compression</AssemblyName>
|
||||
<CreateDocumentationFile>true</CreateDocumentationFile>
|
||||
<TargetFrameworkVersion>v2.0</TargetFrameworkVersion>
|
||||
<FxCopEnabled>false</FxCopEnabled>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="ArchiveException.cs" />
|
||||
<Compile Include="ArchiveFileInfo.cs" />
|
||||
<Compile Include="ArchiveInfo.cs" />
|
||||
<Compile Include="ArchiveProgressEventArgs.cs" />
|
||||
<Compile Include="ArchiveProgressType.cs" />
|
||||
<Compile Include="AssemblyInfo.cs" />
|
||||
<Compile Include="ArchiveFileStreamContext.cs" />
|
||||
<Compile Include="BasicUnpackStreamContext.cs" />
|
||||
<Compile Include="CompressionEngine.cs" />
|
||||
<Compile Include="CompressionLevel.cs" />
|
||||
<Compile Include="CargoStream.cs" />
|
||||
<Compile Include="DuplicateStream.cs" />
|
||||
<Compile Include="IPackStreamContext.cs" />
|
||||
<Compile Include="IUnpackStreamContext.cs" />
|
||||
<Compile Include="OffsetStream.cs" />
|
||||
<Compile Include="SafeNativeMethods.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="Compression.cd" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
</ItemGroup>
|
||||
|
||||
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), wix.proj))\tools\WixBuild.targets" />
|
||||
</Project>
|
|
@ -0,0 +1,381 @@
|
|||
//---------------------------------------------------------------------
|
||||
// <copyright file="CompressionEngine.cs" company="Outercurve Foundation">
|
||||
// Copyright (c) 2004, Outercurve Foundation.
|
||||
// This software is released under Microsoft Reciprocal License (MS-RL).
|
||||
// The license and further copyright text can be found in the file LICENSE.TXT
|
||||
// LICENSE.TXT at the root directory of the distribution.
|
||||
// </copyright>
|
||||
// <summary>
|
||||
// Part of the Deployment Tools Foundation project.
|
||||
// </summary>
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Deployment.Compression
|
||||
{
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
|
||||
/// <summary>
|
||||
/// Base class for an engine capable of packing and unpacking a particular
|
||||
/// compressed file format.
|
||||
/// </summary>
|
||||
public abstract class CompressionEngine : IDisposable
|
||||
{
|
||||
private CompressionLevel compressionLevel;
|
||||
private bool dontUseTempFiles;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the compression engine base class.
|
||||
/// </summary>
|
||||
protected CompressionEngine()
|
||||
{
|
||||
this.compressionLevel = CompressionLevel.Normal;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes the compression engine.
|
||||
/// </summary>
|
||||
~CompressionEngine()
|
||||
{
|
||||
this.Dispose(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when the compression engine reports progress in packing
|
||||
/// or unpacking an archive.
|
||||
/// </summary>
|
||||
/// <seealso cref="ArchiveProgressType"/>
|
||||
public event EventHandler<ArchiveProgressEventArgs> Progress;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a flag indicating whether temporary files are created
|
||||
/// and used during compression.
|
||||
/// </summary>
|
||||
/// <value>True if temporary files are used; false if compression is done
|
||||
/// entirely in-memory.</value>
|
||||
/// <remarks>The value of this property is true by default. Using temporary
|
||||
/// files can greatly reduce the memory requirement of compression,
|
||||
/// especially when compressing large archives. However, setting this property
|
||||
/// to false may yield slightly better performance when creating small
|
||||
/// archives. Or it may be necessary if the process does not have sufficient
|
||||
/// privileges to create temporary files.</remarks>
|
||||
public bool UseTempFiles
|
||||
{
|
||||
get
|
||||
{
|
||||
return !this.dontUseTempFiles;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
this.dontUseTempFiles = !value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compression level to use when compressing files.
|
||||
/// </summary>
|
||||
/// <value>A compression level ranging from minimum to maximum compression,
|
||||
/// or no compression.</value>
|
||||
public CompressionLevel CompressionLevel
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.compressionLevel;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
this.compressionLevel = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes of resources allocated by the compression engine.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
this.Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an archive.
|
||||
/// </summary>
|
||||
/// <param name="streamContext">A context interface to handle opening
|
||||
/// and closing of archive and file streams.</param>
|
||||
/// <param name="files">The paths of the files in the archive
|
||||
/// (not external file paths).</param>
|
||||
/// <exception cref="ArchiveException">The archive could not be
|
||||
/// created.</exception>
|
||||
/// <remarks>
|
||||
/// The stream context implementation may provide a mapping from the
|
||||
/// file paths within the archive to the external file paths.
|
||||
/// </remarks>
|
||||
public void Pack(IPackStreamContext streamContext, IEnumerable<string> files)
|
||||
{
|
||||
if (files == null)
|
||||
{
|
||||
throw new ArgumentNullException("files");
|
||||
}
|
||||
|
||||
this.Pack(streamContext, files, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an archive or chain of archives.
|
||||
/// </summary>
|
||||
/// <param name="streamContext">A context interface to handle opening
|
||||
/// and closing of archive and file streams.</param>
|
||||
/// <param name="files">The paths of the files in the archive (not
|
||||
/// external file paths).</param>
|
||||
/// <param name="maxArchiveSize">The maximum number of bytes for one
|
||||
/// archive before the contents are chained to the next archive, or zero
|
||||
/// for unlimited archive size.</param>
|
||||
/// <exception cref="ArchiveException">The archive could not be
|
||||
/// created.</exception>
|
||||
/// <remarks>
|
||||
/// The stream context implementation may provide a mapping from the file
|
||||
/// paths within the archive to the external file paths.
|
||||
/// </remarks>
|
||||
public abstract void Pack(
|
||||
IPackStreamContext streamContext,
|
||||
IEnumerable<string> files,
|
||||
long maxArchiveSize);
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether a Stream begins with a header that indicates
|
||||
/// it is a valid archive.
|
||||
/// </summary>
|
||||
/// <param name="stream">Stream for reading the archive file.</param>
|
||||
/// <returns>True if the stream is a valid archive
|
||||
/// (with no offset); false otherwise.</returns>
|
||||
public abstract bool IsArchive(Stream stream);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the offset of an archive that is positioned 0 or more bytes
|
||||
/// from the start of the Stream.
|
||||
/// </summary>
|
||||
/// <param name="stream">A stream for reading the archive.</param>
|
||||
/// <returns>The offset in bytes of the archive,
|
||||
/// or -1 if no archive is found in the Stream.</returns>
|
||||
/// <remarks>The archive must begin on a 4-byte boundary.</remarks>
|
||||
public virtual long FindArchiveOffset(Stream stream)
|
||||
{
|
||||
if (stream == null)
|
||||
{
|
||||
throw new ArgumentNullException("stream");
|
||||
}
|
||||
|
||||
long sectionSize = 4;
|
||||
long length = stream.Length;
|
||||
for (long offset = 0; offset <= length - sectionSize; offset += sectionSize)
|
||||
{
|
||||
stream.Seek(offset, SeekOrigin.Begin);
|
||||
if (this.IsArchive(stream))
|
||||
{
|
||||
return offset;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets information about all files in an archive stream.
|
||||
/// </summary>
|
||||
/// <param name="stream">A stream for reading the archive.</param>
|
||||
/// <returns>Information about all files in the archive stream.</returns>
|
||||
/// <exception cref="ArchiveException">The stream is not a valid
|
||||
/// archive.</exception>
|
||||
public IList<ArchiveFileInfo> GetFileInfo(Stream stream)
|
||||
{
|
||||
return this.GetFileInfo(new BasicUnpackStreamContext(stream), null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets information about files in an archive or archive chain.
|
||||
/// </summary>
|
||||
/// <param name="streamContext">A context interface to handle opening
|
||||
/// and closing of archive and file streams.</param>
|
||||
/// <param name="fileFilter">A predicate that can determine
|
||||
/// which files to process, optional.</param>
|
||||
/// <returns>Information about files in the archive stream.</returns>
|
||||
/// <exception cref="ArchiveException">The archive provided
|
||||
/// by the stream context is not valid.</exception>
|
||||
/// <remarks>
|
||||
/// The <paramref name="fileFilter"/> predicate takes an internal file
|
||||
/// path and returns true to include the file or false to exclude it.
|
||||
/// </remarks>
|
||||
public abstract IList<ArchiveFileInfo> GetFileInfo(
|
||||
IUnpackStreamContext streamContext,
|
||||
Predicate<string> fileFilter);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the list of files in an archive Stream.
|
||||
/// </summary>
|
||||
/// <param name="stream">A stream for reading the archive.</param>
|
||||
/// <returns>A list of the paths of all files contained in the
|
||||
/// archive.</returns>
|
||||
/// <exception cref="ArchiveException">The stream is not a valid
|
||||
/// archive.</exception>
|
||||
public IList<string> GetFiles(Stream stream)
|
||||
{
|
||||
return this.GetFiles(new BasicUnpackStreamContext(stream), null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the list of files in an archive or archive chain.
|
||||
/// </summary>
|
||||
/// <param name="streamContext">A context interface to handle opening
|
||||
/// and closing of archive and file streams.</param>
|
||||
/// <param name="fileFilter">A predicate that can determine
|
||||
/// which files to process, optional.</param>
|
||||
/// <returns>An array containing the names of all files contained in
|
||||
/// the archive or archive chain.</returns>
|
||||
/// <exception cref="ArchiveException">The archive provided
|
||||
/// by the stream context is not valid.</exception>
|
||||
/// <remarks>
|
||||
/// The <paramref name="fileFilter"/> predicate takes an internal file
|
||||
/// path and returns true to include the file or false to exclude it.
|
||||
/// </remarks>
|
||||
public IList<string> GetFiles(
|
||||
IUnpackStreamContext streamContext,
|
||||
Predicate<string> fileFilter)
|
||||
{
|
||||
if (streamContext == null)
|
||||
{
|
||||
throw new ArgumentNullException("streamContext");
|
||||
}
|
||||
|
||||
IList<ArchiveFileInfo> files =
|
||||
this.GetFileInfo(streamContext, fileFilter);
|
||||
IList<string> fileNames = new List<string>(files.Count);
|
||||
for (int i = 0; i < files.Count; i++)
|
||||
{
|
||||
fileNames.Add(files[i].Name);
|
||||
}
|
||||
|
||||
return fileNames;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a single file from an archive stream.
|
||||
/// </summary>
|
||||
/// <param name="stream">A stream for reading the archive.</param>
|
||||
/// <param name="path">The path of the file within the archive
|
||||
/// (not the external file path).</param>
|
||||
/// <returns>A stream for reading the extracted file, or null
|
||||
/// if the file does not exist in the archive.</returns>
|
||||
/// <exception cref="ArchiveException">The stream is not a valid
|
||||
/// archive.</exception>
|
||||
/// <remarks>The entire extracted file is cached in memory, so this
|
||||
/// method requires enough free memory to hold the file.</remarks>
|
||||
public Stream Unpack(Stream stream, string path)
|
||||
{
|
||||
if (stream == null)
|
||||
{
|
||||
throw new ArgumentNullException("stream");
|
||||
}
|
||||
|
||||
if (path == null)
|
||||
{
|
||||
throw new ArgumentNullException("path");
|
||||
}
|
||||
|
||||
BasicUnpackStreamContext streamContext =
|
||||
new BasicUnpackStreamContext(stream);
|
||||
this.Unpack(
|
||||
streamContext,
|
||||
delegate(string match)
|
||||
{
|
||||
return String.Compare(
|
||||
match, path, true, CultureInfo.InvariantCulture) == 0;
|
||||
});
|
||||
|
||||
Stream extractStream = streamContext.FileStream;
|
||||
if (extractStream != null)
|
||||
{
|
||||
extractStream.Position = 0;
|
||||
}
|
||||
|
||||
return extractStream;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extracts files from an archive or archive chain.
|
||||
/// </summary>
|
||||
/// <param name="streamContext">A context interface to handle opening
|
||||
/// and closing of archive and file streams.</param>
|
||||
/// <param name="fileFilter">An optional predicate that can determine
|
||||
/// which files to process.</param>
|
||||
/// <exception cref="ArchiveException">The archive provided
|
||||
/// by the stream context is not valid.</exception>
|
||||
/// <remarks>
|
||||
/// The <paramref name="fileFilter"/> predicate takes an internal file
|
||||
/// path and returns true to include the file or false to exclude it.
|
||||
/// </remarks>
|
||||
public abstract void Unpack(
|
||||
IUnpackStreamContext streamContext,
|
||||
Predicate<string> fileFilter);
|
||||
|
||||
/// <summary>
|
||||
/// Called by sublcasses to distribute a packing or unpacking progress
|
||||
/// event to listeners.
|
||||
/// </summary>
|
||||
/// <param name="e">Event details.</param>
|
||||
protected void OnProgress(ArchiveProgressEventArgs e)
|
||||
{
|
||||
if (this.Progress != null)
|
||||
{
|
||||
this.Progress(this, e);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes of resources allocated by the compression engine.
|
||||
/// </summary>
|
||||
/// <param name="disposing">If true, the method has been called
|
||||
/// directly or indirectly by a user's code, so managed and unmanaged
|
||||
/// resources will be disposed. If false, the method has been called by
|
||||
/// the runtime from inside the finalizer, and only unmanaged resources
|
||||
/// will be disposed.</param>
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compresion utility function for converting old-style
|
||||
/// date and time values to a DateTime structure.
|
||||
/// </summary>
|
||||
public static void DosDateAndTimeToDateTime(
|
||||
short dosDate, short dosTime, out DateTime dateTime)
|
||||
{
|
||||
if (dosDate == 0 && dosTime == 0)
|
||||
{
|
||||
dateTime = DateTime.MinValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
long fileTime;
|
||||
SafeNativeMethods.DosDateTimeToFileTime(dosDate, dosTime, out fileTime);
|
||||
dateTime = DateTime.FromFileTimeUtc(fileTime);
|
||||
dateTime = new DateTime(dateTime.Ticks, DateTimeKind.Local);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compresion utility function for converting a DateTime structure
|
||||
/// to old-style date and time values.
|
||||
/// </summary>
|
||||
public static void DateTimeToDosDateAndTime(
|
||||
DateTime dateTime, out short dosDate, out short dosTime)
|
||||
{
|
||||
dateTime = new DateTime(dateTime.Ticks, DateTimeKind.Utc);
|
||||
long filetime = dateTime.ToFileTimeUtc();
|
||||
SafeNativeMethods.FileTimeToDosDateTime(ref filetime, out dosDate, out dosTime);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
//---------------------------------------------------------------------
|
||||
// <copyright file="CompressionLevel.cs" company="Outercurve Foundation">
|
||||
// Copyright (c) 2004, Outercurve Foundation.
|
||||
// This software is released under Microsoft Reciprocal License (MS-RL).
|
||||
// The license and further copyright text can be found in the file LICENSE.TXT
|
||||
// LICENSE.TXT at the root directory of the distribution.
|
||||
// </copyright>
|
||||
// <summary>
|
||||
// Part of the Deployment Tools Foundation project.
|
||||
// </summary>
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Deployment.Compression
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
/// <summary>
|
||||
/// Specifies the compression level ranging from minimum compresion to
|
||||
/// maximum compression, or no compression at all.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Although only four values are enumerated, any integral value between
|
||||
/// <see cref="CompressionLevel.Min"/> and <see cref="CompressionLevel.Max"/> can also be used.
|
||||
/// </remarks>
|
||||
public enum CompressionLevel
|
||||
{
|
||||
/// <summary>Do not compress files, only store.</summary>
|
||||
None = 0,
|
||||
|
||||
/// <summary>Minimum compression; fastest.</summary>
|
||||
Min = 1,
|
||||
|
||||
/// <summary>A compromize between speed and compression efficiency.</summary>
|
||||
Normal = 6,
|
||||
|
||||
/// <summary>Maximum compression; slowest.</summary>
|
||||
Max = 10
|
||||
}
|
||||
}
|
|
@ -0,0 +1,222 @@
|
|||
//---------------------------------------------------------------------
|
||||
// <copyright file="DuplicateStream.cs" company="Outercurve Foundation">
|
||||
// Copyright (c) 2004, Outercurve Foundation.
|
||||
// This software is released under Microsoft Reciprocal License (MS-RL).
|
||||
// The license and further copyright text can be found in the file LICENSE.TXT
|
||||
// LICENSE.TXT at the root directory of the distribution.
|
||||
// </copyright>
|
||||
// <summary>
|
||||
// Part of the Deployment Tools Foundation project.
|
||||
// </summary>
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Deployment.Compression
|
||||
{
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
/// <summary>
|
||||
/// Duplicates a source stream by maintaining a separate position.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// WARNING: duplicate streams are not thread-safe with respect to each other or the original stream.
|
||||
/// If multiple threads use duplicate copies of the same stream, they must synchronize for any operations.
|
||||
/// </remarks>
|
||||
public class DuplicateStream : Stream
|
||||
{
|
||||
private Stream source;
|
||||
private long position;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new duplicate of a stream.
|
||||
/// </summary>
|
||||
/// <param name="source">source of the duplicate</param>
|
||||
public DuplicateStream(Stream source)
|
||||
{
|
||||
if (source == null)
|
||||
{
|
||||
throw new ArgumentNullException("source");
|
||||
}
|
||||
|
||||
this.source = DuplicateStream.OriginalStream(source);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the original stream that was used to create the duplicate.
|
||||
/// </summary>
|
||||
public Stream Source
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.source;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the source stream supports reading.
|
||||
/// </summary>
|
||||
/// <value>true if the stream supports reading; otherwise, false.</value>
|
||||
public override bool CanRead
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.source.CanRead;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the source stream supports writing.
|
||||
/// </summary>
|
||||
/// <value>true if the stream supports writing; otherwise, false.</value>
|
||||
public override bool CanWrite
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.source.CanWrite;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the source stream supports seeking.
|
||||
/// </summary>
|
||||
/// <value>true if the stream supports seeking; otherwise, false.</value>
|
||||
public override bool CanSeek
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.source.CanSeek;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the length of the source stream.
|
||||
/// </summary>
|
||||
public override long Length
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.source.Length;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the position of the current stream,
|
||||
/// ignoring the position of the source stream.
|
||||
/// </summary>
|
||||
public override long Position
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.position;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
this.position = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the original stream from a possible duplicate stream.
|
||||
/// </summary>
|
||||
/// <param name="stream">Possible duplicate stream.</param>
|
||||
/// <returns>If the stream is a DuplicateStream, returns
|
||||
/// the duplicate's source; otherwise returns the same stream.</returns>
|
||||
public static Stream OriginalStream(Stream stream)
|
||||
{
|
||||
DuplicateStream dupStream = stream as DuplicateStream;
|
||||
return dupStream != null ? dupStream.Source : stream;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Flushes the source stream.
|
||||
/// </summary>
|
||||
public override void Flush()
|
||||
{
|
||||
this.source.Flush();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the length of the source stream.
|
||||
/// </summary>
|
||||
/// <param name="value">The desired length of the stream in bytes.</param>
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
this.source.SetLength(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Closes the underlying stream, effectively closing ALL duplicates.
|
||||
/// </summary>
|
||||
public override void Close()
|
||||
{
|
||||
this.source.Close();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads from the source stream while maintaining a separate position
|
||||
/// and not impacting the source stream's position.
|
||||
/// </summary>
|
||||
/// <param name="buffer">An array of bytes. When this method returns, the buffer
|
||||
/// contains the specified byte array with the values between offset and
|
||||
/// (offset + count - 1) replaced by the bytes read from the current source.</param>
|
||||
/// <param name="offset">The zero-based byte offset in buffer at which to begin
|
||||
/// storing the data read from the current stream.</param>
|
||||
/// <param name="count">The maximum number of bytes to be read from the current stream.</param>
|
||||
/// <returns>The total number of bytes read into the buffer. This can be less
|
||||
/// than the number of bytes requested if that many bytes are not currently available,
|
||||
/// or zero (0) if the end of the stream has been reached.</returns>
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
long saveSourcePosition = this.source.Position;
|
||||
this.source.Position = this.position;
|
||||
int read = this.source.Read(buffer, offset, count);
|
||||
this.position = this.source.Position;
|
||||
this.source.Position = saveSourcePosition;
|
||||
return read;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes to the source stream while maintaining a separate position
|
||||
/// and not impacting the source stream's position.
|
||||
/// </summary>
|
||||
/// <param name="buffer">An array of bytes. This method copies count
|
||||
/// bytes from buffer to the current stream.</param>
|
||||
/// <param name="offset">The zero-based byte offset in buffer at which
|
||||
/// to begin copying bytes to the current stream.</param>
|
||||
/// <param name="count">The number of bytes to be written to the
|
||||
/// current stream.</param>
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
long saveSourcePosition = this.source.Position;
|
||||
this.source.Position = this.position;
|
||||
this.source.Write(buffer, offset, count);
|
||||
this.position = this.source.Position;
|
||||
this.source.Position = saveSourcePosition;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Changes the position of this stream without impacting the
|
||||
/// source stream's position.
|
||||
/// </summary>
|
||||
/// <param name="offset">A byte offset relative to the origin parameter.</param>
|
||||
/// <param name="origin">A value of type SeekOrigin indicating the reference
|
||||
/// point used to obtain the new position.</param>
|
||||
/// <returns>The new position within the current stream.</returns>
|
||||
public override long Seek(long offset, SeekOrigin origin)
|
||||
{
|
||||
long originPosition = 0;
|
||||
if (origin == SeekOrigin.Current)
|
||||
{
|
||||
originPosition = this.position;
|
||||
}
|
||||
else if (origin == SeekOrigin.End)
|
||||
{
|
||||
originPosition = this.Length;
|
||||
}
|
||||
|
||||
this.position = originPosition + offset;
|
||||
return this.position;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,127 @@
|
|||
//---------------------------------------------------------------------
|
||||
// <copyright file="IPackStreamContext.cs" company="Outercurve Foundation">
|
||||
// Copyright (c) 2004, Outercurve Foundation.
|
||||
// This software is released under Microsoft Reciprocal License (MS-RL).
|
||||
// The license and further copyright text can be found in the file LICENSE.TXT
|
||||
// LICENSE.TXT at the root directory of the distribution.
|
||||
// </copyright>
|
||||
// <summary>
|
||||
// Part of the Deployment Tools Foundation project.
|
||||
// </summary>
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Deployment.Compression
|
||||
{
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
/// <summary>
|
||||
/// This interface provides the methods necessary for the
|
||||
/// <see cref="CompressionEngine"/> to open and close streams for archives
|
||||
/// and files. The implementor of this interface can use any kind of logic
|
||||
/// to determine what kind of streams to open and where.
|
||||
/// </summary>
|
||||
public interface IPackStreamContext
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the name of the archive with a specified number.
|
||||
/// </summary>
|
||||
/// <param name="archiveNumber">The 0-based index of the archive
|
||||
/// within the chain.</param>
|
||||
/// <returns>The name of the requested archive. May be an empty string
|
||||
/// for non-chained archives, but may never be null.</returns>
|
||||
/// <remarks>The archive name is the name stored within the archive, used for
|
||||
/// identification of the archive especially among archive chains. That
|
||||
/// name is often, but not necessarily the same as the filename of the
|
||||
/// archive package.</remarks>
|
||||
string GetArchiveName(int archiveNumber);
|
||||
|
||||
/// <summary>
|
||||
/// Opens a stream for writing an archive package.
|
||||
/// </summary>
|
||||
/// <param name="archiveNumber">The 0-based index of the archive within
|
||||
/// the chain.</param>
|
||||
/// <param name="archiveName">The name of the archive that was returned
|
||||
/// by <see cref="GetArchiveName"/>.</param>
|
||||
/// <param name="truncate">True if the stream should be truncated when
|
||||
/// opened (if it already exists); false if an existing stream is being
|
||||
/// re-opened for writing additional data.</param>
|
||||
/// <param name="compressionEngine">Instance of the compression engine
|
||||
/// doing the operations.</param>
|
||||
/// <returns>A writable Stream where the compressed archive bytes will be
|
||||
/// written, or null to cancel the archive creation.</returns>
|
||||
/// <remarks>
|
||||
/// If this method returns null, the archive engine will throw a
|
||||
/// FileNotFoundException.
|
||||
/// </remarks>
|
||||
Stream OpenArchiveWriteStream(
|
||||
int archiveNumber,
|
||||
string archiveName,
|
||||
bool truncate,
|
||||
CompressionEngine compressionEngine);
|
||||
|
||||
/// <summary>
|
||||
/// Closes a stream where an archive package was written.
|
||||
/// </summary>
|
||||
/// <param name="archiveNumber">The 0-based index of the archive within
|
||||
/// the chain.</param>
|
||||
/// <param name="archiveName">The name of the archive that was previously
|
||||
/// returned by
|
||||
/// <see cref="GetArchiveName"/>.</param>
|
||||
/// <param name="stream">A stream that was previously returned by
|
||||
/// <see cref="OpenArchiveWriteStream"/> and is now ready to be closed.</param>
|
||||
/// <remarks>
|
||||
/// If there is another archive package in the chain, then after this stream
|
||||
/// is closed a new stream will be opened.
|
||||
/// </remarks>
|
||||
void CloseArchiveWriteStream(int archiveNumber, string archiveName, Stream stream);
|
||||
|
||||
/// <summary>
|
||||
/// Opens a stream to read a file that is to be included in an archive.
|
||||
/// </summary>
|
||||
/// <param name="path">The path of the file within the archive. This is often,
|
||||
/// but not necessarily, the same as the relative path of the file outside
|
||||
/// the archive.</param>
|
||||
/// <param name="attributes">Returned attributes of the opened file, to be
|
||||
/// stored in the archive.</param>
|
||||
/// <param name="lastWriteTime">Returned last-modified time of the opened file,
|
||||
/// to be stored in the archive.</param>
|
||||
/// <returns>A readable Stream where the file bytes will be read from before
|
||||
/// they are compressed, or null to skip inclusion of the file and continue to
|
||||
/// the next file.</returns>
|
||||
[SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters")]
|
||||
Stream OpenFileReadStream(
|
||||
string path,
|
||||
out FileAttributes attributes,
|
||||
out DateTime lastWriteTime);
|
||||
|
||||
/// <summary>
|
||||
/// Closes a stream that has been used to read a file.
|
||||
/// </summary>
|
||||
/// <param name="path">The path of the file within the archive; the same as
|
||||
/// the path provided
|
||||
/// when the stream was opened.</param>
|
||||
/// <param name="stream">A stream that was previously returned by
|
||||
/// <see cref="OpenFileReadStream"/> and is now ready to be closed.</param>
|
||||
void CloseFileReadStream(string path, Stream stream);
|
||||
|
||||
/// <summary>
|
||||
/// Gets extended parameter information specific to the compression
|
||||
/// format being used.
|
||||
/// </summary>
|
||||
/// <param name="optionName">Name of the option being requested.</param>
|
||||
/// <param name="parameters">Parameters for the option; for per-file options,
|
||||
/// the first parameter is typically the internal file path.</param>
|
||||
/// <returns>Option value, or null to use the default behavior.</returns>
|
||||
/// <remarks>
|
||||
/// This method provides a way to set uncommon options during packaging, or a
|
||||
/// way to handle aspects of compression formats not supported by the base library.
|
||||
/// <para>For example, this may be used by the zip compression library to
|
||||
/// specify different compression methods/levels on a per-file basis.</para>
|
||||
/// <para>The available option names, parameters, and expected return values
|
||||
/// should be documented by each compression library.</para>
|
||||
/// </remarks>
|
||||
object GetOption(string optionName, object[] parameters);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
//---------------------------------------------------------------------
|
||||
// <copyright file="IUnpackStreamContext.cs" company="Outercurve Foundation">
|
||||
// Copyright (c) 2004, Outercurve Foundation.
|
||||
// This software is released under Microsoft Reciprocal License (MS-RL).
|
||||
// The license and further copyright text can be found in the file LICENSE.TXT
|
||||
// LICENSE.TXT at the root directory of the distribution.
|
||||
// </copyright>
|
||||
// <summary>
|
||||
// Part of the Deployment Tools Foundation project.
|
||||
// </summary>
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Deployment.Compression
|
||||
{
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
/// <summary>
|
||||
/// This interface provides the methods necessary for the <see cref="CompressionEngine"/> to open
|
||||
/// and close streams for archives and files. The implementor of this interface can use any
|
||||
/// kind of logic to determine what kind of streams to open and where
|
||||
/// </summary>
|
||||
public interface IUnpackStreamContext
|
||||
{
|
||||
/// <summary>
|
||||
/// Opens the archive stream for reading.
|
||||
/// </summary>
|
||||
/// <param name="archiveNumber">The zero-based index of the archive to open.</param>
|
||||
/// <param name="archiveName">The name of the archive being opened.</param>
|
||||
/// <param name="compressionEngine">Instance of the compression engine doing the operations.</param>
|
||||
/// <returns>A stream from which archive bytes are read, or null to cancel extraction
|
||||
/// of the archive.</returns>
|
||||
/// <remarks>
|
||||
/// When the first archive in a chain is opened, the name is not yet known, so the
|
||||
/// provided value will be an empty string. When opening further archives, the
|
||||
/// provided value is the next-archive name stored in the previous archive. This
|
||||
/// name is often, but not necessarily, the same as the filename of the archive
|
||||
/// package to be opened.
|
||||
/// <para>If this method returns null, the archive engine will throw a
|
||||
/// FileNotFoundException.</para>
|
||||
/// </remarks>
|
||||
Stream OpenArchiveReadStream(int archiveNumber, string archiveName, CompressionEngine compressionEngine);
|
||||
|
||||
/// <summary>
|
||||
/// Closes a stream where an archive package was read.
|
||||
/// </summary>
|
||||
/// <param name="archiveNumber">The archive number of the stream to close.</param>
|
||||
/// <param name="archiveName">The name of the archive being closed.</param>
|
||||
/// <param name="stream">The stream that was previously returned by
|
||||
/// <see cref="OpenArchiveReadStream"/> and is now ready to be closed.</param>
|
||||
void CloseArchiveReadStream(int archiveNumber, string archiveName, Stream stream);
|
||||
|
||||
/// <summary>
|
||||
/// Opens a stream for writing extracted file bytes.
|
||||
/// </summary>
|
||||
/// <param name="path">The path of the file within the archive. This is often, but
|
||||
/// not necessarily, the same as the relative path of the file outside the archive.</param>
|
||||
/// <param name="fileSize">The uncompressed size of the file to be extracted.</param>
|
||||
/// <param name="lastWriteTime">The last write time of the file to be extracted.</param>
|
||||
/// <returns>A stream where extracted file bytes are to be written, or null to skip
|
||||
/// extraction of the file and continue to the next file.</returns>
|
||||
/// <remarks>
|
||||
/// The implementor may use the path, size and date information to dynamically
|
||||
/// decide whether or not the file should be extracted.
|
||||
/// </remarks>
|
||||
Stream OpenFileWriteStream(string path, long fileSize, DateTime lastWriteTime);
|
||||
|
||||
/// <summary>
|
||||
/// Closes a stream where an extracted file was written.
|
||||
/// </summary>
|
||||
/// <param name="path">The path of the file within the archive.</param>
|
||||
/// <param name="stream">The stream that was previously returned by <see cref="OpenFileWriteStream"/>
|
||||
/// and is now ready to be closed.</param>
|
||||
/// <param name="attributes">The attributes of the extracted file.</param>
|
||||
/// <param name="lastWriteTime">The last write time of the file.</param>
|
||||
/// <remarks>
|
||||
/// The implementor may wish to apply the attributes and date to the newly-extracted file.
|
||||
/// </remarks>
|
||||
void CloseFileWriteStream(string path, Stream stream, FileAttributes attributes, DateTime lastWriteTime);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,216 @@
|
|||
//---------------------------------------------------------------------
|
||||
// <copyright file="OffsetStream.cs" company="Outercurve Foundation">
|
||||
// Copyright (c) 2004, Outercurve Foundation.
|
||||
// This software is released under Microsoft Reciprocal License (MS-RL).
|
||||
// The license and further copyright text can be found in the file LICENSE.TXT
|
||||
// LICENSE.TXT at the root directory of the distribution.
|
||||
// </copyright>
|
||||
// <summary>
|
||||
// Part of the Deployment Tools Foundation project.
|
||||
// </summary>
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Deployment.Compression
|
||||
{
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
/// <summary>
|
||||
/// Wraps a source stream and offsets all read/write/seek calls by a given value.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This class is used to trick archive an packing or unpacking process
|
||||
/// into reading or writing at an offset into a file, primarily for
|
||||
/// self-extracting packages.
|
||||
/// </remarks>
|
||||
public class OffsetStream : Stream
|
||||
{
|
||||
private Stream source;
|
||||
private long sourceOffset;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new OffsetStream instance from a source stream
|
||||
/// and using a specified offset.
|
||||
/// </summary>
|
||||
/// <param name="source">Underlying stream for which all calls will be offset.</param>
|
||||
/// <param name="offset">Positive or negative number of bytes to offset.</param>
|
||||
public OffsetStream(Stream source, long offset)
|
||||
{
|
||||
if (source == null)
|
||||
{
|
||||
throw new ArgumentNullException("source");
|
||||
}
|
||||
|
||||
this.source = source;
|
||||
this.sourceOffset = offset;
|
||||
|
||||
this.source.Seek(this.sourceOffset, SeekOrigin.Current);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the underlying stream that this OffsetStream calls into.
|
||||
/// </summary>
|
||||
public Stream Source
|
||||
{
|
||||
get { return this.source; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of bytes to offset all calls before
|
||||
/// redirecting to the underlying stream.
|
||||
/// </summary>
|
||||
public long Offset
|
||||
{
|
||||
get { return this.sourceOffset; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the source stream supports reading.
|
||||
/// </summary>
|
||||
/// <value>true if the stream supports reading; otherwise, false.</value>
|
||||
public override bool CanRead
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.source.CanRead;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the source stream supports writing.
|
||||
/// </summary>
|
||||
/// <value>true if the stream supports writing; otherwise, false.</value>
|
||||
public override bool CanWrite
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.source.CanWrite;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the source stream supports seeking.
|
||||
/// </summary>
|
||||
/// <value>true if the stream supports seeking; otherwise, false.</value>
|
||||
public override bool CanSeek
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.source.CanSeek;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the effective length of the stream, which is equal to
|
||||
/// the length of the source stream minus the offset.
|
||||
/// </summary>
|
||||
public override long Length
|
||||
{
|
||||
get { return this.source.Length - this.sourceOffset; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the effective position of the stream, which
|
||||
/// is equal to the position of the source stream minus the offset.
|
||||
/// </summary>
|
||||
public override long Position
|
||||
{
|
||||
get { return this.source.Position - this.sourceOffset; }
|
||||
set { this.source.Position = value + this.sourceOffset; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a sequence of bytes from the source stream and advances
|
||||
/// the position within the stream by the number of bytes read.
|
||||
/// </summary>
|
||||
/// <param name="buffer">An array of bytes. When this method returns, the buffer
|
||||
/// contains the specified byte array with the values between offset and
|
||||
/// (offset + count - 1) replaced by the bytes read from the current source.</param>
|
||||
/// <param name="offset">The zero-based byte offset in buffer at which to begin
|
||||
/// storing the data read from the current stream.</param>
|
||||
/// <param name="count">The maximum number of bytes to be read from the current stream.</param>
|
||||
/// <returns>The total number of bytes read into the buffer. This can be less
|
||||
/// than the number of bytes requested if that many bytes are not currently available,
|
||||
/// or zero (0) if the end of the stream has been reached.</returns>
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
return this.source.Read(buffer, offset, count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a sequence of bytes to the source stream and advances the
|
||||
/// current position within this stream by the number of bytes written.
|
||||
/// </summary>
|
||||
/// <param name="buffer">An array of bytes. This method copies count
|
||||
/// bytes from buffer to the current stream.</param>
|
||||
/// <param name="offset">The zero-based byte offset in buffer at which
|
||||
/// to begin copying bytes to the current stream.</param>
|
||||
/// <param name="count">The number of bytes to be written to the
|
||||
/// current stream.</param>
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
this.source.Write(buffer, offset, count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a byte from the stream and advances the position within the
|
||||
/// source stream by one byte, or returns -1 if at the end of the stream.
|
||||
/// </summary>
|
||||
/// <returns>The unsigned byte cast to an Int32, or -1 if at the
|
||||
/// end of the stream.</returns>
|
||||
public override int ReadByte()
|
||||
{
|
||||
return this.source.ReadByte();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a byte to the current position in the source stream and
|
||||
/// advances the position within the stream by one byte.
|
||||
/// </summary>
|
||||
/// <param name="value">The byte to write to the stream.</param>
|
||||
public override void WriteByte(byte value)
|
||||
{
|
||||
this.source.WriteByte(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Flushes the source stream.
|
||||
/// </summary>
|
||||
public override void Flush()
|
||||
{
|
||||
this.source.Flush();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the position within the current stream, which is
|
||||
/// equal to the position within the source stream minus the offset.
|
||||
/// </summary>
|
||||
/// <param name="offset">A byte offset relative to the origin parameter.</param>
|
||||
/// <param name="origin">A value of type SeekOrigin indicating
|
||||
/// the reference point used to obtain the new position.</param>
|
||||
/// <returns>The new position within the current stream.</returns>
|
||||
public override long Seek(long offset, SeekOrigin origin)
|
||||
{
|
||||
return this.source.Seek(offset + (origin == SeekOrigin.Begin ? this.sourceOffset : 0), origin) - this.sourceOffset;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the effective length of the stream, which is equal to
|
||||
/// the length of the source stream minus the offset.
|
||||
/// </summary>
|
||||
/// <param name="value">The desired length of the
|
||||
/// current stream in bytes.</param>
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
this.source.SetLength(value + this.sourceOffset);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Closes the underlying stream.
|
||||
/// </summary>
|
||||
public override void Close()
|
||||
{
|
||||
this.source.Close();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
//---------------------------------------------------------------------
|
||||
// <copyright file="SafeNativeMethods.cs" company="Outercurve Foundation">
|
||||
// Copyright (c) 2004, Outercurve Foundation.
|
||||
// This software is released under Microsoft Reciprocal License (MS-RL).
|
||||
// The license and further copyright text can be found in the file LICENSE.TXT
|
||||
// LICENSE.TXT at the root directory of the distribution.
|
||||
// </copyright>
|
||||
// <summary>
|
||||
// Part of the Deployment Tools Foundation project.
|
||||
// </summary>
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Deployment.Compression
|
||||
{
|
||||
using System;
|
||||
using System.Security;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
[SuppressUnmanagedCodeSecurity]
|
||||
internal static class SafeNativeMethods
|
||||
{
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
internal static extern bool DosDateTimeToFileTime(
|
||||
short wFatDate, short wFatTime, out long fileTime);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
internal static extern bool FileTimeToDosDateTime(
|
||||
ref long fileTime, out short wFatDate, out short wFatTime);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
//---------------------------------------------------------------------
|
||||
// <copyright file="AssemblyInfo.cs" company="Outercurve Foundation">
|
||||
// Copyright (c) 2004, Outercurve Foundation.
|
||||
// This software is released under Microsoft Reciprocal License (MS-RL).
|
||||
// The license and further copyright text can be found in the file LICENSE.TXT
|
||||
// LICENSE.TXT at the root directory of the distribution.
|
||||
// </copyright>
|
||||
// <summary>
|
||||
// Part of the Deployment Tools Foundation project.
|
||||
// </summary>
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Security;
|
||||
using System.Security.Permissions;
|
||||
|
||||
[assembly: AssemblyDescription("Classes for reading and writing resource data in executable files")]
|
||||
[assembly: ComVisible(false)]
|
||||
[assembly: CLSCompliant(true)]
|
||||
|
||||
// SECURITY: The UnmanagedCode assertions in the resource classes are safe, because
|
||||
// appropriate demands are made for file I/O permissions before reading/writing files.
|
||||
[assembly: SecurityPermission(SecurityAction.RequestMinimum, Assertion = true, UnmanagedCode = true)]
|
||||
|
||||
// SECURITY: Review carefully!
|
||||
// This assembly is designed so that partially trusted callers should be able to
|
||||
// read and write file version info in a path where they have limited
|
||||
// file I/O permission.
|
||||
[assembly: AllowPartiallyTrustedCallers]
|
||||
|
||||
|
||||
[assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "Microsoft.Deployment.Resources.ResourceCollection.#System.Collections.Generic.ICollection`1<Microsoft.Deployment.Resources.Resource>.IsReadOnly")]
|
|
@ -0,0 +1,67 @@
|
|||
//---------------------------------------------------------------------
|
||||
// <copyright file="BitmapResource.cs" company="Outercurve Foundation">
|
||||
// Copyright (c) 2004, Outercurve Foundation.
|
||||
// This software is released under Microsoft Reciprocal License (MS-RL).
|
||||
// The license and further copyright text can be found in the file LICENSE.TXT
|
||||
// LICENSE.TXT at the root directory of the distribution.
|
||||
// </copyright>
|
||||
// <summary>
|
||||
// Part of the Deployment Tools Foundation project.
|
||||
// </summary>
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Deployment.Resources
|
||||
{
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
/// <summary>
|
||||
/// A subclass of Resource which provides specific methods for manipulating the resource data.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The resource is of type <see cref="ResourceType.Bitmap"/> (RT_GROUPICON).
|
||||
/// </remarks>
|
||||
public sealed class BitmapResource : Resource
|
||||
{
|
||||
private const int SizeOfBitmapFileHeader = 14; // this is the sizeof(BITMAPFILEHEADER)
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new BitmapResource object without any data. The data can be later loaded from a file.
|
||||
/// </summary>
|
||||
/// <param name="name">Name of the resource. For a numeric resource identifier, prefix the decimal number with a "#".</param>
|
||||
/// <param name="locale">Locale of the resource</param>
|
||||
public BitmapResource(string name, int locale)
|
||||
: this(name, locale, null)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new BitmapResource object with data. The data can be later saved to a file.
|
||||
/// </summary>
|
||||
/// <param name="name">Name of the resource. For a numeric resource identifier, prefix the decimal number with a "#".</param>
|
||||
/// <param name="locale">Locale of the resource</param>
|
||||
/// <param name="data">Raw resource data</param>
|
||||
public BitmapResource(string name, int locale, byte[] data)
|
||||
: base(ResourceType.Bitmap, name, locale, data)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the bitmap from a .bmp file.
|
||||
/// </summary>
|
||||
/// <param name="path">Path to a bitmap file (.bmp).</param>
|
||||
public void ReadFromFile(string path)
|
||||
{
|
||||
using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read))
|
||||
{
|
||||
// Move past the BITMAPFILEHEADER, and copy the rest of the bitmap as the resource data. Resource
|
||||
// functions expect only the BITMAPINFO struct which exists just beyond the BITMAPFILEHEADER
|
||||
// struct in bitmap files.
|
||||
fs.Seek(BitmapResource.SizeOfBitmapFileHeader, SeekOrigin.Begin);
|
||||
|
||||
base.Data = new byte[fs.Length - BitmapResource.SizeOfBitmapFileHeader];
|
||||
fs.Read(base.Data, 0, base.Data.Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,193 @@
|
|||
//---------------------------------------------------------------------
|
||||
// <copyright file="FixedFileVersionInfo.cs" company="Outercurve Foundation">
|
||||
// Copyright (c) 2004, Outercurve Foundation.
|
||||
// This software is released under Microsoft Reciprocal License (MS-RL).
|
||||
// The license and further copyright text can be found in the file LICENSE.TXT
|
||||
// LICENSE.TXT at the root directory of the distribution.
|
||||
// </copyright>
|
||||
// <summary>
|
||||
// Part of the Deployment Tools Foundation project.
|
||||
// </summary>
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Deployment.Resources
|
||||
{
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Reflection;
|
||||
using System.Collections;
|
||||
using System.Globalization;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
internal class FixedFileVersionInfo
|
||||
{
|
||||
public FixedFileVersionInfo()
|
||||
{
|
||||
// Set reasonable defaults
|
||||
this.signature = 0xFEEF04BD;
|
||||
this.structVersion = 0x00010000; // v1.0
|
||||
this.FileVersion = new Version(0, 0, 0, 0);
|
||||
this.ProductVersion = new Version(0, 0, 0, 0);
|
||||
this.FileFlagsMask = VersionBuildTypes.Debug | VersionBuildTypes.Prerelease;
|
||||
this.FileFlags = VersionBuildTypes.None;
|
||||
this.FileOS = VersionFileOS.NT_WINDOWS32;
|
||||
this.FileType = VersionFileType.Application;
|
||||
this.FileSubtype = VersionFileSubtype.Unknown;
|
||||
this.Timestamp = DateTime.MinValue;
|
||||
}
|
||||
|
||||
private uint signature;
|
||||
private uint structVersion;
|
||||
|
||||
public Version FileVersion
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.fileVersion;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
this.fileVersion = value;
|
||||
}
|
||||
}
|
||||
private Version fileVersion;
|
||||
|
||||
public Version ProductVersion
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.productVersion;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
this.productVersion = value;
|
||||
}
|
||||
}
|
||||
private Version productVersion;
|
||||
|
||||
public VersionBuildTypes FileFlagsMask
|
||||
{
|
||||
get { return this.fileFlagsMask; }
|
||||
set { this.fileFlagsMask = value; }
|
||||
}
|
||||
private VersionBuildTypes fileFlagsMask;
|
||||
|
||||
public VersionBuildTypes FileFlags
|
||||
{
|
||||
get { return this.fileFlags; }
|
||||
set { this.fileFlags = value; }
|
||||
}
|
||||
private VersionBuildTypes fileFlags;
|
||||
|
||||
[SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
public VersionFileOS FileOS
|
||||
{
|
||||
get { return this.fileOS; }
|
||||
set { this.fileOS = value; }
|
||||
}
|
||||
private VersionFileOS fileOS;
|
||||
|
||||
public VersionFileType FileType
|
||||
{
|
||||
get { return this.fileType; }
|
||||
set { this.fileType = value; }
|
||||
}
|
||||
private VersionFileType fileType;
|
||||
|
||||
public VersionFileSubtype FileSubtype
|
||||
{
|
||||
get { return this.fileSubtype; }
|
||||
set { this.fileSubtype = value; }
|
||||
}
|
||||
private VersionFileSubtype fileSubtype;
|
||||
|
||||
public DateTime Timestamp
|
||||
{
|
||||
get { return this.timestamp; }
|
||||
set { this.timestamp = value; }
|
||||
}
|
||||
private DateTime timestamp;
|
||||
|
||||
public void Read(BinaryReader reader)
|
||||
{
|
||||
this.signature = reader.ReadUInt32();
|
||||
this.structVersion = reader.ReadUInt32();
|
||||
this.fileVersion = UInt64ToVersion(reader.ReadUInt64());
|
||||
this.productVersion = UInt64ToVersion(reader.ReadUInt64());
|
||||
this.fileFlagsMask = (VersionBuildTypes) reader.ReadInt32();
|
||||
this.fileFlags = (VersionBuildTypes) reader.ReadInt32();
|
||||
this.fileOS = (VersionFileOS) reader.ReadInt32();
|
||||
this.fileType = (VersionFileType) reader.ReadInt32();
|
||||
this.fileSubtype = (VersionFileSubtype) reader.ReadInt32();
|
||||
this.timestamp = UInt64ToDateTime(reader.ReadUInt64());
|
||||
}
|
||||
|
||||
public void Write(BinaryWriter writer)
|
||||
{
|
||||
writer.Write(this.signature);
|
||||
writer.Write(this.structVersion);
|
||||
writer.Write(VersionToUInt64(this.fileVersion));
|
||||
writer.Write(VersionToUInt64(this.productVersion));
|
||||
writer.Write((int) this.fileFlagsMask);
|
||||
writer.Write((int) this.fileFlags);
|
||||
writer.Write((int) this.fileOS);
|
||||
writer.Write((int) this.fileType);
|
||||
writer.Write((int) this.fileSubtype);
|
||||
writer.Write(DateTimeToUInt64(this.timestamp));
|
||||
}
|
||||
|
||||
public static explicit operator FixedFileVersionInfo(byte[] bytesValue)
|
||||
{
|
||||
FixedFileVersionInfo ffviValue = new FixedFileVersionInfo();
|
||||
using (BinaryReader reader = new BinaryReader(new MemoryStream(bytesValue, false)))
|
||||
{
|
||||
ffviValue.Read(reader);
|
||||
}
|
||||
return ffviValue;
|
||||
}
|
||||
|
||||
public static explicit operator byte[](FixedFileVersionInfo ffviValue)
|
||||
{
|
||||
const int FFVI_LENGTH = 52;
|
||||
|
||||
byte[] bytesValue = new byte[FFVI_LENGTH];
|
||||
using (BinaryWriter writer = new BinaryWriter(new MemoryStream(bytesValue, true)))
|
||||
{
|
||||
ffviValue.Write(writer);
|
||||
}
|
||||
return bytesValue;
|
||||
}
|
||||
|
||||
private static Version UInt64ToVersion(ulong version)
|
||||
{
|
||||
return new Version((int) ((version >> 16) & 0xFFFF), (int) (version & 0xFFFF), (int) (version >> 48), (int) ((version >> 32) & 0xFFFF));
|
||||
}
|
||||
private static ulong VersionToUInt64(Version version)
|
||||
{
|
||||
return (((ulong) (ushort) version.Major) << 16) | ((ulong) (ushort) version.Minor)
|
||||
| (((ulong) (ushort) version.Build) << 48) | (((ulong) (ushort) version.Revision) << 32);
|
||||
}
|
||||
|
||||
private static DateTime UInt64ToDateTime(ulong dateTime)
|
||||
{
|
||||
return (dateTime == 0 ? DateTime.MinValue : DateTime.FromFileTime((long) dateTime));
|
||||
}
|
||||
private static ulong DateTimeToUInt64(DateTime dateTime)
|
||||
{
|
||||
return (dateTime == DateTime.MinValue ? 0 : (ulong) dateTime.ToFileTime());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,129 @@
|
|||
//---------------------------------------------------------------------
|
||||
// <copyright file="GroupIconInfo.cs" company="Outercurve Foundation">
|
||||
// Copyright (c) 2004, Outercurve Foundation.
|
||||
// This software is released under Microsoft Reciprocal License (MS-RL).
|
||||
// The license and further copyright text can be found in the file LICENSE.TXT
|
||||
// LICENSE.TXT at the root directory of the distribution.
|
||||
// </copyright>
|
||||
// <summary>
|
||||
// Part of the Deployment Tools Foundation project.
|
||||
// </summary>
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Deployment.Resources
|
||||
{
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Reflection;
|
||||
using System.Collections;
|
||||
using System.Globalization;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
internal enum GroupIconType
|
||||
{
|
||||
Unknown,
|
||||
Icon,
|
||||
Cursor,
|
||||
}
|
||||
|
||||
internal struct GroupIconDirectoryInfo
|
||||
{
|
||||
public byte width;
|
||||
public byte height;
|
||||
public byte colors;
|
||||
public byte reserved;
|
||||
public ushort planes;
|
||||
public ushort bitsPerPixel;
|
||||
public uint imageSize;
|
||||
public uint imageOffset; // only valid when icon group is read from .ico file.
|
||||
public ushort imageIndex; // only valid when icon group is read from PE resource.
|
||||
}
|
||||
|
||||
internal class GroupIconInfo
|
||||
{
|
||||
private ushort reserved;
|
||||
private GroupIconType type;
|
||||
private GroupIconDirectoryInfo[] images;
|
||||
|
||||
public GroupIconInfo()
|
||||
{
|
||||
this.images = new GroupIconDirectoryInfo[0];
|
||||
}
|
||||
|
||||
public GroupIconDirectoryInfo[] DirectoryInfo { get { return this.images; } }
|
||||
|
||||
public void ReadFromFile(Stream stream)
|
||||
{
|
||||
BinaryReader reader = new BinaryReader(stream);
|
||||
this.Read(reader, true);
|
||||
}
|
||||
|
||||
public void ReadFromResource(byte[] data)
|
||||
{
|
||||
using (BinaryReader reader = new BinaryReader(new MemoryStream(data, false)))
|
||||
{
|
||||
this.Read(reader, false);
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] GetResourceData()
|
||||
{
|
||||
byte[] data = null;
|
||||
|
||||
using (MemoryStream stream = new MemoryStream())
|
||||
{
|
||||
BinaryWriter writer = new BinaryWriter(stream);
|
||||
writer.Write(this.reserved);
|
||||
writer.Write((ushort)this.type);
|
||||
writer.Write((ushort)this.images.Length);
|
||||
for (int i = 0; i < this.images.Length; ++i)
|
||||
{
|
||||
writer.Write(this.images[i].width);
|
||||
writer.Write(this.images[i].height);
|
||||
writer.Write(this.images[i].colors);
|
||||
writer.Write(this.images[i].reserved);
|
||||
writer.Write(this.images[i].planes);
|
||||
writer.Write(this.images[i].bitsPerPixel);
|
||||
writer.Write(this.images[i].imageSize);
|
||||
writer.Write(this.images[i].imageIndex);
|
||||
}
|
||||
|
||||
data = new byte[stream.Length];
|
||||
stream.Seek(0, SeekOrigin.Begin);
|
||||
stream.Read(data, 0, data.Length);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
private void Read(BinaryReader reader, bool readFromFile)
|
||||
{
|
||||
this.reserved = reader.ReadUInt16();
|
||||
this.type = (GroupIconType)reader.ReadUInt16();
|
||||
|
||||
int imageCount = reader.ReadUInt16();
|
||||
this.images = new GroupIconDirectoryInfo[imageCount];
|
||||
for (int i = 0; i < imageCount; ++i)
|
||||
{
|
||||
this.images[i].width = reader.ReadByte();
|
||||
this.images[i].height = reader.ReadByte();
|
||||
this.images[i].colors = reader.ReadByte();
|
||||
this.images[i].reserved = reader.ReadByte();
|
||||
this.images[i].planes = reader.ReadUInt16();
|
||||
this.images[i].bitsPerPixel = reader.ReadUInt16();
|
||||
this.images[i].imageSize = reader.ReadUInt32();
|
||||
if (readFromFile)
|
||||
{
|
||||
this.images[i].imageOffset = reader.ReadUInt32();
|
||||
this.images[i].imageIndex = (ushort)(i + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.images[i].imageIndex = reader.ReadUInt16();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,130 @@
|
|||
//---------------------------------------------------------------------
|
||||
// <copyright file="GroupIconResource.cs" company="Outercurve Foundation">
|
||||
// Copyright (c) 2004, Outercurve Foundation.
|
||||
// This software is released under Microsoft Reciprocal License (MS-RL).
|
||||
// The license and further copyright text can be found in the file LICENSE.TXT
|
||||
// LICENSE.TXT at the root directory of the distribution.
|
||||
// </copyright>
|
||||
// <summary>
|
||||
// Part of the Deployment Tools Foundation project.
|
||||
// </summary>
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Deployment.Resources
|
||||
{
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Reflection;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
/// <summary>
|
||||
/// A subclass of Resource which provides specific methods for manipulating the resource data.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The resource is of type <see cref="ResourceType.GroupIcon"/> (RT_GROUPICON).
|
||||
/// </remarks>
|
||||
public sealed class GroupIconResource : Resource
|
||||
{
|
||||
internal bool dirty;
|
||||
private GroupIconInfo rawGroupIconInfo;
|
||||
private List<Resource> icons;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new GroupIconResource object without any data. The data can be later loaded from a file.
|
||||
/// </summary>
|
||||
/// <param name="name">Name of the resource. For a numeric resource identifier, prefix the decimal number with a "#".</param>
|
||||
/// <param name="locale">Locale of the resource</param>
|
||||
public GroupIconResource(string name, int locale)
|
||||
: this(name, locale, null)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new GroupIconResource object with data. The data can be later saved to a file.
|
||||
/// </summary>
|
||||
/// <param name="name">Name of the resource. For a numeric resource identifier, prefix the decimal number with a "#".</param>
|
||||
/// <param name="locale">Locale of the resource</param>
|
||||
/// <param name="data">Raw resource data</param>
|
||||
public GroupIconResource(string name, int locale, byte[] data)
|
||||
: base(ResourceType.GroupIcon, name, locale, data)
|
||||
{
|
||||
this.RefreshIconGroupInfo(data);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the raw data of the resource. The data is in the format of the RT_GROUPICON resource structure.
|
||||
/// </summary>
|
||||
public override byte[] Data
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this.dirty)
|
||||
{
|
||||
base.Data = this.rawGroupIconInfo.GetResourceData();
|
||||
this.dirty = false;
|
||||
}
|
||||
|
||||
return base.Data;
|
||||
}
|
||||
set
|
||||
{
|
||||
this.RefreshIconGroupInfo(value);
|
||||
|
||||
base.Data = value;
|
||||
this.dirty = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enumerates the the icons in the icon group.
|
||||
/// </summary>
|
||||
public IEnumerable<Resource> Icons { get { return this.icons; } }
|
||||
|
||||
/// <summary>
|
||||
/// Reads the icon group from a .ico file.
|
||||
/// </summary>
|
||||
/// <param name="path">Path to an icon file (.ico).</param>
|
||||
public void ReadFromFile(string path)
|
||||
{
|
||||
this.rawGroupIconInfo = new GroupIconInfo();
|
||||
this.icons = new List<Resource>();
|
||||
using (FileStream fs = File.Open(path, FileMode.Open, FileAccess.Read))
|
||||
{
|
||||
this.rawGroupIconInfo.ReadFromFile(fs);
|
||||
|
||||
// After reading the group icon info header from the file, read all the icons.
|
||||
for (int i = 0; i < this.rawGroupIconInfo.DirectoryInfo.Length; ++i)
|
||||
{
|
||||
ushort index = this.rawGroupIconInfo.DirectoryInfo[i].imageIndex;
|
||||
uint offset = this.rawGroupIconInfo.DirectoryInfo[i].imageOffset;
|
||||
uint size = this.rawGroupIconInfo.DirectoryInfo[i].imageSize;
|
||||
byte[] data = new byte[size];
|
||||
|
||||
fs.Seek(offset, SeekOrigin.Begin);
|
||||
fs.Read(data, 0, data.Length);
|
||||
|
||||
Resource resource = new Resource(ResourceType.Icon, String.Concat("#", index), this.Locale, data);
|
||||
this.icons.Add(resource);
|
||||
}
|
||||
}
|
||||
|
||||
this.dirty = true;
|
||||
}
|
||||
|
||||
private void RefreshIconGroupInfo(byte[] refreshData)
|
||||
{
|
||||
this.rawGroupIconInfo = new GroupIconInfo();
|
||||
this.icons = new List<Resource>();
|
||||
if (refreshData != null)
|
||||
{
|
||||
this.rawGroupIconInfo.ReadFromResource(refreshData);
|
||||
}
|
||||
|
||||
this.dirty = true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
//---------------------------------------------------------------------
|
||||
// <copyright file="NativeMethods.cs" company="Outercurve Foundation">
|
||||
// Copyright (c) 2004, Outercurve Foundation.
|
||||
// This software is released under Microsoft Reciprocal License (MS-RL).
|
||||
// The license and further copyright text can be found in the file LICENSE.TXT
|
||||
// LICENSE.TXT at the root directory of the distribution.
|
||||
// </copyright>
|
||||
// <summary>
|
||||
// Part of the Deployment Tools Foundation project.
|
||||
// </summary>
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Deployment.Resources
|
||||
{
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Reflection;
|
||||
using System.Collections;
|
||||
using System.Globalization;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
internal static class NativeMethods
|
||||
{
|
||||
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
|
||||
internal static extern IntPtr LoadLibraryEx(string fileName, IntPtr hFile, uint flags);
|
||||
internal const uint LOAD_LIBRARY_AS_DATAFILE = 2;
|
||||
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)]
|
||||
internal static extern bool FreeLibrary(IntPtr module);
|
||||
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)]
|
||||
internal static extern bool EnumResourceTypes(IntPtr module, EnumResTypesProc enumFunc, IntPtr param);
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
internal delegate bool EnumResTypesProc(IntPtr module, IntPtr type, IntPtr param);
|
||||
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)]
|
||||
internal static extern bool EnumResourceNames(IntPtr module, IntPtr type, EnumResNamesProc enumFunc, IntPtr param);
|
||||
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)]
|
||||
internal static extern bool EnumResourceNames(IntPtr module, string type, EnumResNamesProc enumFunc, IntPtr param);
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
internal delegate bool EnumResNamesProc(IntPtr module, IntPtr type, IntPtr name, IntPtr param);
|
||||
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)]
|
||||
internal static extern bool EnumResourceLanguages(IntPtr module, IntPtr type, IntPtr name, EnumResLangsProc enumFunc, IntPtr param);
|
||||
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)]
|
||||
internal static extern bool EnumResourceLanguages(IntPtr module, string type, string name, EnumResLangsProc enumFunc, IntPtr param);
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
internal delegate bool EnumResLangsProc(IntPtr module, IntPtr type, IntPtr name, ushort langId, IntPtr param);
|
||||
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
|
||||
internal static extern IntPtr FindResourceEx(IntPtr module, string type, string name, ushort langId);
|
||||
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
|
||||
internal static extern IntPtr LoadResource(IntPtr module, IntPtr resourceInfo);
|
||||
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
|
||||
internal static extern IntPtr LockResource(IntPtr resourceData);
|
||||
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
|
||||
internal static extern uint SizeofResource(IntPtr module, IntPtr resourceInfo);
|
||||
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
|
||||
internal static extern IntPtr BeginUpdateResource(string fileName, [MarshalAs(UnmanagedType.Bool)] bool deleteExistingResources);
|
||||
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)]
|
||||
internal static extern bool UpdateResource(IntPtr updateHandle, IntPtr type, IntPtr name, ushort lcid, IntPtr data, uint dataSize);
|
||||
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)][return: MarshalAs(UnmanagedType.Bool)]
|
||||
internal static extern bool UpdateResource(IntPtr updateHandle, IntPtr type, string name, ushort lcid, IntPtr data, uint dataSize);
|
||||
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)]
|
||||
internal static extern bool UpdateResource(IntPtr updateHandle, string type, string name, ushort lcid, IntPtr data, uint dataSize);
|
||||
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)]
|
||||
internal static extern bool EndUpdateResource(IntPtr updateHandle, [MarshalAs(UnmanagedType.Bool)] bool discardChanges);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,244 @@
|
|||
//---------------------------------------------------------------------
|
||||
// <copyright file="Resource.cs" company="Outercurve Foundation">
|
||||
// Copyright (c) 2004, Outercurve Foundation.
|
||||
// This software is released under Microsoft Reciprocal License (MS-RL).
|
||||
// The license and further copyright text can be found in the file LICENSE.TXT
|
||||
// LICENSE.TXT at the root directory of the distribution.
|
||||
// </copyright>
|
||||
// <summary>
|
||||
// Part of the Deployment Tools Foundation project.
|
||||
// </summary>
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Deployment.Resources
|
||||
{
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Security.Permissions;
|
||||
using System.Reflection;
|
||||
using System.Collections;
|
||||
using System.Globalization;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a Win32 resource which can be loaded from and saved to a PE file.
|
||||
/// </summary>
|
||||
public class Resource
|
||||
{
|
||||
private ResourceType type;
|
||||
private string name;
|
||||
private int locale;
|
||||
private byte[] data;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new Resource object without any data. The data can be later loaded from a file.
|
||||
/// </summary>
|
||||
/// <param name="type">Type of the resource; may be one of the ResourceType constants or a user-defined type.</param>
|
||||
/// <param name="name">Name of the resource. For a numeric resource identifier, prefix the decimal number with a "#".</param>
|
||||
/// <param name="locale">Locale of the resource</param>
|
||||
public Resource(ResourceType type, string name, int locale)
|
||||
: this(type, name, locale, null)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new Resource object with data. The data can be later saved to a file.
|
||||
/// </summary>
|
||||
/// <param name="type">Type of the resource; may be one of the ResourceType constants or a user-defined type.</param>
|
||||
/// <param name="name">Name of the resource. For a numeric resource identifier, prefix the decimal number with a "#".</param>
|
||||
/// <param name="locale">Locale of the resource</param>
|
||||
/// <param name="data">Raw resource data</param>
|
||||
public Resource(ResourceType type, string name, int locale, byte[] data)
|
||||
{
|
||||
if (name == null)
|
||||
{
|
||||
throw new ArgumentNullException("name");
|
||||
}
|
||||
|
||||
this.type = type;
|
||||
this.name = name;
|
||||
this.locale = locale;
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the type of the resource. This may be one of the ResourceType constants
|
||||
/// or a user-defined type name.
|
||||
/// </summary>
|
||||
public ResourceType ResourceType
|
||||
{
|
||||
get { return this.type; }
|
||||
set { this.type = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name of the resource. For a numeric resource identifier, the decimal number is prefixed with a "#".
|
||||
/// </summary>
|
||||
public string Name
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.name;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
throw new ArgumentNullException("value");
|
||||
}
|
||||
|
||||
this.name = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the locale of the resource.
|
||||
/// </summary>
|
||||
public int Locale
|
||||
{
|
||||
get { return this.locale; }
|
||||
set { this.locale = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the raw data of the resource.
|
||||
/// </summary>
|
||||
[SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]
|
||||
public virtual byte[] Data
|
||||
{
|
||||
get { return this.data; }
|
||||
set { this.data = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads the resource data from a file. The file is searched for a resource with matching type, name, and locale.
|
||||
/// </summary>
|
||||
/// <param name="file">Win32 PE file containing the resource</param>
|
||||
[SuppressMessage("Microsoft.Security", "CA2103:ReviewImperativeSecurity")]
|
||||
[SecurityPermission(SecurityAction.Assert, UnmanagedCode = true)]
|
||||
public void Load(string file)
|
||||
{
|
||||
new FileIOPermission(FileIOPermissionAccess.Read, file).Demand();
|
||||
|
||||
IntPtr module = NativeMethods.LoadLibraryEx(file, IntPtr.Zero, NativeMethods.LOAD_LIBRARY_AS_DATAFILE);
|
||||
try
|
||||
{
|
||||
this.Load(module);
|
||||
}
|
||||
finally
|
||||
{
|
||||
NativeMethods.FreeLibrary(module);
|
||||
}
|
||||
}
|
||||
|
||||
internal void Load(IntPtr module)
|
||||
{
|
||||
IntPtr resourceInfo = NativeMethods.FindResourceEx(module, (string) this.ResourceType, this.Name, (ushort) this.Locale);
|
||||
if (resourceInfo != IntPtr.Zero)
|
||||
{
|
||||
uint resourceLength = NativeMethods.SizeofResource(module, resourceInfo);
|
||||
IntPtr resourceData = NativeMethods.LoadResource(module, resourceInfo);
|
||||
IntPtr resourcePtr = NativeMethods.LockResource(resourceData);
|
||||
byte[] resourceBytes = new byte[resourceLength];
|
||||
Marshal.Copy(resourcePtr, resourceBytes, 0, resourceBytes.Length);
|
||||
this.Data = resourceBytes;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.Data = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves the resource to a file. Any existing resource data with matching type, name, and locale is overwritten.
|
||||
/// </summary>
|
||||
/// <param name="file">Win32 PE file to contain the resource</param>
|
||||
[SuppressMessage("Microsoft.Security", "CA2103:ReviewImperativeSecurity")]
|
||||
[SecurityPermission(SecurityAction.Assert, UnmanagedCode = true)]
|
||||
public void Save(string file)
|
||||
{
|
||||
new FileIOPermission(FileIOPermissionAccess.AllAccess, file).Demand();
|
||||
|
||||
IntPtr updateHandle = IntPtr.Zero;
|
||||
try
|
||||
{
|
||||
updateHandle = NativeMethods.BeginUpdateResource(file, false);
|
||||
this.Save(updateHandle);
|
||||
if (!NativeMethods.EndUpdateResource(updateHandle, false))
|
||||
{
|
||||
int err = Marshal.GetLastWin32Error();
|
||||
throw new IOException(String.Format(CultureInfo.InvariantCulture, "Failed to save resource. Error code: {0}", err));
|
||||
}
|
||||
updateHandle = IntPtr.Zero;
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (updateHandle != IntPtr.Zero)
|
||||
{
|
||||
NativeMethods.EndUpdateResource(updateHandle, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal void Save(IntPtr updateHandle)
|
||||
{
|
||||
IntPtr dataPtr = IntPtr.Zero;
|
||||
try
|
||||
{
|
||||
int dataLength = 0;
|
||||
if (this.Data != null)
|
||||
{
|
||||
dataLength = this.Data.Length;
|
||||
dataPtr = Marshal.AllocHGlobal(dataLength);
|
||||
Marshal.Copy(this.Data, 0, dataPtr, dataLength);
|
||||
}
|
||||
bool updateSuccess;
|
||||
if (this.Name.StartsWith("#", StringComparison.Ordinal))
|
||||
{
|
||||
// A numeric-named resource must be saved via the integer version of UpdateResource.
|
||||
IntPtr intName = new IntPtr(Int32.Parse(this.Name.Substring(1), CultureInfo.InvariantCulture));
|
||||
updateSuccess = NativeMethods.UpdateResource(updateHandle, new IntPtr(this.ResourceType.IntegerValue), intName, (ushort) this.Locale, dataPtr, (uint) dataLength);
|
||||
}
|
||||
else
|
||||
{
|
||||
updateSuccess = NativeMethods.UpdateResource(updateHandle, (string) this.ResourceType, this.Name, (ushort) this.Locale, dataPtr, (uint) dataLength);
|
||||
}
|
||||
if (!updateSuccess)
|
||||
{
|
||||
throw new IOException("Failed to save resource. Error: " + Marshal.GetLastWin32Error());
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (dataPtr != IntPtr.Zero)
|
||||
{
|
||||
Marshal.FreeHGlobal(dataPtr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests if type, name, and locale of this Resource object match another Resource object.
|
||||
/// </summary>
|
||||
/// <param name="obj">Resource object to be compared</param>
|
||||
/// <returns>True if the objects represent the same resource; false otherwise.</returns>
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
Resource res = obj as Resource;
|
||||
if (res == null) return false;
|
||||
return this.ResourceType == res.ResourceType && this.Name == res.Name && this.Locale == res.Locale;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a hash code for this Resource object.
|
||||
/// </summary>
|
||||
/// <returns>Hash code generated from the resource type, name, and locale.</returns>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return this.ResourceType.GetHashCode() ^ this.Name.GetHashCode() ^ this.Locale.GetHashCode();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,371 @@
|
|||
//---------------------------------------------------------------------
|
||||
// <copyright file="ResourceCollection.cs" company="Outercurve Foundation">
|
||||
// Copyright (c) 2004, Outercurve Foundation.
|
||||
// This software is released under Microsoft Reciprocal License (MS-RL).
|
||||
// The license and further copyright text can be found in the file LICENSE.TXT
|
||||
// LICENSE.TXT at the root directory of the distribution.
|
||||
// </copyright>
|
||||
// <summary>
|
||||
// Part of the Deployment Tools Foundation project.
|
||||
// </summary>
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Deployment.Resources
|
||||
{
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Security.Permissions;
|
||||
using System.Reflection;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
/// <summary>
|
||||
/// Allows reading and editing of resource data in a Win32 PE file.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// To use this class:<list type="number">
|
||||
/// <item>Create a new ResourceCollection</item>
|
||||
/// <item>Locate resources for the collection by calling one of the <see cref="ResourceCollection.Find(string)"/> methods</item>
|
||||
/// <item>Load data of one or more <see cref="Resource"/>s from a file by calling the <see cref="Load"/> method of the
|
||||
/// Resource class, or load them all at once (more efficient) with the <see cref="Load"/> method of the ResourceCollection.</item>
|
||||
/// <item>Read and/or edit data of the individual Resource objects using the methods on that class.</item>
|
||||
/// <item>Save data of one or more <see cref="Resource"/>s to a file by calling the <see cref="Save"/> method of the
|
||||
/// Resource class, or save them all at once (more efficient) with the <see cref="Save"/> method of the ResourceCollection.</item>
|
||||
/// </list>
|
||||
/// </remarks>
|
||||
public class ResourceCollection : ICollection<Resource>
|
||||
{
|
||||
private List<Resource> resources;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new, empty ResourceCollection.
|
||||
/// </summary>
|
||||
public ResourceCollection()
|
||||
{
|
||||
this.resources = new List<Resource>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Locates all resources in a file, including all resource types and languages. For each located resource,
|
||||
/// a <see cref="Resource"/> instance (or subclass) is added to the collection.
|
||||
/// </summary>
|
||||
/// <param name="resFile">The file to be searched for resources.</param>
|
||||
/// <exception cref="IOException">resources could not be read from the file</exception>
|
||||
[SuppressMessage("Microsoft.Security", "CA2103:ReviewImperativeSecurity")]
|
||||
[SecurityPermission(SecurityAction.Assert, UnmanagedCode = true)]
|
||||
public void Find(string resFile)
|
||||
{
|
||||
new FileIOPermission(FileIOPermissionAccess.Read, resFile).Demand();
|
||||
|
||||
this.Clear();
|
||||
|
||||
IntPtr module = NativeMethods.LoadLibraryEx(resFile, IntPtr.Zero, NativeMethods.LOAD_LIBRARY_AS_DATAFILE);
|
||||
if (module == IntPtr.Zero)
|
||||
{
|
||||
int err = Marshal.GetLastWin32Error();
|
||||
throw new IOException(String.Format(CultureInfo.InvariantCulture, "Failed to load resource file. Error code: {0}", err));
|
||||
}
|
||||
try
|
||||
{
|
||||
if (!NativeMethods.EnumResourceTypes(module, new NativeMethods.EnumResTypesProc(this.EnumResTypes), IntPtr.Zero))
|
||||
{
|
||||
int err = Marshal.GetLastWin32Error();
|
||||
throw new IOException(String.Format(CultureInfo.InvariantCulture, "Failed to enumerate resources. Error code: {0}", err));
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
NativeMethods.FreeLibrary(module);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Locates all resources in a file of a given type, including all languages. For each located resource,
|
||||
/// a <see cref="Resource"/> instance (or subclass) is added to the collection.
|
||||
/// </summary>
|
||||
/// <param name="resFile">The file to be searched for resources.</param>
|
||||
/// <param name="type">The type of resource to search for; may be one of the ResourceType constants or a user-defined type.</param>
|
||||
/// <exception cref="IOException">resources could not be read from the file</exception>
|
||||
[SuppressMessage("Microsoft.Security", "CA2103:ReviewImperativeSecurity")]
|
||||
[SecurityPermission(SecurityAction.Assert, UnmanagedCode = true)]
|
||||
public void Find(string resFile, ResourceType type)
|
||||
{
|
||||
new FileIOPermission(FileIOPermissionAccess.Read, resFile).Demand();
|
||||
|
||||
this.Clear();
|
||||
|
||||
IntPtr module = NativeMethods.LoadLibraryEx(resFile, IntPtr.Zero, NativeMethods.LOAD_LIBRARY_AS_DATAFILE);
|
||||
try
|
||||
{
|
||||
if (!NativeMethods.EnumResourceNames(module, (string) type, new NativeMethods.EnumResNamesProc(this.EnumResNames), IntPtr.Zero))
|
||||
{
|
||||
int err = Marshal.GetLastWin32Error();
|
||||
throw new IOException(String.Format(CultureInfo.InvariantCulture, "EnumResourceNames error. Error code: {0}", err));
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
NativeMethods.FreeLibrary(module);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Locates all resources in a file of a given type and language. For each located resource,
|
||||
/// a <see cref="Resource"/> instance (or subclass) is added to the collection.
|
||||
/// </summary>
|
||||
/// <param name="resFile">The file to be searched for resources.</param>
|
||||
/// <param name="type">The type of resource to search for; may be one of the ResourceType constants or a user-defined type.</param>
|
||||
/// <param name="name">The name of the resource to search for.</param>
|
||||
/// <exception cref="IOException">resources could not be read from the file</exception>
|
||||
[SuppressMessage("Microsoft.Security", "CA2103:ReviewImperativeSecurity")]
|
||||
[SecurityPermission(SecurityAction.Assert, UnmanagedCode = true)]
|
||||
public void Find(string resFile, ResourceType type, string name)
|
||||
{
|
||||
new FileIOPermission(FileIOPermissionAccess.Read, resFile).Demand();
|
||||
|
||||
this.Clear();
|
||||
|
||||
IntPtr module = NativeMethods.LoadLibraryEx(resFile, IntPtr.Zero, NativeMethods.LOAD_LIBRARY_AS_DATAFILE);
|
||||
try
|
||||
{
|
||||
if (!NativeMethods.EnumResourceLanguages(module, (string) type, name, new NativeMethods.EnumResLangsProc(this.EnumResLangs), IntPtr.Zero))
|
||||
{
|
||||
int err = Marshal.GetLastWin32Error();
|
||||
throw new IOException(String.Format(CultureInfo.InvariantCulture, "EnumResourceLanguages error. Error code: {0}", err));
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
NativeMethods.FreeLibrary(module);
|
||||
}
|
||||
}
|
||||
|
||||
private bool EnumResTypes(IntPtr module, IntPtr type, IntPtr param)
|
||||
{
|
||||
if (!NativeMethods.EnumResourceNames(module, type, new NativeMethods.EnumResNamesProc(EnumResNames), IntPtr.Zero))
|
||||
{
|
||||
int err = Marshal.GetLastWin32Error();
|
||||
throw new IOException(String.Format(CultureInfo.InvariantCulture, "EnumResourceNames error! Error code: {0}", err));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool EnumResNames(IntPtr module, IntPtr type, IntPtr name, IntPtr param)
|
||||
{
|
||||
if (!NativeMethods.EnumResourceLanguages(module, type, name, new NativeMethods.EnumResLangsProc(EnumResLangs), IntPtr.Zero))
|
||||
{
|
||||
int err = Marshal.GetLastWin32Error();
|
||||
throw new IOException(String.Format(CultureInfo.InvariantCulture, "EnumResourceLanguages error. Error code: {0}", err));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool EnumResLangs(IntPtr module, IntPtr type, IntPtr name, ushort langId, IntPtr param)
|
||||
{
|
||||
Resource res;
|
||||
if (((int) type) == ResourceType.Version.IntegerValue)
|
||||
{
|
||||
res = new VersionResource(ResourceNameToString(name), langId);
|
||||
}
|
||||
else
|
||||
{
|
||||
res = new Resource(ResourceNameToString(type), ResourceNameToString(name), langId);
|
||||
}
|
||||
|
||||
if (!this.Contains(res))
|
||||
{
|
||||
this.Add(res);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static string ResourceNameToString(IntPtr resName)
|
||||
{
|
||||
if ((resName.ToInt64() >> 16) == 0)
|
||||
{
|
||||
return "#" + resName.ToString();
|
||||
}
|
||||
else
|
||||
{
|
||||
return Marshal.PtrToStringAuto(resName);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// For all resources in the collection, loads their data from a resource file.
|
||||
/// </summary>
|
||||
/// <param name="file">The file from which resources are loaded.</param>
|
||||
[SuppressMessage("Microsoft.Security", "CA2103:ReviewImperativeSecurity")]
|
||||
[SecurityPermission(SecurityAction.Assert, UnmanagedCode = true)]
|
||||
public void Load(string file)
|
||||
{
|
||||
new FileIOPermission(FileIOPermissionAccess.Read, file).Demand();
|
||||
|
||||
IntPtr module = NativeMethods.LoadLibraryEx(file, IntPtr.Zero, NativeMethods.LOAD_LIBRARY_AS_DATAFILE);
|
||||
try
|
||||
{
|
||||
foreach (Resource res in this)
|
||||
{
|
||||
res.Load(module);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
NativeMethods.FreeLibrary(module);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// For all resources in the collection, saves their data to a resource file.
|
||||
/// </summary>
|
||||
/// <param name="file">The file to which resources are saved.</param>
|
||||
[SuppressMessage("Microsoft.Security", "CA2103:ReviewImperativeSecurity")]
|
||||
[SecurityPermission(SecurityAction.Assert, UnmanagedCode = true)]
|
||||
public void Save(string file)
|
||||
{
|
||||
new FileIOPermission(FileIOPermissionAccess.AllAccess, file).Demand();
|
||||
|
||||
IntPtr updateHandle = IntPtr.Zero;
|
||||
try
|
||||
{
|
||||
updateHandle = NativeMethods.BeginUpdateResource(file, false);
|
||||
foreach (Resource res in this)
|
||||
{
|
||||
res.Save(updateHandle);
|
||||
}
|
||||
if (!NativeMethods.EndUpdateResource(updateHandle, false))
|
||||
{
|
||||
int err = Marshal.GetLastWin32Error();
|
||||
throw new IOException(String.Format(CultureInfo.InvariantCulture, "Failed to save resource. Error {0}", err));
|
||||
}
|
||||
updateHandle = IntPtr.Zero;
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (updateHandle != IntPtr.Zero)
|
||||
{
|
||||
NativeMethods.EndUpdateResource(updateHandle, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the element at the specified index.
|
||||
/// </summary>
|
||||
public Resource this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
return (Resource) this.resources[index];
|
||||
}
|
||||
set
|
||||
{
|
||||
this.resources[index] = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new item to the collection.
|
||||
/// </summary>
|
||||
/// <param name="item">The Resource to add.</param>
|
||||
public void Add(Resource item)
|
||||
{
|
||||
this.resources.Add(item);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes an item to the collection.
|
||||
/// </summary>
|
||||
/// <param name="item">The Resource to remove.</param>
|
||||
public bool Remove(Resource item)
|
||||
{
|
||||
return this.resources.Remove(item);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the index of an item in the collection.
|
||||
/// </summary>
|
||||
/// <param name="item">The Resource to search for.</param>
|
||||
/// <returns>The index of the item, or -1 if not found.</returns>
|
||||
public int IndexOf(Resource item)
|
||||
{
|
||||
return this.resources.IndexOf(item);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inserts a item into the collection.
|
||||
/// </summary>
|
||||
/// <param name="index">The insertion index.</param>
|
||||
/// <param name="item">The Resource to insert.</param>
|
||||
public void Insert(int index, Resource item)
|
||||
{
|
||||
this.resources.Insert(index, item);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests if the collection contains an item.
|
||||
/// </summary>
|
||||
/// <param name="item">The Resource to search for.</param>
|
||||
/// <returns>true if the item is found; false otherwise</returns>
|
||||
public bool Contains(Resource item)
|
||||
{
|
||||
return this.resources.Contains(item);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies the collection into an array.
|
||||
/// </summary>
|
||||
/// <param name="array">The array to copy into.</param>
|
||||
/// <param name="arrayIndex">The starting index in the destination array.</param>
|
||||
public void CopyTo(Resource[] array, int arrayIndex)
|
||||
{
|
||||
this.resources.CopyTo(array, arrayIndex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes all resources from the collection.
|
||||
/// </summary>
|
||||
public void Clear()
|
||||
{
|
||||
this.resources.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of resources in the collection.
|
||||
/// </summary>
|
||||
public int Count
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.resources.Count;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an enumerator over all resources in the collection.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public IEnumerator<Resource> GetEnumerator()
|
||||
{
|
||||
return this.resources.GetEnumerator();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return ((IEnumerable) this.resources).GetEnumerator();
|
||||
}
|
||||
|
||||
bool ICollection<Resource>.IsReadOnly
|
||||
{
|
||||
get
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,208 @@
|
|||
//---------------------------------------------------------------------
|
||||
// <copyright file="ResourceType.cs" company="Outercurve Foundation">
|
||||
// Copyright (c) 2004, Outercurve Foundation.
|
||||
// This software is released under Microsoft Reciprocal License (MS-RL).
|
||||
// The license and further copyright text can be found in the file LICENSE.TXT
|
||||
// LICENSE.TXT at the root directory of the distribution.
|
||||
// </copyright>
|
||||
// <summary>
|
||||
// Part of the Deployment Tools Foundation project.
|
||||
// </summary>
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Deployment.Resources
|
||||
{
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Globalization;
|
||||
|
||||
/// <summary>
|
||||
/// Represents either a standard integer resource type or a custom resource type name.
|
||||
/// </summary>
|
||||
public class ResourceType
|
||||
{
|
||||
// Silence warnings about doc-comments
|
||||
#pragma warning disable 1591
|
||||
|
||||
public static ResourceType None { get { return "#0"; } }
|
||||
public static ResourceType Cursor { get { return "#1"; } }
|
||||
public static ResourceType Bitmap { get { return "#2"; } }
|
||||
public static ResourceType Icon { get { return "#3"; } }
|
||||
public static ResourceType Menu { get { return "#4"; } }
|
||||
public static ResourceType Dialog { get { return "#5"; } }
|
||||
public static ResourceType String { get { return "#6"; } }
|
||||
public static ResourceType FontDir { get { return "#7"; } }
|
||||
public static ResourceType Font { get { return "#8"; } }
|
||||
public static ResourceType Accelerator { get { return "#9"; } }
|
||||
public static ResourceType RCData { get { return "#10"; } }
|
||||
public static ResourceType MessageTable { get { return "#11"; } }
|
||||
public static ResourceType GroupCursor { get { return "#12"; } }
|
||||
public static ResourceType GroupIcon { get { return "#14"; } }
|
||||
public static ResourceType Version { get { return "#16"; } }
|
||||
public static ResourceType DialogInclude { get { return "#17"; } }
|
||||
public static ResourceType PlugPlay { get { return "#19"; } }
|
||||
public static ResourceType Vxd { get { return "#20"; } }
|
||||
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Ani")]
|
||||
public static ResourceType AniCursor { get { return "#21"; } }
|
||||
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Ani")]
|
||||
public static ResourceType AniIcon { get { return "#22"; } }
|
||||
public static ResourceType Html { get { return "#23"; } }
|
||||
public static ResourceType Manifest { get { return "#24"; } }
|
||||
|
||||
#pragma warning restore 1591
|
||||
|
||||
private string resourceType;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new resource type from a string resource name.
|
||||
/// </summary>
|
||||
/// <param name="resourceType">String resource name,
|
||||
/// or an integer resource type prefixed by a #.</param>
|
||||
public ResourceType(string resourceType)
|
||||
{
|
||||
if (string.IsNullOrEmpty(resourceType))
|
||||
{
|
||||
throw new ArgumentNullException("resourceType");
|
||||
}
|
||||
|
||||
this.resourceType = resourceType;
|
||||
|
||||
if (this.IsInteger && this.IntegerValue < 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("Invalid integer resource type value.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new integer resource type.
|
||||
/// </summary>
|
||||
/// <param name="resourceType">Integer value of a well-known resource type.</param>
|
||||
public ResourceType(int resourceType)
|
||||
: this("#" + resourceType)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a flag indicating whether the resource type is an integer type.
|
||||
/// </summary>
|
||||
public bool IsInteger
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.resourceType.StartsWith("#", StringComparison.Ordinal);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the integer value of the resource type, or -1 if the resource type is not an integer.
|
||||
/// </summary>
|
||||
public int IntegerValue
|
||||
{
|
||||
get
|
||||
{
|
||||
int value;
|
||||
if (!this.IsInteger ||
|
||||
!Int32.TryParse(this.resourceType.Substring(1), out value))
|
||||
{
|
||||
value = -1;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a string representation of the resource type.
|
||||
/// </summary>
|
||||
/// <returns>The custom resource name, or the name of a well-known resource type.</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
if (this.IsInteger)
|
||||
{
|
||||
switch (this.IntegerValue)
|
||||
{
|
||||
case 0: return "None";
|
||||
case 1: return "Cursor";
|
||||
case 2: return "Bitmap";
|
||||
case 3: return "Icon";
|
||||
case 4: return "Menu";
|
||||
case 5: return "Dialog";
|
||||
case 6: return "String";
|
||||
case 7: return "FontDir";
|
||||
case 8: return "Font";
|
||||
case 9: return "Accelerator";
|
||||
case 10: return "RCData";
|
||||
case 11: return "MessageTable";
|
||||
case 12: return "GroupCursor";
|
||||
case 14: return "GroupIcon";
|
||||
case 16: return "Version";
|
||||
case 17: return "DialogInclude";
|
||||
case 19: return "PlugPlay";
|
||||
case 20: return "Vxd";
|
||||
case 21: return "AniCursor";
|
||||
case 22: return "AniIcon";
|
||||
case 23: return "Html";
|
||||
case 24: return "Manifest";
|
||||
}
|
||||
}
|
||||
|
||||
return this.resourceType;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether one resource type equals another object.
|
||||
/// </summary>
|
||||
/// <param name="obj">Other object.</param>
|
||||
/// <returns>True if equal, else false.</returns>
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return this.Equals(obj as ResourceType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether one resource type equals another.
|
||||
/// </summary>
|
||||
/// <param name="otherType">Other resource type.</param>
|
||||
/// <returns>True if equal, else false.</returns>
|
||||
public bool Equals(ResourceType otherType)
|
||||
{
|
||||
return otherType != null && this.resourceType.Equals(otherType.resourceType, StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a hash code suitable for using the resource type as a dictionary key.
|
||||
/// </summary>
|
||||
/// <returns>Hash code based on the resource type string.</returns>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return this.resourceType.GetHashCode();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implicitly converts a string to a ResourceType.
|
||||
/// </summary>
|
||||
/// <param name="resourceType">String resource type to convert.</param>
|
||||
/// <returns>ResourceType object.</returns>
|
||||
public static implicit operator ResourceType(string resourceType)
|
||||
{
|
||||
return new ResourceType(resourceType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Explicitly converts a ResourceType to a string.
|
||||
/// </summary>
|
||||
/// <param name="resourceType">ResourceType object to convert.</param>
|
||||
/// <returns>The resource type string.</returns>
|
||||
/// <remarks>
|
||||
/// Unlike <see cref="ToString" />, this conversion does not return
|
||||
/// the common name of well-known integer resource types. Therefore,
|
||||
/// the returned string is suitable for passing directly to Win32
|
||||
/// resource APIs that accept resource type strings.
|
||||
/// </remarks>
|
||||
public static explicit operator string(ResourceType resourceType)
|
||||
{
|
||||
return resourceType.resourceType;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<!--
|
||||
<copyright file="Resources.csproj" company="Outercurve Foundation">
|
||||
Copyright (c) 2004, Outercurve Foundation.
|
||||
This software is released under Microsoft Reciprocal License (MS-RL).
|
||||
The license and further copyright text can be found in the file LICENSE.TXT
|
||||
LICENSE.TXT at the root directory of the distribution.
|
||||
</copyright>
|
||||
-->
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
|
||||
<PropertyGroup>
|
||||
<ProjectGuid>{44931ECB-8D6F-4C12-A872-64E261B6A98E}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<RootNamespace>Microsoft.Deployment.Resources</RootNamespace>
|
||||
<AssemblyName>Microsoft.Deployment.Resources</AssemblyName>
|
||||
<TargetFrameworkVersion>v2.0</TargetFrameworkVersion>
|
||||
<CreateDocumentationFile>true</CreateDocumentationFile>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="AssemblyInfo.cs" />
|
||||
<Compile Include="BitmapResource.cs" />
|
||||
<Compile Include="FixedFileVersionInfo.cs" />
|
||||
<Compile Include="GroupIconInfo.cs" />
|
||||
<Compile Include="GroupIconResource.cs" />
|
||||
<Compile Include="NativeMethods.cs" />
|
||||
<Compile Include="Resource.cs" />
|
||||
<Compile Include="ResourceCollection.cs" />
|
||||
<Compile Include="ResourceType.cs" />
|
||||
<Compile Include="VersionEnums.cs" />
|
||||
<Compile Include="VersionInfo.cs" />
|
||||
<Compile Include="VersionResource.cs" />
|
||||
<Compile Include="VersionStringTable.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
|
||||
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), wix.proj))\tools\WixBuild.targets" />
|
||||
</Project>
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче