diff options
author | Michal Strehovský <MichalStrehovsky@users.noreply.github.com> | 2017-05-19 07:47:46 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-05-19 07:47:46 +0300 |
commit | 011a6804018dc8cde17df31891e5de6053a2e83e (patch) | |
tree | 2ac38ca4500e14487d9ccac3177f4dfe5f8fc73f /src | |
parent | 4afa5041466f0742105f35c886a7bbd47ca01765 (diff) | |
parent | e2915ae7dadd03843b6d070b5950b974c0fb988f (diff) |
Merge pull request #3654 from dotnet/nmirror
Merge nmirror to master
Diffstat (limited to 'src')
21 files changed, 644 insertions, 27 deletions
diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GenericLookupResult.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GenericLookupResult.cs index 0fd1c94b0..2b0b548a1 100644 --- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GenericLookupResult.cs +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GenericLookupResult.cs @@ -21,6 +21,45 @@ namespace ILCompiler.DependencyAnalysis ConditionalIndirect, // The slot may be a direct pointer or an indirection cell, depending on the last digit } + // Represents a generic lookup within a canonical method body. + // TODO: unify with NativeFormat.FixupSignatureKind + public enum LookupResultType + { + Invalid, + EEType, // a type + UnwrapNullable, // a type (The type T described by a type spec that is generic over Nullable<T>) + NonGcStatic, // the non-gc statics of a type + GcStatic, // the gc statics of a type + Method, // a method + InterfaceDispatchCell, // the dispatch cell for calling an interface method + MethodDictionary, // a dictionary for calling a generic method + UnboxingStub, // the unboxing stub for a method + ArrayType, // an array of type + DefaultCtor, // default ctor of a type + TlsIndex, // tls index of a type + TlsOffset, // tls offset of a type + AllocObject, // the allocator of a type + GvmVtableOffset, // vtable offset of a generic virtual method + ProfileCounter, // profiling counter cell + MethodLdToken, // a ldtoken result for a method + FieldLdToken, // a ldtoken result for a field + Field, // a field descriptor + IsInst, // isinst helper + CastClass, // castclass helper + AllocArray, // the array allocator of a type + CheckArrayElementType, // check the array element type + TypeSize, // size of the type + FieldOffset, // field offset + CallingConvention, // CallingConventionConverterThunk + VtableOffset, // Offset of a virtual method into the type's vtable + Constrained, // ConstrainedCallDesc + } + + public interface IGenericLookupResultTocWriter + { + void WriteData(GenericLookupResultReferenceType referenceType, LookupResultType slotType, TypeSystemEntity context); + } + /// <summary> /// Represents the result of a generic lookup within a canonical method body. /// The concrete artifact the generic lookup will result in can only be determined after substituting @@ -56,6 +95,8 @@ namespace ILCompiler.DependencyAnalysis public abstract NativeLayoutVertexNode TemplateDictionaryNode(NodeFactory factory); + public abstract void WriteDictionaryTocData(NodeFactory factory, IGenericLookupResultTocWriter writer); + // Call this api to get non-reloc dependencies that arise from use of a dictionary lookup public virtual IEnumerable<DependencyNodeCore<NodeFactory>> NonRelocDependenciesFromUsage(NodeFactory factory) { @@ -147,6 +188,11 @@ namespace ILCompiler.DependencyAnalysis } } + public override void WriteDictionaryTocData(NodeFactory factory, IGenericLookupResultTocWriter writer) + { + writer.WriteData(LookupResultReferenceType(factory), LookupResultType.EEType, _type); + } + protected override int CompareToImpl(GenericLookupResult other, TypeSystemComparer comparer) { return comparer.Compare(_type, ((TypeHandleGenericLookupResult)other)._type); @@ -206,6 +252,11 @@ namespace ILCompiler.DependencyAnalysis } } + public override void WriteDictionaryTocData(NodeFactory factory, IGenericLookupResultTocWriter writer) + { + writer.WriteData(LookupResultReferenceType(factory), LookupResultType.UnwrapNullable, _type); + } + protected override int CompareToImpl(GenericLookupResult other, TypeSystemComparer comparer) { return comparer.Compare(_type, ((UnwrapNullableTypeHandleGenericLookupResult)other)._type); @@ -254,6 +305,11 @@ namespace ILCompiler.DependencyAnalysis return factory.NativeLayout.FieldOffsetDictionarySlot(_field); } + public override void WriteDictionaryTocData(NodeFactory factory, IGenericLookupResultTocWriter writer) + { + writer.WriteData(LookupResultReferenceType(factory), LookupResultType.FieldOffset, _field); + } + protected override int CompareToImpl(GenericLookupResult other, TypeSystemComparer comparer) { return comparer.Compare(_field, ((FieldOffsetGenericLookupResult)other)._field); @@ -314,6 +370,11 @@ namespace ILCompiler.DependencyAnalysis }; } + public override void WriteDictionaryTocData(NodeFactory factory, IGenericLookupResultTocWriter writer) + { + writer.WriteData(LookupResultReferenceType(factory), LookupResultType.VtableOffset, _method); + } + protected override int CompareToImpl(GenericLookupResult other, TypeSystemComparer comparer) { return comparer.Compare(_method, ((VTableOffsetGenericLookupResult)other)._method); @@ -354,6 +415,11 @@ namespace ILCompiler.DependencyAnalysis return factory.NativeLayout.MethodLdTokenDictionarySlot(_method); } + public override void WriteDictionaryTocData(NodeFactory factory, IGenericLookupResultTocWriter writer) + { + writer.WriteData(LookupResultReferenceType(factory), LookupResultType.MethodLdToken, _method); + } + protected override int CompareToImpl(GenericLookupResult other, TypeSystemComparer comparer) { return comparer.Compare(_method, ((MethodHandleGenericLookupResult)other)._method); @@ -394,6 +460,11 @@ namespace ILCompiler.DependencyAnalysis return factory.NativeLayout.FieldLdTokenDictionarySlot(_field); } + public override void WriteDictionaryTocData(NodeFactory factory, IGenericLookupResultTocWriter writer) + { + writer.WriteData(LookupResultReferenceType(factory), LookupResultType.FieldLdToken, _field); + } + protected override int CompareToImpl(GenericLookupResult other, TypeSystemComparer comparer) { return comparer.Compare(_field, ((FieldHandleGenericLookupResult)other)._field); @@ -446,6 +517,11 @@ namespace ILCompiler.DependencyAnalysis return factory.NativeLayout.MethodDictionaryDictionarySlot(_method); } + public override void WriteDictionaryTocData(NodeFactory factory, IGenericLookupResultTocWriter writer) + { + writer.WriteData(LookupResultReferenceType(factory), LookupResultType.MethodDictionary, _method); + } + protected override int CompareToImpl(GenericLookupResult other, TypeSystemComparer comparer) { return comparer.Compare(_method, ((MethodDictionaryGenericLookupResult)other)._method); @@ -493,6 +569,11 @@ namespace ILCompiler.DependencyAnalysis (_method, unboxing: true, functionPointerTarget: factory.MethodEntrypoint(_method.GetCanonMethodTarget(CanonicalFormKind.Specific))); } + public override void WriteDictionaryTocData(NodeFactory factory, IGenericLookupResultTocWriter writer) + { + writer.WriteData(LookupResultReferenceType(factory), LookupResultType.Method, _method); + } + protected override int CompareToImpl(GenericLookupResult other, TypeSystemComparer comparer) { var otherEntry = (MethodEntryGenericLookupResult)other; @@ -558,6 +639,19 @@ namespace ILCompiler.DependencyAnalysis } } + public override void WriteDictionaryTocData(NodeFactory factory, IGenericLookupResultTocWriter writer) + { + if (factory.Target.Abi == TargetAbi.CoreRT) + { + // TODO + throw new NotImplementedException(); + } + else + { + writer.WriteData(LookupResultReferenceType(factory), LookupResultType.InterfaceDispatchCell, _method); + } + } + protected override int CompareToImpl(GenericLookupResult other, TypeSystemComparer comparer) { return comparer.Compare(_method, ((VirtualDispatchGenericLookupResult)other)._method); @@ -620,6 +714,19 @@ namespace ILCompiler.DependencyAnalysis } } + public override void WriteDictionaryTocData(NodeFactory factory, IGenericLookupResultTocWriter writer) + { + if (factory.Target.Abi == TargetAbi.CoreRT) + { + // TODO + throw new NotImplementedException(); + } + else + { + writer.WriteData(LookupResultReferenceType(factory), LookupResultType.InterfaceDispatchCell, _method); + } + } + protected override int CompareToImpl(GenericLookupResult other, TypeSystemComparer comparer) { return comparer.Compare(_method, ((VirtualResolveGenericLookupResult)other)._method); @@ -666,6 +773,11 @@ namespace ILCompiler.DependencyAnalysis return GenericLookupResultReferenceType.Indirect; } + public override void WriteDictionaryTocData(NodeFactory factory, IGenericLookupResultTocWriter writer) + { + writer.WriteData(LookupResultReferenceType(factory), LookupResultType.NonGcStatic, _type); + } + protected override int CompareToImpl(GenericLookupResult other, TypeSystemComparer comparer) { return comparer.Compare(_type, ((TypeNonGCStaticBaseGenericLookupResult)other)._type); @@ -707,6 +819,12 @@ namespace ILCompiler.DependencyAnalysis return factory.NativeLayout.NotSupportedDictionarySlot; } + public override void WriteDictionaryTocData(NodeFactory factory, IGenericLookupResultTocWriter writer) + { + // TODO + throw new NotImplementedException(); + } + protected override int CompareToImpl(GenericLookupResult other, TypeSystemComparer comparer) { return comparer.Compare(_type, ((TypeThreadStaticBaseIndexGenericLookupResult)other)._type); @@ -753,6 +871,11 @@ namespace ILCompiler.DependencyAnalysis return GenericLookupResultReferenceType.Indirect; } + public override void WriteDictionaryTocData(NodeFactory factory, IGenericLookupResultTocWriter writer) + { + writer.WriteData(LookupResultReferenceType(factory), LookupResultType.GcStatic, _type); + } + protected override int CompareToImpl(GenericLookupResult other, TypeSystemComparer comparer) { return comparer.Compare(_type, ((TypeGCStaticBaseGenericLookupResult)other)._type); @@ -798,6 +921,11 @@ namespace ILCompiler.DependencyAnalysis return GenericLookupResultReferenceType.Indirect; } + public override void WriteDictionaryTocData(NodeFactory factory, IGenericLookupResultTocWriter writer) + { + writer.WriteData(LookupResultReferenceType(factory), LookupResultType.AllocObject, _type); + } + protected override int CompareToImpl(GenericLookupResult other, TypeSystemComparer comparer) { return comparer.Compare(_type, ((ObjectAllocatorGenericLookupResult)other)._type); @@ -844,6 +972,11 @@ namespace ILCompiler.DependencyAnalysis return GenericLookupResultReferenceType.Indirect; } + public override void WriteDictionaryTocData(NodeFactory factory, IGenericLookupResultTocWriter writer) + { + writer.WriteData(LookupResultReferenceType(factory), LookupResultType.AllocArray, _type); + } + protected override int CompareToImpl(GenericLookupResult other, TypeSystemComparer comparer) { return comparer.Compare(_type, ((ArrayAllocatorGenericLookupResult)other)._type); @@ -889,6 +1022,11 @@ namespace ILCompiler.DependencyAnalysis return GenericLookupResultReferenceType.Indirect; } + public override void WriteDictionaryTocData(NodeFactory factory, IGenericLookupResultTocWriter writer) + { + writer.WriteData(LookupResultReferenceType(factory), LookupResultType.CastClass, _type); + } + protected override int CompareToImpl(GenericLookupResult other, TypeSystemComparer comparer) { return comparer.Compare(_type, ((CastClassGenericLookupResult)other)._type); @@ -934,6 +1072,11 @@ namespace ILCompiler.DependencyAnalysis return GenericLookupResultReferenceType.Indirect; } + public override void WriteDictionaryTocData(NodeFactory factory, IGenericLookupResultTocWriter writer) + { + writer.WriteData(LookupResultReferenceType(factory), LookupResultType.IsInst, _type); + } + protected override int CompareToImpl(GenericLookupResult other, TypeSystemComparer comparer) { return comparer.Compare(_type, ((IsInstGenericLookupResult)other)._type); @@ -978,6 +1121,11 @@ namespace ILCompiler.DependencyAnalysis return GenericLookupResultReferenceType.Indirect; } + public override void WriteDictionaryTocData(NodeFactory factory, IGenericLookupResultTocWriter writer) + { + writer.WriteData(LookupResultReferenceType(factory), LookupResultType.TlsIndex, _type); + } + protected override int CompareToImpl(GenericLookupResult other, TypeSystemComparer comparer) { return comparer.Compare(_type, ((ThreadStaticIndexLookupResult)other)._type); @@ -1023,6 +1171,11 @@ namespace ILCompiler.DependencyAnalysis return GenericLookupResultReferenceType.Indirect; } + public override void WriteDictionaryTocData(NodeFactory factory, IGenericLookupResultTocWriter writer) + { + writer.WriteData(LookupResultReferenceType(factory), LookupResultType.TlsOffset, _type); + } + protected override int CompareToImpl(GenericLookupResult other, TypeSystemComparer comparer) { return comparer.Compare(_type, ((ThreadStaticOffsetLookupResult)other)._type); @@ -1075,6 +1228,11 @@ namespace ILCompiler.DependencyAnalysis return factory.NativeLayout.DefaultConstructorDictionarySlot(_type); } + public override void WriteDictionaryTocData(NodeFactory factory, IGenericLookupResultTocWriter writer) + { + writer.WriteData(LookupResultReferenceType(factory), LookupResultType.DefaultCtor, _type); + } + protected override int CompareToImpl(GenericLookupResult other, TypeSystemComparer comparer) { return comparer.Compare(_type, ((DefaultConstructorLookupResult)other)._type); @@ -1118,6 +1276,12 @@ namespace ILCompiler.DependencyAnalysis return factory.NativeLayout.CallingConventionConverter(_callingConventionConverter); } + public override void WriteDictionaryTocData(NodeFactory factory, IGenericLookupResultTocWriter writer) + { + // TODO + throw new NotImplementedException(); + } + protected override int CompareToImpl(GenericLookupResult other, TypeSystemComparer comparer) { var otherEntry = (CallingConventionConverterLookupResult)other; @@ -1176,6 +1340,11 @@ namespace ILCompiler.DependencyAnalysis return factory.NativeLayout.TypeSizeDictionarySlot(_type); } + public override void WriteDictionaryTocData(NodeFactory factory, IGenericLookupResultTocWriter writer) + { + writer.WriteData(LookupResultReferenceType(factory), LookupResultType.TypeSize, _type); + } + protected override int CompareToImpl(GenericLookupResult other, TypeSystemComparer comparer) { return comparer.Compare(_type, ((TypeSizeLookupResult)other)._type); @@ -1237,6 +1406,12 @@ namespace ILCompiler.DependencyAnalysis return factory.NativeLayout.ConstrainedMethodUse(_constrainedMethod, _constraintType, _directCall); } + public override void WriteDictionaryTocData(NodeFactory factory, IGenericLookupResultTocWriter writer) + { + // TODO + throw new NotImplementedException(); + } + protected override int CompareToImpl(GenericLookupResult other, TypeSystemComparer comparer) { var otherResult = (ConstrainedMethodUseLookupResult)other; diff --git a/src/Native/Runtime/Debug.h b/src/Native/Runtime/Debug.h index 656a8eff2..926e8fc4c 100644 --- a/src/Native/Runtime/Debug.h +++ b/src/Native/Runtime/Debug.h @@ -12,7 +12,8 @@ enum DebuggerGcProtectionMessage : uint32_t enum DebuggerGcProtectionRequestKind : uint16_t { - ConservativeReporting = 1, + EnsureConservativeReporting = 1, + RemoveConservativeReporting = 2, }; struct GcProtectionMessage @@ -24,7 +25,8 @@ struct GcProtectionMessage struct GcProtectionRequest { - DebuggerGcProtectionRequestKind type; + DebuggerGcProtectionRequestKind kind; uint16_t size; + uint32_t identifier; uint64_t address; }; diff --git a/src/Native/Runtime/DebuggerHook.cpp b/src/Native/Runtime/DebuggerHook.cpp index d4a74db75..26ca90497 100644 --- a/src/Native/Runtime/DebuggerHook.cpp +++ b/src/Native/Runtime/DebuggerHook.cpp @@ -18,12 +18,10 @@ GVAL_IMPL_INIT(UInt32, g_numGcProtectionRequests, 0); { if (g_numGcProtectionRequests > 0) { - // The debugger has some request with respect to GC protection. + // The debugger has some requests with respect to GC protection. // Here we are allocating a buffer to store them GcProtectionRequest* requests = new (nothrow) GcProtectionRequest[g_numGcProtectionRequests]; - // TODO: We need to figure out how to communicate this broken promise to the debugger - // Notifying the debugger the buffer is ready to use GcProtectionMessage command; command.commandCode = DebuggerGcProtectionMessage::RequestBufferReady; @@ -35,7 +33,7 @@ GVAL_IMPL_INIT(UInt32, g_numGcProtectionRequests, 0); // The debugger has filled the requests array for (uint32_t i = 0; i < g_numGcProtectionRequests; i++) { - if (requests[i].type == DebuggerGcProtectionRequestKind::ConservativeReporting) + if (requests[i].kind == DebuggerGcProtectionRequestKind::EnsureConservativeReporting) { // If the request requires extra memory, allocate for it requests[i].address = (uint64_t)new (nothrow) uint8_t[requests[i].size]; @@ -44,6 +42,9 @@ GVAL_IMPL_INIT(UInt32, g_numGcProtectionRequests, 0); } } + // TODO: Consider an optimization to eliminate this message when they is nothing required from the + // debugger side to fill + command.commandCode = DebuggerGcProtectionMessage::ConservativeReportingBufferReady; DebugEventSource::SendCustomEvent((void*)&command, sizeof(command)); @@ -51,22 +52,57 @@ GVAL_IMPL_INIT(UInt32, g_numGcProtectionRequests, 0); for (uint32_t i = 0; i < g_numGcProtectionRequests; i++) { - if (requests[i].type == DebuggerGcProtectionRequestKind::ConservativeReporting) + if (requests[i].kind == DebuggerGcProtectionRequestKind::EnsureConservativeReporting) { - // TODO: Release them when there should be gone DebuggerProtectedBufferList* tail = DebuggerHook::s_debuggerProtectedBuffers; s_debuggerProtectedBuffers = new (std::nothrow) DebuggerProtectedBufferList(); if (s_debuggerProtectedBuffers == nullptr) { // TODO: We cannot handle the debugger request to protect a buffer (we have to break our promise) + // TODO: We need to figure out how to communicate this broken promise to the debugger } else { s_debuggerProtectedBuffers->address = requests[i].address; s_debuggerProtectedBuffers->size = requests[i].size; + s_debuggerProtectedBuffers->identifier = requests[i].identifier; s_debuggerProtectedBuffers->next = tail; } } + else if (requests[i].kind == DebuggerGcProtectionRequestKind::RemoveConservativeReporting) + { + DebuggerProtectedBufferList* prev = nullptr; + DebuggerProtectedBufferList* curr = DebuggerHook::s_debuggerProtectedBuffers; + while (true) + { + if (curr == nullptr) + { + // The debugger is trying to remove a conservatively reported buffer that does not exist + break; + } + if (curr->identifier == requests[i].identifier) + { + DebuggerProtectedBufferList* toDelete = curr; + if (prev == nullptr) + { + // We are trying to remove the head of the linked list + DebuggerHook::s_debuggerProtectedBuffers = curr->next; + } + else + { + prev->next = curr->next; + } + + delete toDelete; + break; + } + else + { + prev = curr; + curr = curr->next; + } + } + } } g_numGcProtectionRequests = 0; diff --git a/src/Native/Runtime/DebuggerHook.h b/src/Native/Runtime/DebuggerHook.h index fb8d0090c..feb1d3e24 100644 --- a/src/Native/Runtime/DebuggerHook.h +++ b/src/Native/Runtime/DebuggerHook.h @@ -21,6 +21,7 @@ struct DebuggerProtectedBufferList { UInt64 address; UInt16 size; + UInt32 identifier; struct DebuggerProtectedBufferList* next; }; diff --git a/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.FileAttributes.cs b/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.FileAttributes.cs new file mode 100644 index 000000000..725a25a71 --- /dev/null +++ b/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.FileAttributes.cs @@ -0,0 +1,17 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +internal partial class Interop +{ + internal partial class Kernel32 + { + internal partial class FileAttributes + { + internal const int FILE_ATTRIBUTE_NORMAL = 0x00000080; + internal const int FILE_ATTRIBUTE_READONLY = 0x00000001; + internal const int FILE_ATTRIBUTE_DIRECTORY = 0x00000010; + internal const int FILE_ATTRIBUTE_REPARSE_POINT = 0x00000400; + } + } +} diff --git a/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.FindClose.cs b/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.FindClose.cs new file mode 100644 index 000000000..03d8c8b32 --- /dev/null +++ b/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.FindClose.cs @@ -0,0 +1,16 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.Win32.SafeHandles; +using System; +using System.Runtime.InteropServices; + +internal partial class Interop +{ + internal partial class Kernel32 + { + [DllImport(Libraries.Kernel32, SetLastError = true)] + internal extern static bool FindClose(IntPtr hFindFile); + } +} diff --git a/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.FindFirstFileEx.cs b/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.FindFirstFileEx.cs new file mode 100644 index 000000000..80b1ddd28 --- /dev/null +++ b/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.FindFirstFileEx.cs @@ -0,0 +1,43 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.Win32.SafeHandles; +using System; +using System.IO; +using System.Runtime.InteropServices; + +internal partial class Interop +{ + internal partial class Kernel32 + { + /// <summary> + /// WARNING: This method does not implicitly handle long paths. Use FindFirstFile. + /// </summary> + [DllImport(Libraries.Kernel32, EntryPoint = "FindFirstFileExW", SetLastError = true, CharSet = CharSet.Unicode)] + private static extern SafeFindHandle FindFirstFileExPrivate(string lpFileName, FINDEX_INFO_LEVELS fInfoLevelId, ref WIN32_FIND_DATA lpFindFileData, FINDEX_SEARCH_OPS fSearchOp, IntPtr lpSearchFilter, int dwAdditionalFlags); + + internal static SafeFindHandle FindFirstFile(string fileName, ref WIN32_FIND_DATA data) + { + fileName = PathInternal.EnsureExtendedPrefixOverMaxPath(fileName); + + // use FindExInfoBasic since we don't care about short name and it has better perf + return FindFirstFileExPrivate(fileName, FINDEX_INFO_LEVELS.FindExInfoBasic, ref data, FINDEX_SEARCH_OPS.FindExSearchNameMatch, IntPtr.Zero, 0); + } + + internal enum FINDEX_INFO_LEVELS : uint + { + FindExInfoStandard = 0x0u, + FindExInfoBasic = 0x1u, + FindExInfoMaxInfoLevel = 0x2u, + } + + internal enum FINDEX_SEARCH_OPS : uint + { + FindExSearchNameMatch = 0x0u, + FindExSearchLimitToDirectories = 0x1u, + FindExSearchLimitToDevices = 0x2u, + FindExSearchMaxSearchOp = 0x3u, + } + } +} diff --git a/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.GetFileAttributesEx.cs b/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.GetFileAttributesEx.cs new file mode 100644 index 000000000..4cce56bd0 --- /dev/null +++ b/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.GetFileAttributesEx.cs @@ -0,0 +1,94 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.Win32.SafeHandles; +using System; +using System.IO; +using System.Runtime.InteropServices; + +internal partial class Interop +{ + internal partial class Kernel32 + { + /// <summary> + /// WARNING: This method does not implicitly handle long paths. Use GetFileAttributesEx. + /// </summary> + [DllImport(Libraries.Kernel32, EntryPoint = "GetFileAttributesExW", SetLastError = true, CharSet = CharSet.Unicode)] + private static extern bool GetFileAttributesExPrivate(string name, GET_FILEEX_INFO_LEVELS fileInfoLevel, ref WIN32_FILE_ATTRIBUTE_DATA lpFileInformation); + + internal static bool GetFileAttributesEx(string name, GET_FILEEX_INFO_LEVELS fileInfoLevel, ref WIN32_FILE_ATTRIBUTE_DATA lpFileInformation) + { + name = PathInternal.EnsureExtendedPrefixOverMaxPath(name); + return GetFileAttributesExPrivate(name, fileInfoLevel, ref lpFileInformation); + } + + internal enum GET_FILEEX_INFO_LEVELS : uint + { + GetFileExInfoStandard = 0x0u, + GetFileExMaxInfoLevel = 0x1u, + } + + internal struct WIN32_FILE_ATTRIBUTE_DATA + { + internal int fileAttributes; + internal uint ftCreationTimeLow; + internal uint ftCreationTimeHigh; + internal uint ftLastAccessTimeLow; + internal uint ftLastAccessTimeHigh; + internal uint ftLastWriteTimeLow; + internal uint ftLastWriteTimeHigh; + internal uint fileSizeHigh; + internal uint fileSizeLow; + + internal void PopulateFrom(ref WIN32_FIND_DATA findData) + { + // Copy the information to data + fileAttributes = (int)findData.dwFileAttributes; + ftCreationTimeLow = findData.ftCreationTime.dwLowDateTime; + ftCreationTimeHigh = findData.ftCreationTime.dwHighDateTime; + ftLastAccessTimeLow = findData.ftLastAccessTime.dwLowDateTime; + ftLastAccessTimeHigh = findData.ftLastAccessTime.dwHighDateTime; + ftLastWriteTimeLow = findData.ftLastWriteTime.dwLowDateTime; + ftLastWriteTimeHigh = findData.ftLastWriteTime.dwHighDateTime; + fileSizeHigh = findData.nFileSizeHigh; + fileSizeLow = findData.nFileSizeLow; + } + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + [BestFitMapping(false)] + internal unsafe struct WIN32_FIND_DATA + { + internal uint dwFileAttributes; + internal FILE_TIME ftCreationTime; + internal FILE_TIME ftLastAccessTime; + internal FILE_TIME ftLastWriteTime; + internal uint nFileSizeHigh; + internal uint nFileSizeLow; + internal uint dwReserved0; + internal uint dwReserved1; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] + internal string cFileName; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)] + internal string cAlternateFileName; + } + + internal struct FILE_TIME + { + internal uint dwLowDateTime; + internal uint dwHighDateTime; + + internal FILE_TIME(long fileTime) + { + dwLowDateTime = (uint)fileTime; + dwHighDateTime = (uint)(fileTime >> 32); + } + + internal long ToTicks() + { + return ((long)dwHighDateTime << 32) + dwLowDateTime; + } + } + } +} diff --git a/src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems b/src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems index 981d35811..ae98a97c7 100644 --- a/src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems +++ b/src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems @@ -472,11 +472,15 @@ <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.CloseHandle.cs"/> <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.CreateFile.cs" Condition="'$(IsProjectNLibrary)' != 'true'" /> <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.CreateFile2.cs" Condition="'$(IsProjectNLibrary)' == 'true'" /> + <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.FileAttributes.cs"/> <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.FILE_INFO_BY_HANDLE_CLASS.cs"/> <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.FileTypes.cs"/> + <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.FindClose.cs"/> + <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.FindFirstFileEx.cs"/> <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.FlushFileBuffers.cs"/> <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.FormatMessage.cs"/> - <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.GetCPInfo.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.GetCPInfo.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.GetFileAttributesEx.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.GetFileInformationByHandleEx.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.GetFileType_SafeHandle.cs"/> <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.GetFullPathNameW.cs"/> diff --git a/src/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFindHandle.cs b/src/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFindHandle.cs new file mode 100644 index 000000000..1c30841da --- /dev/null +++ b/src/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFindHandle.cs @@ -0,0 +1,32 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================ +** +** +** +** A wrapper for find handles +** +** +===========================================================*/ + +using System; +using System.Security; +using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; +using System.Runtime.ConstrainedExecution; +using Microsoft.Win32; + +namespace Microsoft.Win32.SafeHandles +{ + internal sealed class SafeFindHandle : SafeHandleZeroOrMinusOneIsInvalid + { + internal SafeFindHandle() : base(true) { } + + override protected bool ReleaseHandle() + { + return Interop.Kernel32.FindClose(handle); + } + } +} diff --git a/src/System.Private.CoreLib/src/System.Private.CoreLib.csproj b/src/System.Private.CoreLib/src/System.Private.CoreLib.csproj index c080ce9d6..72a1063b9 100644 --- a/src/System.Private.CoreLib/src/System.Private.CoreLib.csproj +++ b/src/System.Private.CoreLib/src/System.Private.CoreLib.csproj @@ -214,6 +214,9 @@ <Compile Include="System\InvokeUtils.cs" /> <Compile Include="System\IO\BinaryReader.cs" /> <Compile Include="System\IO\BinaryWriter.cs" /> + <Compile Include="System\IO\File.cs" /> + <Compile Include="System\IO\File.Windows.cs" Condition="'$(TargetsWindows)'=='true'" /> + <Compile Include="System\IO\File.Unix.cs" Condition="'$(TargetsUnix)'=='true'" /> <Compile Include="System\IO\FileLoadException.CoreRT.cs" /> <Compile Include="System\IO\IOException.cs" /> <Compile Include="System\IO\MemoryStream.cs" /> @@ -298,6 +301,7 @@ <Compile Include="System\Runtime\GcSettings.cs" /> <Compile Include="System\Runtime\TypeLoaderExports.cs" /> <Compile Include="System\Runtime\InitializeFinalizerThread.cs" /> + <Compile Include="System\Runtime\InteropServices\BestFitMappingAttribute.cs" /> <Compile Include="System\Runtime\InteropServices\DllImportAttribute.cs" /> <Compile Include="System\Runtime\InteropServices\GCHandle.cs" /> <Compile Include="System\Runtime\InteropServices\GCHandleType.cs" /> @@ -479,6 +483,7 @@ </ItemGroup> <ItemGroup Condition="'$(TargetsWindows)'=='true'"> <Compile Include="Internal\Runtime\Augments\RuntimeThread.Windows.cs" /> + <Compile Include="Microsoft\Win32\SafeHandles\SafeFindHandle.cs" /> <Compile Include="Microsoft\Win32\SafeHandles\SafeThreadPoolIOHandle.cs" /> <Compile Condition="'$(EnableWinRT)' == 'true'" Include="System\TimeZoneInfo.WinRT.cs" /> <Compile Condition="'$(EnableWinRT)' != 'true'" Include="System\TimeZoneInfo.Win32.cs" /> diff --git a/src/System.Private.CoreLib/src/System/IO/File.Unix.cs b/src/System.Private.CoreLib/src/System/IO/File.Unix.cs new file mode 100644 index 000000000..3cf5bf836 --- /dev/null +++ b/src/System.Private.CoreLib/src/System/IO/File.Unix.cs @@ -0,0 +1,14 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.IO +{ + internal static partial class File + { + internal static bool InternalExists(String path) + { + throw new NotImplementedException(); + } + } +} diff --git a/src/System.Private.CoreLib/src/System/IO/File.Windows.cs b/src/System.Private.CoreLib/src/System/IO/File.Windows.cs new file mode 100644 index 000000000..4bd8e9426 --- /dev/null +++ b/src/System.Private.CoreLib/src/System/IO/File.Windows.cs @@ -0,0 +1,136 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.Win32; +using Microsoft.Win32.SafeHandles; +using System.Runtime.InteropServices; + +namespace System.IO +{ + internal static partial class File + { + internal static bool InternalExists(String path) + { + Interop.Kernel32.WIN32_FILE_ATTRIBUTE_DATA data = new Interop.Kernel32.WIN32_FILE_ATTRIBUTE_DATA(); + int errorCode = FillAttributeInfo(path, ref data, false, true); + + return (errorCode == 0) && (data.fileAttributes != -1) + && ((data.fileAttributes & Interop.Kernel32.FileAttributes.FILE_ATTRIBUTE_DIRECTORY) == 0); + } + + // Returns 0 on success, otherwise a Win32 error code. Note that + // classes should use -1 as the uninitialized state for dataInitialized. + internal static int FillAttributeInfo(String path, ref Interop.Kernel32.WIN32_FILE_ATTRIBUTE_DATA data, bool tryagain, bool returnErrorOnNotFound) + { + int errorCode = 0; + if (tryagain) // someone has a handle to the file open, or other error + { + Interop.Kernel32.WIN32_FIND_DATA findData; + findData = new Interop.Kernel32.WIN32_FIND_DATA(); + + // Remove trailing slash since this can cause grief to FindFirstFile. You will get an invalid argument error + String tempPath = path.TrimEnd(new char[] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar }); + + // For floppy drives, normally the OS will pop up a dialog saying + // there is no disk in drive A:, please insert one. We don't want that. + // SetThreadErrorMode will let us disable this, but we should set the error + // mode back, since this may have wide-ranging effects. + uint oldMode; + bool setThreadErrorModeSuccess = Interop.Kernel32.SetThreadErrorMode(Interop.Kernel32.SEM_FAILCRITICALERRORS, out oldMode); + try + { + bool error = false; + SafeFindHandle handle = Interop.Kernel32.FindFirstFile(tempPath, ref findData); + try + { + if (handle.IsInvalid) + { + error = true; + errorCode = Marshal.GetLastWin32Error(); + + if (errorCode == Interop.Errors.ERROR_FILE_NOT_FOUND || + errorCode == Interop.Errors.ERROR_PATH_NOT_FOUND || + errorCode == Interop.Errors.ERROR_NOT_READY) // floppy device not ready + { + if (!returnErrorOnNotFound) + { + // Return default value for backward compatibility + errorCode = 0; + data.fileAttributes = -1; + } + } + return errorCode; + } + } + finally + { + // Close the Win32 handle + try + { + handle.Dispose(); + } + catch + { + // if we're already returning an error, don't throw another one. + if (!error) + { + throw Win32Marshal.GetExceptionForLastWin32Error(); + } + } + } + } + finally + { + if (setThreadErrorModeSuccess) + Interop.Kernel32.SetThreadErrorMode(oldMode, out oldMode); + } + + // Copy the information to data + data.PopulateFrom(ref findData); + } + else + { + // For floppy drives, normally the OS will pop up a dialog saying + // there is no disk in drive A:, please insert one. We don't want that. + // SetThreadErrorMode will let us disable this, but we should set the error + // mode back, since this may have wide-ranging effects. + bool success = false; + uint oldMode; + bool setThreadErrorModeSuccess = Interop.Kernel32.SetThreadErrorMode(Interop.Kernel32.SEM_FAILCRITICALERRORS, out oldMode); + try + { + success = Interop.Kernel32.GetFileAttributesEx(path, Interop.Kernel32.GET_FILEEX_INFO_LEVELS.GetFileExInfoStandard, ref data); + } + finally + { + if (setThreadErrorModeSuccess) + Interop.Kernel32.SetThreadErrorMode(oldMode, out oldMode); + } + + if (!success) + { + errorCode = Marshal.GetLastWin32Error(); + if (errorCode != Interop.Errors.ERROR_FILE_NOT_FOUND && + errorCode != Interop.Errors.ERROR_PATH_NOT_FOUND && + errorCode != Interop.Errors.ERROR_NOT_READY) // floppy device not ready + { + // In case someone latched onto the file. Take the perf hit only for failure + return FillAttributeInfo(path, ref data, true, returnErrorOnNotFound); + } + else + { + if (!returnErrorOnNotFound) + { + // Return default value for backward compatibility + errorCode = 0; + data.fileAttributes = -1; + } + } + } + } + + return errorCode; + } + } +} diff --git a/src/System.Private.CoreLib/src/System/IO/File.cs b/src/System.Private.CoreLib/src/System/IO/File.cs new file mode 100644 index 000000000..30513c894 --- /dev/null +++ b/src/System.Private.CoreLib/src/System/IO/File.cs @@ -0,0 +1,47 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics; +using System.Security; + +namespace System.IO +{ + internal static partial class File + { + // Tests if a file exists. The result is true if the file + // given by the specified path exists; otherwise, the result is + // false. Note that if path describes a directory, + // Exists will return true. + public static bool Exists(String path) + { + try + { + if (path == null) + return false; + if (path.Length == 0) + return false; + + path = Path.GetFullPath(path); + + // After normalizing, check whether path ends in directory separator. + // Otherwise, FillAttributeInfo removes it and we may return a false positive. + // GetFullPath should never return null + Debug.Assert(path != null, "File.Exists: GetFullPath returned null"); + if (path.Length > 0 && PathInternal.IsDirectorySeparator(path[path.Length - 1])) + { + return false; + } + + return InternalExists(path); + } + catch (ArgumentException) { } + catch (NotSupportedException) { } // Security can throw this on ":" + catch (SecurityException) { } + catch (IOException) { } + catch (UnauthorizedAccessException) { } + + return false; + } + } +} diff --git a/src/System.Private.CoreLib/src/System/Resources/FileBasedResourceGroveler.cs b/src/System.Private.CoreLib/src/System/Resources/FileBasedResourceGroveler.cs index fb48cec1c..07353f4b6 100644 --- a/src/System.Private.CoreLib/src/System/Resources/FileBasedResourceGroveler.cs +++ b/src/System.Private.CoreLib/src/System/Resources/FileBasedResourceGroveler.cs @@ -82,9 +82,6 @@ namespace System.Resources private String FindResourceFile(CultureInfo culture, String fileName) { - throw new NotImplementedException(); - // todo remove the throw and ifdef when File and FileStream are in CoreLib -#if FILE_TYPES_IN_CORELIB Debug.Assert(culture != null, "culture shouldn't be null; check caller"); Debug.Assert(fileName != null, "fileName shouldn't be null; check caller"); @@ -104,7 +101,6 @@ namespace System.Resources return fileName; return null; // give up. -#endif // FILE_TYPES_IN_CORELIB } // Constructs a new ResourceSet for a given file name. The logic in diff --git a/src/System.Private.CoreLib/src/System/Resources/ResourceReader.cs b/src/System.Private.CoreLib/src/System/Resources/ResourceReader.cs index db078c714..7bf3e3d2c 100644 --- a/src/System.Private.CoreLib/src/System/Resources/ResourceReader.cs +++ b/src/System.Private.CoreLib/src/System/Resources/ResourceReader.cs @@ -108,9 +108,6 @@ namespace System.Resources public ResourceReader(String fileName) { - throw new NotImplementedException(); -#if FILE_TYPES_IN_CORELIB - // todo remove the throw and ifdef when File and FileStream are in CoreLib _resCache = new Dictionary<String, ResourceLocator>(FastResourceComparer.Default); _store = new BinaryReader(new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read, DefaultFileStreamBufferSize, FileOptions.RandomAccess), Encoding.UTF8); @@ -123,7 +120,6 @@ namespace System.Resources _store.Dispose(); // If we threw an exception, close the file. throw; } -#endif // FILE_TYPES_IN_CORELIB } public ResourceReader(Stream stream) diff --git a/src/System.Private.Interop/src/System/Runtime/InteropServices/BestFitMappingAttribute.cs b/src/System.Private.CoreLib/src/System/Runtime/InteropServices/BestFitMappingAttribute.cs index e73e64998..a880c62e6 100644 --- a/src/System.Private.Interop/src/System/Runtime/InteropServices/BestFitMappingAttribute.cs +++ b/src/System.Private.CoreLib/src/System/Runtime/InteropServices/BestFitMappingAttribute.cs @@ -2,8 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; - namespace System.Runtime.InteropServices { [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Interface | AttributeTargets.Class | AttributeTargets.Struct, Inherited = false)] @@ -19,4 +17,4 @@ namespace System.Runtime.InteropServices public bool BestFitMapping { get { return _bestFitMapping; } } public bool ThrowOnUnmappableChar; } -} +}
\ No newline at end of file diff --git a/src/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs b/src/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs index d2a9f0c1c..330580c99 100644 --- a/src/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs +++ b/src/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs @@ -297,6 +297,10 @@ namespace System.Runtime // calls to runtime for allocation // [MethodImpl(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhBoxAny")] + public static extern unsafe object RhBoxAny(IntPtr pData, IntPtr pEEType); + + [MethodImpl(MethodImplOptions.InternalCall)] [RuntimeImport(RuntimeLibrary, "RhNewObject")] internal static extern object RhNewObject(EETypePtr pEEType); diff --git a/src/System.Private.Interop/src/System.Private.Interop.csproj b/src/System.Private.Interop/src/System.Private.Interop.csproj index 607c33957..431cc60e2 100644 --- a/src/System.Private.Interop/src/System.Private.Interop.csproj +++ b/src/System.Private.Interop/src/System.Private.Interop.csproj @@ -87,7 +87,6 @@ <ItemGroup> <Compile Include="System\Runtime\InteropServices\AllowReversePInvokeCallsAttribute.cs" /> <Compile Include="System\Runtime\InteropServices\ArrayWithOffset.cs" /> - <Compile Include="System\Runtime\InteropServices\BestFitMappingAttribute.cs" /> <Compile Include="System\Runtime\InteropServices\BStrWrapper.cs" /> <Compile Include="System\Runtime\InteropServices\ClassInterfaceAttribute.cs" /> <Compile Include="System\Runtime\InteropServices\ClassInterfaceType.cs" /> diff --git a/src/System.Private.Interop/src/TypeForwarders.cs b/src/System.Private.Interop/src/TypeForwarders.cs index b9e6dd0da..badf60477 100644 --- a/src/System.Private.Interop/src/TypeForwarders.cs +++ b/src/System.Private.Interop/src/TypeForwarders.cs @@ -10,4 +10,5 @@ [assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Runtime.InteropServices.InAttribute))] [assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.DllNotFoundException))] [assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.EntryPointNotFoundException))] -[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Runtime.InteropServices.MarshalDirectiveException))]
\ No newline at end of file +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Runtime.InteropServices.MarshalDirectiveException))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Runtime.InteropServices.BestFitMappingAttribute))]
\ No newline at end of file diff --git a/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/DebugFuncEval.cs b/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/DebugFuncEval.cs index 401617c8b..1e6790327 100644 --- a/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/DebugFuncEval.cs +++ b/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/DebugFuncEval.cs @@ -45,13 +45,14 @@ namespace Internal.Runtime.TypeLoader // Invoke the target method Internal.Runtime.CallInterceptor.CallInterceptor.MakeDynamicCall(targetAddress, dynamicCallSignature, arguments); - // TODO: We should be able to handle arbitrary return type - object returnValue = arguments.GetVar<int>(0); - GCHandle returnValueHandle = GCHandle.Alloc(returnValue); - - // Signal to the debugger the func eval completes unsafe { + // Box the return + IntPtr input = arguments.GetAddressOfVarData(0); + object returnValue = RuntimeImports.RhBoxAny(input, (IntPtr)param.types[0].ToEETypePtr()); + GCHandle returnValueHandle = GCHandle.Alloc(returnValue); + + // Signal to the debugger the func eval completes FuncEvalCompleteCommand* funcEvalCompleteCommand = stackalloc FuncEvalCompleteCommand[1]; funcEvalCompleteCommand->commandCode = 0; funcEvalCompleteCommand->returnAddress = (long)GCHandle.ToIntPtr(returnValueHandle); |