Skip to content
This repository has been archived by the owner on Jan 23, 2023. It is now read-only.

Commit

Permalink
Implement struct marshalling via IL Stubs instead of via Field… (#26340)
Browse files Browse the repository at this point in the history
* First pass on adding support for byref field homes to il marshalers.

Remove dead code for calculating managed field size.

Add field IL marshal infra up to MarshalInfo::GenerateFieldIL.

Add preliminary changes for MarshalInfo ctor to have the logic handle field marshalling logic. Still need to handle WinRT struct field logic correctly.

First shot at handling fields of WinRT structs.

Cleanup

Clean up entrypoints

Fix cleanup marshal emit.

Disable specific paths on struct marshal stubs.

Implement emitting full struct marshalling stub.

Add StructMarshalInteropStub method name.

Add NDirect::CreateMarshalILStub

Get byvalue StructMarshalling/PInvoke tests passing excluding missing ILMarshalers (ByValArray and ByValTStr).

Correctly classify struct marshal stubs as struct marshal stubs instead of PInvoke stubs.

Implement UnmanagedType.ByValArray IL marshaler.

Implement ILMarshaler equivalent for ansi-char fixed arrays.

Fix parameter mismatch in Native->CLR direction for struct marshalling.

Implement ByValTStr marshalling. Support unaligned fields in IL stubs.

Load CleanupWorkList from param list if in a struct marshalling stub

Implement SafeHandle and CriticalHandle field marshalling in IL struct stubs

Fix handle field marshalers. Add error reporting in struct field IL marshalers consistent with old FieldMarshaler error reporting.

Convert Array-of-nonblittable-struct marshalling to use IL stubs.

Convert LayoutClass marshalling to use IL stubs.
Fix marshalling of LayoutClass fields in structs.

Add non-blittable fixed buffer assert in the struct IL stub path.

Implement Marshal APIs via the IL stubs.

Fix default char marshaler selection.

Move hidden-length-array marshalling over to struct marshalling stubs.

Convert struct marshal IL stub users to use helper that will automatically cleanup on failure to marshal.

Match MarshalInfo::MarshalInfo behavior to ParseNativeType for fields.

Remove old FieldMarshaler-based marshalling.

Fix signed/unsigned mismatch.

Fix IsFieldScenario on non-COMINTEROP plaforms

Fix off-Windows build.

Handle automatic partial cleanup of struct marshaling entirely within the struct stub.

Remove now-unused ValueClassMarshaler. Move DateMarshaler to managed since it doesn't need to be an FCall.

Error out on recursive struct definitions in the IL stub generation as we did in the field marshalling.

Remove FieldMarshalers and replace with a significantly simpler structure (NativeFieldDescriptor) that stores only the needed info for determining validity in WinRT, blittability, and calling convention classification.

This will save 4/8 bytes for every field in a non-auto-layout structure or class loaded into the runtime.

Add explicit test for recursive native layout.

Allow marshalling  as UnmanagedType.Error on all platforms (on non-Windows the behavior matches UnmanagedType.I4).

Collapse common primitive marshalling cases together.

Disable WinRT parameter/retval-only marshalers in field scenarios.

Revert "Collapse common primitive marshalling cases together."

This reverts commit e73b78a.

Fix error marshalling off Windows for uint.

Disable LPStruct marshalling in structs.

Disable copy-constructor marshaler in the field scenario.

Match error messages between MarshalInfo::MarshalInfo and ParseNativeType in the field scenario.

Refactor managed-sequential check out of ParseNativeType.

Remove invalid MARSHAL_TYPE_GENERIC_U8 references.

Add override specifier.

Change ParseNativeType to use MarshalInfo::MarshalInfo to calculate field marshalling info instead of maintaining two native field classification functions.

Clean up native field categories. Remove nsenums.h since it is now unused.

Move CheckIfDisqualifiedFromManagedSequential to class.cpp.

Encapsulate stub flags for struct stubs. Read the BestFitAttribute once for the type instead of per field.

Fix perf regression in by-val arrays of non-blittable structures by caching the MethodDesc* to the struct stub in the "managed marshaler" structure. Now we have a perf improvement!

Fix memory leak in sig creation.

Keep compile-time information for struct stubs around as long as the owning loader allocator (instead of leaking).

Allocate the signature on the same heap as the IL stubs so it shares the same lifetime.

Fix build with fragile NGen support enabled so as to not break partners.

Add missing native field descriptors.

Fix clang build.

Only assert if we're emitting IL (so we don't assert during type-load).

