PatchInterop.cs :  » Installers-Generators » WiX » Microsoft » Tools » WindowsInstallerXml » PatchAPI » C# / CSharp Open Source

Home
C# / CSharp Open Source
1.2.6.4 mono .net core
2.2.6.4 mono core
3.Aspect Oriented Frameworks
4.Bloggers
5.Build Systems
6.Business Application
7.Charting Reporting Tools
8.Chat Servers
9.Code Coverage Tools
10.Content Management Systems CMS
11.CRM ERP
12.Database
13.Development
14.Email
15.Forum
16.Game
17.GIS
18.GUI
19.IDEs
20.Installers Generators
21.Inversion of Control Dependency Injection
22.Issue Tracking
23.Logging Tools
24.Message
25.Mobile
26.Network Clients
27.Network Servers
28.Office
29.PDF
30.Persistence Frameworks
31.Portals
32.Profilers
33.Project Management
34.RSS RDF
35.Rule Engines
36.Script
37.Search Engines
38.Sound Audio
39.Source Control
40.SQL Clients
41.Template Engines
42.Testing
43.UML
44.Web Frameworks
45.Web Service
46.Web Testing
47.Wiki Engines
48.Windows Presentation Foundation
49.Workflows
50.XML Parsers
C# / C Sharp
C# / C Sharp by API
C# / CSharp Tutorial
C# / CSharp Open Source » Installers Generators » WiX 
WiX » Microsoft » Tools » WindowsInstallerXml » PatchAPI » PatchInterop.cs
//-------------------------------------------------------------------------------------------------
// <copyright file="PatchInterop.cs" company="Microsoft">
//    Copyright (c) Microsoft Corporation.  All rights reserved.
//    
//    The use and distribution terms for this software are covered by the
//    Common Public License 1.0 (http://opensource.org/licenses/cpl.php)
//    which can be found in the file CPL.TXT at the root of this distribution.
//    By using this software in any fashion, you are agreeing to be bound by
//    the terms of this license.
//    
//    You must not remove this notice, or any other, from this software.
// </copyright>
// 
// <summary>
// Interop class for the mspatchc.dll. So far, only implements what is needed for delta MSP creation.
// </summary>
//-------------------------------------------------------------------------------------------------

namespace Microsoft.Tools.WindowsInstallerXml.PatchAPI{
    using System;
    using System.Diagnostics.CodeAnalysis;
    using System.Globalization;
    using System.Runtime.InteropServices;

    /// <summary>
    /// Interop class for the mspatchc.dll.
    /// </summary>
    internal static class PatchInterop
    {
        // From WinError.h in the Platform SDK
        internal const ushort FACILITY_WIN32 = 7;

        /// <summary>
        /// Parse a number from text in either hex or decimal.
        /// </summary>
        /// <param name="source">Source value. Treated as hex if it starts 0x (or 0X), decimal otherwise.</param>
        /// <returns>Numeric value that source represents.</returns>
        static internal UInt32 ParseHexOrDecimal(string source)
        {
            string value = source.Trim();
            if (String.Equals(value.Substring(0,2), "0x", StringComparison.OrdinalIgnoreCase))
            {
                return UInt32.Parse(value.Substring(2), NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture.NumberFormat);
            }
            else
            {
                return UInt32.Parse(value, CultureInfo.InvariantCulture.NumberFormat);
            }
        }

