Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
8f13d29
Add IMetaDataImport COM wrapper over MetadataReader for cDAC no-fallb…
Apr 16, 2026
5d8cac0
Trim IMetaDataImport to consumer-used methods, add 8 new implementations
Apr 16, 2026
1618dda
Refactor MetadataImportWrapper to follow cDAC fallback pattern
Apr 16, 2026
5530d5b
Remove CatchHR helper and inline try/catch blocks
Apr 16, 2026
3823ebe
Simplify catch blocks to match cDAC convention
Apr 16, 2026
18d3356
Replace goto Done with thrown exception in EnumGenericParams
Apr 16, 2026
c741b29
Rename MetadataImportWrapper to MetaDataImportImpl
Apr 16, 2026
ba92ef7
Add legacy fallback to all unimplemented methods
Apr 16, 2026
f896ffe
Align MetaDataImportImpl with cDAC style conventions
Apr 16, 2026
eed9f94
Address PR review feedback
Apr 17, 2026
cbe1988
Add IMetaDataAssemblyImport support to MetaDataImportImpl
Apr 17, 2026
a6edbab
Fix premature return and expand DEBUG validation in IMetaDataAssembly…
Apr 17, 2026
5769e64
Address PR review feedback
Apr 17, 2026
0b3961a
Remove GetInterface from no-fallback allowlist
Apr 17, 2026
b1084ef
Implement remaining PR review suggestions
Apr 17, 2026
7f587ed
Add comprehensive blob byte-level verification to #if DEBUG blocks
Apr 17, 2026
eafb70b
Return CLDB_S_TRUNCATION when string buffer is too small
Apr 17, 2026
43b8ec6
Fix native semantic parity in MetaDataImport methods
Apr 17, 2026
7b85527
Add tests for MetaDataImport semantic parity fixes
Apr 17, 2026
2175c4a
Add MetaDataImport dump-based integration tests
Apr 18, 2026
c040832
Fix GetUserString raw byte access and IMetaDataImport2 vtable overread
Apr 18, 2026
80006f0
Fix MetaDataImportImpl double-QI vtable AV for EnumGenericParams
Apr 18, 2026
28196e2
Remove null-forgiving operators from MetaDataImportImpl
Apr 19, 2026
ff1bb43
Make MetadataReader a required parameter in MetaDataImportImpl
Apr 19, 2026
b2af8f6
Fix dump test build: Assert.NotNull returns void in xUnit v2
Apr 19, 2026
4613a7d
Fix MetaDataImportImpl parity gaps: enum dispatch, field offsets, par…
Apr 19, 2026
391b895
Make _cdacEnumHandles thread-safe and use OutputBufferHelpers in asse…
Apr 20, 2026
3f3aad5
Add backward compat for integer contract versions in JSON descriptor
Apr 20, 2026
3cc6cb5
Revert "Add backward compat for integer contract versions in JSON des…
Apr 20, 2026
728f40e
Address PR review feedback
Apr 20, 2026
ef8d77d
Simplify COM interop: remove redundant QI and null-forgiving operator
Apr 21, 2026
7090126
Address PR review feedback (round 2)
Apr 21, 2026
636ea30
Fix dump test build: use IMetaDataImport interface type
Apr 22, 2026
144add0
Remove unused using and fix misleading comment
Apr 22, 2026
817457d
Add CopyStringToBuffer overload with out bool truncated
Apr 22, 2026
434b487
comment nits
Apr 27, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ public sealed unsafe partial class ClrDataModule : ICustomQueryInterface, IXCLRD
// This is an IUnknown pointer for the legacy implementation
private readonly nint _legacyModulePointer;

private MetaDataImportImpl? _metaDataImportImpl;