Determine desciptor for pointer-sized fields based on target not host pointer size.

Don't emit IL stubs that call struct stubs into R2R images since there's not a good way for us to emit the token.

Fix tracing test failures.

Force field marshaling to not stackalloc.

Cache Sytem.RuntimeMethodInfoStub instances created in the VM in the MethodDesc's owning LoaderAllocator.

Struct marshal stubs don't have an MethodDesc context arg.

Copy FieldDesc on NFD assignment.

Fix initialization of stubMethodInfoCache lock owner.

Fix alignment calculation of decimal fields and decimal array fields in NFDs.

Fix Crst leveling.

Enable handling decimal-as-currency fields accurately off-Windows.

Fix deadlock where two threads are both trying to generate an IL stub for the same P/Invoke and one of the parameters needs a struct stub generated.

Fix incorrect check for if we need a struct marshal stub in some of the variant/array cases.

We never need to promote a field value to 8 bytes.

Fix issue with recursive pointer fields.

Shortcut blittable types in stubhelpers.

Use LDFTN + PREPARE_NONVIRUTAL_CALLSITE_USING_CODE instead of LDTOKEN + GetInternalToken.

Revert "Fix Crst leveling."

This reverts commit 1d8e56e.

Revert "Fix initialization of stubMethodInfoCache lock owner."

This reverts commit a095390.

Revert "Cache Sytem.RuntimeMethodInfoStub instances created in the VM in the MethodDesc's owning LoaderAllocator."

This reverts commit 7266538.

Fix case where struct marshal stub is unused in native-only mashalling paths.

PR Feedback.

Clean up terenary statement in dispatchinfo.cpp

Cleanup ILStubResolver::AllocGeneratedIL a little bit.

* Cleanup LayoutClass test.

* Encapsulate getting the entrypoint of a struct marshal stub.

* VTHACK_NONBLITTABLERECORD is only used for hidden-length array marshalling, and VT_RECORD is not used in hidden-length array marshalling.

* Align new entries in mtypes.h

* Whitespace and field ordering changes.
  • Loading branch information
jkoritzinsky authored Oct 24, 2019
1 parent 0a048b8 commit 549d532
Show file tree
Hide file tree
Showing 53 changed files with 3,283 additions and 6,566 deletions.
9 changes: 9 additions & 0 deletions src/System.Private.CoreLib/Resources/Strings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -2401,6 +2401,15 @@
<data name="Interop_Marshal_Unmappable_Char" xml:space="preserve">
<value>Cannot marshal: Encountered unmappable character.</value>
</data>
<data name="Interop_Marshal_SafeHandle_InvalidOperation" xml:space="preserve">
<value>Structures containing SafeHandle fields are not allowed in this operation.</value>
</data>
<data name="Interop_Marshal_CannotCreateSafeHandleField" xml:space="preserve">
<value>SafeHandle fields cannot be created from an unmanaged handle.</value>
</data>
<data name="Interop_Marshal_CannotCreateCriticalHandleField" xml:space="preserve">
<value>CriticalHandle fields cannot be created from an unmanaged handle.</value>
</data>
<data name="InvalidCast_CannotCastNullToValueType" xml:space="preserve">
<value>Null object cannot be converted to a value type.</value>
</data>
Expand Down
188 changes: 152 additions & 36 deletions src/System.Private.CoreLib/src/System/StubHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,75 @@ internal static void ClearNative(IntPtr pNative)
{
Interop.Ole32.CoTaskMemFree(pNative);
}

