diff options
| author | Rob Mensching <rob@firegiant.com> | 2025-11-02 17:06:45 -0800 |
|---|---|---|
| committer | Rob Mensching <rob@firegiant.com> | 2025-11-03 15:15:41 -0800 |
| commit | ea519e45b11587c43579823a19f792a609843f83 (patch) | |
| tree | 6a9a1e59120fc762adcfdf5ec8034c764b57e4fe | |
| parent | d2ba0da55725f2908b67e1470afc7cfd71cb3d1f (diff) | |
| download | wix-robmen/maxpath.tar.gz wix-robmen/maxpath.tar.bz2 wix-robmen/maxpath.zip | |
Better handling of long pathsrobmen/maxpath
By integrating the use of long path prefix (\?\\) and careful use of short
paths we can workaround most of the MSI API long path limitations. It's not
perfect as short paths that still exceed MAX_PATH will fail in most MSI APIs.
But accessing files placed in cabinets and copied around should now be fully
long path supported.
Fixes 3065 9115
21 files changed, 324 insertions, 41 deletions
diff --git a/src/wix/WixToolset.Core.Burn/Bind/ProcessBundleSoftwareTagsCommand.cs b/src/wix/WixToolset.Core.Burn/Bind/ProcessBundleSoftwareTagsCommand.cs index 5231e0be..70b98222 100644 --- a/src/wix/WixToolset.Core.Burn/Bind/ProcessBundleSoftwareTagsCommand.cs +++ b/src/wix/WixToolset.Core.Burn/Bind/ProcessBundleSoftwareTagsCommand.cs | |||
| @@ -69,7 +69,7 @@ namespace WixToolset.Core.Burn.Bind | |||
| 69 | { | 69 | { |
| 70 | var payload = payloadSymbolsById[msiPackage.PayloadRef]; | 70 | var payload = payloadSymbolsById[msiPackage.PayloadRef]; |
| 71 | 71 | ||
| 72 | using (var db = new Database(payload.SourceFile.Path, OpenDatabase.ReadOnly)) | 72 | using (var db = Database.OpenAsReadOnly(payload.SourceFile.Path)) |
| 73 | { | 73 | { |
| 74 | if (db.TableExists("SoftwareIdentificationTag")) | 74 | if (db.TableExists("SoftwareIdentificationTag")) |
| 75 | { | 75 | { |
diff --git a/src/wix/WixToolset.Core.Burn/Bundles/ProcessMsiPackageCommand.cs b/src/wix/WixToolset.Core.Burn/Bundles/ProcessMsiPackageCommand.cs index d6cf1cfd..0b78c545 100644 --- a/src/wix/WixToolset.Core.Burn/Bundles/ProcessMsiPackageCommand.cs +++ b/src/wix/WixToolset.Core.Burn/Bundles/ProcessMsiPackageCommand.cs | |||
| @@ -164,7 +164,7 @@ namespace WixToolset.Core.Burn.Bundles | |||
| 164 | 164 | ||
| 165 | this.CheckIfWindowsInstallerFileTooLarge(this.PackagePayload.SourceLineNumbers, sourcePath, "MSI"); | 165 | this.CheckIfWindowsInstallerFileTooLarge(this.PackagePayload.SourceLineNumbers, sourcePath, "MSI"); |
| 166 | 166 | ||
| 167 | using (var db = new Database(sourcePath, OpenDatabase.ReadOnly)) | 167 | using (var db = Database.OpenAsReadOnly(sourcePath)) |
| 168 | { | 168 | { |
| 169 | // Read data out of the msi database... | 169 | // Read data out of the msi database... |
| 170 | using (var sumInfo = new SummaryInformation(db)) | 170 | using (var sumInfo = new SummaryInformation(db)) |
diff --git a/src/wix/WixToolset.Core.Burn/Bundles/ProcessMspPackageCommand.cs b/src/wix/WixToolset.Core.Burn/Bundles/ProcessMspPackageCommand.cs index b889c2ce..8d1e9c39 100644 --- a/src/wix/WixToolset.Core.Burn/Bundles/ProcessMspPackageCommand.cs +++ b/src/wix/WixToolset.Core.Burn/Bundles/ProcessMspPackageCommand.cs | |||
| @@ -98,7 +98,7 @@ namespace WixToolset.Core.Burn.Bundles | |||
| 98 | 98 | ||
| 99 | try | 99 | try |
| 100 | { | 100 | { |
| 101 | using (var db = new Database(sourcePath, OpenDatabase.ReadOnly | OpenDatabase.OpenPatchFile)) | 101 | using (var db = Database.OpenAsReadOnly(sourcePath, asPatch: true)) |
| 102 | { | 102 | { |
| 103 | // Read data out of the msp database... | 103 | // Read data out of the msp database... |
| 104 | using (var sumInfo = new SummaryInformation(db)) | 104 | using (var sumInfo = new SummaryInformation(db)) |
diff --git a/src/wix/WixToolset.Core.Native/LongPathUtil.cs b/src/wix/WixToolset.Core.Native/LongPathUtil.cs new file mode 100644 index 00000000..c24f1736 --- /dev/null +++ b/src/wix/WixToolset.Core.Native/LongPathUtil.cs | |||
| @@ -0,0 +1,78 @@ | |||
| 1 | // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. | ||
| 2 | |||
| 3 | namespace WixToolset.Core.Native | ||
| 4 | { | ||
| 5 | using System.IO; | ||
| 6 | using System.Runtime.InteropServices; | ||
| 7 | using System.Text; | ||
| 8 | |||
| 9 | internal static class PathUtil | ||
| 10 | { | ||
| 11 | private const int MaxPath = 260; | ||
| 12 | private const string LongPathPrefix = @"\\?\"; | ||
| 13 | |||
| 14 | public static bool CreateOrGetShortPath(string path, out string shortPath) | ||
| 15 | { | ||
| 16 | var fileCreated = false; | ||
| 17 | |||
| 18 | // The file must exist so we can get its short path. | ||
| 19 | if (!File.Exists(path)) | ||
| 20 | { | ||
| 21 | using (File.Create(path)) | ||
| 22 | { | ||
| 23 | } | ||
| 24 | |||
| 25 | fileCreated = true; | ||
| 26 | } | ||
| 27 | |||
| 28 | // Use the short path to avoid issues with long paths in the MSI API. | ||
| 29 | shortPath = GetShortPath(path); | ||
| 30 | |||
| 31 | return fileCreated; | ||
| 32 | } | ||
| 33 | |||
| 34 | public static string GetPrefixedLongPath(string path) | ||
| 35 | { | ||
| 36 | if (path.Length > MaxPath && !path.StartsWith(LongPathPrefix)) | ||
| 37 | { | ||
| 38 | path = LongPathPrefix + path; | ||
| 39 | } | ||
| 40 | |||
| 41 | return path; | ||
| 42 | } | ||
| 43 | |||
| 44 | public static string GetShortPath(string longPath) | ||
| 45 | { | ||
| 46 | var path = GetPrefixedLongPath(longPath); | ||
| 47 | |||
| 48 | var buffer = new StringBuilder(MaxPath); // start with MAX_PATH. | ||
| 49 | |||
| 50 | var result = GetShortPathName(path, buffer, (uint)buffer.Capacity); | ||
| 51 | |||
| 52 | // If result > buffer.Capacity, reallocate and call again (even though we're usually using short names to avoid long path) | ||
| 53 | // so the short path result is still going to end up too long for APIs requiring a short path. | ||
| 54 | if (result > buffer.Capacity) | ||
| 55 | { | ||
| 56 | buffer = new StringBuilder((int)result); | ||
| 57 | |||
| 58 | result = GetShortPathName(path, buffer, (uint)buffer.Capacity); | ||
| 59 | } | ||
| 60 | |||
| 61 | // If we succeeded, return the short path without the prefix. | ||
| 62 | if (result > 0) | ||
| 63 | { | ||
| 64 | path = buffer.ToString(); | ||
| 65 | |||
| 66 | if (path.StartsWith(LongPathPrefix)) | ||
| 67 | { | ||
| 68 | path = path.Substring(LongPathPrefix.Length); | ||
| 69 | } | ||
| 70 | } | ||
| 71 | |||
| 72 | return path; | ||
| 73 | } | ||
| 74 | |||
| 75 | [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] | ||
| 76 | private static extern uint GetShortPathName(string lpszLongPath, StringBuilder lpszShortPath, uint cchBuffer); | ||
| 77 | } | ||
| 78 | } | ||
diff --git a/src/wix/WixToolset.Core.Native/Msi/Database.cs b/src/wix/WixToolset.Core.Native/Msi/Database.cs index 18e5066d..33eb8de6 100644 --- a/src/wix/WixToolset.Core.Native/Msi/Database.cs +++ b/src/wix/WixToolset.Core.Native/Msi/Database.cs | |||
| @@ -18,13 +18,14 @@ namespace WixToolset.Core.Native.Msi | |||
| 18 | /// </summary> | 18 | /// </summary> |
| 19 | /// <param name="path">Path to the database to be opened.</param> | 19 | /// <param name="path">Path to the database to be opened.</param> |
| 20 | /// <param name="type">Persist mode to use when opening the database.</param> | 20 | /// <param name="type">Persist mode to use when opening the database.</param> |
| 21 | public Database(string path, OpenDatabase type) | 21 | private Database(string path, OpenDatabase type) |
| 22 | { | 22 | { |
| 23 | var error = MsiInterop.MsiOpenDatabase(path, (IntPtr)type, out var handle); | 23 | var error = MsiInterop.MsiOpenDatabase(path, (IntPtr)type, out var handle); |
| 24 | if (0 != error) | 24 | if (0 != error) |
| 25 | { | 25 | { |
| 26 | throw new MsiException(error); | 26 | throw new MsiException(error); |
| 27 | } | 27 | } |
| 28 | |||
| 28 | this.Handle = handle; | 29 | this.Handle = handle; |
| 29 | } | 30 | } |
| 30 | 31 | ||
| @@ -34,14 +35,89 @@ namespace WixToolset.Core.Native.Msi | |||
| 34 | public static int MsiMaxStreamNameLength => MsiInterop.MsiMaxStreamNameLength; | 35 | public static int MsiMaxStreamNameLength => MsiInterop.MsiMaxStreamNameLength; |
| 35 | 36 | ||
| 36 | /// <summary> | 37 | /// <summary> |
| 38 | /// Creates a new <see cref="Database"/> with the specified path. | ||
| 39 | /// </summary> | ||
| 40 | /// <param name="path">Path of database to be created.</param> | ||
| 41 | /// <param name="asPatch">Indicates whether the database should be opened as a patch file.</param> | ||
| 42 | public static Database Create(string path, bool asPatch = false) | ||
| 43 | { | ||
| 44 | var fileCreated = false; | ||
| 45 | var mode = OpenDatabase.CreateDirect; | ||
| 46 | |||
| 47 | if (asPatch) | ||
| 48 | { | ||
| 49 | mode |= OpenDatabase.OpenPatchFile; | ||
| 50 | } | ||
| 51 | |||
| 52 | try | ||
| 53 | { | ||
| 54 | fileCreated = PathUtil.CreateOrGetShortPath(path, out var shortPath); | ||
| 55 | |||
| 56 | return new Database(shortPath, mode); | ||
| 57 | } | ||
| 58 | catch // cleanup on error if we created the short path file. | ||
| 59 | { | ||
| 60 | if (fileCreated) | ||
| 61 | { | ||
| 62 | File.Delete(path); | ||
| 63 | } | ||
| 64 | |||
| 65 | throw; | ||
| 66 | } | ||
| 67 | } | ||
| 68 | |||
| 69 | /// <summary> | ||
| 70 | /// Opens an existing <see cref="Database"/> with the specified path. | ||
| 71 | /// </summary> | ||
| 72 | /// <param name="path">Path of database to open.</param> | ||
| 73 | /// <param name="transact">Indicates whether to open the database in transaction mode.</param> | ||
| 74 | /// <param name="asPatch">Indicates whether the database should be opened as a patch file.</param> | ||
| 75 | public static Database Open(string path, bool transact = false, bool asPatch = false) | ||
| 76 | { | ||
| 77 | var mode = transact ? OpenDatabase.Transact : OpenDatabase.Direct; | ||
| 78 | |||
| 79 | if (asPatch) | ||
| 80 | { | ||
| 81 | mode |= OpenDatabase.OpenPatchFile; | ||
| 82 | } | ||
| 83 | |||
| 84 | // Use the short path to avoid issues with long paths in the MSI API. | ||
| 85 | var shortPath = PathUtil.GetShortPath(path); | ||
| 86 | |||
| 87 | return new Database(shortPath, mode); | ||
| 88 | } | ||
| 89 | |||
| 90 | /// <summary> | ||
| 91 | /// Opens an existing <see cref="Database"/> with the specified path. | ||
| 92 | /// </summary> | ||
| 93 | /// <param name="path">Path of database to open.</param> | ||
| 94 | /// <param name="asPatch">Indicates whether the database should be opened as a patch file.</param> | ||
| 95 | public static Database OpenAsReadOnly(string path, bool asPatch = false) | ||
| 96 | { | ||
| 97 | var mode = OpenDatabase.ReadOnly; | ||
| 98 | |||
| 99 | if (asPatch) | ||
| 100 | { | ||
| 101 | mode |= OpenDatabase.OpenPatchFile; | ||
| 102 | } | ||
| 103 | |||
| 104 | // Use the short path to avoid issues with long paths in the MSI API. | ||
| 105 | var shortPath = PathUtil.GetShortPath(path); | ||
| 106 | |||
| 107 | return new Database(shortPath, mode); | ||
| 108 | } | ||
| 109 | |||
| 110 | /// <summary> | ||
| 37 | /// Apply a transform to the MSI. | 111 | /// Apply a transform to the MSI. |
| 38 | /// </summary> | 112 | /// </summary> |
| 39 | /// <param name="transformFile">Path to transform to apply.</param> | 113 | /// <param name="transformFile">Path to transform to apply.</param> |
| 40 | public void ApplyTransform(string transformFile) | 114 | public void ApplyTransform(string transformFile) |
| 41 | { | 115 | { |
| 116 | var shortTransformFile = PathUtil.GetShortPath(transformFile); | ||
| 117 | |||
| 42 | // get the curret validation bits | 118 | // get the curret validation bits |
| 43 | var conditions = TransformErrorConditions.None; | 119 | var conditions = TransformErrorConditions.None; |
| 44 | using (var summaryInfo = new SummaryInformation(transformFile)) | 120 | using (var summaryInfo = new SummaryInformation(shortTransformFile)) |
| 45 | { | 121 | { |
| 46 | try | 122 | try |
| 47 | { | 123 | { |
| @@ -64,7 +140,9 @@ namespace WixToolset.Core.Native.Msi | |||
| 64 | /// <param name="errorConditions">Specifies the error conditions that are to be suppressed.</param> | 140 | /// <param name="errorConditions">Specifies the error conditions that are to be suppressed.</param> |
| 65 | public void ApplyTransform(string transformFile, TransformErrorConditions errorConditions) | 141 | public void ApplyTransform(string transformFile, TransformErrorConditions errorConditions) |
| 66 | { | 142 | { |
| 67 | var error = MsiInterop.MsiDatabaseApplyTransform(this.Handle, transformFile, errorConditions); | 143 | var shortTransformFile = PathUtil.GetShortPath(transformFile); |
| 144 | |||
| 145 | var error = MsiInterop.MsiDatabaseApplyTransform(this.Handle, shortTransformFile, errorConditions); | ||
| 68 | if (0 != error) | 146 | if (0 != error) |
| 69 | { | 147 | { |
| 70 | throw new MsiException(error); | 148 | throw new MsiException(error); |
| @@ -118,7 +196,9 @@ namespace WixToolset.Core.Native.Msi | |||
| 118 | /// shows which properties should be validated to verify that this transform can be applied to the database.</param> | 196 | /// shows which properties should be validated to verify that this transform can be applied to the database.</param> |
| 119 | public void CreateTransformSummaryInfo(Database referenceDatabase, string transformFile, TransformErrorConditions errorConditions, TransformValidations validations) | 197 | public void CreateTransformSummaryInfo(Database referenceDatabase, string transformFile, TransformErrorConditions errorConditions, TransformValidations validations) |
| 120 | { | 198 | { |
| 121 | var error = MsiInterop.MsiCreateTransformSummaryInfo(this.Handle, referenceDatabase.Handle, transformFile, errorConditions, validations); | 199 | var shortTransformFile = PathUtil.GetShortPath(transformFile); |
| 200 | |||
| 201 | var error = MsiInterop.MsiCreateTransformSummaryInfo(this.Handle, referenceDatabase.Handle, shortTransformFile, errorConditions, validations); | ||
| 122 | if (0 != error) | 202 | if (0 != error) |
| 123 | { | 203 | { |
| 124 | throw new MsiException(error); | 204 | throw new MsiException(error); |
| @@ -136,7 +216,9 @@ namespace WixToolset.Core.Native.Msi | |||
| 136 | var folderPath = Path.GetFullPath(Path.GetDirectoryName(idtPath)); | 216 | var folderPath = Path.GetFullPath(Path.GetDirectoryName(idtPath)); |
| 137 | var fileName = Path.GetFileName(idtPath); | 217 | var fileName = Path.GetFileName(idtPath); |
| 138 | 218 | ||
| 139 | var error = MsiInterop.MsiDatabaseImport(this.Handle, folderPath, fileName); | 219 | var shortFolderPath = PathUtil.GetShortPath(folderPath); |
| 220 | |||
| 221 | var error = MsiInterop.MsiDatabaseImport(this.Handle, shortFolderPath, fileName); | ||
| 140 | if (1627 == error) // ERROR_FUNCTION_FAILED | 222 | if (1627 == error) // ERROR_FUNCTION_FAILED |
| 141 | { | 223 | { |
| 142 | throw new WixInvalidIdtException(idtPath); | 224 | throw new WixInvalidIdtException(idtPath); |
| @@ -160,7 +242,9 @@ namespace WixToolset.Core.Native.Msi | |||
| 160 | folderPath = Environment.CurrentDirectory; | 242 | folderPath = Environment.CurrentDirectory; |
| 161 | } | 243 | } |
| 162 | 244 | ||
| 163 | var error = MsiInterop.MsiDatabaseExport(this.Handle, tableName, folderPath, fileName); | 245 | var shortFolderPath = PathUtil.GetShortPath(folderPath); |
| 246 | |||
| 247 | var error = MsiInterop.MsiDatabaseExport(this.Handle, tableName, shortFolderPath, fileName); | ||
| 164 | if (0 != error) | 248 | if (0 != error) |
| 165 | { | 249 | { |
| 166 | throw new MsiException(error); | 250 | throw new MsiException(error); |
| @@ -176,13 +260,29 @@ namespace WixToolset.Core.Native.Msi | |||
| 176 | /// there are no differences between the two databases.</returns> | 260 | /// there are no differences between the two databases.</returns> |
| 177 | public bool GenerateTransform(Database referenceDatabase, string transformFile) | 261 | public bool GenerateTransform(Database referenceDatabase, string transformFile) |
| 178 | { | 262 | { |
| 179 | var error = MsiInterop.MsiDatabaseGenerateTransform(this.Handle, referenceDatabase.Handle, transformFile, 0, 0); | 263 | var fileCreated = false; |
| 180 | if (0 != error && 0xE8 != error) // ERROR_NO_DATA(0xE8) means no differences were found | 264 | |
| 265 | try | ||
| 181 | { | 266 | { |
| 182 | throw new MsiException(error); | 267 | fileCreated = PathUtil.CreateOrGetShortPath(transformFile, out var shortTransformFile); |
| 268 | |||
| 269 | var error = MsiInterop.MsiDatabaseGenerateTransform(this.Handle, referenceDatabase.Handle, shortTransformFile, 0, 0); | ||
| 270 | if (0 != error && 0xE8 != error) // ERROR_NO_DATA(0xE8) means no differences were found | ||
| 271 | { | ||
| 272 | throw new MsiException(error); | ||
| 273 | } | ||
| 274 | |||
| 275 | return (0xE8 != error); | ||
| 183 | } | 276 | } |
| 277 | catch // Cleanup on error | ||
| 278 | { | ||
| 279 | if (fileCreated) | ||
| 280 | { | ||
| 281 | File.Delete(transformFile); | ||
| 282 | } | ||
| 184 | 283 | ||
| 185 | return (0xE8 != error); | 284 | throw; |
| 285 | } | ||
| 186 | } | 286 | } |
| 187 | 287 | ||
| 188 | /// <summary> | 288 | /// <summary> |
diff --git a/src/wix/WixToolset.Core.Native/Msi/Installer.cs b/src/wix/WixToolset.Core.Native/Msi/Installer.cs index b2c2c630..14745469 100644 --- a/src/wix/WixToolset.Core.Native/Msi/Installer.cs +++ b/src/wix/WixToolset.Core.Native/Msi/Installer.cs | |||
| @@ -34,11 +34,13 @@ namespace WixToolset.Core.Native.Msi | |||
| 34 | var buffer = new StringBuilder(65535); | 34 | var buffer = new StringBuilder(65535); |
| 35 | var size = buffer.Capacity; | 35 | var size = buffer.Capacity; |
| 36 | 36 | ||
| 37 | var error = MsiInterop.MsiExtractPatchXMLData(path, 0, buffer, ref size); | 37 | var shortPath = PathUtil.GetShortPath(path); |
| 38 | |||
| 39 | var error = MsiInterop.MsiExtractPatchXMLData(shortPath, 0, buffer, ref size); | ||
| 38 | if (234 == error) | 40 | if (234 == error) |
| 39 | { | 41 | { |
| 40 | buffer.EnsureCapacity(++size); | 42 | buffer.EnsureCapacity(++size); |
| 41 | error = MsiInterop.MsiExtractPatchXMLData(path, 0, buffer, ref size); | 43 | error = MsiInterop.MsiExtractPatchXMLData(shortPath, 0, buffer, ref size); |
| 42 | } | 44 | } |
| 43 | 45 | ||
| 44 | if (error != 0) | 46 | if (error != 0) |
| @@ -57,8 +59,12 @@ namespace WixToolset.Core.Native.Msi | |||
| 57 | /// <param name="hash">Int array that receives the returned file hash information.</param> | 59 | /// <param name="hash">Int array that receives the returned file hash information.</param> |
| 58 | public static void GetFileHash(string filePath, int options, out int[] hash) | 60 | public static void GetFileHash(string filePath, int options, out int[] hash) |
| 59 | { | 61 | { |
| 60 | var hashInterop = new MSIFILEHASHINFO(); | 62 | var hashInterop = new MSIFILEHASHINFO |
| 61 | hashInterop.FileHashInfoSize = 20; | 63 | { |
| 64 | FileHashInfoSize = 20 | ||
| 65 | }; | ||
| 66 | |||
| 67 | filePath = PathUtil.GetPrefixedLongPath(filePath); | ||
| 62 | 68 | ||
| 63 | var error = MsiInterop.MsiGetFileHash(filePath, Convert.ToUInt32(options), hashInterop); | 69 | var error = MsiInterop.MsiGetFileHash(filePath, Convert.ToUInt32(options), hashInterop); |
| 64 | if (0 != error) | 70 | if (0 != error) |
| @@ -76,9 +82,9 @@ namespace WixToolset.Core.Native.Msi | |||
| 76 | } | 82 | } |
| 77 | 83 | ||
| 78 | /// <summary> | 84 | /// <summary> |
| 79 | /// Returns the version string and language string in the format that the installer | 85 | /// Returns the version string and language string in the format that the installer |
| 80 | /// expects to find them in the database. If you just want version information, set | 86 | /// expects to find them in the database. If you just want version information, set |
| 81 | /// lpLangBuf and pcchLangBuf to zero. If you just want language information, set | 87 | /// lpLangBuf and pcchLangBuf to zero. If you just want language information, set |
| 82 | /// lpVersionBuf and pcchVersionBuf to zero. | 88 | /// lpVersionBuf and pcchVersionBuf to zero. |
| 83 | /// </summary> | 89 | /// </summary> |
| 84 | /// <param name="filePath">Specifies the path to the file.</param> | 90 | /// <param name="filePath">Specifies the path to the file.</param> |
| @@ -91,6 +97,8 @@ namespace WixToolset.Core.Native.Msi | |||
| 91 | var versionBuffer = new StringBuilder(versionLength); | 97 | var versionBuffer = new StringBuilder(versionLength); |
| 92 | var languageBuffer = new StringBuilder(languageLength); | 98 | var languageBuffer = new StringBuilder(languageLength); |
| 93 | 99 | ||
| 100 | filePath = PathUtil.GetPrefixedLongPath(filePath); | ||
| 101 | |||
| 94 | var error = MsiInterop.MsiGetFileVersion(filePath, versionBuffer, ref versionLength, languageBuffer, ref languageLength); | 102 | var error = MsiInterop.MsiGetFileVersion(filePath, versionBuffer, ref versionLength, languageBuffer, ref languageLength); |
| 95 | if (234 == error) | 103 | if (234 == error) |
| 96 | { | 104 | { |
diff --git a/src/wix/WixToolset.Core.Native/Msi/OpenDatabase.cs b/src/wix/WixToolset.Core.Native/Msi/OpenDatabase.cs index 18a78f77..6d0226d2 100644 --- a/src/wix/WixToolset.Core.Native/Msi/OpenDatabase.cs +++ b/src/wix/WixToolset.Core.Native/Msi/OpenDatabase.cs | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. | 1 | // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. |
| 2 | 2 | ||
| 3 | namespace WixToolset.Core.Native.Msi | 3 | namespace WixToolset.Core.Native.Msi |
| 4 | { | 4 | { |
diff --git a/src/wix/WixToolset.Core.Native/Msi/SummaryInformation.cs b/src/wix/WixToolset.Core.Native/Msi/SummaryInformation.cs index 3b3dea0f..3eed1274 100644 --- a/src/wix/WixToolset.Core.Native/Msi/SummaryInformation.cs +++ b/src/wix/WixToolset.Core.Native/Msi/SummaryInformation.cs | |||
| @@ -220,8 +220,10 @@ namespace WixToolset.Core.Native.Msi | |||
| 220 | throw new ArgumentNullException(nameof(databaseFile)); | 220 | throw new ArgumentNullException(nameof(databaseFile)); |
| 221 | } | 221 | } |
| 222 | 222 | ||
| 223 | var shortDatabaseFile = PathUtil.GetShortPath(databaseFile); | ||
| 224 | |||
| 223 | var handle = IntPtr.Zero; | 225 | var handle = IntPtr.Zero; |
| 224 | var error = MsiInterop.MsiGetSummaryInformation(IntPtr.Zero, databaseFile, 0, ref handle); | 226 | var error = MsiInterop.MsiGetSummaryInformation(IntPtr.Zero, shortDatabaseFile, 0, ref handle); |
| 225 | if (0 != error) | 227 | if (0 != error) |
| 226 | { | 228 | { |
| 227 | throw new MsiException(error); | 229 | throw new MsiException(error); |
diff --git a/src/wix/WixToolset.Core.Native/WindowsInstallerValidator.cs b/src/wix/WixToolset.Core.Native/WindowsInstallerValidator.cs index 7978304a..434b0887 100644 --- a/src/wix/WixToolset.Core.Native/WindowsInstallerValidator.cs +++ b/src/wix/WixToolset.Core.Native/WindowsInstallerValidator.cs | |||
| @@ -93,7 +93,7 @@ namespace WixToolset.Core.Native | |||
| 93 | 93 | ||
| 94 | try | 94 | try |
| 95 | { | 95 | { |
| 96 | using (var database = new Database(this.DatabasePath, OpenDatabase.Direct)) | 96 | using (var database = Database.Open(this.DatabasePath)) |
| 97 | { | 97 | { |
| 98 | var propertyTableExists = database.TableExists("Property"); | 98 | var propertyTableExists = database.TableExists("Property"); |
| 99 | string productCode = null; | 99 | string productCode = null; |
| @@ -130,7 +130,7 @@ namespace WixToolset.Core.Native | |||
| 130 | 130 | ||
| 131 | try | 131 | try |
| 132 | { | 132 | { |
| 133 | using (var cubeDatabase = new Database(findCubeFile.Path, OpenDatabase.ReadOnly)) | 133 | using (var cubeDatabase = Database.OpenAsReadOnly(findCubeFile.Path)) |
| 134 | { | 134 | { |
| 135 | try | 135 | try |
| 136 | { | 136 | { |
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Bind/BindTransformCommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/Bind/BindTransformCommand.cs index b37c7b95..71dc4881 100644 --- a/src/wix/WixToolset.Core.WindowsInstaller/Bind/BindTransformCommand.cs +++ b/src/wix/WixToolset.Core.WindowsInstaller/Bind/BindTransformCommand.cs | |||
| @@ -413,8 +413,8 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 413 | Directory.CreateDirectory(Path.GetDirectoryName(this.OutputPath)); | 413 | Directory.CreateDirectory(Path.GetDirectoryName(this.OutputPath)); |
| 414 | 414 | ||
| 415 | // create the transform file | 415 | // create the transform file |
| 416 | using (var targetDatabase = new Database(targetDatabaseFile, OpenDatabase.ReadOnly)) | 416 | using (var targetDatabase = Database.OpenAsReadOnly(targetDatabaseFile)) |
| 417 | using (var updatedDatabase = new Database(updatedDatabaseFile, OpenDatabase.ReadOnly)) | 417 | using (var updatedDatabase = Database.OpenAsReadOnly(updatedDatabaseFile)) |
| 418 | { | 418 | { |
| 419 | if (updatedDatabase.GenerateTransform(targetDatabase, this.OutputPath)) | 419 | if (updatedDatabase.GenerateTransform(targetDatabase, this.OutputPath)) |
| 420 | { | 420 | { |
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Bind/ExtractMergeModuleFilesCommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/Bind/ExtractMergeModuleFilesCommand.cs index 94ed0afc..117923e5 100644 --- a/src/wix/WixToolset.Core.WindowsInstaller/Bind/ExtractMergeModuleFilesCommand.cs +++ b/src/wix/WixToolset.Core.WindowsInstaller/Bind/ExtractMergeModuleFilesCommand.cs | |||
| @@ -90,7 +90,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 90 | try | 90 | try |
| 91 | { | 91 | { |
| 92 | // read the module's File table to get its FileMediaInformation entries and gather any other information needed from the module. | 92 | // read the module's File table to get its FileMediaInformation entries and gather any other information needed from the module. |
| 93 | using (var db = new Database(wixMergeRow.SourceFile, OpenDatabase.ReadOnly)) | 93 | using (var db = Database.OpenAsReadOnly(wixMergeRow.SourceFile)) |
| 94 | { | 94 | { |
| 95 | if (db.TableExists("File") && db.TableExists("Component")) | 95 | if (db.TableExists("File") && db.TableExists("Component")) |
| 96 | { | 96 | { |
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Bind/GenerateDatabaseCommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/Bind/GenerateDatabaseCommand.cs index 361f797d..a248dbd5 100644 --- a/src/wix/WixToolset.Core.WindowsInstaller/Bind/GenerateDatabaseCommand.cs +++ b/src/wix/WixToolset.Core.WindowsInstaller/Bind/GenerateDatabaseCommand.cs | |||
| @@ -81,20 +81,13 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 81 | 81 | ||
| 82 | var idtFolder = Path.Combine(baseDirectory, IdtsSubFolder); | 82 | var idtFolder = Path.Combine(baseDirectory, IdtsSubFolder); |
| 83 | 83 | ||
| 84 | var type = OpenDatabase.CreateDirect; | ||
| 85 | |||
| 86 | if (OutputType.Patch == this.Data.Type) | ||
| 87 | { | ||
| 88 | type |= OpenDatabase.OpenPatchFile; | ||
| 89 | } | ||
| 90 | |||
| 91 | try | 84 | try |
| 92 | { | 85 | { |
| 93 | Directory.CreateDirectory(Path.GetDirectoryName(this.OutputPath)); | 86 | Directory.CreateDirectory(Path.GetDirectoryName(this.OutputPath)); |
| 94 | 87 | ||
| 95 | Directory.CreateDirectory(idtFolder); | 88 | Directory.CreateDirectory(idtFolder); |
| 96 | 89 | ||
| 97 | using (var db = new Database(this.OutputPath, type)) | 90 | using (var db = Database.Create(this.OutputPath, asPatch: OutputType.Patch == this.Data.Type)) |
| 98 | { | 91 | { |
| 99 | // If we're not using the default codepage, import a new one into our | 92 | // If we're not using the default codepage, import a new one into our |
| 100 | // database before we add any tables (or the tables would be added | 93 | // database before we add any tables (or the tables would be added |
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Bind/MergeModulesCommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/Bind/MergeModulesCommand.cs index 5f21f496..71a2e367 100644 --- a/src/wix/WixToolset.Core.WindowsInstaller/Bind/MergeModulesCommand.cs +++ b/src/wix/WixToolset.Core.WindowsInstaller/Bind/MergeModulesCommand.cs | |||
| @@ -223,7 +223,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 223 | return; | 223 | return; |
| 224 | } | 224 | } |
| 225 | 225 | ||
| 226 | using (var db = new Database(this.OutputPath, OpenDatabase.Direct)) | 226 | using (var db = Database.Open(this.OutputPath)) |
| 227 | { | 227 | { |
| 228 | // Suppress individual actions. | 228 | // Suppress individual actions. |
| 229 | foreach (var suppressAction in this.Section.Symbols.OfType<WixSuppressActionSymbol>()) | 229 | foreach (var suppressAction in this.Section.Symbols.OfType<WixSuppressActionSymbol>()) |
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Bind/ProcessUncompressedFilesCommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/Bind/ProcessUncompressedFilesCommand.cs index c5f198f8..ff7c6579 100644 --- a/src/wix/WixToolset.Core.WindowsInstaller/Bind/ProcessUncompressedFilesCommand.cs +++ b/src/wix/WixToolset.Core.WindowsInstaller/Bind/ProcessUncompressedFilesCommand.cs | |||
| @@ -64,7 +64,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 64 | 64 | ||
| 65 | var mediaRows = this.Section.Symbols.OfType<MediaSymbol>().ToDictionary(t => t.DiskId); | 65 | var mediaRows = this.Section.Symbols.OfType<MediaSymbol>().ToDictionary(t => t.DiskId); |
| 66 | 66 | ||
| 67 | using (var db = new Database(this.DatabasePath, OpenDatabase.ReadOnly)) | 67 | using (var db = Database.OpenAsReadOnly(this.DatabasePath)) |
| 68 | { | 68 | { |
| 69 | using (var directoryView = db.OpenExecuteView("SELECT `Directory`, `Directory_Parent`, `DefaultDir` FROM `Directory`")) | 69 | using (var directoryView = db.OpenExecuteView("SELECT `Directory`, `Directory_Parent`, `DefaultDir` FROM `Directory`")) |
| 70 | { | 70 | { |
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Bind/UpdateFileFacadesCommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/Bind/UpdateFileFacadesCommand.cs index 52d2146c..75e536c2 100644 --- a/src/wix/WixToolset.Core.WindowsInstaller/Bind/UpdateFileFacadesCommand.cs +++ b/src/wix/WixToolset.Core.WindowsInstaller/Bind/UpdateFileFacadesCommand.cs | |||
| @@ -76,7 +76,8 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 76 | var assemblySymbols = this.Section.Symbols.OfType<AssemblySymbol>().ToDictionary(t => t.Id.Id); | 76 | var assemblySymbols = this.Section.Symbols.OfType<AssemblySymbol>().ToDictionary(t => t.Id.Id); |
| 77 | 77 | ||
| 78 | Parallel.ForEach(facades, | 78 | Parallel.ForEach(facades, |
| 79 | new ParallelOptions{ | 79 | new ParallelOptions |
| 80 | { | ||
| 80 | CancellationToken = this.CancellationToken, | 81 | CancellationToken = this.CancellationToken, |
| 81 | MaxDegreeOfParallelism = this.ThreadCount | 82 | MaxDegreeOfParallelism = this.ThreadCount |
| 82 | }, | 83 | }, |
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Inscribe/InscribeMsiPackageCommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/Inscribe/InscribeMsiPackageCommand.cs index 455057f6..344cccb8 100644 --- a/src/wix/WixToolset.Core.WindowsInstaller/Inscribe/InscribeMsiPackageCommand.cs +++ b/src/wix/WixToolset.Core.WindowsInstaller/Inscribe/InscribeMsiPackageCommand.cs | |||
| @@ -61,7 +61,7 @@ namespace WixToolset.Core.WindowsInstaller.Inscribe | |||
| 61 | return shouldCommit; | 61 | return shouldCommit; |
| 62 | } | 62 | } |
| 63 | 63 | ||
| 64 | using (var database = new Database(databasePath, OpenDatabase.Transact)) | 64 | using (var database = Database.Open(databasePath, transact: true)) |
| 65 | { | 65 | { |
| 66 | // Just use the English codepage, because the tables we're importing only have binary streams / MSI identifiers / other non-localizable content | 66 | // Just use the English codepage, because the tables we're importing only have binary streams / MSI identifiers / other non-localizable content |
| 67 | var codepage = 1252; | 67 | var codepage = 1252; |
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Unbind/UnbindDatabaseCommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/Unbind/UnbindDatabaseCommand.cs index cfa53269..dce0d488 100644 --- a/src/wix/WixToolset.Core.WindowsInstaller/Unbind/UnbindDatabaseCommand.cs +++ b/src/wix/WixToolset.Core.WindowsInstaller/Unbind/UnbindDatabaseCommand.cs | |||
| @@ -86,7 +86,7 @@ namespace WixToolset.Core.WindowsInstaller.Unbind | |||
| 86 | { | 86 | { |
| 87 | if (this.Database == null) | 87 | if (this.Database == null) |
| 88 | { | 88 | { |
| 89 | database = new Database(this.DatabasePath, OpenDatabase.ReadOnly); | 89 | database = Database.OpenAsReadOnly(this.DatabasePath); |
| 90 | this.Database = database; | 90 | this.Database = database; |
| 91 | } | 91 | } |
| 92 | 92 | ||
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Unbind/UnbindTransformCommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/Unbind/UnbindTransformCommand.cs index 8846739a..cc83af72 100644 --- a/src/wix/WixToolset.Core.WindowsInstaller/Unbind/UnbindTransformCommand.cs +++ b/src/wix/WixToolset.Core.WindowsInstaller/Unbind/UnbindTransformCommand.cs | |||
| @@ -263,7 +263,7 @@ namespace WixToolset.Core.WindowsInstaller.Unbind | |||
| 263 | 263 | ||
| 264 | private Database ApplyTransformToSchemaDatabase(string schemaDatabasePath, TransformErrorConditions transformConditions) | 264 | private Database ApplyTransformToSchemaDatabase(string schemaDatabasePath, TransformErrorConditions transformConditions) |
| 265 | { | 265 | { |
| 266 | var msiDatabase = new Database(schemaDatabasePath, OpenDatabase.Transact); | 266 | var msiDatabase = Database.Open(schemaDatabasePath, transact: true); |
| 267 | 267 | ||
| 268 | try | 268 | try |
| 269 | { | 269 | { |
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/LongPathFixture.cs b/src/wix/test/WixToolsetTest.CoreIntegration/LongPathFixture.cs new file mode 100644 index 00000000..2cada64d --- /dev/null +++ b/src/wix/test/WixToolsetTest.CoreIntegration/LongPathFixture.cs | |||
| @@ -0,0 +1,89 @@ | |||
| 1 | // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. | ||
| 2 | |||
| 3 | namespace WixToolsetTest.CoreIntegration | ||
| 4 | { | ||
| 5 | using System.IO; | ||
| 6 | using System.Linq; | ||
| 7 | using WixInternal.Core.TestPackage; | ||
| 8 | using WixInternal.TestSupport; | ||
| 9 | using WixToolset.Data; | ||
| 10 | using WixToolset.Data.Symbols; | ||
| 11 | using Xunit; | ||
| 12 | |||
| 13 | public class LongPathFixture | ||
| 14 | { | ||
| 15 | [Fact] | ||
| 16 | public void TestLongPathSupport() | ||
| 17 | { | ||
| 18 | var testDataFolder = TestData.Get(@"TestData", "SingleFile"); | ||
| 19 | |||
| 20 | using (var fs = new DisposableFileSystem()) | ||
| 21 | { | ||
| 22 | var folder = fs.GetFolder(); | ||
| 23 | |||
| 24 | while (folder.Length < 500) | ||
| 25 | { | ||
| 26 | folder = Path.Combine(folder, new string('z', 100)); | ||
| 27 | } | ||
| 28 | |||
| 29 | CopyDirectory(testDataFolder, folder); | ||
| 30 | |||
| 31 | var baseFolder = fs.GetFolder(); | ||
| 32 | |||
| 33 | while (baseFolder.Length < 500) | ||
| 34 | { | ||
| 35 | baseFolder = Path.Combine(baseFolder, new string('a', 100)); | ||
| 36 | } | ||
| 37 | |||
| 38 | var intermediateFolder = Path.Combine(baseFolder, "obj"); | ||
| 39 | |||
| 40 | var result = WixRunner.Execute( | ||
| 41 | [ | ||
| 42 | "build", | ||
| 43 | Path.Combine(folder, "Package.wxs"), | ||
| 44 | Path.Combine(folder, "PackageComponents.wxs"), | ||
| 45 | "-loc", Path.Combine(folder, "Package.en-us.wxl"), | ||
| 46 | "-bindpath", Path.Combine(folder, "data"), | ||
| 47 | "-intermediateFolder", intermediateFolder, | ||
| 48 | "-o", Path.Combine(baseFolder, "bin", "test.msi") | ||
| 49 | ]); | ||
| 50 | |||
| 51 | result.AssertSuccess(); | ||
| 52 | |||
| 53 | Assert.True(File.Exists(Path.Combine(baseFolder, "bin", "test.msi"))); | ||
| 54 | Assert.True(File.Exists(Path.Combine(baseFolder, "bin", "test.wixpdb"))); | ||
| 55 | Assert.True(File.Exists(Path.Combine(baseFolder, "bin", "PFiles", "Example Corporation MsiPackage", "test.txt"))); | ||
| 56 | |||
| 57 | var intermediate = Intermediate.Load(Path.Combine(baseFolder, "bin", "test.wixpdb")); | ||
| 58 | |||
| 59 | var section = intermediate.Sections.Single(); | ||
| 60 | |||
| 61 | var fileSymbol = section.Symbols.OfType<FileSymbol>().First(); | ||
| 62 | WixAssert.StringEqual(Path.Combine(folder, @"data\test.txt"), fileSymbol[FileSymbolFields.Source].AsPath().Path); | ||
| 63 | WixAssert.StringEqual(@"test.txt", fileSymbol[FileSymbolFields.Source].PreviousValue.AsPath().Path); | ||
| 64 | } | ||
| 65 | } | ||
| 66 | |||
| 67 | private static void CopyDirectory(string sourceFolder, string targetFolder) | ||
| 68 | { | ||
| 69 | // Ensure the target directory exists | ||
| 70 | Directory.CreateDirectory(targetFolder); | ||
| 71 | |||
| 72 | // Copy all files | ||
| 73 | foreach (var file in Directory.GetFiles(sourceFolder)) | ||
| 74 | { | ||
| 75 | var targetFile = Path.Combine(targetFolder, Path.GetFileName(file)); | ||
| 76 | |||
| 77 | File.Copy(file, targetFile); | ||
| 78 | } | ||
| 79 | |||
| 80 | // Recursively copy subdirectories | ||
| 81 | foreach (var subFolder in Directory.GetDirectories(sourceFolder)) | ||
| 82 | { | ||
| 83 | var targetSubFolder = Path.Combine(targetFolder, Path.GetFileName(subFolder)); | ||
| 84 | |||
| 85 | CopyDirectory(subFolder, targetSubFolder); | ||
| 86 | } | ||
| 87 | } | ||
| 88 | } | ||
| 89 | } | ||
diff --git a/src/wix/wixnative/wixnative.manifest b/src/wix/wixnative/wixnative.manifest new file mode 100644 index 00000000..ce288e89 --- /dev/null +++ b/src/wix/wixnative/wixnative.manifest | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | <?xml version="1.0" encoding="UTF-8" standalone="yes"?> | ||
| 2 | <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> | ||
| 3 | <assemblyIdentity name="WixToolset.Tools.WixNative" version="4.0.0.0" processorArchitecture="x86" type="win32"/> | ||
| 4 | <description>WiX Toolset Native</description> | ||
| 5 | <application xmlns="urn:schemas-microsoft-com:asm.v3"><windowsSettings> | ||
| 6 | <longPathAware xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">true</longPathAware> | ||
| 7 | </windowsSettings></application> | ||
| 8 | <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3"><security><requestedPrivileges><requestedExecutionLevel level="asInvoker" uiAccess="false"/></requestedPrivileges></security></trustInfo> | ||
| 9 | </assembly> | ||
diff --git a/src/wix/wixnative/wixnative.vcxproj b/src/wix/wixnative/wixnative.vcxproj index e4474bb1..c4e05a62 100644 --- a/src/wix/wixnative/wixnative.vcxproj +++ b/src/wix/wixnative/wixnative.vcxproj | |||
| @@ -55,6 +55,9 @@ | |||
| 55 | <ClCompile Include="extractcab.cpp" /> | 55 | <ClCompile Include="extractcab.cpp" /> |
| 56 | <ClCompile Include="smartcab.cpp" /> | 56 | <ClCompile Include="smartcab.cpp" /> |
| 57 | </ItemGroup> | 57 | </ItemGroup> |
| 58 | <ItemGroup> | ||
| 59 | <Manifest Include="wixnative.manifest" /> | ||
| 60 | </ItemGroup> | ||
| 58 | 61 | ||
| 59 | <ItemGroup> | 62 | <ItemGroup> |
| 60 | <ClInclude Include="precomp.h" /> | 63 | <ClInclude Include="precomp.h" /> |
