aboutsummaryrefslogtreecommitdiff
path: root/src/WixToolset.Core/PatchAPI/PatchInterop.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/WixToolset.Core/PatchAPI/PatchInterop.cs')
-rw-r--r--src/WixToolset.Core/PatchAPI/PatchInterop.cs1002
1 files changed, 1002 insertions, 0 deletions
diff --git a/src/WixToolset.Core/PatchAPI/PatchInterop.cs b/src/WixToolset.Core/PatchAPI/PatchInterop.cs
new file mode 100644
index 00000000..ce749a33
--- /dev/null
+++ b/src/WixToolset.Core/PatchAPI/PatchInterop.cs
@@ -0,0 +1,1002 @@
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
3namespace WixToolset.PatchAPI
4{
5 using System;
6 using System.Collections.Generic;
7 using System.Diagnostics.CodeAnalysis;
8 using System.Globalization;
9 using System.Runtime.InteropServices;
10
11 /// <summary>
12 /// Interop class for the mspatchc.dll.
13 /// </summary>
14 internal static class PatchInterop
15 {
16 // From WinError.h in the Platform SDK
17 internal const ushort FACILITY_WIN32 = 7;
18
19 /// <summary>
20 /// Parse a number from text in either hex or decimal.
21 /// </summary>
22 /// <param name="source">Source value. Treated as hex if it starts 0x (or 0X), decimal otherwise.</param>
23 /// <returns>Numeric value that source represents.</returns>
24 static internal UInt32 ParseHexOrDecimal(string source)
25 {
26 string value = source.Trim();
27 if (String.Equals(value.Substring(0,2), "0x", StringComparison.OrdinalIgnoreCase))
28 {
29 return UInt32.Parse(value.Substring(2), NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture.NumberFormat);
30 }
31 else
32 {
33 return UInt32.Parse(value, CultureInfo.InvariantCulture.NumberFormat);
34 }
35 }
36
37 /// <summary>
38 /// Create a binary delta file.
39 /// </summary>
40 /// <param name="deltaFile">Name of the delta file to create.</param>
41 /// <param name="targetFile">Name of updated file.</param>
42 /// <param name="targetSymbolPath">Optional paths to updated file's symbols.</param>
43 /// <param name="targetRetainOffsets">Optional offsets to the delta retain sections in the updated file.</param>
44 /// <param name="basisFiles">Optional array of target files.</param>
45 /// <param name="basisSymbolPaths">Optional array of target files' symbol paths (must match basisFiles array).</param>
46 /// <param name="basisIgnoreLengths">Optional array of target files' delta ignore section lengths (must match basisFiles array)(each entry must match basisIgnoreOffsets entries).</param>
47 /// <param name="basisIgnoreOffsets">Optional array of target files' delta ignore section offsets (must match basisFiles array)(each entry must match basisIgnoreLengths entries).</param>
48 /// <param name="basisRetainLengths">Optional array of target files' delta protect section lengths (must match basisFiles array)(each entry must match basisRetainOffsets and targetRetainOffsets entries).</param>
49 /// <param name="basisRetainOffsets">Optional array of target files' delta protect section offsets (must match basisFiles array)(each entry must match basisRetainLengths and targetRetainOffsets entries).</param>
50 /// <param name="apiPatchingSymbolFlags">ApiPatchingSymbolFlags value.</param>
51 /// <param name="optimizePatchSizeForLargeFiles">OptimizePatchSizeForLargeFiles value.</param>
52 /// <param name="retainRangesIgnored">Flag to indicate retain ranges were ignored due to mismatch.</param>
53 /// <returns>true if delta file was created, false if whole file should be used instead.</returns>
54 static public bool CreateDelta(
55 string deltaFile,
56 string targetFile,
57 string targetSymbolPath,
58 string targetRetainOffsets,
59 string[] basisFiles,
60 string[] basisSymbolPaths,
61 string[] basisIgnoreLengths,
62 string[] basisIgnoreOffsets,
63 string[] basisRetainLengths,
64 string[] basisRetainOffsets,
65 PatchSymbolFlagsType apiPatchingSymbolFlags,
66 bool optimizePatchSizeForLargeFiles,
67 out bool retainRangesIgnored
68 )
69 {
70 retainRangesIgnored = false;
71 if (0 != (apiPatchingSymbolFlags & ~(PatchSymbolFlagsType.PATCH_SYMBOL_NO_IMAGEHLP | PatchSymbolFlagsType.PATCH_SYMBOL_NO_FAILURES | PatchSymbolFlagsType.PATCH_SYMBOL_UNDECORATED_TOO)))
72 {
73 throw new ArgumentOutOfRangeException("apiPatchingSymbolFlags");
74 }
75
76 if (null == deltaFile || 0 == deltaFile.Length)
77 {
78 throw new ArgumentNullException("deltaFile");
79 }
80
81 if (null == targetFile || 0 == targetFile.Length)
82 {
83 throw new ArgumentNullException("targetFile");
84 }
85
86 if (null == basisFiles || 0 == basisFiles.Length)
87 {
88 return false;
89 }
90 uint countOldFiles = (uint) basisFiles.Length;
91
92 if (null != basisSymbolPaths)
93 {
94 if (0 != basisSymbolPaths.Length)
95 {
96 if ((uint) basisSymbolPaths.Length != countOldFiles)
97 {
98 throw new ArgumentOutOfRangeException("basisSymbolPaths");
99 }
100 }
101 }
102 // a null basisSymbolPaths is allowed.
103
104 if (null != basisIgnoreLengths)
105 {
106 if (0 != basisIgnoreLengths.Length)
107 {
108 if ((uint) basisIgnoreLengths.Length != countOldFiles)
109 {
110 throw new ArgumentOutOfRangeException("basisIgnoreLengths");
111 }
112 }
113 }
114 else
115 {
116 basisIgnoreLengths = new string[countOldFiles];
117 }
118
119 if (null != basisIgnoreOffsets)
120 {
121 if (0 != basisIgnoreOffsets.Length)
122 {
123 if ((uint) basisIgnoreOffsets.Length != countOldFiles)
124 {
125 throw new ArgumentOutOfRangeException("basisIgnoreOffsets");
126 }
127 }
128 }
129 else
130 {
131 basisIgnoreOffsets = new string[countOldFiles];
132 }
133
134 if (null != basisRetainLengths)
135 {
136 if (0 != basisRetainLengths.Length)
137 {
138 if ((uint) basisRetainLengths.Length != countOldFiles)
139 {
140 throw new ArgumentOutOfRangeException("basisRetainLengths");
141 }
142 }
143 }
144 else
145 {
146 basisRetainLengths = new string[countOldFiles];
147 }
148
149 if (null != basisRetainOffsets)
150 {
151 if (0 != basisRetainOffsets.Length)
152 {
153 if ((uint) basisRetainOffsets.Length != countOldFiles)
154 {
155 throw new ArgumentOutOfRangeException("basisRetainOffsets");
156 }
157 }
158 }
159 else
160 {
161 basisRetainOffsets = new string[countOldFiles];
162 }
163
164 PatchOptionData pod = new PatchOptionData();
165 pod.symbolOptionFlags = apiPatchingSymbolFlags;
166 pod.newFileSymbolPath = targetSymbolPath;
167 pod.oldFileSymbolPathArray = basisSymbolPaths;
168 pod.extendedOptionFlags = 0;
169 PatchOldFileInfoW[] oldFileInfoArray = new PatchOldFileInfoW[countOldFiles];
170 string[] newRetainOffsetArray = ((null == targetRetainOffsets) ? new string[0] : targetRetainOffsets.Split(','));
171 for (uint i = 0; i < countOldFiles; ++i)
172 {
173 PatchOldFileInfoW ofi = new PatchOldFileInfoW();
174 ofi.oldFileName = basisFiles[i];
175 string[] ignoreLengthArray = ((null == basisIgnoreLengths[i]) ? new string[0] : basisIgnoreLengths[i].Split(','));
176 string[] ignoreOffsetArray = ((null == basisIgnoreOffsets[i]) ? new string[0] : basisIgnoreOffsets[i].Split(','));
177 string[] retainLengthArray = ((null == basisRetainLengths[i]) ? new string[0] : basisRetainLengths[i].Split(','));
178 string[] retainOffsetArray = ((null == basisRetainOffsets[i]) ? new string[0] : basisRetainOffsets[i].Split(','));
179 // Validate inputs
180 if (ignoreLengthArray.Length != ignoreOffsetArray.Length)
181 {
182 throw new ArgumentOutOfRangeException("basisIgnoreLengths");
183 }
184
185 if (retainLengthArray.Length != retainOffsetArray.Length)
186 {
187 throw new ArgumentOutOfRangeException("basisRetainLengths");
188 }
189
190 if (newRetainOffsetArray.Length != retainOffsetArray.Length)
191 {
192 // remove all retain range information
193 retainRangesIgnored = true;
194 for (uint j = 0; j < countOldFiles; ++j)
195 {
196 basisRetainLengths[j] = null;
197 basisRetainOffsets[j] = null;
198 }
199 retainLengthArray = new string[0];
200 retainOffsetArray = new string[0];
201 newRetainOffsetArray = new string[0];
202 for (uint j = 0; j < oldFileInfoArray.Length; ++j)
203 {
204 oldFileInfoArray[j].retainRange = null;
205 }
206 }
207
208 // Populate IgnoreRange structure
209 PatchIgnoreRange[] ignoreArray = null;
210 if (0 != ignoreLengthArray.Length)
211 {
212 ignoreArray = new PatchIgnoreRange[ignoreLengthArray.Length];
213 for (int j = 0; j < ignoreLengthArray.Length; ++j)
214 {
215 PatchIgnoreRange ignoreRange = new PatchIgnoreRange();
216 ignoreRange.offsetInOldFile = ParseHexOrDecimal(ignoreOffsetArray[j]);
217 ignoreRange.lengthInBytes = ParseHexOrDecimal(ignoreLengthArray[j]);
218 ignoreArray[j] = ignoreRange;
219 }
220 ofi.ignoreRange = ignoreArray;
221 }
222
223 PatchRetainRange[] retainArray = null;
224 if (0 != newRetainOffsetArray.Length)
225 {
226 retainArray = new PatchRetainRange[retainLengthArray.Length];
227 for (int j = 0; j < newRetainOffsetArray.Length; ++j)
228 {
229 PatchRetainRange retainRange = new PatchRetainRange();
230 retainRange.offsetInOldFile = ParseHexOrDecimal(retainOffsetArray[j]);
231 retainRange.lengthInBytes = ParseHexOrDecimal(retainLengthArray[j]);
232 retainRange.offsetInNewFile = ParseHexOrDecimal(newRetainOffsetArray[j]);
233 retainArray[j] = retainRange;
234 }
235 ofi.retainRange = retainArray;
236 }
237 oldFileInfoArray[i] = ofi;
238 }
239
240 if (CreatePatchFileExW(
241 countOldFiles,
242 oldFileInfoArray,
243 targetFile,
244 deltaFile,
245 PatchOptionFlags(optimizePatchSizeForLargeFiles),
246 pod,
247 null,
248 IntPtr.Zero))
249 {
250 return true;
251 }
252
253 // determine if this is an error or a need to use whole file.
254 int err = Marshal.GetLastWin32Error();
255 switch(err)
256 {
257 case unchecked((int) ERROR_PATCH_BIGGER_THAN_COMPRESSED):
258 break;
259
260 // too late to exclude this file -- should have been caught before
261 case unchecked((int) ERROR_PATCH_SAME_FILE):
262 default:
263 throw new System.ComponentModel.Win32Exception(err);
264 }
265 return false;
266 }
267
268 /// <summary>
269 /// Extract the delta header.
270 /// </summary>
271 /// <param name="delta">Name of delta file.</param>
272 /// <param name="deltaHeader">Name of file to create with the delta's header.</param>
273 static public void ExtractDeltaHeader(string delta, string deltaHeader)
274 {
275 if (!ExtractPatchHeaderToFileW(delta, deltaHeader))
276 {
277 throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());
278 }
279 }
280
281 /// <summary>
282 /// Returns the PatchOptionFlags to use.
283 /// </summary>
284 /// <param name="optimizeForLargeFiles">True if optimizing for large files.</param>
285 /// <returns>PATCH_OPTION_FLAG values</returns>
286 static private UInt32 PatchOptionFlags(bool optimizeForLargeFiles)
287 {
288 UInt32 flags = PATCH_OPTION_FAIL_IF_SAME_FILE | PATCH_OPTION_FAIL_IF_BIGGER | PATCH_OPTION_USE_LZX_BEST;
289 if (optimizeForLargeFiles)
290 {
291 flags |= PATCH_OPTION_USE_LZX_LARGE;
292 }
293 return flags;
294 }
295
296 //---------------------------------------------------------------------
297 // From PatchApi.h
298 //---------------------------------------------------------------------
299
300 //
301 // The following contants can be combined and used as the OptionFlags
302 // parameter in the patch creation apis.
303
304 internal const uint PATCH_OPTION_USE_BEST = 0x00000000; // auto choose best (slower)
305
306 internal const uint PATCH_OPTION_USE_LZX_BEST = 0x00000003; // auto choose best of LXZ A/B (but not large)
307 internal const uint PATCH_OPTION_USE_LZX_A = 0x00000001; // normal
308 internal const uint PATCH_OPTION_USE_LXZ_B = 0x00000002; // better on some x86 binaries
309 internal const uint PATCH_OPTION_USE_LZX_LARGE = 0x00000004; // better support for large files (requires 5.1 or higher applyer)
310
311 internal const uint PATCH_OPTION_NO_BINDFIX = 0x00010000; // PE bound imports
312 internal const uint PATCH_OPTION_NO_LOCKFIX = 0x00020000; // PE smashed locks
313 internal const uint PATCH_OPTION_NO_REBASE = 0x00040000; // PE rebased image
314 internal const uint PATCH_OPTION_FAIL_IF_SAME_FILE = 0x00080000; // don't create if same
315 internal const uint PATCH_OPTION_FAIL_IF_BIGGER = 0x00100000; // fail if patch is larger than simply compressing new file (slower)
316 internal const uint PATCH_OPTION_NO_CHECKSUM = 0x00200000; // PE checksum zero
317 internal const uint PATCH_OPTION_NO_RESTIMEFIX = 0x00400000; // PE resource timestamps
318 internal const uint PATCH_OPTION_NO_TIMESTAMP = 0x00800000; // don't store new file timestamp in patch
319 internal const uint PATCH_OPTION_SIGNATURE_MD5 = 0x01000000; // use MD5 instead of CRC (reserved for future support)
320 internal const uint PATCH_OPTION_INTERLEAVE_FILES = 0x40000000; // better support for large files (requires 5.2 or higher applyer)
321 internal const uint PATCH_OPTION_RESERVED1 = 0x80000000; // (used internally)
322
323 internal const uint PATCH_OPTION_VALID_FLAGS = 0xC0FF0007;
324
325 //
326 // The following flags are used with PATCH_OPTION_DATA SymbolOptionFlags:
327 //
328
329 [Flags]
330 public enum PatchSymbolFlagsType :uint
331 {
332 PATCH_SYMBOL_NO_IMAGEHLP = 0x00000001, // don't use imagehlp.dll
333 PATCH_SYMBOL_NO_FAILURES = 0x00000002, // don't fail patch due to imagehlp failures
334 PATCH_SYMBOL_UNDECORATED_TOO = 0x00000004, // after matching decorated symbols, try to match remaining by undecorated names
335 PATCH_SYMBOL_RESERVED1 = 0x80000000, // (used internally)
336 MaxValue = PATCH_SYMBOL_NO_IMAGEHLP | PATCH_SYMBOL_NO_FAILURES | PATCH_SYMBOL_UNDECORATED_TOO
337 }
338
339 //
340 // The following flags are used with PATCH_OPTION_DATA ExtendedOptionFlags:
341 //
342
343 internal const uint PATCH_TRANSFORM_PE_RESOURCE_2 = 0x00000100; // better handling of PE resources (requires 5.2 or higher applyer)
344 internal const uint PATCH_TRANSFORM_PE_IRELOC_2 = 0x00000200; // better handling of PE stripped relocs (requires 5.2 or higher applyer)
345
346 //
347 // In addition to the standard Win32 error codes, the following error codes may
348 // be returned via GetLastError() when one of the patch APIs fails.
349
350 internal const uint ERROR_PATCH_ENCODE_FAILURE = 0xC00E3101; // create
351 internal const uint ERROR_PATCH_INVALID_OPTIONS = 0xC00E3102; // create
352 internal const uint ERROR_PATCH_SAME_FILE = 0xC00E3103; // create
353 internal const uint ERROR_PATCH_RETAIN_RANGES_DIFFER = 0xC00E3104; // create
354 internal const uint ERROR_PATCH_BIGGER_THAN_COMPRESSED = 0xC00E3105; // create
355 internal const uint ERROR_PATCH_IMAGEHLP_FALURE = 0xC00E3106; // create
356
357 /// <summary>
358 /// Delegate type that the PatchAPI calls for progress notification.
359 /// </summary>
360 /// <param name="context">.</param>
361 /// <param name="currentPosition">.</param>
362 /// <param name="maxPosition">.</param>
363 /// <returns>True for success</returns>
364 public delegate bool PatchProgressCallback(
365 IntPtr context,
366 uint currentPosition,
367 uint maxPosition
368 );
369
370 /// <summary>
371 /// Delegate type that the PatchAPI calls for patch symbol load information.
372 /// </summary>
373 /// <param name="whichFile">.</param>
374 /// <param name="symbolFileName">.</param>
375 /// <param name="symType">.</param>
376 /// <param name="symbolFileCheckSum">.</param>
377 /// <param name="symbolFileTimeDate">.</param>
378 /// <param name="imageFileCheckSum">.</param>
379 /// <param name="imageFileTimeDate">.</param>
380 /// <param name="context">.</param>
381 /// <returns>???</returns>
382 public delegate bool PatchSymloadCallback(
383 uint whichFile, // 0 for new file, 1 for first old file, etc
384 [MarshalAs(UnmanagedType.LPStr)] string symbolFileName,
385 uint symType, // see SYM_TYPE in imagehlp.h
386 uint symbolFileCheckSum,
387 uint symbolFileTimeDate,
388 uint imageFileCheckSum,
389 uint imageFileTimeDate,
390 IntPtr context
391 );
392
393 /// <summary>
394 /// Wraps PATCH_IGNORE_RANGE
395 /// </summary>
396 [StructLayout(LayoutKind.Sequential)]
397 internal class PatchIgnoreRange
398 {
399 public uint offsetInOldFile;
400 public uint lengthInBytes;
401 }
402
403 /// <summary>
404 /// Wraps PATCH_RETAIN_RANGE
405 /// </summary>
406 [StructLayout(LayoutKind.Sequential)]
407 internal class PatchRetainRange
408 {
409 public uint offsetInOldFile;
410 public uint lengthInBytes;
411 public uint offsetInNewFile;
412 }
413
414 /// <summary>
415 /// Wraps PATCH_OLD_FILE_INFO (except for the OldFile~ portion)
416 /// </summary>
417 internal class PatchOldFileInfo
418 {
419 public PatchIgnoreRange[] ignoreRange;
420 public PatchRetainRange[] retainRange;
421 }
422
423 /// <summary>
424 /// Wraps PATCH_OLD_FILE_INFO_W
425 /// </summary>
426 internal class PatchOldFileInfoW : PatchOldFileInfo
427 {
428 public string oldFileName;
429 }
430
431 /// <summary>
432 /// Wraps each PATCH_INTERLEAVE_MAP Range
433 /// </summary>
434 [SuppressMessage("Microsoft.Performance", "CA1812:AvoidUninstantiatedInternalClasses"), StructLayout(LayoutKind.Sequential)]
435 internal class PatchInterleaveMapRange
436 {
437 public uint oldOffset;
438 public uint oldLength;
439 public uint newLength;
440 }
441
442 /// <summary>
443 /// Wraps PATCH_INTERLEAVE_MAP
444 /// </summary>
445 internal class PatchInterleaveMap
446 {
447 public PatchInterleaveMapRange[] ranges = null;
448 }
449
450
451 /// <summary>
452 /// Wraps PATCH_OPTION_DATA
453 /// </summary>
454 [BestFitMapping(false, ThrowOnUnmappableChar = true)]
455 internal class PatchOptionData
456 {
457 public PatchSymbolFlagsType symbolOptionFlags; // PATCH_SYMBOL_xxx flags
458 [MarshalAs(UnmanagedType.LPStr)] public string newFileSymbolPath; // always ANSI, never Unicode
459 [MarshalAs(UnmanagedType.LPStr)] public string[] oldFileSymbolPathArray; // array[ OldFileCount ]
460 public uint extendedOptionFlags;
461 public PatchSymloadCallback symLoadCallback = null;
462 public IntPtr symLoadContext = IntPtr.Zero;
463 public PatchInterleaveMap[] interleaveMapArray = null; // array[ OldFileCount ] (requires 5.2 or higher applyer)
464 public uint maxLzxWindowSize = 0; // limit memory requirements (requires 5.2 or higher applyer)
465 }
466
467 //
468 // Note that PATCH_OPTION_DATA contains LPCSTR paths, and no LPCWSTR (Unicode)
469 // path argument is available, even when used with one of the Unicode APIs
470 // such as CreatePatchFileW. This is because the unlerlying system services
471 // for symbol file handling (IMAGEHLP.DLL) only support ANSI file/path names.
472 //
473
474 //
475 // A note about PATCH_RETAIN_RANGE specifiers with multiple old files:
476 //
477 // Each old version file must have the same RetainRangeCount, and the same
478 // retain range LengthInBytes and OffsetInNewFile values in the same order.
479 // Only the OffsetInOldFile values can differ between old foles for retain
480 // ranges.
481 //
482
483 //
484 // The following prototypes are (some of the) interfaces for creating patches from files.
485 //
486
487 /// <summary>
488 /// Creates a new delta.
489 /// </summary>
490 /// <param name="oldFileCount">Size of oldFileInfoArray.</param>
491 /// <param name="oldFileInfoArray">Target file information.</param>
492 /// <param name="newFileName">Name of updated file.</param>
493 /// <param name="patchFileName">Name of delta to create.</param>
494 /// <param name="optionFlags">PATCH_OPTION_xxx.</param>
495 /// <param name="optionData">Optional PATCH_OPTION_DATA structure.</param>
496 /// <param name="progressCallback">Delegate for progress callbacks.</param>
497 /// <param name="context">Context for progress callback delegate.</param>
498 /// <returns>true if successfull, sets Marshal.GetLastWin32Error() if not.</returns>
499 [DllImport("mspatchc.dll", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true)]
500 [return: MarshalAs(UnmanagedType.Bool)]
501 internal static extern bool CreatePatchFileExW(
502 uint oldFileCount, // maximum 255
503 [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(PatchAPIMarshaler), MarshalCookie="PATCH_OLD_FILE_INFO_W")]
504 PatchOldFileInfoW[] oldFileInfoArray,
505 string newFileName, // input file (required)
506 string patchFileName, // output file (required)
507 uint optionFlags,
508 [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(PatchAPIMarshaler), MarshalCookie="PATCH_OPTION_DATA")]
509 PatchOptionData optionData,
510 [MarshalAs (UnmanagedType.FunctionPtr)]
511 PatchProgressCallback progressCallback,
512 IntPtr context
513 );
514
515 /// <summary>
516 /// Extracts delta header from delta.
517 /// </summary>
518 /// <param name="patchFileName">Name of delta file.</param>
519 /// <param name="patchHeaderFileName">Name of file to create with delta header.</param>
520 /// <returns>true if successfull, sets Marshal.GetLastWin32Error() if not.</returns>
521 [DllImport("mspatchc.dll", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true)]
522 [return: MarshalAs(UnmanagedType.Bool)]
523 internal static extern bool ExtractPatchHeaderToFileW(
524 string patchFileName, // input file
525 string patchHeaderFileName // output file
526 );
527
528 // TODO: Add rest of APIs to enable custom binders to perform more exhaustive checks
529
530 /// <summary>
531 /// Marshals arguments for the CreatePatch~ APIs
532 /// </summary>
533 [SuppressMessage("Microsoft.Performance", "CA1812:AvoidUninstantiatedInternalClasses")]
534 internal class PatchAPIMarshaler : ICustomMarshaler
535 {
536 internal static ICustomMarshaler GetInstance(string cookie)
537 {
538 return new PatchAPIMarshaler(cookie);
539 }
540
541 private enum MarshalType
542 {
543 PATCH_OPTION_DATA,
544 PATCH_OLD_FILE_INFO_W
545 };
546 private PatchAPIMarshaler.MarshalType marshalType;
547
548 private PatchAPIMarshaler(string cookie)
549 {
550 this.marshalType = (PatchAPIMarshaler.MarshalType) Enum.Parse(typeof(PatchAPIMarshaler.MarshalType), cookie);
551 }
552
553 //
554 // Summary:
555 // Returns the size of the native data to be marshaled.
556 //
557 // Returns:
558 // The size in bytes of the native data.
559 public int GetNativeDataSize()
560 {
561 return Marshal.SizeOf(typeof(IntPtr));
562 }
563
564 //
565 // Summary:
566 // Performs necessary cleanup of the managed data when it is no longer needed.
567 //
568 // Parameters:
569 // ManagedObj:
570 // The managed object to be destroyed.
571 public void CleanUpManagedData(object ManagedObj)
572 {
573 }
574
575 //
576 // Summary:
577 // Performs necessary cleanup of the unmanaged data when it is no longer needed.
578 //
579 // Parameters:
580 // pNativeData:
581 // A pointer to the unmanaged data to be destroyed.
582 public void CleanUpNativeData(IntPtr pNativeData)
583 {
584 if (IntPtr.Zero == pNativeData)
585 {
586 return;
587 }
588
589 switch (this.marshalType)
590 {
591 case PatchAPIMarshaler.MarshalType.PATCH_OPTION_DATA:
592 this.CleanUpPOD(pNativeData);
593 break;
594 default:
595 this.CleanUpPOFI_A(pNativeData);
596 break;
597 }
598 }
599
600 //
601 // Summary:
602 // Converts the managed data to unmanaged data.
603 //
604 // Parameters:
605 // ManagedObj:
606 // The managed object to be converted.
607 //
608 // Returns:
609 // Returns the COM view of the managed object.
610 public IntPtr MarshalManagedToNative(object ManagedObj)
611 {
612 if (null == ManagedObj)
613 {
614 return IntPtr.Zero;
615 }
616
617 switch(this.marshalType)
618 {
619 case PatchAPIMarshaler.MarshalType.PATCH_OPTION_DATA:
620 return this.MarshalPOD(ManagedObj as PatchOptionData);
621 case PatchAPIMarshaler.MarshalType.PATCH_OLD_FILE_INFO_W:
622 return this.MarshalPOFIW_A(ManagedObj as PatchOldFileInfoW[]);
623 default:
624 throw new InvalidOperationException();
625 }
626 }
627
628
629 //
630 // Summary:
631 // Converts the unmanaged data to managed data.
632 //
633 // Parameters:
634 // pNativeData:
635 // A pointer to the unmanaged data to be wrapped.
636 //
637 // Returns:
638 // Returns the managed view of the COM data.
639 public object MarshalNativeToManaged(IntPtr pNativeData)
640 {
641 return null;
642 }
643
644 // Implementation *************************************************
645
646 // PATCH_OPTION_DATA offsets
647 private static readonly int symbolOptionFlagsOffset = Marshal.SizeOf(typeof(Int32));
648 private static readonly int newFileSymbolPathOffset = 2*Marshal.SizeOf(typeof(Int32));
649 private static readonly int oldFileSymbolPathArrayOffset = 2*Marshal.SizeOf(typeof(Int32)) + Marshal.SizeOf(typeof(IntPtr));
650 private static readonly int extendedOptionFlagsOffset = 2*Marshal.SizeOf(typeof(Int32)) + 2*Marshal.SizeOf(typeof(IntPtr));
651 private static readonly int symLoadCallbackOffset = 3*Marshal.SizeOf(typeof(Int32)) + 2*Marshal.SizeOf(typeof(IntPtr));
652 private static readonly int symLoadContextOffset = 3*Marshal.SizeOf(typeof(Int32)) + 3*Marshal.SizeOf(typeof(IntPtr));
653 private static readonly int interleaveMapArrayOffset = 3*Marshal.SizeOf(typeof(Int32)) + 4*Marshal.SizeOf(typeof(IntPtr));
654 private static readonly int maxLzxWindowSizeOffset = 3*Marshal.SizeOf(typeof(Int32)) + 5*Marshal.SizeOf(typeof(IntPtr));
655 private static readonly int patchOptionDataSize = 4*Marshal.SizeOf(typeof(Int32)) + 5*Marshal.SizeOf(typeof(IntPtr));
656
657 // PATCH_OLD_FILE_INFO offsets
658 private static readonly int oldFileOffset = Marshal.SizeOf(typeof(Int32));
659 private static readonly int ignoreRangeCountOffset = Marshal.SizeOf(typeof(Int32)) + Marshal.SizeOf(typeof(IntPtr));
660 private static readonly int ignoreRangeArrayOffset = 2*Marshal.SizeOf(typeof(Int32)) + Marshal.SizeOf(typeof(IntPtr));
661 private static readonly int retainRangeCountOffset = 2*Marshal.SizeOf(typeof(Int32)) + 2*Marshal.SizeOf(typeof(IntPtr));
662 private static readonly int retainRangeArrayOffset = 3*Marshal.SizeOf(typeof(Int32)) + 2*Marshal.SizeOf(typeof(IntPtr));
663 private static readonly int patchOldFileInfoSize = 3*Marshal.SizeOf(typeof(Int32)) + 3*Marshal.SizeOf(typeof(IntPtr));
664
665 // Methods and data used to preserve data needed for cleanup
666
667 // This dictionary holds the quantity of items internal to each native structure that will need to be freed (the OldFileCount)
668 private static readonly Dictionary<IntPtr, int> OldFileCounts = new Dictionary<IntPtr, int>();
669 private static readonly object OldFileCountsLock = new object();
670
671 private IntPtr CreateMainStruct(int oldFileCount)
672 {
673 int nativeSize;
674 switch(this.marshalType)
675 {
676 case PatchAPIMarshaler.MarshalType.PATCH_OPTION_DATA:
677 nativeSize = patchOptionDataSize;
678 break;
679 case PatchAPIMarshaler.MarshalType.PATCH_OLD_FILE_INFO_W:
680 nativeSize = oldFileCount*patchOldFileInfoSize;
681 break;
682 default:
683 throw new InvalidOperationException();
684 }
685
686 IntPtr native = Marshal.AllocCoTaskMem(nativeSize);
687
688 lock (PatchAPIMarshaler.OldFileCountsLock)
689 {
690 PatchAPIMarshaler.OldFileCounts.Add(native, oldFileCount);
691 }
692
693 return native;
694 }
695
696 private static void ReleaseMainStruct(IntPtr native)
697 {
698 lock (PatchAPIMarshaler.OldFileCountsLock)
699 {
700 PatchAPIMarshaler.OldFileCounts.Remove(native);
701 }
702 Marshal.FreeCoTaskMem(native);
703 }
704
705 private static int GetOldFileCount(IntPtr native)
706 {
707 lock (PatchAPIMarshaler.OldFileCountsLock)
708 {
709 return PatchAPIMarshaler.OldFileCounts[native];
710 }
711 }
712
713 // Helper methods
714
715 private static IntPtr OptionalAnsiString(string managed)
716 {
717 return (null == managed) ? IntPtr.Zero : Marshal.StringToCoTaskMemAnsi(managed);
718 }
719
720 private static IntPtr OptionalUnicodeString(string managed)
721 {
722 return (null == managed) ? IntPtr.Zero : Marshal.StringToCoTaskMemUni(managed);
723 }
724
725 // string array must be of the same length as the number of old files
726 private static IntPtr CreateArrayOfStringA(string[] managed)
727 {
728 if (null == managed)
729 {
730 return IntPtr.Zero;
731 }
732
733 int size = managed.Length * Marshal.SizeOf(typeof(IntPtr));
734 IntPtr native = Marshal.AllocCoTaskMem(size);
735
736 for (int i = 0; i < managed.Length; ++i)
737 {
738 Marshal.WriteIntPtr(native, i*Marshal.SizeOf(typeof(IntPtr)), OptionalAnsiString(managed[i]));
739 }
740
741 return native;
742 }
743
744 // string array must be of the same length as the number of old files
745 private static IntPtr CreateArrayOfStringW(string[] managed)
746 {
747 if (null == managed)
748 {
749 return IntPtr.Zero;
750 }
751
752 int size = managed.Length * Marshal.SizeOf(typeof(IntPtr));
753 IntPtr native = Marshal.AllocCoTaskMem(size);
754
755 for (int i = 0; i < managed.Length; ++i)
756 {
757 Marshal.WriteIntPtr(native, i*Marshal.SizeOf(typeof(IntPtr)), OptionalUnicodeString(managed[i]));
758 }
759
760 return native;
761 }
762
763 private static IntPtr CreateInterleaveMapRange(PatchInterleaveMap managed)
764 {
765 if (null == managed)
766 {
767 return IntPtr.Zero;
768 }
769
770 if (null == managed.ranges)
771 {
772 return IntPtr.Zero;
773 }
774
775 if (0 == managed.ranges.Length)
776 {
777 return IntPtr.Zero;
778 }
779
780 IntPtr native = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(UInt32))
781 + managed.ranges.Length*(Marshal.SizeOf(typeof(PatchInterleaveMap))));
782 WriteUInt32(native, (uint) managed.ranges.Length);
783
784 for (int i = 0; i < managed.ranges.Length; ++i)
785 {
786 Marshal.StructureToPtr(managed.ranges[i], (IntPtr)((Int64)native + i*Marshal.SizeOf(typeof(PatchInterleaveMap))), false);
787 }
788 return native;
789 }
790
791 private static IntPtr CreateInterleaveMap(PatchInterleaveMap[] managed)
792 {
793 if (null == managed)
794 {
795 return IntPtr.Zero;
796 }
797
798 IntPtr native = Marshal.AllocCoTaskMem(managed.Length * Marshal.SizeOf(typeof(IntPtr)));
799
800 for (int i = 0; i < managed.Length; ++i)
801 {
802 Marshal.WriteIntPtr(native, i*Marshal.SizeOf(typeof(IntPtr)), CreateInterleaveMapRange(managed[i]));
803 }
804
805 return native;
806 }
807
808 private static void WriteUInt32(IntPtr native, uint data)
809 {
810 Marshal.WriteInt32(native, unchecked((int) data));
811 }
812
813 private static void WriteUInt32(IntPtr native, int offset, uint data)
814 {
815 Marshal.WriteInt32(native, offset, unchecked((int) data));
816 }
817
818 // Marshal operations
819
820 private IntPtr MarshalPOD(PatchOptionData managed)
821 {
822 if (null == managed)
823 {
824 throw new ArgumentNullException("managed");
825 }
826
827 IntPtr native = this.CreateMainStruct(managed.oldFileSymbolPathArray.Length);
828 Marshal.WriteInt32(native, patchOptionDataSize); // SizeOfThisStruct
829 WriteUInt32(native, symbolOptionFlagsOffset, (uint) managed.symbolOptionFlags);
830 Marshal.WriteIntPtr(native, newFileSymbolPathOffset, PatchAPIMarshaler.OptionalAnsiString(managed.newFileSymbolPath));
831 Marshal.WriteIntPtr(native, oldFileSymbolPathArrayOffset, PatchAPIMarshaler.CreateArrayOfStringA(managed.oldFileSymbolPathArray));
832 WriteUInt32(native, extendedOptionFlagsOffset, managed.extendedOptionFlags);
833
834 // GetFunctionPointerForDelegate() throws an ArgumentNullException if the delegate is null.
835 if (null == managed.symLoadCallback)
836 {
837 Marshal.WriteIntPtr(native, symLoadCallbackOffset, IntPtr.Zero);
838 }
839 else
840 {
841 Marshal.WriteIntPtr(native, symLoadCallbackOffset, Marshal.GetFunctionPointerForDelegate(managed.symLoadCallback));
842 }
843
844 Marshal.WriteIntPtr(native, symLoadContextOffset, managed.symLoadContext);
845 Marshal.WriteIntPtr(native, interleaveMapArrayOffset, PatchAPIMarshaler.CreateInterleaveMap(managed.interleaveMapArray));
846 WriteUInt32(native, maxLzxWindowSizeOffset, managed.maxLzxWindowSize);
847 return native;
848 }
849
850 private IntPtr MarshalPOFIW_A(PatchOldFileInfoW[] managed)
851 {
852 if (null == managed)
853 {
854 throw new ArgumentNullException("managed");
855 }
856
857 if (0 == managed.Length)
858 {
859 return IntPtr.Zero;
860 }
861
862 IntPtr native = this.CreateMainStruct(managed.Length);
863
864 for (int i = 0; i < managed.Length; ++i)
865 {
866 PatchAPIMarshaler.MarshalPOFIW(managed[i], (IntPtr)((Int64)native + i * patchOldFileInfoSize));
867 }
868
869 return native;
870 }
871
872 private static void MarshalPOFIW(PatchOldFileInfoW managed, IntPtr native)
873 {
874 PatchAPIMarshaler.MarshalPOFI(managed, native);
875 Marshal.WriteIntPtr(native, oldFileOffset, PatchAPIMarshaler.OptionalUnicodeString(managed.oldFileName)); // OldFileName
876 }
877
878 private static void MarshalPOFI(PatchOldFileInfo managed, IntPtr native)
879 {
880 Marshal.WriteInt32(native, patchOldFileInfoSize); // SizeOfThisStruct
881 WriteUInt32(native, ignoreRangeCountOffset,
882 (null == managed.ignoreRange) ? 0 : (uint) managed.ignoreRange.Length); // IgnoreRangeCount // maximum 255
883 Marshal.WriteIntPtr(native, ignoreRangeArrayOffset, MarshalPIRArray(managed.ignoreRange)); // IgnoreRangeArray
884 WriteUInt32(native, retainRangeCountOffset,
885 (null == managed.retainRange) ? 0 : (uint) managed.retainRange.Length); // RetainRangeCount // maximum 255
886 Marshal.WriteIntPtr(native, retainRangeArrayOffset, MarshalPRRArray(managed.retainRange)); // RetainRangeArray
887 }
888
889 private static IntPtr MarshalPIRArray(PatchIgnoreRange[] array)
890 {
891 if (null == array)
892 {
893 return IntPtr.Zero;
894 }
895
896 if (0 == array.Length)
897 {
898 return IntPtr.Zero;
899 }
900
901 IntPtr native = Marshal.AllocCoTaskMem(array.Length*Marshal.SizeOf(typeof(PatchIgnoreRange)));
902
903 for (int i = 0; i < array.Length; ++i)
904 {
905 Marshal.StructureToPtr(array[i], (IntPtr)((Int64)native + (i*Marshal.SizeOf(typeof(PatchIgnoreRange)))), false);
906 }
907
908 return native;
909 }
910
911 private static IntPtr MarshalPRRArray(PatchRetainRange[] array)
912 {
913 if (null == array)
914 {
915 return IntPtr.Zero;
916 }
917
918 if (0 == array.Length)
919 {
920 return IntPtr.Zero;
921 }
922
923 IntPtr native = Marshal.AllocCoTaskMem(array.Length*Marshal.SizeOf(typeof(PatchRetainRange)));
924
925 for (int i = 0; i < array.Length; ++i)
926 {
927 Marshal.StructureToPtr(array[i], (IntPtr)((Int64)native + (i*Marshal.SizeOf(typeof(PatchRetainRange)))), false);
928 }
929
930 return native;
931 }
932
933 // CleanUp operations
934
935 private void CleanUpPOD(IntPtr native)
936 {
937 Marshal.FreeCoTaskMem(Marshal.ReadIntPtr(native, newFileSymbolPathOffset));
938
939 if (IntPtr.Zero != Marshal.ReadIntPtr(native, oldFileSymbolPathArrayOffset))
940 {
941 for (int i = 0; i < GetOldFileCount(native); ++i)
942 {
943 Marshal.FreeCoTaskMem(
944 Marshal.ReadIntPtr(
945 Marshal.ReadIntPtr(native, oldFileSymbolPathArrayOffset),
946 i*Marshal.SizeOf(typeof(IntPtr))));
947 }
948
949 Marshal.FreeCoTaskMem(Marshal.ReadIntPtr(native, oldFileSymbolPathArrayOffset));
950 }
951
952 if (IntPtr.Zero != Marshal.ReadIntPtr(native, interleaveMapArrayOffset))
953 {
954 for (int i = 0; i < GetOldFileCount(native); ++i)
955 {
956 Marshal.FreeCoTaskMem(
957 Marshal.ReadIntPtr(
958 Marshal.ReadIntPtr(native, interleaveMapArrayOffset),
959 i*Marshal.SizeOf(typeof(IntPtr))));
960 }
961
962 Marshal.FreeCoTaskMem(Marshal.ReadIntPtr(native, interleaveMapArrayOffset));
963 }
964
965 PatchAPIMarshaler.ReleaseMainStruct(native);
966 }
967
968 private void CleanUpPOFI_A(IntPtr native)
969 {
970 for (int i = 0; i < GetOldFileCount(native); ++i)
971 {
972 PatchAPIMarshaler.CleanUpPOFI((IntPtr)((Int64)native + i*patchOldFileInfoSize));
973 }
974
975 PatchAPIMarshaler.ReleaseMainStruct(native);
976 }
977
978 private static void CleanUpPOFI(IntPtr native)
979 {
980 if (IntPtr.Zero != Marshal.ReadIntPtr(native, oldFileOffset))
981 {
982 Marshal.FreeCoTaskMem(Marshal.ReadIntPtr(native, oldFileOffset));
983 }
984
985 PatchAPIMarshaler.CleanUpPOFIH(native);
986 }
987
988 private static void CleanUpPOFIH(IntPtr native)
989 {
990 if (IntPtr.Zero != Marshal.ReadIntPtr(native, ignoreRangeArrayOffset))
991 {
992 Marshal.FreeCoTaskMem(Marshal.ReadIntPtr(native, ignoreRangeArrayOffset));
993 }
994
995 if (IntPtr.Zero != Marshal.ReadIntPtr(native, retainRangeArrayOffset))
996 {
997 Marshal.FreeCoTaskMem(Marshal.ReadIntPtr(native, retainRangeArrayOffset));
998 }
999 }
1000 }
1001 }
1002}