/// <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)); }
/// <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++; } }
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); } } } }