Example #1
0
        /// <summary>
        /// Gets the size of the <paramref name="value"/> object, in bytes.
        /// </summary>
        /// <param name="value">Object to get the size of.</param>
        /// <returns>The <paramref name="value"/> size, if it's not <see langword="null"/>; otherwise, <c>0</c>.</returns>
        /// <exception cref="ArgumentException"><paramref name="value"/> type is not supported.</exception>
        /// <remarks>
        /// This method is called recursively, if the <paramref name="value"/> is a collection.
        /// </remarks>
        public static int GetObjectSize(object value)
        {
            if (value == null)
            {
                return(0);
            }

            Type type = value.GetType();

            if (type.IsValueType)
            {
                return(Marshal.SizeOf(value));
            }

            string strValue = value as string;

            if (strValue != null)
            {
                return(Encoding.Unicode.GetByteCount(strValue + '\0'));
            }

            // If the value given is an enumerable collection, call this method recursively.
            IEnumerable enumerable = value as IEnumerable;

            if (enumerable != null)
            {
                return(enumerable.Cast <object>().Sum(element => MarshalingHelper.GetObjectSize(element)));
            }

            throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "Type {0} is not a collection or a value type.", type));
        }
Example #2
0
        /// <summary>
        /// Marshals the <paramref name="values"/> given (<i>ignoring the <see langword="null"/> objects</i>) to the <paramref name="destination"/>.
        /// </summary>
        /// <param name="destination">The pointer to marshal the <paramref name="values"/> array to.</param>
        /// <param name="destinationSize">Size of the buffer the <paramref name="destination"/> points to.</param>
        /// <param name="values">The values to be marshaled.</param>
        /// <exception cref="ArgumentNullException">
        /// <paramref name="destination"/> is <see cref="IntPtr.Zero"/>.
        ///     <para>-or-</para>
        /// <paramref name="values"/> array is <see langword="null"/> or empty.
        /// </exception>
        /// <exception cref="ArgumentException">Total size of the <paramref name="values"/> objects is zero.</exception>
        /// <exception cref="ArgumentOutOfRangeException"><paramref name="destinationSize"/> is not large enough to store all <paramref name="values"/>.</exception>
        public static void MarshalObjectsToPointer(IntPtr destination, int destinationSize, params object[] values)
        {
            if (destination == IntPtr.Zero)
            {
                throw new ArgumentNullException(nameof(destination));
            }

            if (values == null || !values.Any())
            {
                throw new ArgumentNullException(nameof(values));
            }

            // Calculate the object sizes and save it for future use.
            // This will also validate the 'values' array elements.
            List <int> valueSizes = values.Select(MarshalingHelper.GetObjectSize).ToList();

            // Calculate the total size of the values given.
            int totalSize = valueSizes.Sum();

            if (totalSize <= 0)
            {
                throw new ArgumentException("The total size of the values given is zero.");
            }

            if (destinationSize < totalSize)
            {
                throw new ArgumentOutOfRangeException(
                          nameof(destinationSize),
                          string.Format(CultureInfo.InvariantCulture, "Buffer is too small ({0} bytes) to hold the values given, it should be at least {1} bytes.", destinationSize, totalSize));
            }

            NativeMethods.ZeroMemory(destination, (uint)destinationSize);

            // Marshal each value (skipping 'null') to the destination pointer.
            int    currentValueIndex = 0;
            IntPtr currentPointer    = destination;

            foreach (object value in values)
            {
                if (value != null)
                {
                    MarshalingHelper.MarshalObjectToPointer(value, currentPointer);
                    currentPointer += valueSizes[currentValueIndex];
                }

                currentValueIndex++;
            }
        }