        /// <summary>
        /// Create a binary delta file.
        /// </summary>
        /// <param name="deltaFile">Name of the delta file to create.</param>
        /// <param name="targetFile">Name of updated file.</param>
        /// <param name="targetSymbolPath">Optional paths to updated file's symbols.</param>
        /// <param name="targetRetainOffsets">Optional offsets to the delta retain sections in the updated file.</param>
        /// <param name="basisFiles">Optional array of target files.</param>
        /// <param name="basisSymbolPaths">Optional array of target files' symbol paths (must match basisFiles array).</param>
        /// <param name="basisIgnoreLengths">Optional array of target files' delta ignore section lengths (must match basisFiles array)(each entry must match basisIgnoreOffsets entries).</param>
        /// <param name="basisIgnoreOffsets">Optional array of target files' delta ignore section offsets (must match basisFiles array)(each entry must match basisIgnoreLengths entries).</param>
        /// <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>
        /// <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>
        /// <param name="apiPatchingSymbolFlags">ApiPatchingSymbolFlags value.</param>
        /// <param name="optimizePatchSizeForLargeFiles">OptimizePatchSizeForLargeFiles value.</param>
        /// <param name="retainRangesIgnored">Flag to indicate retain ranges were ignored due to mismatch.</param>
        /// <returns>true if delta file was created, false if whole file should be used instead.</returns>
        static public bool CreateDelta(
                string deltaFile,
                string targetFile,
                string targetSymbolPath,
                string targetRetainOffsets,
                string[] basisFiles,
                string[] basisSymbolPaths,
                string[] basisIgnoreLengths,
                string[] basisIgnoreOffsets,
                string[] basisRetainLengths,
                string[] basisRetainOffsets,
                PatchSymbolFlagsType apiPatchingSymbolFlags,
                bool optimizePatchSizeForLargeFiles,
                out bool retainRangesIgnored
                )
        {
            retainRangesIgnored = false;
            if (0 != (apiPatchingSymbolFlags & ~(PatchSymbolFlagsType.PATCH_SYMBOL_NO_IMAGEHLP | PatchSymbolFlagsType.PATCH_SYMBOL_NO_FAILURES | PatchSymbolFlagsType.PATCH_SYMBOL_UNDECORATED_TOO)))
            {
                throw new ArgumentOutOfRangeException("apiPatchingSymbolFlags");
            }

            if (null == deltaFile || 0 == deltaFile.Length)
            {
                throw new ArgumentNullException("deltaFile");
            }

            if (null == targetFile || 0 == targetFile.Length)
            {
                throw new ArgumentNullException("targetFile");
            }

            if (null == basisFiles || 0 == basisFiles.Length)
            {
                return false;
            }
            uint countOldFiles = (uint) basisFiles.Length;

            if (null != basisSymbolPaths)
            {
                if (0 != basisSymbolPaths.Length)
                {
                    if ((uint) basisSymbolPaths.Length != countOldFiles)
                    {
                        throw new ArgumentOutOfRangeException("basisSymbolPaths");
                    }
                }
            }
            // a null basisSymbolPaths is allowed.

            if (null != basisIgnoreLengths)
            {
                if (0 != basisIgnoreLengths.Length)
                {
                    if ((uint) basisIgnoreLengths.Length != countOldFiles)
                    {
                        throw new ArgumentOutOfRangeException("basisIgnoreLengths");
                    }
                }
            }
            else
            {
                basisIgnoreLengths = new string[countOldFiles];
            }

            if (null != basisIgnoreOffsets)
            {
                if (0 != basisIgnoreOffsets.Length)
                {
                    if ((uint) basisIgnoreOffsets.Length != countOldFiles)
                    {
                        throw new ArgumentOutOfRangeException("basisIgnoreOffsets");
                    }
                }
            }
            else
            {
                basisIgnoreOffsets = new string[countOldFiles];
            }

            if (null != basisRetainLengths)
            {
                if (0 != basisRetainLengths.Length)
                {
                    if ((uint) basisRetainLengths.Length != countOldFiles)
                    {
                        throw new ArgumentOutOfRangeException("basisRetainLengths");
                    }
                }
            }
            else
            {
                basisRetainLengths = new string[countOldFiles];
            }

            if (null != basisRetainOffsets)
            {
                if (0 != basisRetainOffsets.Length)
                {
                    if ((uint) basisRetainOffsets.Length != countOldFiles)
                    {
                        throw new ArgumentOutOfRangeException("basisRetainOffsets");
                    }
                }
            }
            else
            {
                basisRetainOffsets = new string[countOldFiles];
            }

            PatchOptionData pod = new PatchOptionData();
            pod.symbolOptionFlags = apiPatchingSymbolFlags;
            pod.newFileSymbolPath = targetSymbolPath;
            pod.oldFileSymbolPathArray = basisSymbolPaths;
            pod.extendedOptionFlags = 0;
            PatchOldFileInfoW[] oldFileInfoArray = new PatchOldFileInfoW[countOldFiles];
            string[] newRetainOffsetArray = ((null == targetRetainOffsets) ? new string[0] : targetRetainOffsets.Split(','));
            for (uint i = 0; i < countOldFiles; ++i)
            {
                PatchOldFileInfoW ofi = new PatchOldFileInfoW();
                ofi.oldFileName = basisFiles[i];
                string[] ignoreLengthArray = ((null == basisIgnoreLengths[i]) ? new string[0] : basisIgnoreLengths[i].Split(','));
                string[] ignoreOffsetArray = ((null == basisIgnoreOffsets[i]) ? new string[0] : basisIgnoreOffsets[i].Split(','));
                string[] retainLengthArray = ((null == basisRetainLengths[i]) ? new string[0] : basisRetainLengths[i].Split(','));
                string[] retainOffsetArray = ((null == basisRetainOffsets[i]) ? new string[0] : basisRetainOffsets[i].Split(','));
                // Validate inputs
                if (ignoreLengthArray.Length != ignoreOffsetArray.Length)
                {
                    throw new ArgumentOutOfRangeException("basisIgnoreLengths");
                }

                if (retainLengthArray.Length != retainOffsetArray.Length)
                {
                    throw new ArgumentOutOfRangeException("basisRetainLengths");
                }

                if (newRetainOffsetArray.Length != retainOffsetArray.Length)
                {
                    // remove all retain range information
                    retainRangesIgnored = true;
                    for (uint j = 0; j < countOldFiles; ++j)
                    {
                        basisRetainLengths[j] = null;
                        basisRetainOffsets[j] = null;
                    }
                    retainLengthArray = new string[0];
                    retainOffsetArray = new string[0];
                    newRetainOffsetArray = new string[0];
                    for (uint j = 0; j < oldFileInfoArray.Length; ++j)
                    {
                        oldFileInfoArray[j].retainRange = null;
                    }
                }

                // Populate IgnoreRange structure
                PatchIgnoreRange[] ignoreArray = null;
                if (0 != ignoreLengthArray.Length)
                {
                    ignoreArray = new PatchIgnoreRange[ignoreLengthArray.Length];
                    for (int j = 0; j < ignoreLengthArray.Length; ++j)
                    {
                        PatchIgnoreRange ignoreRange = new PatchIgnoreRange();
                        ignoreRange.offsetInOldFile = ParseHexOrDecimal(ignoreOffsetArray[j]);
                        ignoreRange.lengthInBytes = ParseHexOrDecimal(ignoreLengthArray[j]);
                        ignoreArray[j] = ignoreRange;
                    }
                    ofi.ignoreRange = ignoreArray;
                }

                PatchRetainRange[] retainArray = null;
                if (0 != newRetainOffsetArray.Length)
                {
                    retainArray = new PatchRetainRange[retainLengthArray.Length];
                    for (int j = 0; j < newRetainOffsetArray.Length; ++j)
                    {
                        PatchRetainRange retainRange = new PatchRetainRange();
                        retainRange.offsetInOldFile = ParseHexOrDecimal(retainOffsetArray[j]);
                        retainRange.lengthInBytes = ParseHexOrDecimal(retainLengthArray[j]);
                        retainRange.offsetInNewFile = ParseHexOrDecimal(newRetainOffsetArray[j]);
                        retainArray[j] = retainRange;
                    }
                    ofi.retainRange = retainArray;
                }
                oldFileInfoArray[i] = ofi;
            }

            if (CreatePatchFileExW(
                    countOldFiles,
                    oldFileInfoArray,
                    targetFile,
                    deltaFile,
                    PatchOptionFlags(optimizePatchSizeForLargeFiles),
                    pod,
                    null,
                    IntPtr.Zero))
            {
                return true;
            }

            // determine if this is an error or a need to use whole file.
            int err = Marshal.GetLastWin32Error();
            switch(err)
            {
            case unchecked((int) ERROR_PATCH_BIGGER_THAN_COMPRESSED):
                break;

            // too late to exclude this file -- should have been caught before
            case unchecked((int) ERROR_PATCH_SAME_FILE):
            default:
                throw new System.ComponentModel.Win32Exception(err);
            }
            return false;
        }

