Metadata A Pi
Metadata A Pi
Metadata API
This document specifies the API for emitting and importing metadata for the Common
Language Runtime (CLR). This API is unmanaged and intended for use by compilers
and loaders – low-level tools that require fast access to metadata with a minimum of
assistance for traversing relationships (such as the class hierarchy) or for
manipulating collections (such as members on a class)
Browsers and other tools, seeking a higher-level API, may instead use the managed
Reflection interfaces, specified separately
Page 1
Metadata API
Page 2
Metadata API
3.2.3 DefineCustomAttribute........................................................................28
3.2.4 SetCustomAttributeValue....................................................................29
3.3 Building Type Definitions..........................................................................29
3.3.1 DefineTypeDef.....................................................................................29
3.3.2 SetTypeDefProps.................................................................................30
3.4 Declaring and Defining Members..............................................................31
3.4.1 DefineMethod......................................................................................31
3.4.2 SetMethodProps..................................................................................32
3.4.3 DefineField.......................................................................................... 33
3.4.4 SetFieldProps.......................................................................................34
3.4.5 DefineNestedType...............................................................................34
3.4.6 DefineParam........................................................................................35
3.4.7 SetParamProps....................................................................................36
3.4.8 DefineMethodImpl...............................................................................36
3.4.9 SetRVA................................................................................................ 37
3.4.10 SetFieldRVA.........................................................................................37
3.4.11 DefinePinvokeMap...............................................................................38
3.4.12 SetPinvokeMap....................................................................................38
3.4.13 SetFieldMarshal...................................................................................38
3.5 Building Type and Member References.....................................................39
3.5.1 DefineTypeRefByName........................................................................39
3.5.2 DefineImportType................................................................................40
3.5.3 DefineMemberRef................................................................................40
3.5.4 DefineImportMember..........................................................................41
3.5.5 DefineModuleRef.................................................................................42
3.5.6 SetParent............................................................................................. 43
3.6 Declaring Events and Properties...............................................................43
3.6.1 DefineProperty....................................................................................43
3.6.2 SetPropertyProps.................................................................................44
3.6.3 DefineEvent.........................................................................................45
3.6.4 SetEventProps.....................................................................................46
3.7 Specifying Layout Information for a Class.................................................47
3.7.1 SetClassLayout....................................................................................47
3.8 Miscellaneous............................................................................................ 48
3.8.1 GetTokenFromSig................................................................................48
3.8.2 GetTokenFromTypeSpec.....................................................................48
Page 3
Metadata API
3.8.3 DefineUserString.................................................................................48
3.8.4 DeleteToken........................................................................................49
3.9 Order of Emission......................................................................................49
4 MetaDataImport.............................................................................................. 52
4.1 Enumerating Collections...........................................................................52
4.1.1 CloseEnum Method..............................................................................53
4.1.2 CountEnum Method.............................................................................53
4.1.3 ResetEnum..........................................................................................53
4.1.4 IsValidToken........................................................................................53
4.1.5 EnumTypeDefs....................................................................................54
4.1.6 EnumInterfaceImpls............................................................................54
4.1.7 EnumMembers....................................................................................54
4.1.8 EnumMembersWithName....................................................................55
4.1.9 EnumMethods......................................................................................55
4.1.10 EnumMethodsWithName.....................................................................56
4.1.11 EnumUnresolvedMethods....................................................................56
4.1.12 EnumMethodSemantics.......................................................................57
4.1.13 EnumFields..........................................................................................57
4.1.14 EnumFieldsWithName.........................................................................57
4.1.15 EnumParams.......................................................................................58
4.1.16 EnumMethodImpls...............................................................................58
4.1.17 EnumProperties...................................................................................59
4.1.18 EnumEvents........................................................................................59
4.1.19 EnumTypeRefs....................................................................................59
4.1.20 EnumMemberRefs...............................................................................60
4.1.21 EnumModuleRefs.................................................................................60
4.1.22 EnumCustomAttributes.......................................................................61
4.1.23 EnumSignatures..................................................................................61
4.1.24 EnumTypeSpecs..................................................................................61
4.1.25 EnumUserStrings.................................................................................62
4.2 Finding a Specific Item in Metadata..........................................................62
4.2.1 FindTypeDefByName...........................................................................62
4.2.2 FindMember........................................................................................63
4.2.3 FindMethod..........................................................................................63
4.2.4 FindField.............................................................................................. 64
4.2.5 FindMemberRef...................................................................................64
Page 4
Metadata API
4.2.6 FindTypeRef........................................................................................65
4.3 Obtaining Properties of a Specified Object................................................65
4.3.1 GetScopeProps....................................................................................65
4.3.2 GetModuleFromScope.........................................................................66
4.3.3 GetTypeDefProps.................................................................................66
4.3.4 GetNestedClassProps..........................................................................66
4.3.5 GetInterfaceImplProps.........................................................................67
4.3.6 GetCustomAttributeProps....................................................................67
4.3.7 GetCustomAttributeByName...............................................................68
4.3.8 GetMemberProps.................................................................................69
4.3.9 GetMethodProps..................................................................................69
4.3.10 GetFieldProps......................................................................................69
4.3.11 GetParamProps....................................................................................70
4.3.12 GetParamForMethodIndex...................................................................71
4.3.13 GetPinvokeMap...................................................................................71
4.3.14 GetFieldMarshal...................................................................................71
4.3.15 GetRVA................................................................................................ 72
4.3.16 GetTypeRefProps.................................................................................72
4.3.17 GetMemberRefProps............................................................................72
4.3.18 GetModuleRefProps.............................................................................73
4.3.19 GetPropertyProps................................................................................73
4.3.20 GetEventProps.....................................................................................74
4.3.21 GetMethodSemantics..........................................................................75
4.3.22 GetClassLayout...................................................................................75
4.3.23 GetSigFromToken................................................................................76
4.3.24 GetTypeSpecFromToken.....................................................................76
4.3.25 GetUserString......................................................................................76
4.3.26 GetNameFromToken............................................................................77
4.3.27 ResolveTypeRef...................................................................................77
5 IMetaDataTables.............................................................................................78
6 MethodImpls................................................................................................... 79
6.1 Intro.......................................................................................................... 79
6.2 Details....................................................................................................... 79
6.3 ReNaming Recommendations...................................................................80
6.4 Notes........................................................................................................ 80
7 NestedTypes................................................................................................... 82
Page 5
Metadata API
7.1 Introduction............................................................................................... 82
7.2 Definition.................................................................................................. 82
7.3 Supported Features...................................................................................82
7.4 Visibility, Subclassing, and Member Access..............................................84
7.5 Naming..................................................................................................... 85
7.6 Naked Instances........................................................................................86
7.7 C++ “Member Classes”............................................................................86
7.8 C++ “Friends”..........................................................................................87
7.9 Example - Simple......................................................................................87
7.10 Example – Less Simple..............................................................................89
8 Distinguished Custom Attributes....................................................................91
8.1 Pseudo Custom Attributes (PCAs).............................................................91
8.2 CAs that affect Runtime............................................................................92
9 Bitmasks......................................................................................................... 93
9.1 Token Types [CorTokenType]....................................................................93
9.2 Scope Open Flags [CorOpenFlags]............................................................94
9.3 Options for Size Calculation [CorSaveSize]...............................................94
9.4 Flags for Types [CorTypeAttr]...................................................................94
9.5 Flags for Fields [CorFieldAttr]....................................................................97
9.6 Flags for Methods [CorMethodAttr]...........................................................97
9.7 Flags for Method Parameters [CorParamAttr]............................................98
9.8 Flags for Properties [CorPropertyAttr].......................................................99
9.9 Flags for Events [CorEventAttr].................................................................99
9.10 Flags for MethodSemantics [CorMethodSemanticsAttr]............................99
9.11 Flags for Method Implementations [CorMethodImpl]...............................100
9.12 Flags for Security [CorDeclSecurity]........................................................100
9.13 Struct for Field Offsets [COR_FIELD_OFFSET]..........................................101
9.14 Typedef for Signatures [PCOR_SIGNATURE]............................................101
9.15 Flags for PInvoke Interop [CorPinvokeMap].............................................101
9.16 SetOptions: Duplicate Checking [CorCheckDuplicatesFor]......................102
9.17 SetOptions: Ref-to-Def Optimizations [CorRefToDefCheck].....................102
9.18 SetOptions: Token Remap Notification [CorNotificationForTokenMovement]
103
9.19 SetOptions: Edit & Continue [CorSetENC]...............................................103
9.20 SetOptions: Out-of-Order Errors [CorErrorIfEmitOutOfOrder]..................104
9.21 SetOptions: Hide Deleted Tokens [CorImportOptions].............................104
Page 6
Metadata API
Page 7
Metadata API
Page 8
Metadata API
Page 9
Metadata API
disk file. (We use the word “module” to mean a file that contains metadata; typically
it will be a .OBJ, .EXE or .DLL file that also contains MSIL code; but it can also be a file
containing only metadata)
We call each separate area of metadata a scope. Each scope corresponds to a
module. Usually that module has been saved, or will be saved, to an on-disk file. But
there’s no need to do so: scripting tools frequently generate in-memory metadata
that is never persisted into a file. We use the term scope because it represents the
scope within which metadata tokens are defined. That’s to say, a metadata token
with value N completely identifies an in-memory structure (for example, holding
details of a class definition) within a given scope. But that same value N may
correspond to a completely different in-memory structure for a different scope.
To establish an in-memory metadata scope, use CoCreateInstance for
IMetadataDispenserEx to create a new scope or to open an existing set of metadata
data structures from a file or memory location. With each Define or Open, the caller
specifies which API to receive: The emit API interface, used to write to a metadata
scope, is IMetadataEmit. The import API, which allows tools to read from a metadata
scope, is IMetadataImport.
The metadata APIs described in this specification allow a component's metadata to
be accessed without the class being loaded by the runtime. The primary design goals
for this API include maximizing performance and minimizing overhead – the metadata
engine stops just short of providing direct access to the in-memory data structures.
On the other hand, when a class is loaded at runtime, the loader imports the
metadata into its own data structures, which can be browsed via the Runtime
Reflection services, documented as a separate specification. The Reflection services
do much more work for the client than the metadata APIs do, such as automatically
walking the inheritance hierarchy to obtain information about inherited methods and
fields; the metadata APIs return only the direct member declarations for a given class
and expect the API client to make additional calls to walk the hierarchy and
enumerate inherited methods. The former approach exposes a higher-level view of
metadata, where the latter approach puts the API client in complete control of
walking the data structures.
Consistent with the primary design goals, the metadata APIs perform a minimum of
semantic error checking. These methods assume that the tools and services that
emit metadata are enforcing the object system rules outlined in the common type
system and that any additional checking on the part of the metadata engine during
development time is superfluous. Specific comments about what checks are being
performed accompany the specification of each method in this document.
Page 10
Metadata API
Page 11
Metadata API
Page 12
Metadata API
Page 13
Metadata API
optimizations may result in remapping metadata tokens that the tool is going to
expect to be able to use again to emit the implementation and/or RVA information.
This means that the tool and the metadata engine must work together to track token
remaps.
The sequence of calls for persisting metadata during compilation, then, is:
IMetaDataEmit::SetHandler, to supply an IUnknown interface that the
metadata engine can use to query for IID_IMapToken to notify the client of
token remaps. SetHandler may be called at any point after the metadata
scope is created, but certainly before a call to GetSaveSize.
IMetaDataEmit::GetSaveSize, to obtain the save size of the metadata
binary. GetSaveSize uses the IMapToken interface supplied in SetHandler to
notify the client of any token remaps. Note that if SetHandler was not used to
supply an IMapToken interface, no optimizations are performed. This enables
a compiler that is emitting an interim .obj file to skip unneeded optimizations
that are likely to have to be redone after the link and Merge phase, anyway
(see below).
IMetaDataEmit::Save, to persist the metadata binary, after SetRVA and
other IMetaDataEmit methods are used, as needed, to emit the final
implementation metadata.
The next level of complication comes in the linker phase, when multiple compilation
units are to be merged into a single integrated PE file. In this case, not only do the
metadata scopes need to be merged, but the RVAs will change again as the new PE
file is emitted. In the merge phase, the IMetaDataEmit::Merge method, working with
a single import and a single emit scope with each call, remaps metadata tokens from
the import scope into the emit scope. In addition, the merge may encounter
continuable errors that it needs to be able to notify the client of. After the merge is
complete, emitting the final PE file involves a call to IMetaDataEmit::GetSaveSize,
and another round of token remapping.
The sequence of calls for emitting and persisting metadata by the linker is:
IMetaDataEmit::SetHandler, to supply an IUnknown interface that the
metadata engine can use to query for not only IID_IMapToken, as above, but
also for IID_IMetaDataError. The latter interface is used to notify the client of
any continuable errors that arise from Merge.
IMetaDataEmit::Merge, to merge a specified metadata scope into the
current emit scope. Merge uses the IMapToken interface to notify the client of
token remaps and it uses IMetaDataError to notify the client of continuable
errors.
IMetaDataEmit::GetSaveSize, to obtain the target save size of the
metadata binary. GetSaveSize uses the IMapToken interface supplied in
SetHandler to notify the client of any token remaps. Observe that a tool must
be prepared to handle token remaps in Merge and then again in GetSaveSize
after various format optimizations are performed. The last notification for a
token is the one that is the final mapping that the tool should rely on.
IMetaDataEmit::Save, to persist the metadata binary, after SetRVA and
other IMetaDataEmit methods are used, as needed, to emit the final
implementation metadata.
Page 14
Metadata API
In the general case, there are probably styles of interaction that lie between these two. Some tools may
want the metadata engine to own optimizations but may not be interested in token remap information. Or,
they may want remap information only for some token types and not others. In truth, a compiler may not
even be interested in performing optimizations when emitting an .obj. In future milestones, we are looking
at a degree of tuning that is client-specified that offers a range of balance between complexity and
optimization.
1.3.3 IMapToken
Any client that implements IMapToken must implement the following method(s):
1.3.4 IMetaDataError
Any client that implements IMetaDataError must implement the following method(s):
Page 15
Metadata API
Page 16
Metadata API
Page 17
Metadata API
In get methods, you may pass null in for parameters you are not interested in
getting back.
In set methods, there’s generally no return. You pass in the token for the thing
to be updated, along with the values to update, and the metadata APIs perform
the update.
Page 18
Metadata API
2 IMetadataDispenserEx
The dispenser API is used to map existing metadata so that it can be inspected (and
added to), or to create a fresh in-memory area to define new metadata. In this
section, we also include methods to control how the metadata API operates.
2.1 DefineScope
HRESULT DefineScope(REFCLSID rclsid, DWORD dwCreateFlags,
REFIID riid, IUnknown **ppIUnk)
Create a fresh area in memory, into which you can create new metadata using the
MetaData Emit API. DefineScope creates a set of in-memory metadata tables of the
specified class, generates a unique guid (module version identifier, or mvid) for the
metadata, and creates an entry in the Module able for the compilation unit being
emitted. If successful, the requested metadata interface is returned. Note that a
developer may attach attributes to the metadata scope as a whole using
IMetadataEmit::SetModuleProps or IMetadataEmit::DefineCustomAttribute, as
appropriate.
in/out Parameter Description Required?
2.2 OpenScope
HRESULT OpenScope(LPCWSTR wzScope, DWORD dwOpenFlags,
REFIID riid, IUnknown **ppIUnk)
Open an existing file, and map its metadata into memory. That in-memory copy of
the metadata can then be queried using methods from the IMetaDataImport or
added-to using method from the IMetaDataEmit interfaces. Note that the target file
must contain CLR metadata, else the method will fail.
in/out Parameter Description Required?
Page 19
Metadata API
2.3 OpenScopeOnMemory
HRESULT OpenScopeOnMemory(LPCVOID pData, ULONG cbData,
DWORD dwOpenFlags, REFIID riid, IUnknown **ppIUnk);
Treat the area of memory specified by the pData and cbData arguments as CLR
metaData. This metaData can then be queried using methods from the
IMetaDataImport interface. This is similar to the OpenScope method, except that
metaData of interest already exists in-memory, rather than in a file on-disk.
in/out Parameter Description Required?
2.4 SetOption
You can control how your calls to the metadata API are handled. These settings are
transient; they are not persisted to disk.
The settings are gathered into the following categories:
Duplicate checks Each time you call a method on IMetaDataEmit that creates a
new item, you can ask it to check whether the item already exists in the current
scope. You can control which items are checked and which are not. For example,
you can ask for checking on MethodDefs; in this case, when you call DefineMethod, it
will check that the method does not already exist in the current scope. This check
uses the key that uniquely identifies a given method: parent type, name and
signature
Ref-to-Def optimizations By default, the metadata engine will convert Refs to
Defs where it can (where the referenced item actually exists in the current scope).
You can control which Refs are optimized in this way
Notifications on token movement Controls which token remaps (during
metadata merge) call you back. (Use SetHandler to establish your IMapToken
interface)
ENC Modes – allow control over behaviour of EditAndContinue
Page 20
Metadata API
EmitOutOfOrder Allows you to control which out-of-order ‘errors’ call you back.
(Use SetHandler to establish your IMetaDataError interface). Emitting metadata ‘out-
of-order’ is not fatal – it’s just that if you emit it in an order favoured by the metadata
engine, the metadata is more compact and efficient to search)
Import Options Specify which sorts of deleted metadata tokens are returned in any
enumeration. (See DeleteToken for more information)
Generate TCE Adaptors – yes or no
NameSpace Specifies a different namespace than the one provided by the type
library being imported.
ThreadSafetyOptions Specifies whether you want the metadata engine to take out
reader/writer locks to ensure thread safety (default assumes access is single-
threaded by the caller, so no locks are taken)
optionId argument must point to one of the following GUIDs, defined in Cor.h:
MetaDataCheckDuplicatesFor. pvalue must be a variant of type UI4, holding a
bitmask of which duplicate checks you require. See the CorCheckDuplicatesFor
enum in CorHdr.h
MetaDataRefToDefCheck. pvalue must be a variant of type UI4, holding a
bitmask of which checks you require. See the CorRefToDefCheck enum in
CorHdr.h
MetaDataNotificationForTokenMovement. pvalue must be a variant of type UI4,
holding a bitmask of which notifications you require. See the
CorNotificationForTokenMovement enum in CorHdr.h
MetaDataSetUpdate. pvalue must be a variant of type UI4, holding a bitmask of
which checks you require. See the CorSetUpdate enum in CorHdr.h
MetaDataErrorIfEmitOutOfOrder. pvalue must be a variant of type UI4, holding a
bitmask of which checks you require. See the CorErrorIfEmitOutOfOrder enum in
CorHdr.h
MetaDataImportOption. pvalue must be a variant of type UI4, holding a bitmask
of which deleted items you want reported in an enumeration of the metadata.
See the CorImportOptions enum in CorHdr.h
MetaDataGenerateTCEAdapters. pvalue must be a variant of type BOOL. If set
true, then when we import a type library, we will translate event source interfaces
to add/remove methods.
MetaDataTypeLibImportNamespace. pvalue must be a variant of type BSTR,
EMPTY or NULL. If pvalue represents a nil value, then the current namespace is
set to null; otherwise the current namespace is set to the string held in the
variant’s BSTR
Page 21
Metadata API
2.5 GetOption
Returns the settings for the current metadata scope. See SetOption for details.
Page 22
Metadata API
3 IMetaDataEmit
The emitter API is used by compilers to generate in-memory and on-disk metadata.
This API is implemented directly over the low-level metadata engine APIs, generating
records into the various data structures, which are converted at “save” time to the
target on-disk format.
3.1.1 SetModuleProps
HRESULT SetModuleProps(LPCWSTR wzName)
Records a name for the current scope. This can be any string you want. It is for
information only. It is not used by the Runtime
in/out Parameter Description Required?
3.1.2 Save
HRESULT Save(LPCWSTR wzFile, DWORD dwSaveFlags)
Saves all of the metadata in the current scope to the specified file. The method
leaves all of the metadata intact
in/out Parameter Description Required?
in wzFile Name of file to save to. If null, the in-memory copy will be no
saved to the last location that was used
3.1.3 SaveToStream
HRESULT SaveToStream(IStream *pIStream, DWORD dwSaveFlags)
Saves all of the metadata in the current scope to the specified stream. The method
leaves all of the metadata intact.
in/out Parameter Description Required?
3.1.4 SaveToMemory
HRESULT SaveToMemory(void *pbData, ULONG cbData)
Page 23
Metadata API
Saves all of the metadata in the current scope to the specified area of memory. The
method leaves all of the metadata intact.
in/out Parameter Description Required?
3.1.5 GetSaveSize
HRESULT GetSaveSize(CorSaveSize fSave, DWORD *pdwSaveSize)
Calculates the space required, in bytes, to save all of the metadata in the current
scope. (Specifically, a call to the SaveToStream method would emit this number of
bytes)
If the caller implements the IMapToken interface (via SetHandler or Merge), then
GetSaveSize will perform two passes over the metadata in order to optimize and
compress it. Otherwise, no optimizations are performed.
If optimization is performed, the first pass simply sorts the metadata structures so as
to tune the performance of import-time searches. This step will likely result in
moving records around, with the side-effect that tokens the tool has retained for
future reference are invalidated. (Metadata does not inform its caller of these token
changes until after the second pass, however). In the second pass, various
optimizations are performed that are intended to reduce the overall size of the
metadata, such as optimizing away (early binding) mdTypeRefs and mdMemberRefs
when the reference is to a type or member that is declared in the current metadata
scope. In this pass, another round of token mapping occurs. After this pass, the
metadata engine notifies the caller, via its IMapToken interface, of any changed
token values.
in/out Parameter Description Required?
fSave should be one of cssAccurate (the default), or cssQuick (see the CorSaveSize
enum in CorHdr.h). cssAccurate will return the exact save size but takes longer to
calculate. cssQuick will return a size, padded for safety, but takes less time to
calculate. fSave can also have the cssDiscardTransientCAs bit set – this tells
GetSaveSize that it can throw away discardable custom attributes
3.1.6 MergeEx
HRESULT MergeEx(IMetaDataImport *pImport, IMapToken *pIMap,
IUnknown *pHandler)
Starts a merge of metadata from the scope defined by pImport into the current
metadata scope. In so doing, tokens from the imported scope are remapped into the
current scope. MergeEx uses the IMapToken interface supplied by the caller to notify
Page 24
Metadata API
the caller of each remap; it uses the IMetaDataError interface supplied by the caller
to notify the caller of any errors.
This routine can be called for several import scopes. The actual merge operation,
across all these import scopes is triggered by calling the routine MergeEndEx
in/out Parameter Description Required?
3.1.7 MergeEndEx
HRESULT MergeEndEx( )
This routine triggers the actual merge of metadata, of all import scopes specified by
preceding calls to MergeEx into the current output scope.
During merge, various errors may be encountered, as follows:
The following special conditions apply to the merge:
An MVID is never imported, since it is unique to that other metadata
No existing module-wide properties are overwritten. So, if module properties
were already set for the current scope, no module properties are imported. But, if
module properties have not been set in the current scope, they will be imported
once-only, when they are first encountered. If they are encountered again, they
must be duplicates (eg, when merging .obj files during a VC link step); if they are
not duplicates, based on comparing the values of all module properties (except
MVID), we raise an error
For TypeDefs, no duplicates will be merged into the current scope. The check for
duplicates is based on fully-qualified name + guid + version number. If there is a
match on name or on guid and any of the other two elements is different, we
raise an error. Else, if there is a full match on 3 items, MergeEx does a cursory
check to ensure the entries are indeed duplicates – we raise an error if they are
not. This cursory check is based on:
Same member declarations, in same order. (However, members flagged as
mdPrivateScope are not included in this check; they are merged specially; see
later)
Same class layout
Observe that this means that a TypeDef must always be fully and consistently
defined in every metadata scope in which it is declared; if its member
implementations (for a class) are spread across multiple compilation units (as
in VC), the full definition is assumed to be present in every scope and not
incremental to each scope. For example, if parameter names are relevant to
the contract, they must be emitted the same way into every scope; if they are
not relevant, they should not be emitted into metadata
The exception is that a TypeDef may have incremental members flagged as
mdPrivateScope. On encountering these, MergeEx will incrementally add
them to the current scope without regard for duplicates (since only the
Page 25
Metadata API
compiler understands the private scope, the compiler must be responsible for
enforcing rules)
When merging members that have RVAs, we do not import/merge any of this
information – the compiler is expected to re-emit it
Custom values or attributes are merged only at the time we merge the item they
are attached to. For example, custom values associated with a class will be
merged when the class is first encountered. If custom values are associated with
TypeDefs or MemberDefs that were specific to the compilation unit (e.g., time
stamp of member compile), these will not be handled specially and it is up to the
compiler to remove or update such metadata.
3.1.8 SetHandler
HRESULT SetHandler(IUnknown *pUnk)
Registers a handler interface through which the caller may receive notification of
errors (IMetaDataError) and of token remaps (IMapToken).
The metadata engine sends notification on the map token interface provided by
SetHandler() for compilers who do not generate records in an optimized way and
would like to save optimized. If IMapToken is not provided via SetHandler, no
optimization will be performed on save except where several import scopes have
been merged using the provided IMapToken on merge for each scope.
in/out Parameter Description Required?
Page 26
Metadata API
Second, the programmer defines an instance of that attribute class (let’s call it an
attribute-object) and attaches it to some programming element. Here is an example
of defining two Location attribute-objects and attaching them to two classes,
Television and Refrigerator. Note that we define the attribute-object by providing a
literal string argument to its Location constructor method:
Page 27
Metadata API
3.2.3 DefineCustomAttribute
HRESULT DefineCustomAttribute(mdToken tkOwner, mdToken tkAttrib,
void const *pBlob, ULONG cbBlob, mdCustomAttribute *pca)
Page 28
Metadata API
The format of pBlob for defining a custom attribute is defined in the “Metadata
Structures” spec. (broadly speaking, the blob records the argument values to the
class constructor, together with zero or more values for named fields/properites – in
other words, the information needed to instantiate the object specified at the time
the metadata was emitted). If the constructor requires no arguments, then there is
no need to provide a blob argument.
3.2.4 SetCustomAttributeValue
HRESULT SetCustomAttributeValue(mdCustomAttribute pca,
void const *pBlob, ULONG cbBlob)
Sets the value of an existing custom attribute to have a new value. The value that
was previously defined is replaced with this new value.
in/out Parameter Description Required?
3.3.1 DefineTypeDef
HRESULT DefineTypeDef(LPCWSTR wzName, CLASSVERSION *pVer,
DWORD dwTypeDefFlags, mdToken tkExtends,
mdToken rtkImplements[], mdTypeDef *ptd)
Defines a type. A flag in dwTypeDefFlags specifies whether the type being created is
a common type system reference type (class or interface) or a common type system
value type.
Duplicates are disallowed. So, within any scope, wzName must be unique.
Depending on the parameters supplied, this method, as a side effect, may also create
an InterfaceImpl record for each interface inherited or implemented by this type.
None of these InterfaceImpl tokens are returned by this method – if a client wants to
later add/modify these InterfaceImpls, it must use IMetaDataImport to enumerate
them. If COM semantics of ‘default interface’ are desired, then it’s important to
supply the default interface as the first in rtkImplements[]; a custom attribute set on
the class will indicate that it does have a default interface (which is always assumed
to be the first InterfaceImpl declared for the class). Refer to the COM Integration
spec for more details.
Page 29
Metadata API
3.3.2 SetTypeDefProps
HRESULT SetTypeDefProps(mdTypeDef td, CLASSVERSION *pVer,
DWORD dwTypeDefFlags, mdToken tkExtends,
DWORD mdToken rtkImplements[])
Sets the attributes of an existing type, previously defined using the DefineTypeDef
method. This is useful when the original definition supplied only minimal information,
perhaps corresponding to a forward reference in the compiler’s source language.
Note that you cannot use this method to change the type’s name. In all other
respects however, SetTypeDefProps has essentially the same behavior as
DefineTypeDef and, depending on the parameters supplied, it may also create one or
more InterfaceImpl data structures.
If you supply a value for any argument, it will supersede the value you
supplied in the earlier call to DefineTypeDef. If you want to leave the
original value unchanged, mark that argument as “to be ignored” – see
section 1.5.5 for details.
in/out Parameter Description Required?
in rtkImplements[] Array of tokens for the interfaces that this type implements. no
These TypeRef tokens are obtained via DefineImportType
Page 30
Metadata API
3.4.1 DefineMethod
HRESULT DefineMethod(mdTypeDef td, LPCWSTR wzName,
DWORD dwMethodFlags, PCCOR_SIGNATURE pvSig, ULONG cbSig,
ULONG ulCodeRVA, DWORD dwImplFlags, mdMethodDef *pmd)
Page 31
Metadata API
methods, but it should be sufficient to say that even if mdPrivateScope members are
interleaved in method sequences they are simply ignored when it comes to layout.
Method implementation information is often not known at the time the method is
declared, e.g. in languages where the front-end calls DefineMethod but it is the
backend that supplies implementation information and the linker that supplies code
address information. As such, ulCodeRVA and dwImplFlags are not required to be
supplied with DefineMethod. They may be supplied later via SetMethodImplFlags or
SetRVA, as appropriate.
In some situations, such as PInvoke or COMinterop scenarios, the method body will
not be supplied, and ulCodeRVA will remain 0. In these situations, the method should
not be tagged as abstract, since the runtime will locate the implementation. (See
interop specs for more detail).
in/out Parameter Description Required?
3.4.2 SetMethodProps
HRESULT SetMethodProps(mdMethodDef md, DWORD dwMethodFlags,
ULONG ulCodeRVA, DWORD dwImplFlags)
Page 32
Metadata API
If you supply a value for any optional argument, that value will supersede the
previous, supplied to DefineMethod. If you want to leave the original value
unchanged, mark the argument as “to be ignored” – see section 1.5.5 for details.
3.4.3 DefineField
HRESULT DefineField(mdTypeDef td, LPCWSTR wzName,
DWORD dwFieldFlags, PCCOR_SIGNATURE pvSig, ULONG cbSig,
DWORD dwDefType, void const *pValue, ULONG cchValue,
mdFieldDef *pmd)
Global data may need initialization upon module load. The design approach is for the compiler to emit one
or more function definitions that correspond to the initializers. Rather than providing any runtime support
for calling the initializers, the compiler will call them explicitly, in the appropriate sequence, from the
body of the module entry point. As such, there is neither special-purpose metadata nor runtime support
needed to initialize the module’s static data members.
Page 33
Metadata API
3.4.4 SetFieldProps
HRESULT SetFieldProps(mdFieldDef fd, DWORD dwFieldFlags,
DWORD dwDefType, void const *pValue, ULONG cchValue)
Sets the properties of an existing field. See the description of DefineField for more
information.
If you supply a value for any optional argument, that value will supersede
the previous, supplied to DefineField. If you want to leave the original
value unchanged, mark the argument as “to be ignored” – see section 1.5.5
for details.
in/out Parameter Description Required?
3.4.5 DefineNestedType
HRESULT DefineNestedType(LPCWSTR wzName, CLASSVERSION *pVer,
Page 34
Metadata API
Defines a type that is lexically nested within an enclosing type. This call is analogous
to DefineTypeDef – but has an extra argument, tdEncloser, to denote the type that
encloses this type. (see DefineTypeDef – section 3.3.1 for more detail)
in/out Parameter Description Required?
3.4.6 DefineParam
HRESULT DefineParam(mdMethodDef md, ULONG ulParamSeq,
LPCWSTR wzName, DWORD dwParamFlags, DWORD dwDefType,
void const *pValue, ULONG cchValue, mdParamDef *ppd)
Defines extra information for a method parameter (beyond what could have been
supplied in the definition of its corresponding method signature)
You can use this method to save a default value for the property, via the dwDefType,
pValue and cchValue parameters – see 1.5.3 for details.
Note that even if you specify that all optional parameters to this call are to be
ignored (see 1.5.5), metadata will still create a ParamDef record and return its
assigned token.
Page 35
Metadata API
3.4.7 SetParamProps
HRESULT SetParamProps(mdParamDef pd, LPCWSTR wzName,
DWORD dwParamFlags, DWORD dwDefType,
void const *pValue, ULONG cchValue)
Sets the attributes for a specified method parameter. See the description of
DefineParam for details.
in/out Parameter Description Required?
If you supply a value for any optional argument, that value will supersede the
previous, supplied to DefineParam. If you want to leave the original value
unchanged, mark the argument as “to be ignored” – see section 1.5.5 for details.
3.4.8 DefineMethodImpl
HRESULT DefineMethodImpl(mdTypeDef td, mdToken tkBody,
mdToken tkDecl)
Page 36
Metadata API
3.4.9 SetRVA
HRESULT SetRVA(mdMethodDef md, ULONG ulRVA)
3.4.10 SetFieldRVA
HRESULT SetFieldRVA(mdFieldDef fd, ULONG ulRVA)
Page 37
Metadata API
3.4.11 DefinePinvokeMap
HRESULT DefinePinvokeMap(mdToken tk, DWORD dwMappingFlags,
LPCWSTR wzImportName, mdModuleRef mrImportDLL)
Defines information for a method that will be used by PInvoke (Runtime service that
supports inter-operation with unmanaged code)
in/out Parameter Description Required?
tk is an mdMethodDef token
dwMappingFlags is a bitmask from the CorPinvokeMap enum in CorHdr.h
wzImportName may be the simple name of the imported function (eg “MessageBox”)
or its ordinal, encoded as a decimal integer preceded by a # character (eg “#123”)
3.4.12 SetPinvokeMap
HRESULT SetPinvokeMap(mdToken tk, DWORD dwMappingFlags,
LPCWSTR wzImportName, mdModuleRef mrImportDLL)
Sets information for a method that will be used by PInvoke (runtime service that
supports inter-operation with unmanaged code)
in/out Parameter Description Required?
tk is an mdMethodDef token
dwMappingFlags is a bitmask from the CorPinvokeMap enum in CorHdr.h.
wzImportName may be the simple name of the imported function (eg “MessageBox”)
or its ordinal, encoded as a decimal integer preceded by a # character (eg “#123”)
3.4.13 SetFieldMarshal
HRESULT SetFieldMarshal(mdToken tk, PCCOR_SIGNATURE pvUnmgdType,
ULONG cbUnmgdType)
Page 38
Metadata API
3.5.1 DefineTypeRefByName
HRESULT DefineTypeRefByName(mdToken tkResScope,
LPCWSTR wzName, mdTypeRef *ptr)
Defines a reference to a type that exists in another module. This method does not
look into that other module. Therefore, attempting to resolve the type reference
might fail at runtime
in/out Parameter Description Required?
Page 39
Metadata API
If you don’t know the final module in which the reference will resolve, you
may supply a nil token. However, this is only valid as a temporary state. The
token must be fixed up by the time the Runtime loader ‘sees’ this TypeRef.
One example where this is used is when VC compiles separate .cpp files into
separate .obj files. The Linker ‘joins’ them together into one image (.dll
or .exe file) – as part of that process, it calls metadata Merge code with
optimizes these nil-scoped TypeRefs to be replaced by the corresponding
TypeDef. This ‘trick’ does not work if the TypeRef would have to resolve
outside the merged image
3.5.2 DefineImportType
HRESULT DefineImportType(IMetaDataAssemblyImport *pAssemImport,
const void *pbHashValue, ULONG cbHashValue,
mdExecutionLocation tkExec, IMetaDataImport *pImport,
mdTypeDef tdImport, IMetaDataAssemblyEmit *pAssemEmit,
mdTypeRef *ptr)
Defines a reference to a type that exists in another module or assembly. The method
looks up the tdImport token in that other module, specified via a combination of
pAssemImport, pbHashValue, cbHashValue, tkExec and pImport, and retrieves its
properties. It uses this information to define a TypeRef in the current scope.
in/out Parameter Description Required?
in tdImport TypeRef token for target Type within pImport scope yes
3.5.3 DefineMemberRef
HRESULT DefineMemberRef(mdToken tkImport, LPCWSTR wzName,
PCCOR_SIGNATURE pvSig, ULONG cbSig, mdMemberRef *pmr)
Page 40
Metadata API
module , for its class or interface (tkImport). If the target member is a global-variable
or global-function, then tkImport must be the mdModuleRef token for that module.
You obtain the tkImport token from a previous call to DefineTypeRefByName,
DefineImportType, or DefineModuleRef.
You can specify tkImport as mdTokenNil. This indicates that the imported member’s
parent will be resolved later by the compiler or linker (the typical scenario is when a
global function or data member is being imported from a .obj file that will ultimately
be linked into the current module and the metadata merged). Ultimately, all
MemberRefs must be fully-resolved to have a consistent, loadable module.
Note: every member reference must have a reference scope that is one of:
TypeRef token, if member is referenced on an imported type
ModuleRef token, if member is a global-variable or global-function
MethodDef token, if member is a call site signature for a vararg method defined
in the same module
TypeSpec token, if member is a member of a constructed type (eg an array)
Note too: as an optimization (see Metadata Optimizations), tkImport may be an
mdMethodDef, if the reference is not really an import but is simply a callsite
reference that could not be optimized away. This can occur when a call is made to a
vararg function where additional arguments are passed on the call. In this case, we
can’t just optimize the MemberRef away if we otherwise could (see Metadata
Optimizations for details), but at the same time there is no need to incur the extra
runtime overhead to do a full resolution when the resolution may be early bound. So,
we persist the “parent” of the MemberRef as the MethodDef token of the method
declaration and the MemberRef is called “fully resolved.”
in/out Parameter Description Required?
in tkImport Token for the target member’s class or interface. Or, if the yes
member is global, the ModuleRef for that other file
3.5.4 DefineImportMember
HRESULT DefineImportMember(IMetaDataAssemblyImport *pAssemImport,
const void *pbHashValue, ULONG cbHashValue,
mdExecutionLocation tkExec, IMetaDataImport *pImport,
mdToken mbMember, IMetaDataAssemblyEmit *pAssemEmit,
mdToken tkParent, mdMemberRef *pmr)
Page 41
Metadata API
in tkParent TypeRef or ModuleRef token for the class that owns the yes
target member member
3.5.5 DefineModuleRef
HRESULT DefineModuleRef(LPCWSTR wzName, mdModuleRef *pmur)
Defines a reference to another module. Note that the method does not check
whether the specified external module actually exists.
wzName should be a file name and extension – but no drive letter or file path. For
example, “c:\MyApp\Widgets.dll” is wrong – use “Widgets.dll”
Page 42
Metadata API
3.5.6 SetParent
HRESULT SetParent(mdMemberRef mr, mdToken tk)
Sets the parent of a MemberRef to a new value. This method is typically used by a
compiler or tool (like VC) that emits individual .obj files, each with its own metadata;
these .obj files are later merged into a single image. This method is used to fix up
module import scopes.
in/out Parameter Description Required?
3.6.1 DefineProperty
HRESULT DefineProperty(mdTypeDef td, LPCWSTR wzProperty,
DWORD dwPropFlags, PCCOR_SIGNATURE pvSig, ULONG cbSig,
DWORD dwDefType, void const *pValue, ULONG cchValue,
mdMethodDef mdSetter, mdMethodDef mdGetter,
mdMethodDef rmdOtherMethods[], mdFieldDef fdBackingField,
mdProperty *pmdProp)
A property is like a field within a class. But instead of accessing the value stored in
that field location, a property can execute set/get code. You might use this, for
example, to range-check a value before setting the property; but the code can also
be as complex as the developer chooses. A language may choose to have users
write syntax that looks like regular field access (x = foo.prop) but execute property
accessor code, ‘behind the scenes’.
Examples of using properties include:
enhanced UI semantics, by presenting the object’s state as the values of its
properties and allowing the user to manipulate state by changing the values
through the UI
enhanced language support, by abstracting a notion of a property name/identifier
that can be used in lieu of explicit method invocation in assignment statements
and expressions
Page 43
Metadata API
rich infrastructure services such as transparent persistence for properties that are
tagged as being part of the persistent state of the object
A property is defined, using DefineProperty, in a similar way to how you would define
a method of a class. As for a method, you specify the property by giving its owner,
name, type, and formal parameter list. For indexed properties, the property can be
said to have a signature that is its return type plus the types of its parameters.
You can define more than just setter and getter methods for a property. Simply
provide their tokens in the rmdOtherMethods[] array.
In this version of the runtime, there is no built-in support for properties at runtime.
That’s to say, compilers that provide properties must resolve any compile-time
reference to a property into its corresponding method invocation; the metadata
provides the information necessary for the compiler to do that resolution. In support
of dynamic invocation, the Reflection APIs provide this same feature, to resolve
property-to-method.
You can use this method to save a default value for the property, via the dwDefType, pValue and
cchValue parameters – see 1.5.3 for details.
in/out Parameter Description Required?
3.6.2 SetPropertyProps
HRESULT SetPropertyProps(mdProperty pr, DWORD dwPropFlags,
DWORD dwDefType, void const *pValue, ULONG cchValue,
mdMethodDef mdSetter, mdMethodDef mdGetter,
mdMethodDef rmdOtherMethods[], mdFieldDef fdBackingField)
Page 44
Metadata API
Sets the information stored in metadata for a property, previously defined with a call
to DefineProperty.
You can use this method to save a default value for the property, via the dwDefType,
pValue and cchValue parameters – see 1.5.3 for details.
If you supply a value for any optional argument, that value will supersede
the previous, supplied to DefineProperty. If you want to leave the original
value unchanged, mark the argument as “to be ignored” – see section 1.5.5
for details.
in/out Parameter Description Required?
3.6.3 DefineEvent
An event is treated in metadata in a similar manner to a property – as a collection of
methods defined upon a class or interface. But runtime provides no support for
events: the compiler must translate all references to events into calls to the
appropriate method.
Page 45
Metadata API
3.6.4 SetEventProps
HRESULT DefineEvent(mdEvent ev, DWORD dwEventFlags,
mdToken tkEventType, mdMethodDef mdAddOn,
mdMethodDef mdRemoveOn, mdMethodDef mdFire,
mdMethodDef rmdOtherMethod[])
Changes the properties of an existing event. See DefineEvent for more information.
If you supply a value for any argument, it will supersede the value you
supplied in the earlier call to DefineEvent. If you want to leave the original
value unchanged, mark that argument as “to be ignored” – see section
1.5.5 for details.
Page 46
Metadata API
3.7.1 SetClassLayout
HRESULT SetClassLayout (mdTypeDef td, DWORD dwPackSize,
COR_FIELD_OFFSET rFieldOffsets[], ULONG ulClassSize)
Page 47
Metadata API
The COR_FIELD_OFFSET is a simple struct with two fields: an mdFieldDef to define the
field, and a ULONG to specify the byte offset from the start of the object, at which
this field should start (offsets start at zero).
3.8 Miscellaneous
3.8.1 GetTokenFromSig
HRESULT GetTokenFromSig(PCCOR_SIGNATURE pvSig, ULONG cbSig,
mdSignature *pmsig)
Stores a signature into the Blob heap, returning a metadata token that can be used
to reference it later. That token represents an index into the StandAloneSig table.
This method creates new entries in metadata, so its name is perhaps misleading –
you might think of it instead as being “DefineStandAloneSig”
in/out Parameter Description Required?
3.8.2 GetTokenFromTypeSpec
HRESULT GetTokenFromTypeSpec(PCCOR_SIGNATURE pvSig, ULONG cbSig,
mdTypeSpec *ptypespec)
Stores a type specification into the Blob heap, returning a metadata token that can
be used to reference it later. That token represents an index into the TypeSpec
table. This method creates new entries in metadata, so its name is perhaps
misleading – you might think of it instead as being “DefineTypeSpec”
in/out Parameter Description Required?
3.8.3 DefineUserString
HRESULT DefineUserString(LPCWSTR wzString, ULONG cchString,
mdString *pstk)
Stores a user string into the UserString heap in metadata, returning a token that can
be used to retrieve it later. This token is unlike any other in metadata – it is not an
index for a row in a metadata table – its lower 3 bytes are the actual byte offset
within the UserString heap at which the string is stored.
Page 48
Metadata API
3.8.4 DeleteToken
HRESULT DeleteToken(mdToken tk)
Deletes the specified token from the current metadata scope. The only sorts of token
you can delete are: TypeDef, MethodDef, FieldDef, Event, Property, ComType and
CustomAttribute.
This support is for EditAndContinue and incremental-compilation scenarios – where a
compiler wants to make a small change to the metadata, without re-emitting all of it
again. The information identified by the token is not physically erased (an expensive
operation that would require a token remap). Instead, they are marked ‘deleted’ –
we set the xxRTSpecialName bit in their attributes flag, and append “_Delete” to
their name. For CustomAttribute, their parent is set to nil.
Compilers who use this method take on responsibility for dealing with any
inconsistencies it makes in the metadata (eg live references to these deleted tokens)
In order to use this method, you must first call SetOption, specifying the
MetaDataSetUpdate guid, and setting the MDUpdateIncremental flag. You must then
open reopen the scope in read-write mode.
in/out Parameter Description Required?
3.8.5 DefineSecurityAttributeSet
HRESULT DefineSecurityAttributeSet(mdToken tkObj,
COR_SECATTR rSecAttrs[],
ULONG cSecAttrs,
ULONG *pulErrorAttr)
Defines a collection of security permissions, attached to the item whose metadata
token is tkObj.
Page 49
Metadata API
out pulErrorAttr If method fails, specifies the index in rSecAttrs[] of element yes
that caused the problem
Each element of the array defines a custom attribute blob that describes one
corresponding Security Attribute (see section 11)
Field Table
TypeDef Table
FieldList Column
Page 50
Metadata API
Each time you call DefineTypeDef, the metadata engine stores the information you
supply into the next row of the TypeDef table. The picture shows a row for Type-A,
and one for Type-B. Similarly, each time you call DefineField, the metadata engine
stores the information you supply into the next row of the Field table. The TypeDef
table includes a column called FieldList that points to the first field for that type. This
picture shows what happens if you define in the order – Type-A, Type-B, Field-A-1
thru Field-A-4, Field-B-1, Field-B-2. With this ordering, the fields owned by each Type
lie in a contiguous run in the Field table.
The next picture shows what happens if you interleave the definitions –
FieldList Column
Here, the order of definition was: Type-A, Type-B, Field-A-1, Field-B-1, Field-A-2, Field-
B-2, Field-A-3, Field-A-4. The metadata engine creates an intermediate FieldMap
table – as far as the TypeDef table is concerned, it looks like all the Fields for Type-A
lie in a contiguous run – but their order in the Field table is not contiguous.
Global functions and fields are parented by an artificial Type created by the metadata
engine (it’s called <Module> and is always the first row in the TypeDef table) – in all
other respects, for ordering rules, they behave like members of a genuine Type.
Hopefully, this simple picture makes the ordering constraints easy to understand.
Just as you should emit a Type’s Fields so they lie in a contiguous run, the same holds
true for each Type’s Methods, Events and Properties. Similarly, for each Method,
emit its Parameters so they also lie in a contiguous run.
Note that there’s no other ordering constraint omitted by the above rules. In
particular, for our example, there’s no ordering constraint between definition of Type-
A’s fields, and definition of Type-B. To be absolutely clear, the following orders are
all good (we abbreviate Type-A to tA, and Field-A-1 to fA1, etc) –
Note that, you can choose to emit definitions interleaved, but then have the
metadata engine remove these intermediate ‘map’ tables before saving the
metadata to disk. This involves moving table rows to make them contiguous – but
since these row numbers, or RIDs, make up the corresponding metadata tokens, this
Page 51
Metadata API
results in a remapping of tokens already assigned. If you want to do this, you must
call SetHandle (as explained earlier) to register for these token remap ‘events’ – you
must then fix up any affected tokens that you already generated into your IL code
stream. (most compilers jump thru any hoops they can to avoid doing this!)
Page 52
Metadata API
4 MetaDataImport
The import interface is used to consume an existing metadata section from a PE file
or other source (eg stand-alone runtime metadata binary or type library). The design
of these interfaces is intended primarily for tools/services that will be importing type
information (eg development tools) or managing deployed components (eg
resolution/activation services). The following groups of methods are defined:
Enumerating collections of items in the metadata scope
Finding a specific item with a specific set of characteristics
Getting properties of a specified item
Resolving import references
In this example, pImp is the IMetaDataImport pointer returned from a previous call to
OpenScope. (We have omitted error handling to keep the example simple)
Note: When enumerating collections of members for a class, EnumMembers returns
only members defined directly on the class: it does not return any members that the
class inherits, even if it provides an implementation for those inherited members. To
enumerate those inherited members, the caller must explicitly walk the inheritance
chain (the rules for which may vary depending upon the language/compiler that
emitted the original metadata).
Page 53
Metadata API
Frees the memory previously allocated for the enumeration. Note that the hEnum
argument is that obtained from a previous EnumXXX call (for example,
EnumTypeDefs)
in/out Parameter Description Required?
Returns the number of items in the enumeration. Note that the hEnum argument is
that obtained from a previous EnumXXX call (for example, EnumTypeDefs).
in/out Parameter Description Required?
4.1.3 ResetEnum
HRESULT ResetEnum(HCORENUM hEnum, ULONG ulPos);
Reset the enumeration to the position specified by pulCount. So, if you reset the
enumeration to the value 5, say, then a subsequent call to the corresponding
EnumXXX method will return items, starting at the 5th (where counting starts at item
number zero). Note that the hEnum argument is that obtained from a previous
EnumXXX call (for example, EnumTypeDefs)
in/out Parameter Description Required?
4.1.4 IsValidToken
BOOL IsValidToken(mdToken tk)
Returns true if tk is a valid metadata token in the current scope. [The method checks
the token type is one of those in the CorTokenType enumeration in CorHdr.h, and
then that its RID is less than or equal to the current count of those token types]
Page 54
Metadata API
4.1.5 EnumTypeDefs
HRESULT EnumTypeDefs(HCORENUM *phEnum, mdTypeDef rTokens[],
ULONG cTokens, ULONG *pcTokens)
Enumerates all TypedDefs within the current scope. Note: the collection will contain
Classes, Interfaces, etc, as well as any TypeDefs added via an extensibility
mechanism.
in/out Parameter Description Required?
4.1.6 EnumInterfaceImpls
HRESULT EnumInterfaceImpls(HCORENUM *phEnum, mdTypeDef td,
mdInterfaceImpl rTokens[], ULONG cTokens, ULONG *pcTokens)
4.1.7 EnumMembers
HRESULT EnumMembers(HCORENUM *phEnum, mdTypeDef cl,
mdToken rTokens[], ULONG cTokens, ULONG *pcTokens)
Enumerates all members (fields and methods, but not properties or events) defined
by the class specified by cl. This does not include any members inherited by that
class; even in the case where this TypeDef actually implements an inherited method.
Page 55
Metadata API
4.1.8 EnumMembersWithName
HRESULT EnumMembersWithName(HCORENUM *phEnum, mdTypeDef cl,
LPCWSTR wzName, mdToken rTokens[], ULONG cTokens,
ULONG *pcTokens)
Enumerates all members (fields and methods, but not properties or events) defined
by the specified TypeDef, and that also have the specified name. This does not
include any members inherited by the TypeDef; even in the case where this TypeDef
actually implements an inherited method. This method is like calling EnumMembers,
but discarding all tokens except those corresponding to the specified name.
in/out Parameter Description Required?
4.1.9 EnumMethods
HRESULT EnumMethods(HCORENUM *phEnum, mdTypeDef cl,
mdMethodDef rTokens[], ULONG cTokens, ULONG *pcTokens)
Enumerates all methods defined by the specified TypeDef. Tokens are returned in
the same order they were emitted. If you supply a nil token for the cl argument the
method will enumerate the global functions defined for the module as a whole.
Page 56
Metadata API
4.1.10 EnumMethodsWithName
HRESULT EnumMethodsWithName(HCORENUM *phEnum, mdTypeDef cl,
LPCWSTR wzName, mdMethodDef rTokens[], ULONG cTokens,
ULONG *pcTokens)
Enumerates all methods defined by the specified TypeDef (cl), and that also have the
specified name (wzName). This method is like calling EnumMethods, but discarding
all tokens except those corresponding to the specified name.
in/out Parameter Description Required?
Note that supplying a nil token for the cl parameter will enumerate only the global
functions with that name defined for the module as a whole.
4.1.11 EnumUnresolvedMethods
HRESULT EnumUnresolvedMethods(HCORENUM *phEnum,
mdMethodDef rTokens[], ULONG cTokens, ULONG *pcTokens)
Enumerates all methods in the current scope that have been declared but are not
implemented.
The enumeration excludes all methods defined at modules scope (globals), or those
defined on Interfaces or Abstract classes. Beyond those, for each method marked
miForwardRef, it is included into the “unresolved” enumeration if either:
mdPinvokeImpl = 0
miRuntime = 0
Put another way, “unresolved” methods are class methods marked miForwardRef but
which are not implemented in unmanaged code (reached via PInvoke) nor
implemented internally by the Runtime itelf
Page 57
Metadata API
4.1.12 EnumMethodSemantics
HRESULT EnumMethodSemantics(HCORENUM *phEnum, mdMethodDef mb,
mdToken rTokens[], ULONG cTokens, ULONG *pcTokens)
Enumerates all semantics for a given method. (See GetMethodSemantics for how a
method’s semantics are derived)
in/out Parameter Description Required?
4.1.13 EnumFields
HRESULT EnumFields(HCORENUM *phEnum, mdTypeDef cl,
mdFieldDef rTokens[], ULONG cTokens, ULONG *pcTokens)
Enumerates all fields defined on a specified TypeDef. The tokens are returned in the
same order as originally emitted into metadata. If you specify cl as nil, the method
will enumerate all the global static data members defined in the current scope.
in/out Parameter Description Required?
4.1.14 EnumFieldsWithName
HRESULT EnumFieldsWithName(HCORENUM *phEnum, mdTypeDef cl,
Page 58
Metadata API
Enumerates all fields defined by the specified TypeDef (cl), and that also have the
specified name (wzName).
in/out Parameter Description Required?
Note that supplying a nil token for the cl parameter will enumerate any module-
global functions with the specified name.
4.1.15 EnumParams
HRESULT EnumParams(HCORENUM *phEnum, mdMethodDef md,
mdParamDef rTokens[], ULONG cTokens, ULONG *pcTokens)
Enumerates all attributed parameters for the method specified by md. By attributed
parameters, we mean those parameters of a method which have been explicitly
defined via a call to DefineParam
in/out Parameter Description Required?
Note that you can find the number of parameters and their types from the signature
returned in GetMethodProps
4.1.16 EnumMethodImpls
HRESULT EnumMethodImpls(HCORENUM *phEnum, mdTypeDef td,
mdToken rBody[], mdToken rDecl[], ULONG cTokens,
ULONG *pcTokens)
Enumerates all MethodImpls in the current scope for the TypeDef specified by td
Page 59
Metadata API
4.1.17 EnumProperties
HRESULT EnumProperties (HCORENUM *phEnum, mdTypeDef td,
mdProperty rTokens[], ULONG cTokens, ULONG *pcTokens)
4.1.18 EnumEvents
HRESULT EnumEvents (HCORENUM *phEnum, mdTypeDef td, mdEvent rTokens[],
ULONG cTokens, ULONG *pcTokens)
in td Token for the type on which the events are defined yes
4.1.19 EnumTypeRefs
HRESULT EnumTypeRefs(HCORENUM *phEnum, mdTypeRef rTokens[],
ULONG cTokens, ULONG *pcTokens)
Page 60
Metadata API
Enumerates all TypeRef tokens that are defined in the current scope.
in/out Parameter Description Required?
4.1.20 EnumMemberRefs
HRESULT EnumMemberRefs(HCORENUM *phEnum, mdToken tkParent,
mdMemberRef rTokens[], ULONG cTokens, ULONG *pcTokens)
Enumerates all MemberRef tokens in the current scope for the specified parent.
tkParent may be a TypeRef, MethodDef, TypeDef, ModuleRef or nil; in the latter case,
we return tokens that reference global-fields or global-functions.
in/out Parameter Description Required?
4.1.21 EnumModuleRefs
HRESULT EnumModuleRefs (HCORENUM *phEnum, mdModuleRef rTokens[],
ULONG cTokens, ULONG *pcTokens)
Page 61
Metadata API
4.1.22 EnumCustomAttributes
HRESULT EnumCustomAttributes (HCORENUM *phEnum, mdToken tk,
mdToken tkType, mdCustomValue rTokens[],
ULONG cTokens, ULONG *pcTokens)
tkOwner is the token for the owner – that’s to say, the metadata item this custom
attribute, or custom value, is attached to. If you specify tkOwner as nil, we
enumerate all custom attributes in the scope
If you want to enumerate custom attributes, then supply tkType as the mdMethodDef
or mdMemberRef token for its constructor method. If you want to enumerate custom
values, then supply tkType as the mdTypeRef token with which that custom value
was defined. tkType is used to filter the answer: if specified as null, no filtering is
done
4.1.23 EnumSignatures
HRESULT EnumSignatures(HCORENUM *phEnum, mdSignature rTokens[],
ULONG cTokens, ULONG *pcTokens)
Enumerates all stand-alone signatures defined within the current scope, by looking at
each row of the StandAloneSig table. These signatures were defined by previous
calls to the GetTokenFromSig method
in/out Parameter Description Required?
4.1.24 EnumTypeSpecs
HRESULT EnumTypeSpecs(HCORENUM *phEnum, mdTypeSpec rTokens[],
ULONG cTokens, ULONG *pcTokens)
Page 62
Metadata API
Enumerates all TypeSpecs defined within the current scope, by looking at each row of
the TypeSpec table. These TypeSpecs were previously defined by previous calls to
the GetTokenFromTypeSpec method
in/out Parameter Description Required?
4.1.25 EnumUserStrings
HRESULT EnumUserStrings(HCORENUM *phEnum, mdString rTokens[],
ULONG cTokens, ULONG *pcTokens)
Enumerates all user strings stored within the current scope, by scanning the entire
UserString heap. These are the strings stored by previous calls to the
DefineUserString method
in/out Parameter Description Required?
WARNING: The only scenario in which this method is expected to be used is for a
metadata browser, rather than by a compiler
4.2.1 FindTypeDefByName
HRESULT FindTypeDefByName(LPCWSTR wzName, mdToken tkEncloser,
mdTypeDef *ptd)
Finds the type definition (class, interface, value-type) with the given name. If this is
a nested type, supply tkEncloser as the TypeDef or TypeRef token for its
immediately-enclosing type. If this is not a nested type, supply tkEncloser as nil
in/out Parameter Description Required?
Page 63
Metadata API
4.2.2 FindMember
HRESULT FindMember(mdTypeDef td, LPCWSTR wzName,
PCCOR_SIGNATURE pvSig, ULONG cbSig, mdToken *pmd)
Finds a specified member (field or method) in the current metadata scope. The
member you want is specified by td (its enclosing class or interface), wzName (its
name) and, optionally, its signature (pvSig, cbSig). If td is specified as mdTokenNil,
then the lookup is done for a global-variable or global-function. Recall that you may
have multiple members with the same name on a class or interface; supply its
signature to find the unique match.
FindMember only finds members that were defined directly on the class or interface;
it does not find inherited members. (FindMember is simply a helper method – it first
calls FindMethod; if that doesn’t find a match, it then calls FindField)
The signature passed in to FindMember must have been generated in the current
scope. That’s because signatures are bound to a particular scope. As discussed in
the Metadata-Structures Spec, a signature can embed a token that identifies the
enclosing Class or ValueType (the token is an index into the local TypeRef table). In
other words, you cannot build a runtime signature outside the context of the current
scope that can be used as input to FindMember.
in/out Parameter Description Required?
4.2.3 FindMethod
HRESULT FindMethod(mdTypeDef td, LPCWSTR wzName,
PCCOR_SIGNATURE pvSig, ULONG cbSig, mdMethodDef *pmd)
Finds a specified method in the current metadata scope. The field you want is
specified by td (its enclosing class or interface), wzName (its name) and optionally,
its signature (pvSig, cbSig). If td is specified as mdTokenNil, then the lookup is done
for a global-function.
See FindMember description for more details.
Page 64
Metadata API
4.2.4 FindField
HRESULT FindField(mdTypeDef td, LPCWSTR wzName,
PCCOR_SIGNATURE pvSig, ULONG cbSig, mdFieldDef *pmd)
Finds a specified field in the current metadata scope. The field you want is specified
by td (its enclosing class or interface), wzName (its name) and optionally, its
signature (pvSig, cbSig). If td is specified as mdTokenNil, then the lookup is done for
a global-variable.
See FindMember description for more details.
in/out Parameter Description Required?
4.2.5 FindMemberRef
HRESULT FindMemberRef(mdTypeRef td, LPCWSTR wzName,
PCCOR_SIGNATURE pvSig, ULONG cbSig, mdMemberRef *pmr)
Finds a member reference in the current metadata scope. The reference you want is
specified by td (its owner class or interface), wzName (its name) and optionally, its
signature (pvSig, cbSig). If td is specified as mdTokenNil, then the lookup is done for
a global-variable or global-function.
The signature passed in to FindMember must have been generated in the current
scope. See FindMember description for details.
Page 65
Metadata API
4.2.6 FindTypeRef
HRESULT FindTypeRef(mdToken tkResScope, LPCWSTR wzName, mdTypeRef *ptr)
4.3.1 GetScopeProps
HRESULT GetScopeProps (LPWSTR wzName, ULONG cchName, ULONG *pchName,
GUID *pmvid)
Gets the properties for the current metadata scope that were set with a previous call
to SetModuleProps.
Page 66
Metadata API
4.3.2 GetModuleFromScope
HRESULT GetModuleFromScope (mdModule *pModule)
Gets the token for the module definition for the current scope.
in/out Parameter Description Required?
4.3.3 GetTypeDefProps
HRESULT GetTypeDefProps (mdTypeDef td, LPWSTR wzTypeDef,
ULONG cchTypeDef, ULONG *pchTypeDef, CLASSVERSION *pver,
DWORD *pdwTypeDefFlags,
mdToken *ptkExtends)
4.3.4 GetNestedClassProps
HRESULT GetNestedClassProps (mdTypeDef tdNested,
mdTypeDef *ptdEncloser)
Page 67
Metadata API
4.3.5 GetInterfaceImplProps
HRESULT GetInterfaceImplProps (mdInterfaceImpl iImpl,
mdTypeDef *pClass, mdToken *ptkIface)
Gets the information stored in metadata for a specified interface implementation.
Each time you call DefineTypeDef or SetTypeDefProps to define a type, you can
specify which interfaces that class implements, if any. For example, suppose a class
has an mdTypeDef token value of 0x02000007. And suppose it implements three
interfaces whose types have tokens 0x02000003 (TypeDef), 0x0100000A (TypeRef)
and 0x0200001C (TypeDef). Conceptually, this information is stored into an interface
implementation table like this:
Row Number Class Token Interface Token
4
5 02000007 02000003
6 02000007 0100000A
7
8 02000007 0200001C
GetInterfaceImplProps will return the information held in the row whose token you
provide in the iImpl argument. (Recall, the token is a 4-byte value; the lower 3 bytes
hold the row number, or RID; the upper byte holds the token type – 0x09 for
mdtInterfaceImpl).
[You obtain the value for iImpl by calling the EnumInterfaceImpls method]
in/out Parameter Description Required?
4.3.6 GetCustomAttributeProps
HRESULT GetCustomAttributeProps (mdCustomAttribute caOrCv,
mdToken *ptkOwner, mdToken *ptkType,
void const **ppBlob, ULONG *pcbBlob)
Page 68
Metadata API
ptk is the token for the owner – that’s to say, the metadata item this custom
attribute, or custom value, is attached to. A custom attribute can be attached to any
sort of owner, with the sole exception of an mdCustomAttribute. A custom value can
be attached to any sort of owner.
If caOrCv is a custom attribute, then ptkType is the mdMethodDef or mdMemberRef
token for its constructor method. If caOrCv is a custom value, then ptkType is an
mdTypeRef (that need not resolve to a matching TypeDef)
4.3.7 GetCustomAttributeByName
HRESULT GetCustomAttributeByName (mdToken tdOwner, LPCWSTR wzName,
const void **ppBlob, ULONG *pcbBlob)
Returns the information stored for a custom attribute or custom value, where you
specify the target by its owner and name. See GetCustomAttributeProps for more
detail.
In the case of a custom value, the name is simply the name you gave it via your call
to DefineTypeRef. In the case of a genuine custom attribute, the name is the name
of the attribute class (see Section 3.2.3)
in/out Parameter Description Required?
Note that it is quite legal to define multiple custom attributes for the same owner;
they may even have the same name. GetCustomAttributeByName returns only one
of those multiple instances (in fact, the first it encounters, but that behaviour is not
guaranteed). Use the EnumCustomAttributes if you want to find them all.
Note: if you need to determine whether this custom ‘blob’ is a custom value or a
genuine custom attribute, you need to check its token; that’s to say, the token
supplied as the tkAttrib argument in the DefineCustomAttribute call that created it.
Page 69
Metadata API
4.3.8 GetMemberProps
HRESULT GetMemberProps(mdToken md, mdTypeDef *pClass, LPWSTR wzName,
ULONG cchName, ULONG *pchName, DWORD *pdwAttr,
PCCOR_SIGNATURE *ppSig, ULONG *pcbSig, ULONG *pulCodeRVA,
DWORD *pdwImplFlags, DWORD *pdwDefType, void const **ppValue, ULONG
*pcbValue)
Gets the information stored in metadata for a specified member definition. This is a
simple helper method: if md is a MethodDef, then we call GetMethodProps; if md is a
FieldDef, then we call GetFieldProps. See these other methods for details.
4.3.9 GetMethodProps
HRESULT GetMethodProps(mdMethodDef md, mdTypeDef *pClass,
LPWSTR wzName, ULONG cchName, ULONG *pchName,
DWORD *pdwAttr, PCCOR_SIGNATURE *ppvSig, ULONG *pcbSig,
ULONG *pulCodeRVA, DWORD *pdwImplFlags)
Retrieves a method definition in the current metadata scope. The method you want
is specified by md (its MethodDef token).
in/out Parameter Description Required?
4.3.10 GetFieldProps
HRESULT GetFieldProps(mdFieldDef fd, mdTypeDef *pClass, LPWSTR wzName,
ULONG cchMember, ULONG *pchMember, DWORD *pdwAttr, PCCOR_SIGNATURE
*ppvSig, ULONG *pcbSig, DWORD *pdwDefType, void const **ppValue,
ULONG *pcbValue)
Page 70
Metadata API
4.3.11 GetParamProps
HRESULT GetParamProps (mdParamDef pd, mdMethodDef pmd,
ULONG *pulSequence, LPWSTR wzName, ULONG cchName,
ULONG *pchName, DWORD *pdwAttr, DWORD *pdwDefType,
void const **ppValue, ULONG *pcbValue)
Page 71
Metadata API
4.3.12 GetParamForMethodIndex
HRESULT GetParamForMethodIndex(mdMethodDef md, ULONG ulParamSeq,
mdParamDef *ppd)
Returns the definition of parameter number ulParamSeq for the method (or global-
function) whose token is md. A value of 0 for ulParamSeq denotes the return value;
parameters are numbered starting at 1.
in/out Parameter Description Required?
4.3.13 GetPinvokeMap
HRESULT GetPinvokeMap(mdToken tk, DWORD *pdwMappingFlags,
LPCWSTR wzName, ULONG cchName,
ULONG *pchName, mdModuleRef *pmrImportDLL)
Returns the PInvoke information stored for a given method. (PInvoke is a Runtime
service that supports inter-operation with unmanaged code)
in/out Parameter Description Required?
4.3.14 GetFieldMarshal
HRESULT GetFieldMarshal(mdToken tk, PCCOR_SIGNATURE *ppNativeType,
ULONG *pcbNativeType)
Returns the marshaling information for a field, method return, or method parameter
(See SetFieldMarshal for details)
Page 72
Metadata API
tk is an mdFieldDef or mdParamDef
4.3.15 GetRVA
HRESULT GetRVA(mdToken tk, ULONG *pulCodeRVA, DWORD *pdwImplFlags)
Returns the code RVA and implementation flags for a given member.
in/out Parameter Description Required?
tk must be one of mdMethodDef mdFieldDef. In the latter case, the field must be a
global-variable
dwImplFlags is a bitmask from the CorMethodImpl enum in CorHdr.h (not relevant if
tk is an mdFieldDef)
4.3.16 GetTypeRefProps
HRESULT GetTypeRefProps(mdTypeRef tr, mdToken *ptkResScope,
LPWSTR wzName, ULONG cchName, ULONG *pchName)
4.3.17 GetMemberRefProps
HRESULT GetMemberRefProps(mdMemberRef mr, mdToken *ptk,
LPWSTR wzMember, ULONG cchMember, ULONG *pchMember,
PCCOR_SIGNATURE *ppSig, ULONG *pcbSig)
Page 73
Metadata API
4.3.18 GetModuleRefProps
HRESULT GetModuleRefProps(mdModuleRef mr, LPWSTR wzName,
ULONG cchName, ULONG *pchName)
4.3.19 GetPropertyProps
HRESULT GetPropertyProps(mdProperty prop, mdTypeDef *pClass,
LPWSTR wzName, ULONG cchName, ULONG *pchName,
DWORD *pdwFlags, PCCOR_SIGNATURE *ppSig, ULONG *pbSig,
DWORD *pdwDefType, const void **ppValue,
ULONG *pcbValue, mdMethodDef *pmdSetter,
mdMethodDef *pmdGetter, mdMethodDef rmdOtherMethods[],
ULONG cMax, ULONG *pcOtherMethods, mdFieldDef *pmdBackingField)
Page 74
Metadata API
4.3.20 GetEventProps
HRESULT GetEventProps(mdEvent ev, mdTypeDef *pClass, LPCWSTR wzEvent,
ULONG cchEvent, ULONG *pchEvent, DWORD *pdwEventFlags,
mdToken *ptkEventType, mdMethodDef *pmdAddOn,
mdMethodDef *pmdRemoveOn, mdMethodDef *pmdFire,
mdMethodDef rOtherMethods[], ULONG cOtherMethods,
ULONG *pcOtherMethods)
Returns the information previously defined for a given event. See DefineEvent for
more information
Page 75
Metadata API
out rOtherMethods[] Array to hold tokens for the event’s other methods no
4.3.21 GetMethodSemantics
HRESULT GetMethodSemantics(mdMethodDef md, mdToken tkProp,
DWORD *pdwSemantics)
Returns the semantic flags for a given property. Note that there is no Define method
that creates those flags – they are derived from the DefineProperty method – for
example, if a method was specified as a Getter, then that method’s semantic flags
would have the msGetter bit set.
in/out Parameter Description Required?
4.3.22 GetClassLayout
HRESULT GetClassLayout(mdTypeDef td, DWORD *pdwPackSize,
COR_FIELD_OFFSET rFieldOffsets[], ULONG cMax,
ULONG *pcFieldOffsets, ULONG *pulClassSize)
Returns the layout of fields for a class, defined by an earlier call to SetClassLayout.
Page 76
Metadata API
4.3.23 GetSigFromToken
HRESULT GetSigFromToken(mdSignature tkSig, PCCOR_SIGNATURE *ppSig,
ULONG *pcbSig)
Returns the signature for a given standalone-signature token (the tkSig parameter
effectively indexes a row in the StandAloneSig table)
in/out Parameter Description Required?
4.3.24 GetTypeSpecFromToken
HRESULT GetTypeSpecFromToken(mdTypeSpec typespec,
PCCOR_SIGNATURE *ppSig, ULONG *pcbSig)
Returns the TypeSpec whose token is typespec (the typespec parameter effectively
indexes a row in the TypeSpec table)
in/out Parameter Description Required?
4.3.25 GetUserString
HRESULT GetUserString(mdString stk, LPWSTR wzString,
ULONG cchString, ULONG *pchString)
Page 77
Metadata API
Returns the user string, previously stored into metadata (by the DefineUserString
method), whose token is stk.
in/out Parameter Description Required?
4.3.26 GetNameFromToken
HRESULT GetNameFromToken(mdToken tk, MDUTF8CSTR *pwzName)
Returns a pointer, within metadata structures, to the name string for token tk. tk
must be one of mdModule, mdTypeRef, mdTypeDef, mdFieldDef, mdMethodDef,
mdParamDef, mdMemberRef, mdEvent, mdProperty, mdModuleRef, else the method
will return failure
in/out Parameter Description Required?
4.3.27 ResolveTypeRef
HRESULT ResolveTypeRef(mdTypeRef tr, REFIID riid, IUnknown **ppIScope,
mdTypeDef *ptd)
Resolves a given TypeRef token, by looking for its definition in other modules. If
found, it returns an interface to that module scope in ppIScope, as well as the type
definition token, in that module, for the requested type.
in/out Parameter Description Required?
riid specifies the interface you would like returned for the module that holds the
definition of the referenced type. Typically this would be IID_IMetaDataImport; see
OpenScope for more information
Page 78
Metadata API
5 IMetaDataTables
There is a further interface used to query metadata – it is called IMetaDataTables,
and is defined in the Cor.h header file. It provides very low-level read-access to
metadata information – at the level of the physical tables. The layout of these tables
is not guaranteed stable, and may change.
In order to see an example of how to use this API, see the sample code for the
“MetaInfo” tool which ships with the .NET SDK. In particular, those code paths
corresponding to the “-raw” and “-heaps” command line switches to that tool.
Page 79
Metadata API
6 MethodImpls
6.1 Intro
A MethodImpl is a record in MetaData that allows a class to implement two or more
inherited methods, whose names and signatures match. For example, class C
implements interfaces I and J – both interfaces include a method int Foo (int). How
does C provide two implementations, one for I::Foo and one for J::Foo? [The only
solution today is for the programmer to avoid the name collision by changing one of
I::Foo or J::Foo]
6.2 Details
MethodImpls record a 3-way association among tokens. The three items associated
together are:
class being defined
method whose body we want to use for the implementation
method whose MethodTable slot we want to use
Page 80
Metadata API
We don’t require a MethodDef for the last method, since we are providing no code.
Instead, we hijack the code for method F provided by class B.
The prefix mr represents a MethodRef token. In the case where I, J, B and C all lie in
the same module, then MethodRefs such as mrFinB would be changed to the
corresponding MethodDef mdFinB through Ref-to-Def folding (or the compiler might
do so itself)
Note that the body referenced in a MethodImpl has two constraints:
It must be a virtual function. It cannot be for a non-virtual
It must be implemented by a parent of the current class. You cannot hijack
arbitrary virtual functions from classes that are unrelated to the current class
– even when their name and signature matches what’s required.
6.4 Notes
The third column in a MethodImpl must be supplied as non-nil. (otherwise,
there’s a possible loophole that allows a compiler to use MethodImpls to
rename an inherited method within a sub-class)
Page 81
Metadata API
Page 82
Metadata API
7 NestedTypes
7.1 Introduction
This appendix summarizes support for nested types. It explains both how they are
stored and retrieved from metadata, and the semantics given them by the runtime.
We include examples, and explanation/exploration of what is provided and what not.
7.2 Definition
A Type is any of: Class, ValueType, Interface or Delegate. A NestedType is a Type
whose definition, in the source language, is lexically enclosed within the definition of
anotherTtype. We refer to these two by the names, “nested” Type and “encloser”
Type; sometimes as “nestee” and “nester”, respectively.
The support provided by the Runtime for nested Types is quite minimal. In essence,
metadata provides an extra association for a NestedType – the TypeDef token of its
encloser. And, at runtime, the Common Language Runtime (CLR) provides access
from nestee methods to members defined within its encloser.
Page 83
Metadata API
4. A NestedType has access to all members of its enclosing type, without restriction.
In this regard, it behaves just like a part of the implementation of the enclosing
type. That is:
[exported] class EnclosingClass {
family static int i;
private static int j;
public class NestedClass {
void bar () {
j = 1; // OK
EnclosingClass.j = 1; // OK
i = 1; // OK
EnclosingClass.i = 1; // OK
}
}
}
5. NestedTypes may be nested arbitrarily deep
6. A NestedType may be subtyped, and may subtype another Type, entirely
independently of its nesting hierarchy. For example:
class A {
family static int i;
class X {
X() {
i = 1; // OK – sets A.i
A.i = 1; // OK – same effect as previous line
}
}
}
class Y : A.X {
void foo() {
i = 1; // won’t compiler - unknown identifier
A.i = 1; // won’t compile - i not accessible - Y not in the scope of A,
// even though Y inherits from A.X
}
}
class B : A {
class Z : X {
void bar () {
i = 1; // OK – sets the i field (in base class A)
A.i = 1; // OK – allowed since Z, via inheritance from X, is within
// the scope of A
B.i = 1; // OK – since B derives from A
}
}
}
Note that in the bar method, all 3 assignments update the same field (ie the same
cell in memory). As another example, a NestedType could inherit from its enclosing
type:
public class EnclosingClass {
public static int i;
private static int j;
private virtual void Foo() { }
public class NestedClass : EnclosingClass {
private override void Foo() { }
void bar () {
j = 1; // OK
this.j = 1; // OK
i = 1; // OK
this.i = 1; // OK
}
}
}
As the above examples illustrate, resolving references through the inheritance chain
and through the nesting hierarchy can get complex. Lightning will give precedence
Page 84
Metadata API
to the inheritance chain; if there is no resolution within that chain, then it will
traverse the nesting hierarchy.
7. When emitting metadata:
o A NestedType will be emitted using DefineNestedType:
o Mark its visibility nested (see revised type visibility rules, below).
o Like any member within a Type, its name must be unique within that Type.
Because the Type is a NestedType, it does not conflict with any module-
level Type of the same name. There is no need for compilers to mangle
the name of the nestee in order to make it unique at module-level. The
runtime loader will take account of the Type’s nested status when it comes
to find the right Type to load.
o The definition of a NestedType must occur in the same module as that of
its encloser
o In the current implementation, metadata actually preserves the order of
emitted TypeDefs; however, tools should not rely that metadata
enumerations will return NestedTypes before, or after, their enclosers
o The TypeDef for a NestedType has one extra item of metadata, compared
with a regular TypeDef. This additional item is the token for its enclosing
type. This is persisted internally in a two-column look-aside table, holding the
Typedefs of nestee and encloser. The Runtime uses this to determine
whether the enclosing Type is, or is not, visible outside of the assembly. Note
that because we losslessly capture the nesting hierarchy in metadata, there is
no parsing of mangled type names required to “guess” the nesting structure
o References to a NestedType will be emitted as TypeRefs. Upon resolution,
the Runtime will observe that the visibility is nested and thus will apply
member access rules
8. While importing a metadata file, suppose a language or tool, that is blind to
NestedTypes, stumbles upon the definition of a NestedType. Firstly, it must
recognize the Type as nested because it has one of the possible tdNestedXXX bits
set in its TypeDef flags. [Every language or tool must recognize all bits in the
CorTypeAttr enum – including tdNestedXXX. It need not implement the semantics
those bits demand; it can simply stay away; but it must know enough to make
that choice] Having found a NestedType, the compiler/tool has two choices:
o Don't expose that nested type
o Expose that nested type as a module-level type, iff its encloser were
visible and the member access rule on the nested type is NestedPublic
9. You can freely nest all Types – Classes, ValueTypes, Interfaces and Delegates.
[The common case will be a Class which nests an Interface, but Runtime supports
any permutations]
Page 85
Metadata API
7.5 Naming
Within a module, all Types must of course have a unique name. And within a Type,
all NestedTypes must of course have a unique name. Note that metadata does not
require that the names of NestedTypes be unique within the module.
[Note that a previous proposed design for nested types did require that names of
nested types be module-wide unique. Since it would be unreasonable for the
language to pass this burden upwards to its users, it required the language to
generate a uniqufied name for each nested type. A de facto standard of emerged
earlier during development of concatenating the type names together, with a $
separator character. This would generate AA$BB as the name for the inner class in
the last example. Whilst this worked OK for each language individually, it required
that the mangling scheme be agreed across all languages that used the Runtime and
wanted to share code]
Page 86
Metadata API
In order to resolve this reference, walk ‘up the tree’ towards the root. Since this is
driven by token, rather than by name, we find a unique path to the root. (If done by
name, we could start with 10 “foo”s. We would end up with one unique path to root,
but the interim tracking cost would be high)
This scheme works because NestedTypes must be defined within the same module as
their enclosing type – never by a TypeRef.
[This proposed scheme replaces the current de facto practice where the compile
invents a mangled name of A$B$C or similar. With this new proposal, we don’t
mangle names. And we circumvent the view that a TypeRef for A$B$C refers to a
global class with a name of A$B$C]
Page 87
Metadata API
class A {
private: static int i;
class B {
void m();
}
}
method m cannot reference the private field i of its enclosing class. However, in the
definition of Runtime NestedTypes, it is clear that the Runtime would allow such an
access to proceed – it would pass verification and run. Does this represent a
problem?
The answer is no. The C++ compiler can use the support provided by Runtime for
NestedTypes; it can deny any attempted access to field i by method m at compile
time, and so preserve the semantics of the language. If another language imports
that module’s metadata there is again no problem, since it cannot emit code that
runs within the nested lexical scope of class A. [It can of course emit code that
attempts to access field i from outside of class A, but the Runtime would correctly fail
that access since the field is marked private]
Page 88
Metadata API
one static field, bsi, of type int. The compiler calls DefineField, passing the name bsi,
a flags value of fdPublic|fdStatic, and a signature of ELEMENT_TYPE_I4. This call
returns a FieldDef token, which we’ll call fdbsi. Class B also has one instance field,
bii, of type int. The compiler calls DefineField, passing the name bii, a flags value of
fdPublic, and a signature of ELEMENT_TYPE_I4. This call returns a FieldDef token,
which we’ll call fdbii.
From this point forwards in time, metadata recognizes class B as nested solely
because its flags value is one of the tdNestedXXX bunch – that’s to say, one of
tdNestedPublic, tdNestedPrivate, tdNestedFamily, tdNestedAssembly,
tdNestedFamANDAssem or tdNestedFamORAssem.
Note that, as far as metadata is concerned, the only thing different about class B
compared with class A, is that it is marked as “nested”, and therefore has an
associated encloser class (defined via tdA). Conversely, class A is not nested – its
flags value is simply tdPublic – and it therefore has no associaated encloser.
Note too that, as explained above, an instance of class A (ie an A object) has only one
field, which we called aii. In particular, there is no field containing a reference to a B
object; nor yet space allocated within the body of A to hold a B object. So, if we
attempt to compile the following code fragment, it will fail, as noted in the
comments:
public static void main(String[] args) {
A a = new A(); // create an A object
a.aii = 42; // works fine
a.bii = 9; // doesn’t work - no such field
a.B.bii = 9; // doesn’t work - no such field
}
Let’s go on and now create an instance of the nestee and see how nester and nestee
relate:
public static void main(String[] args) {
A a = new A(); // create an A object
A.B b = new A.B(); // create a B object
A.asi = 1; //
a.aii = 2; //
A.B.bsi = 3; //
b.bsi = 4; // reach static field via object
b.bii = 4; //
}
So far there is nothing at all surprising – the user of course has to specify he wants to
create that nested class B – saying B on its own doesn’t work since there is no
TypeDef for anything called B (just a NestedTypeDef). Each language may invent its
own syntax for how to ‘reach’ B – the example has chosen the ‘obvious’ one of A.B.
But apart from this naming wrinkle, everything would work the same if, instead of the
nested B, we had been creating and operating upon a class C, defined at top level.
Where the behaviour of nested types does differ is that they lie within the lexical
scope of their encloser, and so have unbridled access to all fields, properties and
methods of that encloser – even if those fields, properties and methods are marked
private. In this respect, the nested type is on a par with other methods and
properties defined within the encloser. Here is an example that illustrates this:
Page 89
Metadata API
public class B {
private static int bsi = 3;
private int bii = 4;
public static void ShowBsi() {Console.WriteLine("B.bsi = " + B.bsi);}
public void ShowBii() {Console.WriteLine(" bii = " + bii);}
public static void bsm() {
asi = 10; // same as: A.asi = 10;
bsi = 11; // same as: B.bsi = 11;
}
public void bim(A x) {
asi = 13; // same as: A.asi = 13;
bsi = 14; // same as: B.bsi = 14;
bii = 15;
x.asi = 16;
x.aii = 17;
}
}
}
Page 90
Metadata API
public class AA {
public int aii;
public BB pbb; // DefineField(“pbb”, fdPublic, sig) => fdpbb
public class BB {
public int bii;
public AA paa; // DefineField(“paa”, fdPublic, sig) => fdpaa
}
}
With this definition of AA, we can write programs like the following, and things work
fine:
public static void main(String[] args) {
AA aa = new AA(); // create an AA object
AA.BB bb = new AA.BB(); // create a BB object
aa.pbb = bb; // hook ‘em one way
bb.paa = aa; // hook ‘em the other
aa.aii = 1; // works fine
aa.pbb.bii = 2; // works fine
bb.paa.aii = 3; // works fine, even if aii were private
}
A language may choose to hide all of this plumbing detail from their users, and
present a model that automatically provides an object reference field with the
encloser, and nestee, etc. The point is, metadata will not generate such plumbing. If
a language wants it, the compiler must emit, via DefineField calls, as shown in the
code comment above.
Such additions, by the compiler, can clearly be used as a route to implement “inner”
classes
Page 91
Metadata API
Page 92
Metadata API
System.SerializableAttribute
System.NonSerializedAttribute
For a definition of these PCAs, see the online doc for .NET Framwork class libraries, or
the “Data Interop” spec.
Page 93
Metadata API
CAs that affect the security checks performed upon method invocations at runtime:
System.Security.DynamicSecurityMethodAttribute
System.Security.Permissions.SecurityAttribute
System.Security.Permissions.CodeAccessSecurityAttribute
System.Security.Permissions.EnvironmentPermissionAttribute
System.Security.Permissions.FileDialogPermissionAttribute
System.Security.Permissions.FileIOPermissionAttribute
System.Security.Permissions.IsolatedStoragePermissionAttribute
System.Security.Permissions.IsolatedStorageFilePermissionAttribute
System.Security.Permissions.PermissionSetAttribute
System.Security.Permissions.PublisherIdentityPermissionAttribute
System.Security.Permissions.ReflectionPermissionAttribute
System.Security.Permissions.RegistryPermissionAttribute
System.Security.Permissions.SecurityPermissionAttribute
System.Security.Permissions.SiteIdentityPermissionAttribute
System.Security.Permissions.StrongNameIdentityPermissionAttribute
System.Security.Permissions.UIPermissionAttribute
System.Security.Permissions.ZoneIdentityPermissionAttribute
System.Security.Permissions.PrincipalPermissionAttribute
System.Security.SuppressUnmanagedCodeSecurityAttribute
System.Security.UnverifiableCodeAttribute
CA that denotes a TLS (thread-local storage) field:
System.ThreadStatic
The following CAs are used by the ALink tool to transfer information between Modules
and Assemblies (they are temporarily ‘hung off’ a TypeRef to a class called
AssemblyAttributesGoHere) then merged by ALink and ‘hung off’ the assembly:
System.Runtime.CompilerServices.AssemblyOperatingSystemAttribute
System.Runtime.CompilerServices.AssemblyProcessorAttribute
System.Runtime.CompilerServices.AssemblyCultureAttribute
System.Runtime.CompilerServices.AssemblyVersionAttribute
System.Runtime.CompilerServices.AssemblyKeyFileAttribute
System.Runtime.CompilerServices.AssemblyKeyNameAttribute
System.Runtime.CompilerServices.AssemblyDelaySignAttribute
9 Bitmasks
This section explains the various bitmasks used to define attributes of Types,
Methods, Fields, etc. All of the enums described in this section are defined in
CorHdr.h, which ships with the .NET SDK
Page 94
Metadata API
mdtFieldDef 0x04000000
mdtMethodDef 0x06000000
mdtParamDef 0x08000000
mdtInterfaceImpl 0x09000000
mdtMemberRef 0x0a000000
mdtCustomAttribute 0x0c000000
mdtPermission 0x0e000000
mdtSignature 0x11000000
mdtEvent 0x14000000
mdtProperty 0x17000000
mdtModuleRef 0x1a000000
mdtTypeSpec 0x1b000000
mdtAssembly 0x21000000
mdtAssemblyRef 0x25000000
mdtFile 0x29000000
mdtComType 0x2a000000
mdtManifestResource 0x2b000000
mdtExecutionLocation 0x2d000000
mdtString 0x70000000
mdtName 0x71000000
Page 95
Metadata API
Page 96
Metadata API
show which settings are illegal. The table includes horizontal, shaded bands: these
gather together flags that are mutually exclusive. Specifically:
If defining a nested type or valuetype, you must set exactly one of the block of
flags tdNestedPublic thru tdNestedFamOrAssem
If defining a class, valuetype or unmanaged valuetype, you must set exactly one
of tdAutoLayout, tdLayoutSequential or tdExplicitLayout
Figure 1 – Legal Flag Combinations from CorTypeAttr
Class Interface ValueType Unmgd ValueType
tdClass
tdInterface
tdValueType
tdUnmanagedValueTyp
e
tdNotPublic
tdPublic
tdNestedPublic
tdNestedPrivate
tdNestedFamily
tdNestedAssembly
tdNestedFamANDAssem
tdNestedFamOrAssem
tdAutoLayout
tdLayoutSequential
tdExplicitLayout
tdAbstract
tdSealed
tdSpecialName
tdRTSpecialName
Notes:
The runtime also takes note of each Type’s inheritance chain to decide how to treat
them –
System.ValueType
System.Enum
System.MarshalByRefObject
System.ContextBoundObject
Page 97
Metadata API
Reserved for internal use : do not set these via the metadata APIs
fdRTSpecialName
fdHasFieldMarshal
fdHasSecurity
fdHasDefault
fdHasFieldRVA
Page 98
Metadata API
mdFamily : callable only by methods within its family; ie, its own type and
any sub-types
mdAssem : callable only by methods within its assembly
mdFamANDAssem : callable only by methods lying in the intersection of its
family and assembly
mdFamORAssem : callable only by methods lying in the union of its family
and assembly
mdPrivateScope : method cannot be called (typically used by a compiler for
a method whose scope is restricted to its compiland; eg C++ static global
function)
Method Contract
mdStatic : method is defined for a type (else an instance method). Note this
flag is encoded into a field signature; you don’t need to specify both, but if
you do, they should match
mdFinal : method may not be over-ridden by a sub-class. Mutually exclusive
with mdAbstract
mdVirtual : method is virtual
mdAbstract : method has no implementation in this class. Mutually
exclusive with mdFinal
mdHideBySig : method is hidden by name + signature, else just by name
mdUnmanagedExport : method is managed, but exported via the EAT, to
unmanaged code (runtime inserts a marshalling thunk)
mdPinvokeImpl : method is called via PInvoke dispatch
mdSpecialName : method is special : its name says in what way. Used, for
example, for operator overload
Vtable Layout
mdReuseSlot : reuse an existing slot. Note that if the superclass’ declaration
is deleted, and you have no references to that method in your
implementation, then your declaration will be used to create the slot (may
hide)
mdNewSlot : method always gets a new slot (hides)
Reserved for internal use : do not set these via the metadata APIs
mdRTSpecialName : method is treated specially by the runtime. If set, you
must also set the mdSpecialName bit (eg “.ctor”)
mdHasSecurity
mdRequireSecObject
Page 99
Metadata API
pdHasDefault
pdHasFieldMarshal
pdReserved3
pdReserved4
Page 100
Metadata API
Page 101
Metadata API
mdFieldDef ridOfField;
ULONG ulOffset;
Page 102
Metadata API
MDDupTypeDef
MDDupInterfaceImpl
MDDupMethodDef
MDDupTypeRef
MDDupMemberRef
MDDupMethodImpl
MDDupCustomValue
MDDupCustomAttribute
MDDupParamDef
MDDupPermission
MDDupProperty
MDDupEvent
MDDupFieldDef
MDDupSignature
MDDupModuleRef
MDDupTypeSpec
MDDupImplMap
MDDupOrdinalMap
MDDupAssemblyRef
MDDupFile
MDDupComType
MDDupManifestResource
MDDupExecutionLocation
MDDupDefault : the default, set to MDNoDupChecks | MDDupTypeRef |
MDDupMemberRef | MDDupSignature | MDDupTypeSpec
MDDupAll : set all bits on
MDDupENC : default for Edit & Continue – same as MDDupAll
Page 103
Metadata API
MDNotifyParamDef
MDNotifyMethodImpl
MDNotifyInterfaceImpl
MDNotifyProperty
MDNotifyEvent
MDNotifySignature
MDNotifyTypeSpec
MDNotifyCustomValue
MDNotifyCustomAttribute
MDNotifySecurityValue
MDNotifyPermission
MDNotifyModuleRef
MDNotifyNameSpace
MDNotifyDebugTokens : covers all debug tokens
MDNotifyAssemblyRef
MDNotifyFile
MDNotifyComType
MDNotifyResource
MDNotifyExecutionLocation
MDNotifyDefault : MDNotifyTypeRef | MDNotifyMethodDef |
MDNotifyMemberRef | MDNotifyFieldDef
MDNotifyAll : set all bits on
Page 104
Metadata API
Page 105
Metadata API
Page 106
Metadata API
Modifiers
ELEMENT_TYPE_CMOD_REQD : required modifier; if a compiler imports a
type with this modifier set, it should only use the type if it ‘understands’ the
required semantic of the language that defined the type
ELEMENT_TYPE_CMOD_OPT : optional modifier; if a compiler imports a type
with this modifier set, it is OK to use
ELEMENT_TYPE_MODIFIER : set this bit, together with either of the
following:
ELEMENT_TYPE_SENTINEL : sentinel to mark end of predefined arguments
in a varargs method signature
ELEMENT_TYPE_PINNED : object is pinned against garbage reclamation
Modifier Bits : ‘or’ these bits into the previous values, if required (actually two bits
in the high nybble of the calling convention byte)
IMAGE_CEE_CS_CALLCONV_HASTHIS : JIT a ‘this’ argument for this method
IMAGE_CEE_CS_CALLCONV_EXPLICITTHIS : this parameter is explicitly in
the signature
Page 107
Metadata API
IMAGE_CEE_CS_END
IMAGE_CEE_CS_VOID
IMAGE_CEE_CS_I4
IMAGE_CEE_CS_I8
IMAGE_CEE_CS_R4
IMAGE_CEE_CS_R8
IMAGE_CEE_CS_PTR
IMAGE_CEE_CS_OBJECT
IMAGE_CEE_CS_STRUCT4
IMAGE_CEE_CS_STRUCT32
IMAGE_CEE_CS_BYVALUE
Page 108
Metadata API
10 Signatures
The word signature is conventionally used to describe the type info for a function or
method – that’s to say, the type of each of its parameters, and the type of its return
value. Within metadata, we extend the use of the word signature to also describe
the type info for fields, properties and local variables. Each Signature is stored as a
(counted) byte array in the Blob heap. There are five sorts of Signature, as follows:
MethodDefSig
MethodRefSig – differs from a MethodDefSig only for VARARG calls
FieldSig
PropertySig
LocalVarSig
You can tell which sort of Signature blob you are looking at from the value of its
leading byte (see later)
This section defines the binary blob format for each sort of Signature. For the most
part, we use syntax diagrams (hopefully easier to understand than formal XML or
EBNF)
Note that Signatures are ‘compressed’ before being stored into the blob heap. It’s
actually the compiler or code generator who is responsible for compressing them,
before passing them into the metadata engine. However, all compilers use the same
small family of helper functions, defined in Cor.h, to do this task –
CorSigCompressData / CorSigUncompressData
CorSigCompressSignedInt / CorSigUncompressSignedInt
CorSigCompressToken / CorSigUncompressToken
(Note that CorSigCompressSignedInt is not currently used to build in Signatures). In
order to uncompress a value in a Signature, you must know (from its position in the
Signature) whether to call CorSigUncompressData or CorSigUncompressToken
Signatures include two modifiers called:
ELEMENT_TYPE_BYREF – this element ‘points’ to data item which may be
allocated from the GC heap, or from elsewhere. It may ‘point’ to the start of
an object, or to the interior of an object. Either way, the GC is notified of its
existence; if it actually ‘points’ into the heap, then GC knows to update its
value if it moves the object pointed-to during a garbage collection. This
modifier can only occur in the definition of Param (section 10.10) or RetType
(section 10.11). It may not occur within the definition of a Field (section 10.4)
[conceptually you could imagine a runtime that did support BYREF fields, but
ours doesn’t – BYREFs, especially those that point into the interior of an object
in the GC heap, are expensive to track – since there’s no very strong
requirement for BYREF fields, we excluded them]
ELEMENT_TYPE_PTR – this element ‘points’ to a data item which is not
allocated from the GC heap. This modifier can occur in the definition of Param
(section 10.10) or RetType (section 10.11) or Field (section 10.4)
Page 109
Metadata API
10.1 MethodDefSig
A MethodDefSig is indexed by the Method.Signature column. It captures the
signature of a method or global function. The syntax chart for a MethodDefSig looks
like this:
MethodDefSig
VARARG
RetType Param
10.2 MethodRefSig
A MethodRefSig is indexed by the MemberRef.Signature column. This provides the
callsite Signature for a method. Normally, this callsite Signature must match exactly
the Signature specified in the definition of the target method. For example, if a
method Foo is defined that takes two uint32s and returns void; then any callsite must
index a signature that takes exactly two uint32s and returns void. In this case, the
Page 110
Metadata API
syntax chart for a MethodRefSig is identical with that for a MethodDefSig – see
section 10.1
The Signature at a callsite differs from that at its definition, only for a method with
the VARARG calling convention. In this case, the callsite Signature is extended to
include info about the extra VARARG arguments (for example, corresponding to the
“...” in C syntax). The syntax chart for this case is:
10.3 StandAloneMethodSig
A StandAloneMethodSig is indexed by the StandAloneSig.Signature column. It is
typically created as preparation for executing a calli instruction. It is very similar to a
MethodRefSig, in that it represents a callsite signature, but its calling convention may
specify an unmanaged target (the calli instruction invokes either managed, or
unmanaged code). Its syntax chart looks like this:
Page 111
Metadata API
StandAloneMethodSig
VARARG
STDCALL
THISCALL
FASTCALL
Page 112
Metadata API
10.4 FieldSig
A FieldSig is indexed by the Field.Signature column, or by the MemberRef.Signature
column (in the case where it specifies a reference to a field, not a method, of course).
The Signature captures the field’s definition. The field may be a static or instance
field in a class, or it may be a global variable. The syntax chart for a FieldSig looks
like this:
FieldSig
FIELD Type
10.5 PropertySig
A PropertySig is indexed by the Property.Type column. It captures the type info for a
Property – that’s to say:
how many parameters are supplied to its setter method
the base type of the Property – the type returned by its getter method
type info for each parameter in the getter method – that’s to say, the index
parameters
The syntax chart for a PropertySig looks like this:
PropertySig
Page 113
Metadata API
10.6 LocalVarSig
A LocalVarSig is indexed by the StandAloneSig.Signature column. It captures the
type of all the local variables in a method. Its syntax chart looks like this:
LocalVarSig
10.7 CustomMod
The CustomMod (custom modifier) item in Signatures has a syntax chart like this:
CustomMod
CMOD_OPT TypeDefEncoded
CMOD_REQD TypeRefEncoded
Page 114
Metadata API
This item is followed by an metadata token that indexes a row in the TypeDef table
or the TypeRef table. However, these tokens are encoded and compressed – see
section 10.8 for details
If the CustomModifier is tagged CMOD_OPT, then any importing compiler can freely
ignore it entirely. Conversely, if the CustomModifier is tagged CMOD_REQD, any
importing compiler must ‘understand’ the semantic implied by this CustomModifier in
order to reference the surrounding Signature.
A typical use for a CustomModifier is for VC++ to tag a const parameter to a method
So, instead of the original, regular TypeRef token value of 0x01000012, requiring 4
bytes of space in the Signature blob, we encode it as a single byte.
Note that there are two helper functions in Cor.h – CorSigCompressToken and
CorSigUncompressToken that combine these steps together (encoding the target
table type and compressing)
10.9 Constraint
The Constraint item in Signatures currently has only one possible value –
ELEMENT_TYPE_PINNED, which specifies that the target type is pinned in the runtime
heap, and will not be moved by the actions of garbage collection. Note that the
Compiler calls CorCompressData to compress the value for Modifier before inserting
into the Signature blob; but today’s value is small enough that it compresses to a
single byte.
Page 115
Metadata API
A Constraint can only be applied within a LocalVarSig (not a FieldSig). The Type of
the local variable must either be a reference type (in other words, it points to the
actual variable – for example, an Object, or a String); or it must include the BYREF
item. The reason is that local variables are allocated on the runtime stack – they are
never allocated from the runtime heap; so unless the local variable points at an
object allocated in the GC heap, pinning makes no sense.
[Note: in previous versions, Constraint could also include a VOLATILE value.
However, this constraint was removed from the Signature – compilers instead issue IL
instructions that indicate the target variable is volatile]
10.10 Param
The Param (parameter) item in Signatures has a syntax chart like this:
Param
TYPEDBYREF
10.11 RetType
The RetType (return type) item in Signatures has a syntax chart like this:
Page 116
Metadata API
RetType
TYPEDBYREF
VOID
RetType is identical to Param except for one extra possibility, that it can include the
type VOID. This chart uses the following abbreviations:
BYREF for ELEMENT_TYPE_BYREF
TYPEDBYREF for ELEMENT_TYPE_TYPEDBYREF (see section 10.10)
VOID for ELEMENT_TYPE_VOID
CustomMod is defined in section 10.7. Type is defined in section 10.12
10.12 Type
The Type item in Signatures can be quite complicated. Below is a simple EBNF
grammar for Type. As usual, “|” separates alternatives, “*” denotes zero or more
occurrences, “?” denotes zero or one occurrence. Note that the last four productions
are all recursive: PTR, GENERICARRAY and SZARRAY are left-recursive, whilst ARRAY
is right-recursive.
Type := Intrinsic
| VALUETYPE TypeDefOrRefEncoded
| CLASS TypeDefOrRefEncoded
| STRING
| OBJECT
| PTR CustomMod* VOID
| PTR CustomMod* Type
| FNPTR MethodDefSig
| FNPTR MethodRefSig
| ARRAY Type ArrayShape
| SZARRAY CustomMod* Type
For compactness, we have missed out the ELEMENT_TYPE_ prefixes in this list. So,
for example, “CLASS” is shorthand for ELEMENT_TYPE_CLASS (see the
CorElementType enum defined in CorHdr.h)
10.12.1 Intrinsic
This represents the set of simple value types provided by the runtime. They are
defined as follows:
BOOLEAN | CHAR | I1 | U1 | I2 | U2 | I4 | U4 | I8 | U8 | R4 | R8 | I | U
Page 117
Metadata API
However, CLS does not support this full range of intrinsic types – it excludes those in
listed in the CLS rule below
10.13 ArrayShape
An ArrayShape has the following syntax chart:
ArrayShape
Page 118
Metadata API
Page 119
Metadata API
11 Custom Attributes
Programmers can attach CustomAttributes a programming element, such as a
method or field. Each CustomAttribute is defined, by the programmer, as a regular
Type to metadata.
A CustomAttribute within metadata is a triple of (tokenParent, tokenMethod, blob)
stored into metadata. The blob holds the arguments to the class constructor method
specified by tokenMethod. The runtime has a full understanding of the contents of
this blob; on request, it will instantiate the attribute-object that the blob represents,
attaching it to the item whose token is tokenParent.
Page 120
Metadata API
Allow tools, services, and third parties (the primary customers for this
mechanism) to extend the types of information that may be carried in metadata
without having to depend on the runtime to maintain and version that information
Although each language or tool will provide a language-specific syntax and
conventions for using custom attributes, the self-describing nature of these
attributes will enable tools to provide drop-down lists and other developer aids
Runtime reflection services will support browsing over these custom attributes,
since they are self-describing.
Page 121
Metadata API
Note any class may have multiple attribute-objects ‘hooked’ to it. These can be of
different types, or even of the same type.
All binary data is persisted in little-endian format (least signficant bytes come first
in the file). The format for floats and doubles is IEEE-754. For 8-byte doubles, the
more-significant 4 bytes is emitted after the less-significant 4 bytes. There is just
one exception to the little-endian rule – the “PackedLen” count that precedes a string
– a one-two-or-four byte item – is always encoded big-endian.
Note that, if the constructor method takes no arguments, and you don’t want to
specify any extra named fields or properties, you can omit the blob entirely.
11.3 Prolog
The prolog simply identifies the blob that follows. It consists of a two-byte ID. In the
first release, set this to the value 1.
The prolog is obviously a hedge against future extensions to this blob format.
Page 122
Metadata API
For SZARRAY, the number of elements as an I4, followed by the value (in its
full field width) of each element
For arguments of type System.Enum, emit the actual value of their underlying type.
[As a specific example, a particular Enum might use 4-byte integers as its underlying
type, and should therefore be saved as a SERIALIZATION_TYPE_I4 value]
For arguments of System.Type, emit the actual type as a String, including the full
assembly name of the defining module
Note that SERIALIZATION_TYPE_BOOLEAN items are encoded in a single byte, with
False = 0 and True = 1. [This contrasts with how CLR lays out Booleans in memory –
a single Boolean occupies 4 bytes, whereas each element of a Boolean array
occupies just 1 byte]
If the attribute-class provides several constructors, overload resolution to the
appropriate MethodDef or MethodRef must be done at compile time (ie, no late-
binding). Runtime cannot therefore perform automatic widening (for example, store
16 bit integer, but widen to signature’s parameter type of 32 bits)
For the length-in-bytes of a UTF8 string, we use the standard 1,2 or 4 byte
“PackedLen” encoding used within metadata (see the description of helper routine
CorSigCompressData in section 10):
If the length-in-bytes lies between 0 and 127, encode as a one-byte integer
(bit #7 is obviously clear, integer held in bits #6 thru #0)
If the length-in-bytes lies between 2^8 and 2^14 encode as a two-byte
integer with bit #15 set, bit #14 clear (integer held in bits #13 thru #0)
Otherwise, encode as a 4-byte integer, with bit #31 set, bit #30 set, bit #29
clear (integer held n bits #28 thru #0)
A null string should be represented with the reserved single byte 0xFF, and no
following data. (The value of 0xFF is a reserved value in metadata’s count
prefix)
The table below shows several examples. The first column shows an example count
value (one-byte, two-byte and three-byte). The second column shows the
corresponding size, expressed as a normal integer.
Thus, by examining the most significant bits of a “PackedLen” field, code can
determine whether it occupies 1, 2 or 4 bytes, as well as its value. For this to work,
Page 123
Metadata API
the “PackedLen” is stored in big-endian order – most significant byte at the smallest
offset within the file. [see CPackedLen::GetLength and CPackedLen::PutLength
methods in the Lightning source tree at $/Com99/Src/Utilcode/StgPooli.cpp code for
details]
There is clearly scope to compact the above binary format, in the same way that
existing metadata structures have been optimized to avoid “bloat”. Possible
techniques are legion. The first release of the runtime does not include any such
optimizations (except for “PackedLen”)
Page 124
Metadata API
“Hello” – a one-byte count, plus 5 bytes of UTF8 encoded characters. The second
argument is an enumeration with a 4-byte integer base type; we serialize Green as
its value (of 1). The third argument is a 3-element BOOLEAN array – so we have a 4-
byte element count with value 3, followed by 3 bytes for each boolean value, in order
(False = 0, True = 1). (Recall that BOOLEAN arrays are stored with one byte per
element. This contrasts with a simple BOOLEAN, which is stored as a 4-byte
quantity). The last value is a two-byte value of zero, giving the total number of
named fields and named properties (see later).
Page 125
Metadata API
Page 126
Metadata API
Page 127
Metadata API
12 CustomAttributes – Syntax
This section summarizes the syntax charts for defining CustomAttribute objects,
detailed in section 11. It makes no sense unless you have read that section (and
even then).
A valid Custom Attribute has the following syntax chart:
CustomAttrib
All binary values are stored in little-endian format (except PackedLen items – used
only as counts for the number of bytes to follow in a Utf8 string)
CustomAttrib starts with a Prolog – a U2, with value 0x0001
Next comes a description of the fixed arguments for the constructor method. Their
number and type is found by examining that constructor method’s MethodDef; this
info is not repeated in the CustomAttrib itself. As the syntax chart shows, there can
be zero or more FixedArgs. (note that VARARG constructor methods are not allowed
in the definition of Custom Attributes)
Next is a description of the optional “named” fields and properties. This starts with
NumNamed – a U2 giving the number of “named” properties or fields that follow.
Note that NumNamed must always be present. If its value is zero, there are no
“named” properties or fields to follow (and of course, in this case, the CustomAttrib
must end immediately after NumNamed) In the case where NumNamed is non-zero,
it is followed by NumNamed repeats of NamedArgs
FixedArg
SerType Val
Page 128
Metadata API
NamedArg
PROPERTY
ENUM SerType
Val
Page 129
Metadata API
13 Marshalling Descriptor
A Marshalling Descriptor is like a signature – it’s a blob of binary data. It describes
how a field or parameter (which, as usual, covers the method return, as parameter
number 0) should be marshalled when calling to or from unmanaged coded via
PInvoke dispatch or IJW (“It Just Works”) thunking.
The blob has the following format –
MarshalSpec :==
NativeInstrinsic
| CUSTOMMARSHALLER Guid UnmanagedType ManagedType Cookie
| FIXEDARRAY NumElem ArrayElemType
| SAFEARRAY SafeArrayElemType
| ARRAY ArrayElemType ParamNum ElemMult NumElem
NativeInstrinsic :==
BOOLEAN | I1 | U1 | I2 | U2 | I4 | U4 | I8 | U8 | R4 | R8
| BSTR | LPSTR | LPWSTR | LPTSTR | FIXEDSYSSTRING | STRUCT
| INTF |FIXEDARRAY | INT | UINT | BYVALSTR | ANSIBSTR | TBSTR
| VARIANTBOOL | FUNC | LPVOID | ASANY | LPSTRUCT | ERROR | MAX
For compactness, we have omitted the NATIVE_TYPE_ prefixes in the above lists. So,
for example, “ARRAY” is shorthand for NATIVE_TYPE_ARRAY (see the CorNativeType
enum defined in CorHdr.h) Note that NativeIntrinsic excludes those elements of the
CorNativeType enum commented as “deprecated”
Guid is a counted-Utf8 string – eg “{90883F05-3D28-11D2-8F17-00A0C9A6186D}” –
it must include leading { and trailing } and be exactly 38 characters long
UnmanagedType is a counted-Utf8 string – eg “Point”
ManagedType is a counted-Utf8 string – eg “System.Util.MyGeometry” – it must be
the fully-qualified name (namespace and name) of a managed Type defined within
the current assembly (that Type must implement ICustomMarshaller, and provides a
“to” and “from” marshalling method)
Cookie is a counted-Utf8 string – eg “123” – an empty string is allowed
Page 130
Metadata API
NumElem is an integer that tells us how many elements are in the array
ArrayElemType :==
NativeInstrinsic | BOOLEAN | I1 | U1 | I2 | U2
| I4 | U4 | I8 | U8 | R4 | R8 | BSTR | LPSTR | LPWSTR | LPTSTR
| FIXEDSYSSTRING | STRUCT | INTF | INT | UINT | BYVALSTR
| ANSIBSTR | TBSTR | VARIANTBOOL | FUNC | LPVOID | ASANY
| LPSTRUCT | ERROR | MAX
The value MAX is used to indicate “no info”
SafeArrayElemType :== I2 | I4 | R4 | R8 | CY | DATE | BSTR | DISPATCH |
| ERROR | BOOL | VARIANT | UNKNOWN | DECIMAL | I1 | UI1 | UI2
| UI4 | INT | UINT
where each is prefixed by VT_. Note that these VT_xxx form a subset of the standard
OLE constants (defined, for example, in the file WType.h that ships with Visual Studio,
installed to the default directory “Program Files\Microsoft Visual Studion\VC98\
Include”)
ParamNum is an integer, which says which parameter in the method call provides the
number of elements in the array – see below
ElemMult is an integer (says by what factor to multiply – see below)
For example, in the method declaration:
Foo (int ar1[], int size1, byte ar2[], int size2)
The ar1 parameter might own a row in the FieldMarshal table, which indexes a
MarshalSpec in the Blob heap with the format:
ARRAY MAX 2 1 0
This says the parameter is marshalled to a NATIVE_TYPE_ARRAY. There is no
additional info about the type of each element (signified by that NATIVE_TYPE_MAX).
The value of ParamNum is 2, which tells us that parameter number 2 in the method
(the one called “size1”) will tell us the number of elements in the actual array – let’s
suppose its value on a particular call were 42. The value of ElemMult is 1. The value
of NumElem is 0. The calculated total size, in bytes, of the array is given by the
formula:
if ParamNum == 0
SizeInBytes = NumElem * sizeof (elem)
else
SizeInBytes = ( @ParamNum * ElemMult + NumElem ) * sizeof (elem)
endif
We have used the syntax “@ParamNum” to denote the value passed in for parameter
number ParamNum – it would be 42 in this example. The size of each element is
calculated from the metadata for the ar1 parameter in Foo’s signature – an
ELEMENT_TYPE_I4 of size 4 bytes.
Note that, just as in signature blobs, every simple scalar, such as integers or Utf8
byte-counts, are stored in compressed format, using the CorSigCompressData helper
routines (see section 10 for details)
Page 131
Metadata API
Page 132
Metadata API
A programmer can avoid the inefficiency incurred with managed boolean fields by
declaring them as 2-byte or 4-byte integers instead.
In all other respects, except its predefined field layout in memory, a “formatted”
object looks just like a regular managed object. In particular, managed code can
read and write all its fields with MSIL instructions.
When it comes to call an unmanaged function, PInvoke locates the DLL where it lives,
loads that DLL into process memory, finds the function address in memory, pushes
its arguments onto the stack (marshalling if required) and transfers control to the
address for the unmanaged code. If the arguments are isomorphic, then no
marshalling is required.
To build the corresponding PInvoke metadata, you need only call DefineMethod,
DefinePinvokeMap and DefineParam for each parameter. (Individual compilers may
choose to structure their definitions differently – a DefineMethod followed by a
SetMethodProps, for example, but the suggested sequence is possible)
On the other hand, building PInvoke metadata can also come quite involved, as
witnessed by the size of this spec, and the other specs, listed below, that support it.
This happens if your unmanaged function accepts struct arguments, and requires
non-default marshalling. Here is a second, more complicated example:
[sysstruct(format=ClassFormat.Auto)]
public class LOGFONT {
Page 133
Metadata API
class C {
[sysimport(dll="gdi32.dll",charset=CharacterSet.Auto)]
public static extern int CreateFontIndirect(
[in, nativetype(NativeType.NativeTypePtr)]
LOGFONT lplf // characteristics
);
public static void Main() {
LOGFONT lf = new LOGFONT();
lf.lfHeight = 9;
lf.lfFaceName = "Arial";
int i = CreateFontIndirectA(lf);
Console.WriteLine(i);
}
}
To build the PInvoke metadata for this example requires calls to most of the following
routines in the IMetaDataEmit interface:
DefineMethod: Define a method, with its CLR method signature
DefinePinvokeMap: Specify PInvoke info for a method
DefineTypeDef: Define a CLR class or valuetype, used as an argument to a
PInvoke-dispatched function
DefineField: Define a data field within a class
SetClassLayout: Supply additional info on the class layout, such as field packing
SetFieldMarshal: Supply non-default marshaling for a function argument, function
return value, or a field within a struct
For more info on specific areas touched upon in this spec, see the following
documents:
Platform Invoke Usage Guide for an overview of PInvoke from the user’s
perspective
Metadata Interfaces for an overview of metadata, and details of specific routines
MetadataStructures for the format of signatures (methods and fields)
Page 134
Metadata API
Page 135
Metadata API
the name of the method). Whilst this is used for regular managed methods, we do
not support its use for PInvoke.
Page 136
Metadata API
Page 137
Metadata API
dwTypeDefFlags is a bitmask from the CorTypeAttr enum in CorHdr.h. You must set
either tdLayoutSequential, or tdExplicitLayout (not both). You should set tdAnsiClass,
tdUnicodeClass or tdAutoClass.
If your struct has no unions, then set tdLayoutSequential, and, if necessary, call
SetClassLayout to provide more details. If you are in the unfortunate position that
your struct includes unions (sometimes called overlays, depending upon source
language), or your struct includes weird padding between fields, then you must set
tdExplicitLayout, and follow with a call to SetClassLayout to provide more details.
The string formatting flags say how managed strings (which are always encoded in
Unicode) should be marshalled to and from unmanaged code:
Page 138
Metadata API
dwPackSize is the packing size between adjacent fields. For each field in sequence,
the runtime looks at its size, and current offset within the struct. It lays the field
Page 139
Metadata API
down to start at its natural offset, or the pack size, whichever results in the smaller
offset. This matches precisely the semantics of the C and C++ #pragma pack
compiler directive
rFieldOffsets is not required in this instance. Specify it as zero
ulClassSize is optional. If you specify this argument, then PInvoke will marshal this
struct argument by making a blind, byte-by-byte copy of the managed object. [This
technique is used by Visual C++]
The tokField is the token for the target field; the ulOffset is the byte offset within the
struct at which it starts. The struct is assumed to start at offset 0. (So, if you specify
just one field, 4 bytes wide, with a ulOffset of 1000, then you create a managed
struct that is 1004 bytes long). Terminate the rFieldOffsets[] array with a field token
of mdTokenNil.
Page 140
Metadata API
For details of how to build the native type signature, see the MetadataStructures
spec
Page 141