|
|||||||||||||||||||||||
|
|||||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
Introduction
In this article, we'll take a look at the undocumented and internal (but
useful) class called Disclaimers: The information presented here is intended for educational purposes only - using any of the internal Framework classes in production code is just asking for trouble, especially considering the relatively low version number (and implied maturity level) of the current framework. In addition, the code presented with the article has only been tested with version 1.1 of the .NET Framework. FusionFirst off, what exactly is Fusion? Fusion is the internal name of the CLR
assembly loader/binder subsystem. Loading an assembly in a managed app is
somewhat more complicated than loading a DLL from a non-managed app. When you
load a DLL using
Speaking of the GAC, some readers may not be aware that the Explorer shell extension that presents a custom view of the GAC & Zap cache can be disabled by adding a binary value named 'DisableCacheViewer' to the registry key HKLM\Software\Microsoft\Fusion and setting it to a non-zero value. This comes in handy if you want to see the physical directory structure in Explorer. Spelunking the FrameworkNow that we have an overly simplified view of Fusion and it's caches, let's turn our attention to the code - what it does and how it works. Readers who have used the .NET configuration and wizard MMC snap-ins may have
wondered how some of that functionality was implemented. The first time I saw
the 'Fix an Application' applet, for example, I was immediately curious
about the source of the application list since it was presented so quickly
(that is, after I finished chuckling about the name of that
applet). Using
Process Explorer, I quickly discovered that this particular
applet is housed in a managed executable called ConfigWizards.exe.
Then using a hand-rolled dependency viewer for .NET (which may be the topic of
a future article, see Fig 1
for a teaser), I stumbled upon the mscorcfg assembly. Most of the
publicly exposed classes in mscorcfg are related to UI
logic used by the control panel applet and, as such, are not generally useful
outside of that context. However, taking a closer look at mscorcfg with
the absolutely indispensable Reflector
tool reveals a few gems. One such gem is the At this point, some readers may have popped open Reflector,
looked at the Update: Greg Fee, who is a primary developer on the CLR CAS team, started blogging recently and this blog entry has more information on the importance of reflection permissions. Armed with Reflector (did I
mention that this is an exceedingly useful tool?) and the knowledge that
reflection lets us use any type in any assembly, let's take a closer look
at The The sample codeSo now that we know how to access the managed apps list (aka Fusion Application History) and the contents of the Fusion caches, all that remains is to collect that information and throw it into a simple (read: very quick, very dirty, only-intended-for-demonstration-purposes) GUI: The GUI consists of the standard tree-splitter-info pane layout. The tree is
organized into the four assembly collections, which are further divided into
product name groups (where possible). The product name information is obtained
by passing a calculated file name of each assembly (where possible) to the
framework The sample code is mostly trivial but the wrapper classes Let's take a quick look at each of these classes. Here is the utility class
public class Reflected
{
static object Get(object src, string strName, BindingFlags type)
{
bool bStatic = (src is Type);
Type t = bStatic ? (Type)src : src.GetType();
BindingFlags bindingFlags = BindingFlags.NonPublic | BindingFlags.Public |
BindingFlags.DeclaredOnly |
(bStatic ? BindingFlags.Static : BindingFlags.Instance) |
type;
object target = (bStatic ? null : src);
return (t.InvokeMember(strName, bindingFlags, null, target, null));
}
public static object GetProperty(object src, string strName)
{
return (Get(src, strName, BindingFlags.GetProperty));
}
public static object GetField(object src, string strName)
{
return (Get(src, strName, BindingFlags.GetField));
}
}
The real work is done in the private Here is the public struct AssemInfo
{
public readonly string Name;
public readonly string Locale;
public readonly string Codebase;
public readonly string Modified;
public readonly string OSType;
public readonly string OSVersion;
public readonly string ProcType;
public readonly string PublicKey;
public readonly string PublicKeyToken;
public readonly string Version;
public readonly Fusion.CacheType CacheType;
public readonly string sCustom;
public readonly string sFusionName;
public AssemInfo(object assemInfo)
{
Name = (string)Reflected.GetField(assemInfo, "Name");
Locale = (string)Reflected.GetField(assemInfo, "Locale");
Codebase = (string)Reflected.GetField(assemInfo, "Codebase");
Modified = (string)Reflected.GetField(assemInfo, "Modified");
OSType = (string)Reflected.GetField(assemInfo, "OSType");
OSVersion = (string)Reflected.GetField(assemInfo, "OSVersion");
ProcType = (string)Reflected.GetField(assemInfo, "ProcType");
PublicKey = (string)Reflected.GetField(assemInfo, "PublicKey");
PublicKeyToken = (string)Reflected.GetField(assemInfo, "PublicKeyToken");
Version = (string)Reflected.GetField(assemInfo, "Version");
uint nCacheType = (uint)Reflected.GetField(assemInfo, "nCacheType");
CacheType = (Fusion.CacheType)nCacheType;
sCustom = (string)Reflected.GetField(assemInfo, "sCustom");
sFusionName = (string)Reflected.GetField(assemInfo, "sFusionName");
}
}
Nothing terribly heart pounding here - And finally, the public class Fusion
{
public enum CacheType
{
Zap = 0x1,
GAC = 0x2,
Download = 0x4
}
static Type FusionType;
static Fusion()
{
Assembly a = Assembly.Load("mscorcfg, "
+ "Version=1.0.5000.0, "
+ "Culture=neutral, "
+ "PublicKeyToken=b03f5f7f11d50a3a");
FusionType = a.GetType("Microsoft.CLRAdmin.Fusion");
}
public static String GetCacheTypeString(UInt32 nFlag)
{
object[] args = new object[]{ nFlag };
BindingFlags bindingFlags = (BindingFlags)314;
return ((String)
(FusionType.InvokeMember("GetCacheTypeString",
bindingFlags,
null,
null,
args)));
}
public static void ReadCache(ArrayList alAssems, UInt32 nFlag)
{
object[] args = new object[]{ alAssems, nFlag };
BindingFlags bindingFlags = (BindingFlags)314;
FusionType.InvokeMember("ReadCache",
bindingFlags,
null,
null,
args);
}
public static StringCollection GetKnownFusionApps()
{
object[] args = new object[0];
BindingFlags bindingFlags = (BindingFlags)314;
return ((StringCollection)
(FusionType.InvokeMember("GetKnownFusionApps",
bindingFlags,
null,
null,
args)));
}
}
There are couple of small items to note about this code. First, notice that there is a hard-coded version reference in the type constructor. This practice should obviously be avoided in production code (duh). Second, note how we use a uniform 'cookie-cutter' approach in each of the member implementations. It's as though each of the method bodies started life as a copy-paste snippet - only the names, arg lists and return value casts change. (In actuality, the method bodies didn't start life as snippets; they were generated by a hand-rolled reflection wrapper generator that uses CodeDOM - yet another possible topic for a future article). Points of InterestHopefully you've taken away the following points from this article: (and if not, I blame Jack Mott)
History
|
||||||||||||||||||||||