        /// <summary>
        /// Extract the delta header.
        /// </summary>
        /// <param name="delta">Name of delta file.</param>
        /// <param name="deltaHeader">Name of file to create with the delta's header.</param>
        static public void ExtractDeltaHeader(string delta, string deltaHeader)
        {
            if (!ExtractPatchHeaderToFileW(delta, deltaHeader))
            {
                throw new System.ComponentModel.Win32Exception();
            }
        }

        /// <summary>
        /// Returns the PatchOptionFlags to use.
        /// </summary>
        /// <param name="optimizeForLargeFiles">True if optimizing for large files.</param>
        /// <returns>PATCH_OPTION_FLAG values</returns>
        static private UInt32 PatchOptionFlags(bool optimizeForLargeFiles)
        {
            UInt32 flags = PATCH_OPTION_FAIL_IF_SAME_FILE | PATCH_OPTION_FAIL_IF_BIGGER;
            if (optimizeForLargeFiles)
            {
                flags |= PATCH_OPTION_USE_LZX_BEST;
            }
            else
            {
                flags |= PATCH_OPTION_USE_LZX_BEST | PATCH_OPTION_USE_LZX_LARGE;
            }
            return flags;
        }

        //---------------------------------------------------------------------
        // From PatchApi.h
        //---------------------------------------------------------------------

        //
        // The following contants can be combined and used as the OptionFlags
        // parameter in the patch creation apis.

        internal const uint PATCH_OPTION_USE_BEST          = 0x00000000; // auto choose best (slower)

        internal const uint PATCH_OPTION_USE_LZX_BEST      = 0x00000003; // auto choose best of LXZ A/B (but not large)
        internal const uint PATCH_OPTION_USE_LZX_A         = 0x00000001; // normal
        internal const uint PATCH_OPTION_USE_LXZ_B         = 0x00000002; // better on some x86 binaries
        internal const uint PATCH_OPTION_USE_LZX_LARGE     = 0x00000004; // better support for large files (requires 5.1 or higher applyer)

        internal const uint PATCH_OPTION_NO_BINDFIX        = 0x00010000; // PE bound imports
        internal const uint PATCH_OPTION_NO_LOCKFIX        = 0x00020000; // PE smashed locks
        internal const uint PATCH_OPTION_NO_REBASE         = 0x00040000; // PE rebased image
        internal const uint PATCH_OPTION_FAIL_IF_SAME_FILE = 0x00080000; // don't create if same
        internal const uint PATCH_OPTION_FAIL_IF_BIGGER    = 0x00100000; // fail if patch is larger than simply compressing new file (slower)
        internal const uint PATCH_OPTION_NO_CHECKSUM       = 0x00200000; // PE checksum zero
        internal const uint PATCH_OPTION_NO_RESTIMEFIX     = 0x00400000; // PE resource timestamps
        internal const uint PATCH_OPTION_NO_TIMESTAMP      = 0x00800000; // don't store new file timestamp in patch
        internal const uint PATCH_OPTION_SIGNATURE_MD5     = 0x01000000; // use MD5 instead of CRC (reserved for future support)
        internal const uint PATCH_OPTION_INTERLEAVE_FILES  = 0x40000000; // better support for large files (requires 5.2 or higher applyer)
        internal const uint PATCH_OPTION_RESERVED1         = 0x80000000; // (used internally)

