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