Determining Whether a DLL or EXE Is a Managed Component


This article was an afterthought. There was a question asked in the CodeGuru forums dealing with a way to determine whether a given DLL/EXE is built as managed or not, and, in addition, to know the .NET framework the DLL is reliant on. All this programatically, of course. Given a DLL, I could make a guess whether it is a COM DLL or not (by querying for DllRegisterServer, DllGetClassObject, and such exports). This was much more intriguing because, unlike other kinds of FLLs/EXEs, the managed code had to run within the .NET runtime environment, and so, the loader has to know well in advance that it is a managed component and do the necessary tasks to prepare the managed code to execute. This article is a byproduct of the quest for an answer to that question and several other questions that came up in the process.

The question itself can be broken up into two parts:

  1. How do we know, given a file, that it is a .NET managed component?

  2. Given a .NET managed component, how do you know what other assemblies (indirectly the framework version) it is dependent on?

Let me deal with each of these questions in that order.


This is how it all started. The first thing I attempted was to look at what the dependent DLLs were. I fired up a sample managed DLL in depends.exe (dependency walker). It’s just one import from one DLL _CorDllMain from MSCOREE.dll for DLLs and _CorExeMain from MSCOREE.dll for EXEs. What about all the code for the classes the assembly exposes? If you are familiar with .NET, you will know that all this is put into the assembly as metadata and the runtime uses this metadata for the actual action. So, no exports whatsoever, no imports whatsoever. Surely, the loader is doing something. The DLL has all the information, but it is not exposed in a traditional way (exported functions). There is a level of indirection and there should be some way to get to the metadata.

The next thing that came to my mind is Microsoft’s PE file format. Having played with it sometime before and having seen the loads of information in there, I decided to take a peek again at the PE structures to see whether there was anything that could take me in the right direction. (Pretty much all the basic PE file format structures are “document”ed in WinNT.H.) I opened this file, and sure enough; there was some information that could possibly be it. It was called the CLR 2.0 header structure. A little research on the Internet gave me the confidence that I was headed the right direction.

PE Header

So, basically this is it. There is a PE structure like the one below; it has headers followed by sections of data. The PE Headers has NT Headers (IMAGE_NT_HEADERS in WinNT.h) that encapsulate optional headers (IMAGE_OPTIONAL_HEADER in WinNT.h). The Optional Header has an array of IMAGE_DATA_DIRECTORY entries called DataDirectory. The IMAGE_DATA_DIRECTORY entry actually points to the actual location of the data within the PE file (called module when loaded). The entry you are interested in is at index IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR. This entry is what points to the .NET information. If this entry exists and the VirtualAddress field points to a valid area within one of the Sections following, it means the component is a managed component.

The following image shows the basic structure of how the metadata can be reached.

All this can be translated into the following code that given a path to the file. It will return TRUE if it is a managed component and FALSE otherwise.

BOOL IsManaged(LPTSTR lpszImageName)
BOOL bIsManaged = FALSE; //variable that indicates whether
//managed or not.
TCHAR szPath[MAX_PATH]; //for convenience

HANDLE hFile = CreateFile(lpszImageName, GENERIC_READ,

//attempt the standard paths (Windows dir and system dir) if
//CreateFile failed in the first place.
//try to locate in Windows directory
hFile = CreateFile(szPath, GENERIC_READ, FILE_SHARE_READ,

//try to locate in system directory
hFile = CreateFile(szPath, GENERIC_READ, FILE_SHARE_READ,

HANDLE hOpenFileMapping = CreateFileMapping(hFile,NULL,
BYTE* lpBaseAddress = NULL;

//Map the file, so it can be simply be acted on as a
//contiguous array of bytes
lpBaseAddress = (BYTE*)MapViewOfFile(hOpenFileMapping,

//having mapped the executable, now start navigating
//through the sections

//DOS header is straightforward. It is the topmost
//structure in the PE file
//i.e. the one at the lowest offset into the file

//the only important data in the DOS header is the
//the e_lfanew points to the offset of the beginning
//of NT Headers data

//store the section header for future use. This will
//later be need to check to see if metadata lies within
//the area as indicated by the section headers

//Now, start parsing
//First of all check if it is a PE file. All assemblies
//are PE files.
if(pNTHeaders->Signature == IMAGE_NT_SIGNATURE)
//start parsing COM table (this is what points to
//the metadata and other information)
DWORD dwNETHeaderTableLocation =

//.NET header data does exist for this module;
//find its location in one of the sections

//valid address obtained. Suffice it to say,
//this is good enough to identify this as a
//valid managed component
bIsManaged = TRUE;
cout << “Not PE file\r\n”;
return bIsManaged;

That answers the first question.

More by Author

Must Read