        internal const uint PATCH_OPTION_VALID_FLAGS       = 0xC0FF0007;

        //
        // The following flags are used with PATCH_OPTION_DATA SymbolOptionFlags:
        //

        [Flags]
        public enum PatchSymbolFlagsType :uint
        {
            PATCH_SYMBOL_NO_IMAGEHLP       = 0x00000001, // don't use imagehlp.dll
            PATCH_SYMBOL_NO_FAILURES       = 0x00000002, // don't fail patch due to imagehlp failures
            PATCH_SYMBOL_UNDECORATED_TOO   = 0x00000004, // after matching decorated symbols, try to match remaining by undecorated names
            PATCH_SYMBOL_RESERVED1         = 0x80000000, // (used internally)
            MaxValue = PATCH_SYMBOL_NO_IMAGEHLP | PATCH_SYMBOL_NO_FAILURES | PATCH_SYMBOL_UNDECORATED_TOO
        }

        //
        // The following flags are used with PATCH_OPTION_DATA ExtendedOptionFlags:
        //

        internal const uint PATCH_TRANSFORM_PE_RESOURCE_2  = 0x00000100; // better handling of PE resources (requires 5.2 or higher applyer)
        internal const uint PATCH_TRANSFORM_PE_IRELOC_2    = 0x00000200; // better handling of PE stripped relocs (requires 5.2 or higher applyer)

        //
        // In addition to the standard Win32 error codes, the following error codes may
        // be returned via GetLastError() when one of the patch APIs fails.

        internal const uint ERROR_PATCH_ENCODE_FAILURE         = 0xC00E3101; // create
        internal const uint ERROR_PATCH_INVALID_OPTIONS        = 0xC00E3102; // create
        internal const uint ERROR_PATCH_SAME_FILE              = 0xC00E3103; // create
        internal const uint ERROR_PATCH_RETAIN_RANGES_DIFFER   = 0xC00E3104; // create
        internal const uint ERROR_PATCH_BIGGER_THAN_COMPRESSED = 0xC00E3105; // create
        internal const uint ERROR_PATCH_IMAGEHLP_FALURE        = 0xC00E3106; // create

        /// <summary>
        /// Delegate type that the PatchAPI calls for progress notification.
        /// </summary>
        /// <param name="context">.</param>
        /// <param name="currentPosition">.</param>
        /// <param name="maxPosition">.</param>
        /// <returns>True for success</returns>
        public delegate bool PatchProgressCallback(
                IntPtr context,
                uint currentPosition,
                uint maxPosition
                );

        /// <summary>
        /// Delegate type that the PatchAPI calls for patch symbol load information.
        /// </summary>
        /// <param name="whichFile">.</param>
        /// <param name="symbolFileName">.</param>
        /// <param name="symType">.</param>
        /// <param name="symbolFileCheckSum">.</param>
        /// <param name="symbolFileTimeDate">.</param>
        /// <param name="imageFileCheckSum">.</param>
        /// <param name="imageFileTimeDate">.</param>
        /// <param name="context">.</param>
        /// <returns>???</returns>
        public delegate bool PatchSymloadCallback(
                                                 uint whichFile, // 0 for new file, 1 for first old file, etc
                [MarshalAs(UnmanagedType.LPStr)] string symbolFileName,
                                                 uint symType,   // see SYM_TYPE in imagehlp.h
                                                 uint symbolFileCheckSum,
                                                 uint symbolFileTimeDate,
                                                 uint imageFileCheckSum,
                                                 uint imageFileTimeDate,
                                                 IntPtr context
                                                 );

        /// <summary>
        /// Wraps PATCH_IGNORE_RANGE
        /// </summary>
        [StructLayout(LayoutKind.Sequential)]
        internal class PatchIgnoreRange
        {
            public uint offsetInOldFile;
            public uint lengthInBytes;
        }

        /// <summary>
        /// Wraps PATCH_RETAIN_RANGE
        /// </summary>
        [StructLayout(LayoutKind.Sequential)]
        internal class PatchRetainRange
        {
            public uint offsetInOldFile;
            public uint lengthInBytes;
            public uint offsetInNewFile;
        }

        /// <summary>
        /// Wraps PATCH_OLD_FILE_INFO (except for the OldFile~ portion)
        /// </summary>
        internal class PatchOldFileInfo
        {
            public PatchIgnoreRange[] ignoreRange;
            public PatchRetainRange[] retainRange;
        }

        /// <summary>
        /// Wraps PATCH_OLD_FILE_INFO_W
        /// </summary>
        internal class PatchOldFileInfoW : PatchOldFileInfo
        {
            public string oldFileName;
        }

        /// <summary>
        /// Wraps each PATCH_INTERLEAVE_MAP Range
        /// </summary>
        [SuppressMessage("Microsoft.Performance", "CA1812:AvoidUninstantiatedInternalClasses"), StructLayout(LayoutKind.Sequential)]
        internal class PatchInterleaveMapRange
        {
            public uint oldOffset;
            public uint oldLength;
            public uint newLength;
        }