Example #3
0
        public static void MarshalObjectToPointer(object value, IntPtr destination)
        {
            if (value == null)
            {
                throw new ArgumentNullException(nameof(value));
            }

            if (destination == IntPtr.Zero)
            {
                throw new ArgumentNullException(nameof(destination));
            }

            Type valueType = value.GetType();

            // If a structure or a primitive was passed, marshal it and exit.
            if (valueType.IsValueType)
            {
                if (valueType.IsPrimitive)
                {
                    MarshalingHelper.MarshalPrimitiveToPointer(value, destination);
                }
                else
                {
                    Marshal.StructureToPtr(value, destination, true);
                }

                return;
            }

            // If the value passed is not a structure and a primitive type,
            // check, whether it's an enumerable collection we can marshal.
            if (!(value is IEnumerable))
            {
                throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "Type {0} cannot be marshaled.", value.GetType()));
            }

            //
            // We've found out that the value passed is an enumerable collection.
            //

            if (value is Array && ((Array)value).Length == 0)
            {
                // We don't want to do anything with empty arrays, so just exit.
                return;
            }

            // First, we want to use the pre-defined marshaling methods for arrays of primitives.
            if (value is byte[])
            {
                Marshal.Copy((byte[])value, 0, destination, ((byte[])value).Length);
            }
            else if (value is char[])
            {
                Marshal.Copy((char[])value, 0, destination, ((char[])value).Length);
            }
            else if (value is short[])
            {
                Marshal.Copy((short[])value, 0, destination, ((short[])value).Length);
            }
            else if (value is int[])
            {
                Marshal.Copy((int[])value, 0, destination, ((int[])value).Length);
            }
            else if (value is long[])
            {
                Marshal.Copy((long[])value, 0, destination, ((long[])value).Length);
            }
            else if (value is float[])
            {
                Marshal.Copy((float[])value, 0, destination, ((float[])value).Length);
            }
            else if (value is double[])
            {
                Marshal.Copy((double[])value, 0, destination, ((double[])value).Length);
            }
            else if (value is string)
            {
                byte[] stringAsByteArray = Encoding.Unicode.GetBytes((string)value + '\0');
                Marshal.Copy(stringAsByteArray, 0, destination, stringAsByteArray.Length);
            }
            else
            {
                // It wasn't collection of primitives that we received, iterate over it and try to marshal each element.
                IntPtr currentPointer = destination;
                foreach (object element in (IEnumerable)value)
                {
                    // Recursively call this method to marshal the element in the collection.
                    MarshalingHelper.MarshalObjectToPointer(element, currentPointer);

                    // And don't forget to move the current pointer to a new location,
                    // so the next element won't overwrite the marshaled data.
                    currentPointer += MarshalingHelper.GetObjectSize(element);
                }
            }
        }
        /// <summary>
        /// Sets the reparse point data for the <paramref name="path"/> given.
        /// </summary>
        /// <param name="path">File or directory to set the reparse point data for.</param>
        /// <param name="data">
        /// Reparse point data to be set.
        /// It should be a value type, or an <see cref="IEnumerable"/>, and it should not contain
        /// reparse point data header information, because this function handles it separately.
        /// </param>
        /// <param name="reparseTag">Reparse point tag.</param>
        /// <param name="reparseGuid">Reparse point <see cref="Guid"/>. Must be specified, if the <paramref name="reparseTag"/> is a non-Microsoft tag.</param>
        /// <exception cref="ArgumentNullException"><paramref name="path"/> is <see langword="null"/> or empty.</exception>
        /// <exception cref="ArgumentException">
        /// <paramref name="data"/> is not a collection or a value type.
        ///     <para>-or-</para>
        /// <paramref name="reparseTag"/> is a Microsoft tag, but the <paramref name="reparseGuid"/> is not <see langword="null"/>.
        ///     <para>-or-</para>
        /// <paramref name="reparseTag"/> is a non-Microsoft tag, but the <paramref name="reparseGuid"/> is <see langword="null"/>.
        /// </exception>
        /// <exception cref="InvalidOperationException">
        /// Reparse point <paramref name="data"/> cannot be set for the <paramref name="path"/>.
        /// </exception>
        /// <exception cref="IOException">
        /// <paramref name="path"/> cannot be accessed.
        /// </exception>
        /// <remarks>
        /// This method will <i>NOT</i> update file attributes.<br/>
        /// For example, the <see cref="FileAttributes.ReparsePoint"/> will not be set.
        /// </remarks>
        public static void SetReparsePointData(string path, object data, int reparseTag, Guid?reparseGuid)
        {
            if (string.IsNullOrEmpty(path))
            {
                throw new ArgumentNullException(nameof(path));
            }

            ReparsePointHelper.ValidateTagAndGuid(reparseTag, reparseGuid);

            bool   isMicrosoftTag = ReparsePointHelper.IsMicrosoftTag(reparseTag);
            string normalizedPath = LongPathCommon.NormalizePath(path);

            if (!LongPathCommon.Exists(normalizedPath))
            {
                throw new IOException(string.Format(CultureInfo.InvariantCulture, "{0} cannot be found.", path));
            }

            using (SafeFileHandle handle = NativeMethods.CreateFile(
                       normalizedPath,
                       AccessRights.GenericWrite,
                       FileShare.None,
                       IntPtr.Zero,
                       FileMode.Open,
                       EFileAttributes.OpenReparsePoint | EFileAttributes.BackupSemantics,
                       IntPtr.Zero))
            {
                if (handle.IsInvalid)
                {
                    Exception nativeException = Marshal.GetExceptionForHR(Marshal.GetHRForLastWin32Error());
                    throw new IOException(string.Format(CultureInfo.InvariantCulture, "Unable to open: {0}", path), nativeException);
                }

                int dataSize = MarshalingHelper.GetObjectSize(data);

                object header = isMicrosoftTag
                                ? (object)new ReparseDataBufferHeader {
                    ReparseDataLength = unchecked ((ushort)dataSize), ReparseTag = reparseTag
                }
                                : new ReparseGuidDataBufferHeader     {
                    ReparseDataLength = unchecked ((ushort)dataSize), ReparseTag = reparseTag, ReparseGuid = reparseGuid.Value
                };

                int headerSize    = Marshal.SizeOf(header);
                int tagDataLength = headerSize + dataSize;

                using (ResizableBuffer buffer = new ResizableBuffer(Math.Max(ReparsePointHelper.BufferSize, tagDataLength)))
                {
                    MarshalingHelper.MarshalObjectToPointer(new[] { header, data }, buffer.DangerousGetPointer());

                    // Set the reparse point data.
                    int  bytesReturned;
                    bool success = NativeMethods.DeviceIoControl(
                        handle,
                        ReparsePointHelper.SetReparsePointControlCode,
                        buffer.DangerousGetPointer(),
                        tagDataLength,
                        IntPtr.Zero,
                        0,
                        out bytesReturned,
                        IntPtr.Zero);

                    if (!success)
                    {
                        Exception nativeException = Marshal.GetExceptionForHR(Marshal.GetHRForLastWin32Error());
                        throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "Unable to set the reparse point data: {0}", path), nativeException);
                    }
                }
            }
        }