internal static unsafe void ConvertFixedToNative(int flags, string strManaged, IntPtr pNativeBuffer, int length)
{
int numChars = strManaged.Length;
if (numChars >= length)
{
numChars = length - 1;
}

byte* buffer = (byte*)pNativeBuffer;

// Flags defined in ILFixedCSTRMarshaler::EmitConvertContentsCLRToNative(ILCodeStream* pslILEmit).
bool throwOnUnmappableChar = 0 != (flags >> 8);
bool bestFit = 0 != (flags & 0xFF);
uint defaultCharUsed = 0;

int cbWritten;

fixed (char* pwzChar = strManaged)
{
#if PLATFORM_WINDOWS
cbWritten = Interop.Kernel32.WideCharToMultiByte(
Interop.Kernel32.CP_ACP,
bestFit ? 0 : Interop.Kernel32.WC_NO_BEST_FIT_CHARS,
pwzChar,
numChars,
buffer,
length,
IntPtr.Zero,
throwOnUnmappableChar ? new IntPtr(&defaultCharUsed) : IntPtr.Zero);
#else
cbWritten = Encoding.UTF8.GetBytes(pwzChar, numChars, buffer, length);
#endif
}

if (defaultCharUsed != 0)
{
throw new ArgumentException(SR.Interop_Marshal_Unmappable_Char);
}

if (cbWritten == (int)length)
{
cbWritten--;
}

buffer[cbWritten] = 0;
}

internal static unsafe string ConvertFixedToManaged(IntPtr cstr, int length)
{
int allocSize = length + 2;
if (allocSize < length)
{
throw new OutOfMemoryException();
}
Span<sbyte> originalBuffer = new Span<sbyte>((byte*)cstr, length);

Span<sbyte> tempBuf = stackalloc sbyte[allocSize];

originalBuffer.CopyTo(tempBuf);
tempBuf[length - 1] = 0;
tempBuf[length] = 0;
tempBuf[length + 1] = 0;

fixed (sbyte* buffer = tempBuf)
{
return new string(buffer, 0, string.strlen((byte*)buffer));
}
}
} // class CSTRMarshaler

internal static class UTF8Marshaler
Expand Down Expand Up @@ -443,23 +512,17 @@ internal static void ClearNative(IntPtr pNative)
}
} // class AnsiBSTRMarshaler

internal static class WSTRBufferMarshaler
internal static class FixedWSTRMarshaler
{
internal static IntPtr ConvertToNative(string strManaged)
internal static unsafe void ConvertToNative(string? strManaged, IntPtr nativeHome, int length)
{
Debug.Fail("NYI");
return IntPtr.Zero;
}
ReadOnlySpan<char> managed = strManaged;
Span<char> native = new Span<char>((char*)nativeHome, length);

internal static string? ConvertToManaged(IntPtr bstr)
{
Debug.Fail("NYI");
return null;
}
int numChars = Math.Min(managed.Length, length - 1);

internal static void ClearNative(IntPtr pNative)
{
Debug.Fail("NYI");
managed.Slice(0, numChars).CopyTo(native);
native[numChars] = '\0';
}
} // class WSTRBufferMarshaler

Expand Down Expand Up @@ -573,26 +636,45 @@ internal static class ObjectMarshaler

#endif // FEATURE_COMINTEROP