        /// <summary>
        /// Wraps PATCH_INTERLEAVE_MAP
        /// </summary>
        internal class PatchInterleaveMap
        {
            public PatchInterleaveMapRange[] ranges = null;
        }


        /// <summary>
        /// Wraps PATCH_OPTION_DATA
        /// </summary>
        [BestFitMapping(false, ThrowOnUnmappableChar = true)]
        internal class PatchOptionData
        {
            public PatchSymbolFlagsType symbolOptionFlags;          // PATCH_SYMBOL_xxx flags
            [MarshalAs(UnmanagedType.LPStr)] public string               newFileSymbolPath;          // always ANSI, never Unicode
            [MarshalAs(UnmanagedType.LPStr)] public string[]             oldFileSymbolPathArray;     // array[ OldFileCount ]
            public uint                 extendedOptionFlags;
            public PatchSymloadCallback symLoadCallback = null;
            public IntPtr symLoadContext = IntPtr.Zero;
            public PatchInterleaveMap[] interleaveMapArray = null;  // array[ OldFileCount ] (requires 5.2 or higher applyer)
            public uint maxLzxWindowSize = 0;       // limit memory requirements (requires 5.2 or higher applyer)
        }

        //
        // Note that PATCH_OPTION_DATA contains LPCSTR paths, and no LPCWSTR (Unicode)
        // path argument is available, even when used with one of the Unicode APIs
        // such as CreatePatchFileW. This is because the unlerlying system services
        // for symbol file handling (IMAGEHLP.DLL) only support ANSI file/path names.
        //

        //
        // A note about PATCH_RETAIN_RANGE specifiers with multiple old files:
        //
        // Each old version file must have the same RetainRangeCount, and the same
        // retain range LengthInBytes and OffsetInNewFile values in the same order.
        // Only the OffsetInOldFile values can differ between old foles for retain
        // ranges.
        //

        //
        // The following prototypes are (some of the) interfaces for creating patches from files.
        //

        /// <summary>
        /// Creates a new delta.
        /// </summary>
        /// <param name="oldFileCount">Size of oldFileInfoArray.</param>
        /// <param name="oldFileInfoArray">Target file information.</param>
        /// <param name="newFileName">Name of updated file.</param>
        /// <param name="patchFileName">Name of delta to create.</param>
        /// <param name="optionFlags">PATCH_OPTION_xxx.</param>
        /// <param name="optionData">Optional PATCH_OPTION_DATA structure.</param>
        /// <param name="progressCallback">Delegate for progress callbacks.</param>
        /// <param name="context">Context for progress callback delegate.</param>
        /// <returns>true if successfull, sets Marshal.GetLastWin32Error() if not.</returns>
        [DllImport("mspatchc.dll", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        internal static extern bool CreatePatchFileExW(
                uint oldFileCount,      // maximum 255
                [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(PatchAPIMarshaler), MarshalCookie="PATCH_OLD_FILE_INFO_W")]
                PatchOldFileInfoW[] oldFileInfoArray,
                string newFileName,     // input file  (required)
                string patchFileName,   // output file (required)
                uint optionFlags,
                [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(PatchAPIMarshaler), MarshalCookie="PATCH_OPTION_DATA")]
                PatchOptionData optionData,
                [MarshalAs (UnmanagedType.FunctionPtr)]
                PatchProgressCallback progressCallback,
                IntPtr context
                );