public ClrDataModule(TargetPointer address, Target target, IXCLRDataModule? legacyImpl)
{
_address = address;
Expand All @@ -49,19 +51,70 @@ public ClrDataModule(TargetPointer address, Target target, IXCLRDataModule? lega

private const uint CORDEBUG_JIT_DEFAULT = 0x1;
private const uint CORDEBUG_JIT_DISABLE_OPTIMIZATION = 0x3;
private static readonly Guid IID_IMetaDataImport = Guid.Parse("7DAC8207-D3AE-4c75-9B67-92801A497D44");

CustomQueryInterfaceResult ICustomQueryInterface.GetInterface(ref Guid iid, out nint ppv)
{
ppv = default;
if (!LegacyFallbackHelper.CanFallback() || _legacyModulePointer == 0)
return CustomQueryInterfaceResult.NotHandled;

// Legacy DAC implementation of IXCLRDataModule handles QIs for IMetaDataImport by creating and
// passing out an implementation of IMetaDataImport. Note that it does not do COM aggregation.
// It simply returns a completely separate object. See ClrDataModule::QueryInterface in task.cpp
if (iid == IID_IMetaDataImport && Marshal.QueryInterface(_legacyModulePointer, iid, out ppv) >= 0)
// The returned MetaDataImportImpl also implements IMetaDataImport2 and IMetaDataAssemblyImport,
// so consumers can QI the returned object for those interfaces as well.
//
// IMPORTANT: Some consumers (e.g. ClrMD) QI for IMetaDataImport but then access IMetaDataImport2
// vtable slots beyond the IMetaDataImport vtable boundary. This works with native C++ COM objects
// (where the vtable for IMetaDataImport and IMetaDataImport2 is unified) but breaks with managed
// [GeneratedComInterface] CCWs which create separate vtables per interface. To handle this, we
// always return the IMetaDataImport2 vtable pointer when asked for IMetaDataImport. Since
// IMetaDataImport2 inherits from IMetaDataImport, the first slots are identical.
if (iid == typeof(IMetaDataImport).GUID)
{
MetaDataImportImpl? wrapper = _metaDataImportImpl;
if (wrapper is null)
{
MetadataReader? reader = null;
IMetaDataImport? legacyImport = null;

try
{
ILoader loader = _target.Contracts.Loader;
Contracts.ModuleHandle moduleHandle = loader.GetModuleHandleFromModulePtr(_address);
reader = _target.Contracts.EcmaMetadata.GetMetadata(moduleHandle);
}
catch
{
}

try
{
Guid iidMetaDataImport = typeof(IMetaDataImport).GUID;
if (_legacyModulePointer != 0 && Marshal.QueryInterface(_legacyModulePointer, iidMetaDataImport, out nint ppMdi) >= 0)
{
legacyImport = ComInterfaceMarshaller<IMetaDataImport>.ConvertToManaged((void*)ppMdi);
Marshal.Release(ppMdi);
}
}
catch
{
}
Comment thread
max-charlamb marked this conversation as resolved.

if (reader is null)
return CustomQueryInterfaceResult.NotHandled;

Comment thread
max-charlamb marked this conversation as resolved.
wrapper = new MetaDataImportImpl(reader, legacyImport);
Comment thread
max-charlamb marked this conversation as resolved.
_metaDataImportImpl ??= wrapper;
wrapper = _metaDataImportImpl;
Comment thread
max-charlamb marked this conversation as resolved.
Comment thread
max-charlamb marked this conversation as resolved.
}
Comment thread
max-charlamb marked this conversation as resolved.

nint pUnk = (nint)ComInterfaceMarshaller<IMetaDataImport2>.ConvertToUnmanaged(wrapper);

// ConvertToUnmanaged returns a COM pointer for IMetaDataImport2.
// We return this directly as ppv so that consumers (e.g. ClrMD) that QI for
// IMetaDataImport but access IMetaDataImport2 vtable slots get the full vtable.
ppv = pUnk;
return CustomQueryInterfaceResult.Handled;
}

return CustomQueryInterfaceResult.NotHandled;
}
Expand Down
Loading
Loading