internal static class ValueClassMarshaler
internal sealed class HandleMarshaler
{
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void ConvertToNative(IntPtr dst, IntPtr src, IntPtr pMT, ref CleanupWorkListElement pCleanupWorkList);
internal static unsafe IntPtr ConvertSafeHandleToNative(SafeHandle? handle, bool hasCleanupWorkList, ref CleanupWorkListElement? cleanupWorkList)
{
if (!hasCleanupWorkList)
{
throw new InvalidOperationException(SR.Interop_Marshal_SafeHandle_InvalidOperation);
}

[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void ConvertToManaged(IntPtr dst, IntPtr src, IntPtr pMT);
if (handle is null)
{
throw new ArgumentNullException(nameof(handle));
}

[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void ClearNative(IntPtr dst, IntPtr pMT);
} // class ValueClassMarshaler
return StubHelpers.AddToCleanupList(ref cleanupWorkList, handle);
}

internal static unsafe void ThrowSafeHandleFieldChanged()
{
throw new NotSupportedException(SR.Interop_Marshal_CannotCreateSafeHandleField);
}

internal static unsafe void ThrowCriticalHandleFieldChanged()
{
throw new NotSupportedException(SR.Interop_Marshal_CannotCreateCriticalHandleField);
}
}

internal static class DateMarshaler
{
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern double ConvertToNative(DateTime managedDate);
internal static double ConvertToNative(DateTime managedDate)
{
return managedDate.ToOADate();
}

// The return type is really DateTime but we use long to avoid the pain associated with returning structures.
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern long ConvertToManaged(double nativeDate);
internal static long ConvertToManaged(double nativeDate)
{
return DateTime.DoubleDateToTicks(nativeDate);
}
} // class DateMarshaler

#if FEATURE_COMINTEROP
Expand Down Expand Up @@ -640,6 +722,7 @@ internal struct MarshalerState
#pragma warning disable CA1823 // not used by managed code
private IntPtr m_pElementMT;
private IntPtr m_Array;
private IntPtr m_pManagedNativeArrayMarshaler;
private int m_NativeDataValid;
private int m_BestFitMap;
private int m_ThrowOnUnmappableChar;
Expand All @@ -648,7 +731,7 @@ internal struct MarshalerState
}