        /// <summary>
        /// Extracts delta header from delta.
        /// </summary>
        /// <param name="patchFileName">Name of delta file.</param>
        /// <param name="patchHeaderFileName">Name of file to create with delta header.</param>
        /// <returns>true if successfull, sets Marshal.GetLastWin32Error() if not.</returns>
        [DllImport("mspatchc.dll", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        internal static extern bool ExtractPatchHeaderToFileW(
                string patchFileName,      // input file
                string patchHeaderFileName // output file
                );

        // TODO: Add rest of APIs to enable custom binders to perform more exhaustive checks

        /// <summary>
        /// Marshals arguments for the CreatePatch~ APIs
        /// </summary>
        [SuppressMessage("Microsoft.Performance", "CA1812:AvoidUninstantiatedInternalClasses")]
        internal class PatchAPIMarshaler : ICustomMarshaler
        {
            static ICustomMarshaler GetInstance(string cookie)
            {
                return new PatchAPIMarshaler(cookie);
            }

            private enum MarshalType
            {
                PATCH_OPTION_DATA,
                PATCH_OLD_FILE_INFO_W
            };
            private MarshalType marshalType;

            public PatchAPIMarshaler(string cookie)
            {
                marshalType = (MarshalType) Enum.Parse(typeof(MarshalType), cookie);
            }

            //
            // Summary:
            //     Returns the size of the native data to be marshaled.
            //
            // Returns:
            //     The size in bytes of the native data.
            public int GetNativeDataSize()
            {
                return Marshal.SizeOf(typeof(IntPtr));
            }

            //
            // Summary:
            //     Performs necessary cleanup of the managed data when it is no longer needed.
            //
            // Parameters:
            //     ManagedObj:
            //       The managed object to be destroyed.
            public void CleanUpManagedData(object ManagedObj)
            {
            }

            //
            // Summary:
            //     Performs necessary cleanup of the unmanaged data when it is no longer needed.
            //
            // Parameters:
            //     pNativeData:
            //       A pointer to the unmanaged data to be destroyed.
            public void CleanUpNativeData(IntPtr pNativeData)
            {
                if (IntPtr.Zero == pNativeData)
                {
                    return;
                }

                switch (marshalType)
                {
                case MarshalType.PATCH_OPTION_DATA:
                    CleanUpPOD(pNativeData);
                    break;
                default:
                    CleanUpPOFI_A(pNativeData);
                    break;
                }
            }

            //
            // Summary:
            //     Converts the managed data to unmanaged data.
            //
            // Parameters:
            //     ManagedObj:
            //       The managed object to be converted.
            //
            // Returns:
            //     Returns the COM view of the managed object.
            public IntPtr MarshalManagedToNative(object ManagedObj)
            {
                if (null == ManagedObj)
                {
                    return IntPtr.Zero;
                }

                switch(marshalType)
                {
                case MarshalType.PATCH_OPTION_DATA:
                    return MarshalPOD(ManagedObj);
                case MarshalType.PATCH_OLD_FILE_INFO_W:
                    return MarshalPOFIW_A(ManagedObj);
                default:
                    throw new InvalidOperationException();
                }
            }


            //
            // Summary:
            //     Converts the unmanaged data to managed data.
            //
            // Parameters:
            //     pNativeData:
            //       A pointer to the unmanaged data to be wrapped.
            //
            // Returns:
            //     Returns the managed view of the COM data.
            public object MarshalNativeToManaged(IntPtr pNativeData)
            {
                return null;
            }

            // Implementation *************************************************

            // PATCH_OPTION_DATA offsets
            private static readonly int symbolOptionFlagsOffset      =   Marshal.SizeOf(typeof(Int32));
            private static readonly int newFileSymbolPathOffset      = 2*Marshal.SizeOf(typeof(Int32));
            private static readonly int oldFileSymbolPathArrayOffset = 2*Marshal.SizeOf(typeof(Int32)) + Marshal.SizeOf(typeof(IntPtr));
            private static readonly int extendedOptionFlagsOffset    = 2*Marshal.SizeOf(typeof(Int32)) + 2*Marshal.SizeOf(typeof(IntPtr));
            private static readonly int symLoadCallbackOffset        = 3*Marshal.SizeOf(typeof(Int32)) + 2*Marshal.SizeOf(typeof(IntPtr));
            private static readonly int symLoadContextOffset         = 3*Marshal.SizeOf(typeof(Int32)) + 3*Marshal.SizeOf(typeof(IntPtr));
            private static readonly int interleaveMapArrayOffset     = 3*Marshal.SizeOf(typeof(Int32)) + 4*Marshal.SizeOf(typeof(IntPtr));
            private static readonly int maxLzxWindowSizeOffset       = 3*Marshal.SizeOf(typeof(Int32)) + 5*Marshal.SizeOf(typeof(IntPtr));
            private static readonly int patchOptionDataSize          = 4*Marshal.SizeOf(typeof(Int32)) + 5*Marshal.SizeOf(typeof(IntPtr));

            // PATCH_OLD_FILE_INFO offsets
            private static readonly int oldFileOffset          =   Marshal.SizeOf(typeof(Int32));
            private static readonly int ignoreRangeCountOffset =   Marshal.SizeOf(typeof(Int32)) + Marshal.SizeOf(typeof(IntPtr));
            private static readonly int ignoreRangeArrayOffset = 2*Marshal.SizeOf(typeof(Int32)) + Marshal.SizeOf(typeof(IntPtr));
            private static readonly int retainRangeCountOffset = 2*Marshal.SizeOf(typeof(Int32)) + 2*Marshal.SizeOf(typeof(IntPtr));
            private static readonly int retainRangeArrayOffset = 3*Marshal.SizeOf(typeof(Int32)) + 2*Marshal.SizeOf(typeof(IntPtr));
            private static readonly int patchOldFileInfoSize   = 3*Marshal.SizeOf(typeof(Int32)) + 3*Marshal.SizeOf(typeof(IntPtr));

            private int oldFileCount;

            // Helper methods

            private static IntPtr OptionalAnsiString(string managed)
            {
                return (null == managed) ? IntPtr.Zero : Marshal.StringToCoTaskMemAnsi(managed);
            }

            private static IntPtr OptionalUnicodeString(string managed)
            {
                return (null == managed) ? IntPtr.Zero : Marshal.StringToCoTaskMemUni(managed);
            }

            // string array must be of the same length as the number of old files
            private IntPtr CreateArrayOfStringA(string[] managed)
            {
                if (null == managed)
                {
                    return IntPtr.Zero;
                }
                if (0 != this.oldFileCount && this.oldFileCount != managed.Length)
                {
                    throw new ArgumentOutOfRangeException("managed");
                }
                this.oldFileCount = managed.Length;
                int size = this.oldFileCount * Marshal.SizeOf(typeof(IntPtr));
                IntPtr native = Marshal.AllocCoTaskMem(size);
                for (int i = 0; i < this.oldFileCount; ++i)
                {
                    Marshal.WriteIntPtr(native, i*Marshal.SizeOf(typeof(IntPtr)), OptionalAnsiString(managed[i]));
                }
                return native;
            }

            // string array must be of the same length as the number of old files
            private IntPtr CreateArrayOfStringW(string[] managed)
            {
                if (null == managed)
                {
                    return IntPtr.Zero;
                }
                if (0 != this.oldFileCount && this.oldFileCount != managed.Length)
                {
                    throw new ArgumentOutOfRangeException("managed");
                }
                this.oldFileCount = managed.Length;
                int size = this.oldFileCount * Marshal.SizeOf(typeof(IntPtr));
                IntPtr native = Marshal.AllocCoTaskMem(size);
                for (int i = 0; i < this.oldFileCount; ++i)
                {
                    Marshal.WriteIntPtr(native, i*Marshal.SizeOf(typeof(IntPtr)), OptionalUnicodeString(managed[i]));
                }
                return native;
            }

            private static IntPtr CreateInterleaveMapRange(PatchInterleaveMap managed)
            {
                if (null == managed)
                {
                    return IntPtr.Zero;
                }
                if (null == managed.ranges)
                {
                    return IntPtr.Zero;
                }
                if (0 == managed.ranges.Length)
                {
                    return IntPtr.Zero;
                }

                IntPtr native = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(UInt32))
                                        + managed.ranges.Length*(Marshal.SizeOf(typeof(PatchInterleaveMap))));
                WriteUInt32(native, (uint) managed.ranges.Length);
                for (int i = 0; i < managed.ranges.Length; ++i)
                {
                    Marshal.StructureToPtr(managed.ranges[i], (IntPtr)((Int64)native + i*Marshal.SizeOf(typeof(PatchInterleaveMap))), false);
                }
                return native;
            }

            private IntPtr CreateInterleaveMap(PatchInterleaveMap[] managed)
            {
                if (null == managed)
                {
                    return IntPtr.Zero;
                }

                if (managed.Length != this.oldFileCount)
                {
                    throw new ArgumentOutOfRangeException("managed");
                }

                IntPtr native = Marshal.AllocCoTaskMem(managed.Length * Marshal.SizeOf(typeof(IntPtr)));
                for (int i = 0; i < managed.Length; ++i)
                {
                    Marshal.WriteIntPtr(native, i*Marshal.SizeOf(typeof(IntPtr)), CreateInterleaveMapRange(managed[i]));
                }
                return native;
            }

            private static void WriteUInt32(IntPtr native, uint data)
            {
                Marshal.WriteInt32(native, unchecked((int) data));
            }

            private static void WriteUInt32(IntPtr native, int offset, uint data)
            {
                Marshal.WriteInt32(native, offset, unchecked((int) data));
            }

            // Marshal operations

            private IntPtr MarshalPOD(object managedObj)
            {
                PatchOptionData managed = managedObj as PatchOptionData;
                if (null == managed)
                {
                    throw new ArgumentNullException("managedObj");
                }
                IntPtr native = Marshal.AllocCoTaskMem(patchOptionDataSize);
                Marshal.WriteInt32(native, patchOptionDataSize); // SizeOfThisStruct
                WriteUInt32(native, symbolOptionFlagsOffset, (uint) managed.symbolOptionFlags);
                Marshal.WriteIntPtr(native, newFileSymbolPathOffset, OptionalAnsiString(managed.newFileSymbolPath));
                Marshal.WriteIntPtr(native, oldFileSymbolPathArrayOffset, CreateArrayOfStringA(managed.oldFileSymbolPathArray));
                WriteUInt32(native, extendedOptionFlagsOffset, managed.extendedOptionFlags);

                // GetFunctionPointerForDelegate() throws an ArgumentNullException if the delegate is null.
                if (null == managed.symLoadCallback)
                {
                    Marshal.WriteIntPtr(native, symLoadCallbackOffset, IntPtr.Zero);
                }
                else
                {
                    Marshal.WriteIntPtr(native, symLoadCallbackOffset, Marshal.GetFunctionPointerForDelegate(managed.symLoadCallback));
                }

                Marshal.WriteIntPtr(native, symLoadContextOffset, managed.symLoadContext);
                Marshal.WriteIntPtr(native, interleaveMapArrayOffset, this.CreateInterleaveMap(managed.interleaveMapArray));
                WriteUInt32(native, maxLzxWindowSizeOffset, managed.maxLzxWindowSize);

                return native;
            }

            private IntPtr MarshalPOFIW_A(object managedObj)
            {
                PatchOldFileInfoW[] managed = managedObj as PatchOldFileInfoW[];
                if (null == managed)
                {
                    throw new ArgumentNullException("managedObj");
                }

                this.oldFileCount = managed.Length;
                if (0 == this.oldFileCount)
                {
                    return IntPtr.Zero;
                }
                IntPtr native = Marshal.AllocCoTaskMem(this.oldFileCount*patchOldFileInfoSize);
                for (int i = 0; i < this.oldFileCount; ++i)
                {
                    MarshalPOFIW(managed[i], (IntPtr)((Int64)native + i * patchOldFileInfoSize));
                }
                return native;
            }

            private static void MarshalPOFIW(PatchOldFileInfoW managed, IntPtr native)
            {
                MarshalPOFI(managed, native);
                Marshal.WriteIntPtr(native, oldFileOffset, OptionalUnicodeString(managed.oldFileName)); // OldFileName
            }

            private static void MarshalPOFI(PatchOldFileInfo managed, IntPtr native)
            {
                Marshal.WriteInt32(native, patchOldFileInfoSize); // SizeOfThisStruct
                WriteUInt32(native, ignoreRangeCountOffset,
                                    (null == managed.ignoreRange) ? 0 : (uint) managed.ignoreRange.Length); // IgnoreRangeCount // maximum 255
                Marshal.WriteIntPtr(native, ignoreRangeArrayOffset, MarshalPIRArray(managed.ignoreRange));  // IgnoreRangeArray
                WriteUInt32(native, retainRangeCountOffset,
                                    (null == managed.retainRange) ? 0 : (uint) managed.retainRange.Length); // RetainRangeCount // maximum 255
                Marshal.WriteIntPtr(native, retainRangeArrayOffset, MarshalPRRArray(managed.retainRange));  // RetainRangeArray
            }

            private static IntPtr MarshalPIRArray(PatchIgnoreRange[] array)
            {
                if (null == array)
                {
                    return IntPtr.Zero;
                }
                if (0 == array.Length)
                {
                    return IntPtr.Zero;
                }
                IntPtr native = Marshal.AllocCoTaskMem(array.Length*Marshal.SizeOf(typeof(PatchIgnoreRange)));
                for (int i = 0; i < array.Length; ++i)
                {
                    Marshal.StructureToPtr(array[i], (IntPtr)((Int64)native + (i*Marshal.SizeOf(typeof(PatchIgnoreRange)))), false);
                }
                return native;
            }

            private static IntPtr MarshalPRRArray(PatchRetainRange[] array)
            {
                if (null == array)
                {
                    return IntPtr.Zero;
                }
                if (0 == array.Length)
                {
                    return IntPtr.Zero;
                }
                IntPtr native = Marshal.AllocCoTaskMem(array.Length*Marshal.SizeOf(typeof(PatchRetainRange)));
                for (int i = 0; i < array.Length; ++i)
                {
                    Marshal.StructureToPtr(array[i], (IntPtr)((Int64)native + (i*Marshal.SizeOf(typeof(PatchRetainRange)))), false);
                }
                return native;
            }

            // CleanUp operations

            private void CleanUpPOD(IntPtr native)
            {
                Marshal.FreeCoTaskMem(Marshal.ReadIntPtr(native, newFileSymbolPathOffset));
                if (IntPtr.Zero != Marshal.ReadIntPtr(native, oldFileSymbolPathArrayOffset))
                {
                    for (int i = 0; i < this.oldFileCount; ++i)
                    {
                        Marshal.FreeCoTaskMem(
                                Marshal.ReadIntPtr(
                                        Marshal.ReadIntPtr(native, oldFileSymbolPathArrayOffset),
                                        i*Marshal.SizeOf(typeof(IntPtr))));
                    }
                    Marshal.FreeCoTaskMem(Marshal.ReadIntPtr(native, oldFileSymbolPathArrayOffset));
                }
                if (IntPtr.Zero != Marshal.ReadIntPtr(native, interleaveMapArrayOffset))
                {
                    for (int i = 0; i < this.oldFileCount; ++i)
                    {
                        Marshal.FreeCoTaskMem(
                                Marshal.ReadIntPtr(
                                        Marshal.ReadIntPtr(native, interleaveMapArrayOffset),
                                        i*Marshal.SizeOf(typeof(IntPtr))));
                    }
                    Marshal.FreeCoTaskMem(Marshal.ReadIntPtr(native, interleaveMapArrayOffset));
                }
                Marshal.FreeCoTaskMem(native);
            }

            private void CleanUpPOFI_A(IntPtr native)
            {
                for (int i = 0; i < this.oldFileCount; ++i)
                {
                    CleanUpPOFI((IntPtr)((Int64)native + i*patchOldFileInfoSize));
                }
                Marshal.FreeCoTaskMem(native);
            }

            private static void CleanUpPOFI(IntPtr native)
            {
                if (IntPtr.Zero != Marshal.ReadIntPtr(native, oldFileOffset))
                {
                    Marshal.FreeCoTaskMem(Marshal.ReadIntPtr(native, oldFileOffset));
                }
                CleanUpPOFIH(native);
            }

            private static void CleanUpPOFIH(IntPtr native)
            {
                if (IntPtr.Zero != Marshal.ReadIntPtr(native, ignoreRangeArrayOffset))
                {
                    Marshal.FreeCoTaskMem(Marshal.ReadIntPtr(native, ignoreRangeArrayOffset));
                }
                if (IntPtr.Zero != Marshal.ReadIntPtr(native, retainRangeArrayOffset))
                {
                    Marshal.FreeCoTaskMem(Marshal.ReadIntPtr(native, retainRangeArrayOffset));
                }
            }
        }
    }
}
www.java2v.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.