Classification of Custom Attributes

Classification of Custom Attributes

Having decided to concentrate on the custom attributes recognized by the common language runtime or the tools dealing with managed PE files, let’s see which custom attributes are intended for various subsystems of the runtime and tools.

Before proceeding, however, I must mention one custom attribute that stands apart from any classification and is truly unique. It is the attribute System.AttributeUsageAttribute, which can (and should) be owned only by the custom attribute types. Make no mistake—custom attributes can’t own custom attributes, but the type of a custom attribute is always an instance constructor of some class. This class should own the custom attribute System.AttributeUsageAttribute, which identifies what kinds of metadata items can own the custom attributes typed after this class, whether these custom attributes are inheritable, and whether multiple custom attributes of this type can be owned by the same metadata item. Because all operations concerning custom attributes are performed through Reflection, AttributeUsageAttribute can be considered the only custom attribute intended exclusively for Reflection itself. The instance constructor of the AttributeUsageAttribute type has one int32 parameter, representing the binary flags for various metadata items as potential owners of the custom attribute typed after the attributed class. The flags are defined in the enumeration System.AttributeTargets.

The following should save you the time of looking up this enumeration in the disassembly of Mscorlib.dll:

  .class public auto ansi serializable sealed AttributeTargets          extends System.Enum   {      // The following custom attribute is intended for the compilers      // And indicates that the values of the enum are binary flags      // And hence can be bitwise OR'ed      .custom instance void System.FlagsAttribute::.ctor()               = ( 01 00 00 00 )      .field public specialname rtspecialname int32 value__      .field public static literal valuetype System.AttributeTargets              Assembly = int32(0x00000001)      .field public static literal valuetype System.AttributeTargets              Module = int32(0x00000002)      .field public static literal valuetype System.AttributeTargets              Class = int32(0x00000004)      .field public static literal valuetype System.AttributeTargets              Struct = int32(0x00000008) // Value type      .field public static literal valuetype System.AttributeTargets              Enum = int32(0x00000010)      .field public static literal valuetype System.AttributeTargets              Constructor = int32(0x00000020)      .field public static literal valuetype System.AttributeTargets              Method = int32(0x00000040)      .field public static literal valuetype System.AttributeTargets              Property = int32(0x00000080)      .field public static literal valuetype System.AttributeTargets              Field = int32(0x00000100)      .field public static literal valuetype System.AttributeTargets              Event = int32(0x00000200)      .field public static literal valuetype System.AttributeTargets              Interface = int32(0x00000400)      .field public static literal valuetype System.AttributeTargets              Parameter = int32(0x00000800)      .field public static literal valuetype System.AttributeTargets              Delegate = int32(0x00001000)      .field public static literal valuetype System.AttributeTargets              ReturnValue = int32(0x00002000)      .field public static literal valuetype System.AttributeTargets              All = int32(0x00003FFF)   } // End of class AttributeTargets

As you can see, Reflection’s list of potential custom attribute owners is somewhat narrower than the metadata’s list of 19 tables. Perhaps we needn’t worry about the custom attributes of the interface implementations and stand-alone signatures just yet.

The remaining two characteristics of AttributeUsageAttribute—the Boolean properties Inherited and AllowMultiple—must be defined through the name/value pairs. The defaults are All for the potential custom attribute owners, true for Inherited, and false for AllowMultiple.

You’ll find this information useful when (note that I’m not saying “if”) you decide to invent your own custom attributes. And now, back to our classification scheme.

Execution Engine and JIT Compiler

The execution engine and the JIT (just-in-time) compiler of the common language runtime recognize three custom attributes:

  • System.Diagnostics.DebuggableAttribute  This attribute, which can be owned by the assembly or the module, sets a special debug mode for the JIT compiler. The instance constructor has two Boolean parameters, the first enabling the JIT compiler tracking the extra information about the generated code, and the second disabling JIT compiler optimizations. The ILAsm compiler automatically emits this custom attribute when the /DEBUG command-line option is specified. The ILDASM outputs this attribute but comments it out.

  • System.Security.UnverifiableCodeAttribute  This attribute, which can be owned by the module, indicates that the module contains unverifiable code. Thus, because the result is known, IL code verification procedures don’t have to be performed. The instance constructor has no parameters.

  • System.ThreadStaticAttribute  This attribute, which can be owned by a field, indicates that the static field is not shared between the threads. Instead, the common language runtime creates an individual copy of the static field for each thread. The effect is approximately the same as mapping the static field to the thread local storage (TLS) data, but this effect is achieved on the level of the runtime rather than that of the operating system.

Interoperation Subsystem

All the custom attribute types in this group belong to the namespace System. Runtime.InteropServices. The following list refers to them by their class names only:

  • ClassInterfaceAttribute  This attribute, which can be owned by the assembly or a TypeDef (class), specifies whether a COM class interface is generated for the attributed type. This attribute type has two instance constructors, each having a single parameter. The first constructor takes a value of enumerator ClassInterfaceType; the second takes an int16 argument. The acceptable argument values are 0 (no automatic interface generation), 1 (automatic IDispatch interface generation), or 2 (automatic dual interface generation).

  • ComAliasNameAttribute  This attribute, which can be owned by a parameter (including the return value), a field, or a property, indicates the COM alias for the attributed item. The instance constructor has a single string parameter.

  • ComConversionLossAttribute  This attribute, which can be owned by any item, indicates that information about a class or an interface was lost when it was imported from a type library to an assembly. The instance constructor has no parameters.

  • ComRegisterFunctionAttribute This attribute, which can be owned by a method, indicates that the method must be called when an assembly is registered for use from COM. This allows for the execution of user-defined code during the registration process. The instance constructor has no parameters.

  • ComUnregisterFunctionAttribute  This attribute, which can be owned by a method, indicates that the method must be called when an assembly is unregistered from COM. The instance constructor has no parameters.

  • ComSourceInterfacesAttribute  This attribute, which can be owned by a TypeDef, identifies a list of interfaces that are exposed as COM event sources for the attributed type. This attribute type has five instance constructors; the most useful one has a single string parameter, the value of which should contain a space-separated list of all interface types in Reflection notation. (See “Custom Attribute Value Encoding,” earlier in this chapter.)

  • ComVisibleAttribute  This attribute, which can be owned by the assembly, a TypeDef, a method, a field, or a property, indicates whether the attributed item is visible to classic COM. The instance constructor has one Boolean parameter having a value of true if the item is visible.

  • DispIdAttribute  This attribute, which can be owned by a method, a field, a property, or an event, specifies the COM DispId of the attributed item. The instance constructor has one int32 parameter, the value of the DispId.

  • GuidAttribute  This attribute, which can be owned by the assembly or a TypeDef, specifies an explicit GUID if the GUID automatically generated by the runtime is for some reason not guid—I mean, good—enough. The instance constructor has one string parameter, which should contain the GUID value in standard literal representation without the surrounding curly braces.

  • ImportedFromTypeLibAttribute  This attribute, which can be owned by the assembly, indicates that the types defined within the assembly were originally defined in a COM type library. The attribute is set automatically by the TlbImp.exe utility. The instance constructor has one string parameter, which should contain the filename of the imported type library.

  • InterfaceTypeAttribute  This attribute, which can be owned by a TypeDef (interface), indicates the COM-specific interface type this interface is exposed as. The instance constructor has one int16 parameter. A value of 0 indicates a dual interface, a value of 1 indicates IUnknown, and a value of 2 indicates IDispatch.

  • ProgIdAttribute  This attribute, which can be owned by a TypeDef (class), explicitly specifies the COM ProgId of the attributed class. Normally, the ProgId strings are generated automatically as a full class name (namespace plus name), but the ProgId length is limited to 39 bytes plus a 0 terminator. The namespaces and class names in .NET are rather long-winded, so there’s a good chance 39 bytes won’t even cover the namespace. The instance constructor has one string parameter, which should contain the ProgId string.

  • TypeLibFuncAttribute  This attribute, which can be owned by a method, specifies the COM function flags that were originally imported from the type library. (The COM function flags are described in COM literature and on the Microsoft Developer Network [MSDN].) This attribute is generated automatically by the TlbImp.exe utility. The instance constructor has one int16 parameter, the flags’ value.

  • TypeLibTypeAttribute  This attribute, which can be owned by a TypeDef, is similar to TypeLibFuncAttribute except that COM type flags are specified instead of COM function flags.

  • TypeLibVarAttribute  This attribute, which can be owned by a field, is similar to TypeLibFuncAttribute and TypeLibTypeAttribute except that the flags in question are COM variable flags.

Security

Security-related custom attributes are special attributes that are converted to DeclSecurity metadata records. Usually, the security custom attributes don’t make it past the compilation stage—they are converted and cease to exist. In one scenario, however, the security custom attributes do “survive” the compilation and are emitted into the PE file. This happens when the security attributes owned by the assembly are specified in the assembly modules, further linked to the assembly by the assembly linker tool. In this case, the assembly-owned security attributes are converted to DeclSecurity metadata records by the assembly linker, but they remain in the assembly modules, although they play no role.

One of the security custom attributes belongs to the namespace System.Security:

  • SuppressUnmanagedCodeSecurityAttribute This attribute, which can be owned by a method or a TypeDef, indicates that the security check of the unmanaged code invoked through the P/Invoke mechanism must be suppressed. The instance constructor has no parameters. This custom attribute differs from other security attributes in that it is not converted to DeclSecurity metadata and hence stays intact once emitted.

The rest of the security custom attributes belong to the namespace System.Security.Permissions. The ownership of all security custom attributes is limited to the assembly, a TypeDef (class or value type), and a method. The instance constructors of these attributes have one int16 parameter, the action type code. Chapter 14, “Security Attributes,” discusses the security action types and their respective codes as well as various types of permissions.

The following list offers a brief description of the security custom attributes; you can find further details in Chapter 14.

  • SecurityAttribute  This generic security attribute is the base class of all other security attributes.

  • CodeAccessSecurityAttribute  This attribute is the base class of the code access security attributes. Other attributes derived from this one are used to secure access to the resources or securable operations.

  • EnvironmentPermissionAttribute  This attribute sets the security action for the environment permissions that are to be applied to the code.

  • FileDialogPermissionAttribute  This attribute sets the security action for file open/save dialog permissions.

  • FileIOPermissionAttribute  This attribute sets the security action for the file input/output permissions (read, write, append and so on).

  • IsolatedStorageFilePermissionAttribute  This attribute sets the security action for the permissions related to the isolated storage files (available storage per user, the kind of isolated storage containment).

  • PermissionSetAttribute  This attribute sets the security action not for one permission but for a whole permission set, specified in a string or an XML file or a named permission set.

  • PrincipalPermissionAttribute  This attribute sets the security action for the principal security permissions (security checks against the active principal).

  • PublisherIdentityPermissionAttribute  This attribute sets the security action for the security permissions related to the software publisher’s identity.

  • ReflectionPermissionAttribute  This attribute sets the security action for the Reflection permissions.

  • RegistryPermissionAttribute  This attribute sets the security action for the registry access permissions (read, write, create a key).

  • SecurityPermissionAttribute  This attribute sets the security action for the security permissions.

  • SiteIdentityPermissionAttribute  This attribute sets the security action for the site identity permissions.

  • StrongNameIdentityPermissionAttribute  This attribute sets the security action for the assembly’s strong name manipulation permissions.

  • UIPermissionAttribute  This attribute sets the security action for the user interface permissions (window flags, Clipboard manipulation flags).

  • UrlIdentityPermissionAttribute  This attribute sets the security action for the URL permissions.

  • ZoneIdentityPermissionAttribute  This attribute sets the security action for the security zone (MyComputer, Intranet, Internet, Trusted, Untrusted).

Remoting Subsystem

The following custom attributes are recognized by the remoting subsystem of the common language runtime and can be owned by a TypeDef:

  • System.Runtime.Remoting.Contexts.ContextAttribute  This custom attribute class, which sets the remoting context, is a base class of all context attribute classes. It provides the default implementations of the interfaces IContextAttribute and IContextProperty. The instance constructor has one string parameter, the attribute name.

  • System.Runtime.Remoting.Contexts.SynchronizationAttribute This custom attribute specifies the synchronization requirement and the re-entrance capability of the attributed class. It defines the class behavior in the synchronized contexts (contexts having the Synchronization property). The presence of an instance of this property in a context enforces a synchronization domain for the context (and all contexts that share the same instance). This means that at any instant, at most one thread could be executing in all contexts that share this property instance. The synchronization requirement flags are as follows:

1

The class should not be instantiated in a context that has synchronization

2

It is irrelevant to the class whether or not the context has synchronization

4

The class should be instantiated in a context that has synchronization

8

The class should be instantiated in a context with a new instance of the Synchronization property

This attribute type has four instance constructors, as follows:

Constructor with no parameters

Defaults the synchronization requirement to 1 and the re-entrancy flag to false

Constructor with one int32 parameter

Sets the synchronization requirement and defaults the re-entrancy flag

Constructor with one Boolean parameter

Sets the re-entrancy flag and defaults the synchronization requirement

Constructor with int32 and Boolean parameters

Sets both values

  • System.Runtime.Remoting.Activation.UrlAttribute  This attribute is used at the call site to specify the URL of the site where the activation will happen. The instance constructor has one string parameter, which contains the target URL.

The information provided here is rather brief, but a protracted discussion of the topics related to remoting implementation goes far beyond the scope of this book. This is one of those occasions when one has to remember that modesty is a virtue.

Visual Studio .NET Debugger

The following two custom attributes are recognized by the Microsoft Visual Studio .NET debugger. They are not recognized by the .NET Framework debugger (Cordbg.exe). Both of these custom attributes belong to the namespace System.Diagnostics.

  • DebuggerHiddenAttribute  This attribute, which can be owned by a method or a property, signals the debugger not to stop in the attributed method and not to allow a breakpoint to be set in the method. The instance constructor has no parameters.

  • DebuggerStepThroughAttribute  This attribute is the same as DebuggerHiddenAttribute except that it does allow a breakpoint to be set in the method.

Assembly Linker

The five custom attributes listed in this section are intended for the assembly linker tool (Al.exe). They specify the characteristics of the assembly that the assembly linker is about to create from several modules.

The most fascinating aspect of these attributes is their ownership. Think about it: when the attributes are declared, no assembly exists yet; if it did, we wouldn’t need these attributes in the first place. Hence, the attributes are declared in one or more of the modules that will make up the future assembly. What in a module might serve as an owner of these attributes? The solution is straightforward: the .NET Framework class library defines the System.Runtime.CompilerServices.AssemblyAttributesGoHere class, and the assembly-specific attributes are assigned to the TypeRef of this class. Ownership of the assembly-specific attributes is the only reason this class exists.

All of the assembly-specific attributes, described in the following list, belong to the namespace System.Reflection:

  • AssemblyCultureAttribute  This attribute specifies the culture of the assembly. The instance constructor has one string parameter, which contains the culture identification string.

  • AssemblyVersionAttribute  This attribute specifies the version of the assembly. The instance constructor has one string parameter, which contains the text representation of the version: dot-separated decimal values of the major version, the minor version, the build, and the revision. Everything beyond the major version can be omitted. If major and minor versions are specified, the build and/or the revision can be omitted or specified as an asterisk, which leads to automatic computation of these values at the assembly linker run time. The build number is computed as the present day’s number counting since January 1, 2000. The revision number is computed as the number of seconds that have elapsed since midnight, local time, modulo 2.

  • AssemblyKeyFileAttribute  This attribute specifies the name of the file containing the key pair used to generate the strong name signature. The instance constructor has one string parameter.

  • AssemblyKeyNameAttribute  This attribute specifies the name of the key container holding the key pair used to generate the strong name signature. The instance constructor has one string parameter.

  • AssemblyDelaySignAttribute  This attribute specifies whether the assembly is signed immediately at the time of generation or delay-signed—in other words, fully prepared to be signed later by the strong name signing utility (Sn.exe). The instance constructor has one Boolean parameter, true, indicating that the assembly is delay-signed.

Common Language Specification (CLS) Compliance

The following two custom attributes are intended for the compilers and similar tools. Types of both of the custom attributes belong to the System namespace.

  • ObsoleteAttribute  This attribute, which can be owned by a TypeDef, a method, a field, a property, or an event, indicates that the item is not to be used any more. The attribute holds two characteristics: a string message to be produced when the obsolete item is used, and a Boolean flag indicating whether use of the item should be treated as an error. This attribute type has three instance constructors, as follows:

Constructor with no parameters

Produces no message and no error

Constructor with a string parameter

Produces a message but no error

Constructor with string and Boolean parameters

Produces a message and an error flag

  • CLSComplianceAttribute  This attribute, which can be owned by anything, indicates the (claimed) CLS compliance or noncompliance of the attributed item. Assigning this attribute to an assembly doesn’t make the assembly CLS-compliant or noncompliant; it’s simply an expression of your opinion on the matter. The instance constructor has one Boolean parameter; a value of true indicates CLS compliance.

Pseudocustom Attributes

As mentioned earlier, custom attributes are a lifesaver for compilers. Once a language is given the syntax to express a custom attribute, it’s free to use this syntax to describe various metadata oddities its principal syntax can’t express. The parallel evolution of the common language runtime and the managed compilers, with the runtime getting ahead now and then, created the concept of the so-called pseudocustom attributes. These attributes are perceived and treated by the compilers as other custom attributes are, but they are never emitted as such. Instead of emitting these attributes, the metadata emission API sets specific values of the metadata.

The following are the 13 pseudocustom attributes:

  • System.Runtime.InteropServices.ComImportAttribute  This attribute sets the import flag of a type definition. The instance constructor has no parameters.

  • System.Runtime.InteropServices.DllImportAttribute  This attribute sets the method flag pinvokeimpl, the implementation flag preservesig, and the name of the unmanaged library from which the method is imported. The instance constructor has one string parameter, the name of the unmanaged library. The entry point name and the marshaling flags are specified through the name/value pairs of the EntryPoint, CharSet, SetLastError, ExactSpelling, and CallingConvention properties.

  • System.SerializableAttribute  This attribute sets the serializable flag of a type definition. The instance constructor has no parameters.

  • System.NonSerializedAttribute  This attribute sets the notserialized field flag. The instance constructor has no parameters.

  • System.Runtime.InteropServices.InAttribute  This attribute sets the parameter flag in. The instance constructor has no parameters.

  • System.Runtime.InteropServices.OutAttribute  This attribute sets the parameter flag out. The instance constructor has no parameters.

  • System.Runtime.InteropServices.OptionalAttribute  This attribute sets the parameter flag opt. The instance constructor has no parameters.

  • System.Runtime.CompilerServices.MethodImplAttribute  This attribute sets the method implementation flags. The instance constructor has one int16 parameter, the implementation flags.

  • System.Runtime.InteropServices.MarshalAsAttribute  This attribute is used on fields and method parameters for managed/unmanaged marshaling. The instance constructor has one int16 parameter, the native type.

  • System.Runtime.InteropServices.PreserveSigAttribute This attribute sets the preservesig method implementation flag. The instance constructor has no parameters.

  • System.Runtime.InteropServices.StructLayoutAttribute This attribute sets the layout flags of a type definition (auto, sequential, or explicit), the string marshaling flags (ansi, unicode, or autochar), and the characteristics .pack and .size. The instance constructor has one int16 parameter, the layout flag. The .pack and .size characteristics and the string marshaling flags are specified through the name/value pairs of the Pack, Size, and CharSet properties, respectively.

  • System.Runtime.InteropServices.FieldOffsetAttribute  This attribute sets the field offset (ordinal) in explicit or sequential class layouts. The instance constructor has one int32 parameter, the offset or ordinal value.

  • System.Security.DynamicSecurityMethodAttribute  This attribute sets the method flag reqsecobj. The instance constructor has no parameters.

ILAsm syntax is adequate to describe all the parameters and characteristics listed here and does not use the pseudocustom attributes.

caution

As a matter of fact, I should warn you against using the pseudocustom attributes instead of ILAsm keywords and constructs. Using pseudocustom attributes rather than keywords is not a bright idea in part because the keywords are shorter than the custom attribute declarations. In addition, you should not forget that the ILAsm compiler, which has no use for custom attributes, treats them with philosophical resignation—in other words, it emits them just as they are, without analysis. Hence, if you specify important flags through pseudocustom attributes, the compiler will not see these flags and as a result could come to the wrong conclusions.



Inside Microsoft. NET IL Assembler
Inside Microsoft .NET IL Assembler
ISBN: 0735615470
EAN: 2147483647
Year: 2005
Pages: 147
Authors: SERGE LIDIN

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net