[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void CreateMarshaler(IntPtr pMarshalState, IntPtr pMT, int dwFlags);
internal static extern void CreateMarshaler(IntPtr pMarshalState, IntPtr pMT, int dwFlags, IntPtr pManagedMarshaler);

[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void ConvertSpaceToNative(IntPtr pMarshalState, ref object pManagedHome, IntPtr pNativeHome);
Expand All @@ -664,17 +747,38 @@ internal static extern void ConvertSpaceToManaged(IntPtr pMarshalState, ref obje
internal static extern void ConvertContentsToManaged(IntPtr pMarshalState, ref object pManagedHome, IntPtr pNativeHome);

[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void ClearNative(IntPtr pMarshalState, IntPtr pNativeHome, int cElements);
internal static extern void ClearNative(IntPtr pMarshalState, ref object pManagedHome, IntPtr pNativeHome, int cElements);

[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void ClearNativeContents(IntPtr pMarshalState, IntPtr pNativeHome, int cElements);
internal static extern void ClearNativeContents(IntPtr pMarshalState, ref object pManagedHome, IntPtr pNativeHome, int cElements);
} // class MngdNativeArrayMarshaler

internal static class MngdFixedArrayMarshaler
{
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void CreateMarshaler(IntPtr pMarshalState, IntPtr pMT, int dwFlags, int cElements, IntPtr pManagedMarshaler);

[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void ConvertSpaceToNative(IntPtr pMarshalState, ref object pManagedHome, IntPtr pNativeHome);

[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void ConvertContentsToNative(IntPtr pMarshalState, ref object pManagedHome, IntPtr pNativeHome);

[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void ConvertSpaceToManaged(IntPtr pMarshalState, ref object pManagedHome, IntPtr pNativeHome);

[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void ConvertContentsToManaged(IntPtr pMarshalState, ref object pManagedHome, IntPtr pNativeHome);

[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void ClearNativeContents(IntPtr pMarshalState, ref object pManagedHome, IntPtr pNativeHome);
} // class MngdFixedArrayMarshaler

#if FEATURE_COMINTEROP
internal static class MngdSafeArrayMarshaler
{
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void CreateMarshaler(IntPtr pMarshalState, IntPtr pMT, int iRank, int dwFlags);
internal static extern void CreateMarshaler(IntPtr pMarshalState, IntPtr pMT, int iRank, int dwFlags, IntPtr pManagedMarshaler);

[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void ConvertSpaceToNative(IntPtr pMarshalState, ref object pManagedHome, IntPtr pNativeHome);
Expand All @@ -695,7 +799,7 @@ internal static class MngdSafeArrayMarshaler
internal static class MngdHiddenLengthArrayMarshaler
{
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void CreateMarshaler(IntPtr pMarshalState, IntPtr pMT, IntPtr cbElementSize, ushort vt);
internal static extern void CreateMarshaler(IntPtr pMarshalState, IntPtr pMT, IntPtr cbElementSize, ushort vt, IntPtr pManagedMarshaler);

[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void ConvertSpaceToNative(IntPtr pMarshalState, ref object pManagedHome, IntPtr pNativeHome);
Expand Down Expand Up @@ -971,7 +1075,8 @@ private unsafe IntPtr ConvertArrayToNative(object pManagedHome, int dwFlags)
MngdNativeArrayMarshaler.CreateMarshaler(
pvArrayMarshaler,
IntPtr.Zero, // not needed as we marshal primitive VTs only
dwArrayMarshalerFlags);
dwArrayMarshalerFlags,
IntPtr.Zero); // not needed as we marshal primitive VTs only

IntPtr pNativeHome;
IntPtr pNativeHomeAddr = new IntPtr(&pNativeHome);
Expand Down Expand Up @@ -1460,6 +1565,17 @@ internal struct NativeVariant
#endif
} // struct NativeVariant

// This NativeDecimal type is very similar to the System.Decimal type, except it requires an 8-byte alignment
// like the native DECIMAL type instead of the 4-byte requirement of the System.Decimal type.
[StructLayout(LayoutKind.Sequential)]
internal struct NativeDecimal
{
private ushort reserved;
private ushort signScale;
private uint hi32;
private ulong lo64;
}

internal abstract class CleanupWorkListElement
{
private CleanupWorkListElement? m_Next;
Expand All @@ -1476,7 +1592,7 @@ public void Destroy()
}
}

public static void AddToCleanupList(ref CleanupWorkListElement list, CleanupWorkListElement newElement)
public static void AddToCleanupList(ref CleanupWorkListElement? list, CleanupWorkListElement newElement)
{
if (list == null)
{
Expand Down Expand Up @@ -1560,14 +1676,14 @@ internal static class StubHelpers
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void ThrowInteropParamException(int resID, int paramIdx);

internal static IntPtr AddToCleanupList(ref CleanupWorkListElement pCleanupWorkList, SafeHandle handle)
internal static IntPtr AddToCleanupList(ref CleanupWorkListElement? pCleanupWorkList, SafeHandle handle)
{
SafeHandleCleanupWorkListElement element = new SafeHandleCleanupWorkListElement(handle);
CleanupWorkListElement.AddToCleanupList(ref pCleanupWorkList, element);
return element.AddRef();
}

internal static void KeepAliveViaCleanupList(ref CleanupWorkListElement pCleanupWorkList, object obj)
internal static void KeepAliveViaCleanupList(ref CleanupWorkListElement? pCleanupWorkList, object obj)
{
KeepAliveCleanupWorkListElement element = new KeepAliveCleanupWorkListElement(obj);
CleanupWorkListElement.AddToCleanupList(ref pCleanupWorkList, element);
Expand Down Expand Up @@ -1713,7 +1829,7 @@ internal static void CheckStringLength(uint length)
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern unsafe void FmtClassUpdateCLRInternal(object obj, byte* pNative);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern unsafe void LayoutDestroyNativeInternal(byte* pNative, IntPtr pMT);
internal static extern unsafe void LayoutDestroyNativeInternal(object obj, byte* pNative);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern object AllocateInternal(IntPtr typeHandle);

Expand Down
23 changes: 12 additions & 11 deletions src/debug/daccess/nidump.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

#include <comcallablewrapper.h>
#include <gcdump.h>
#include <fieldmarshaler.h>

#if !defined(FEATURE_CORESYSTEM)
#include <algorithm>
Expand Down Expand Up @@ -8383,36 +8384,36 @@ NativeImageDumper::DumpEEClassForMethodTable( PTR_MethodTable mt )
VERBOSE_TYPES );
DisplayWriteFieldInt( m_numCTMFields, eecli->m_numCTMFields,
EEClassLayoutInfo, VERBOSE_TYPES );
PTR_FieldMarshaler fmArray = eecli->GetFieldMarshalers();
DisplayWriteFieldAddress( m_pFieldMarshalers,
PTR_NativeFieldDescriptor fmArray = eecli->GetNativeFieldDescriptors();
DisplayWriteFieldAddress( m_pNativeFieldDescriptors,
DPtrToPreferredAddr(fmArray),
eecli->m_numCTMFields
* MAXFIELDMARSHALERSIZE,
* sizeof(NativeFieldDescriptor),
EEClassLayoutInfo, VERBOSE_TYPES );
/* REVISIT_TODO Wed 03/22/2006
* Dump the various types of FieldMarshalers.
* Dump the various types of NativeFieldDescriptors.
*/
#if 0
DisplayStartArrayWithOffset( m_pFieldMarshalers, NULL,
DisplayStartArrayWithOffset( m_pNativeFieldDescriptors, NULL,
EEClassLayoutInfo, VERBOSE_TYPES );
for( unsigned i = 0; i < eecli->m_numCTMFields; ++i )
{
/* REVISIT_TODO Wed 03/22/2006
* Try to display the type of the field marshaler in the future.
*/
PTR_FieldMarshaler current = fmArray + i;
DisplayStartStructure( "FieldMarshaler",
PTR_NativeFieldDescriptor current = fmArray + i;
DisplayStartStructure( "NativeFieldDescriptor",
DPtrToPreferredAddr(current),
sizeof(*current), VERBOSE_TYPES );
WriteFieldFieldDesc( m_pFD, PTR_FieldDesc(TO_TADDR(current->m_pFD)),
FieldMarshaler, VERBOSE_TYPES );
DisplayWriteFieldInt( m_dwExternalOffset,
current->m_dwExternalOffset, FieldMarshaler,
NativeFieldDescriptor, VERBOSE_TYPES );
DisplayWriteFieldInt( m_offset,
current->m_offset, NativeFieldDescriptor,
VERBOSE_TYPES );
DisplayEndStructure( VERBOSE_TYPES ); //FieldMarshaler
}

DisplayEndArray( "Number of FieldMarshalers", VERBOSE_TYPES ); //m_pFieldMarshalers
DisplayEndArray( "Number of NativeFieldDescriptors", VERBOSE_TYPES ); //m_pNativeFieldDescriptors
#endif

DisplayEndStructure( EECLASSES ); //LayoutInfo
Expand Down
2 changes: 2 additions & 0 deletions src/dlls/mscorrc/mscorrc.rc
Original file line number Diff line number Diff line change
Expand Up @@ -758,6 +758,8 @@ BEGIN
IDS_CLASSLOAD_WRONGCPU "Could not load file or assembly '%1'. This assembly was compiled for a different processor."

IDS_CANNOT_MARSHAL "Type '%1' cannot be marshaled as an unmanaged structure; no meaningful size or offset can be computed."

IDS_CANNOT_MARSHAL_RECURSIVE_DEF "Type '%1' cannot be marshaled as an unmanaged structure; its native layout contains a recursive definition so no meaningful size can be computed."

IDS_INVALID_REDIM "Illegal attempt to replace or redimension a fixed or locked SafeArray."

Expand Down
1 change: 1 addition & 0 deletions src/dlls/mscorrc/resource.h
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,7 @@
#define IDS_EE_ADUNLOAD_CANT_UNWIND_THREAD 0x1769

#define IDS_CANNOT_MARSHAL 0x1770
#define IDS_CANNOT_MARSHAL_RECURSIVE_DEF 0x1771
#define IDS_EE_HASH_VAL_FAILED 0x1772


Expand Down
Loading

0 comments on commit 549d532

Please sign in to comment.