Click here to Skip to main content
12,078,370 members (50,417 online)
Click here to Skip to main content

Stats

140.7K views
2.4K downloads
80 bookmarked
Posted

Shell Extensions for .NET Assemblies

, 17 Nov 2005 Ms-PL
Shell extensions to distinguish between .NET assemblies and Win32 applications and libraries.
// AssemblyInfo.cpp : Implementation of CAssemblyInfo

#include "stdafx.h"
#include "resource.h"
#include "AssemblyInfo.h"
#include <oleauto.h>

#define MAKEPTR(cast, ptr, addValue) (cast)((DWORD_PTR)(ptr) + (DWORD_PTR)(addValue))

// Define the char array for hex characters.
const WCHAR c_szHex[] = L"0123456789abcdefg";

// CAssemblyInfo

// Returns success codes of S_OK and S_FALSE in place of the optional boolean return
// value, or an error code and IErrorInfo otherwise.
STDMETHODIMP CAssemblyInfo::IsAssembly(BSTR path, VARIANT_BOOL* retVal)
{
	HRESULT hr;
	FILEINFO FileInfo = { FILEINFOTYPE_CORHEADER };

	hr = GetFileInfo(path, &FileInfo);

	// Don't err when retVal is NULL because
	// internal implementation may pass NULL.
	if (SUCCEEDED(hr) && retVal)
	{
		*retVal = FileInfo.varfCorHeader;
	}

	return hr;
}

STDMETHODIMP CAssemblyInfo::GetFileType(BSTR path, FileType* retVal)
{
	CW2TEX<MAX_PATH> szPath(path);
	FILEINFO FileInfo = { FILEINFOTYPE(FILEINFOTYPE_CORHEADER | FILEINFOTYPE_MACHINEARCH) };
	FileType ft = Other; // Default to Other.
	HRESULT hr = NOERROR;
	LPTSTR pszExtension;

	if (path && retVal)
	{
		// Check the extension first to boost performance.
		pszExtension = PathFindExtension(szPath);
		if (_tcsicmp(pszExtension, TEXT(".dll")) == 0)
		{
			ft = Win32Library;
		}
		else if (_tcsicmp(pszExtension, TEXT(".exe")) == 0)
		{
			ft = Win32Executable;
		}
		else if (_tcsicmp(pszExtension, TEXT(".netmodule")) == 0)
		{
			ft = DotNETModule;
		}

		// For EXE, DLL, and MOD get the file information.
		if (Other != ft)
		{
			hr = GetFileInfo(path, &FileInfo);
			if (SUCCEEDED(hr))
			{
				// If this is a managed assembly and not a .netmodule increment the FileType
				// by the relative offset for managed assemblies.
				if (DotNETModule != ft && VARIANT_TRUE == FileInfo.varfCorHeader)
					ft = FileType(ft + OFFSET_DOTNET);

				// If the file targets IA64 increment relative offset for IA64.
				if (IMAGE_FILE_MACHINE_IA64 == FileInfo.wMachineArch)
					ft = FileType(ft + OFFSET_IA64);
				// Else if the file targets X64 increment by relative offset for X64.
				else if (IMAGE_FILE_MACHINE_AMD64 == FileInfo.wMachineArch)
					ft = FileType(ft + OFFSET_X64);
			}
		}

		*retVal = ft;
	}
	else
	{
		hr = E_POINTER;
	}

	return hr;
}

STDMETHODIMP CAssemblyInfo::GetPublicKeyToken(BSTR path, BSTR* retVal)
{
	BYTE b;
	CComBSTR* bstrToken = NULL;
	HRESULT hr;
	LPBYTE pToken = NULL;
	ULONG cbToken = 0;
	ULONG i, j;

	static BOOL fError = FALSE;

	if (path && retVal)
	{
		// Default the return value to NULL.
		*retVal = NULL;

		// First make sure the path points to an assembly.
		hr = IsAssembly(path, NULL);
		if (S_OK == hr)
		{
			try
			{
				if (!fError && g_mscoree.StrongNameTokenFromAssembly(path, &pToken, &cbToken))
				{
					bstrToken = new CComBSTR(cbToken * 2); // 2 chars / byte for hex.

					// Encode the BYTE[] to hexadecimal.
					for (i = 0, j = 0; i < cbToken; i++)
					{
						b = pToken[i];
						bstrToken->m_str[j++] = c_szHex[b >> 4];
						bstrToken->m_str[j++] = c_szHex[b & 0xf];
					}

					// Free the buffer allocated by StrongNameTokenFromAssembly.
					g_mscoree.StrongNameFreeBuffer(pToken);

					bstrToken->CopyTo(retVal);
				}
			}
			catch (...)
			{
				fError = TRUE;
			}
		}
	}
	else
	{
		hr = E_POINTER;
	}

	if (bstrToken)
	{
		delete bstrToken;
		bstrToken = NULL;
	}

	return hr;
}

STDMETHODIMP CAssemblyInfo::GetPublicKey(BSTR path, SAFEARRAY** retVal)
{
	HRESULT hr;
	LPBYTE pKeyBlob = NULL;
	LPBYTE pToken = NULL;
	SAFEARRAY* psa;
	ULONG cbKeyBlob = 0;
	ULONG cbToken = 0;
	LPBYTE pbData = NULL;
	
	static BOOL fError = FALSE;

	if (path && retVal)
	{
		// First make sure the path points to an assembly.
		hr = IsAssembly(path, NULL);
		if (S_OK == hr)
		{
			try
			{
				if (!fError && g_mscoree.StrongNameTokenFromAssemblyEx(path, &pToken, &cbToken, &pKeyBlob, &cbKeyBlob) && cbKeyBlob > 0)
				{
					psa = SafeArrayCreateVector(VT_UI1, 0, cbKeyBlob);
					if (psa)
					{
						// Lock the SA and copy the element data.
						hr = SafeArrayAccessData(psa, (LPVOID*)&pbData);
						if (SUCCEEDED(hr))
						{
							memcpy(pbData, pKeyBlob, (size_t)cbKeyBlob);
							hr = SafeArrayUnaccessData(psa);
							if (SUCCEEDED(hr))
								*retVal = psa;
						}
					}
					else
						hr = E_OUTOFMEMORY;

					// Free the buffers allocated by StrongNameTokenFromAssembly.
					g_mscoree.StrongNameFreeBuffer(pToken);
					g_mscoree.StrongNameFreeBuffer(pKeyBlob);

				}
			}
			catch (...)
			{
				fError = TRUE;
			}
		}
	}
	else
	{
		hr = E_POINTER;
	}

	return hr;
}

HRESULT CAssemblyInfo::GetFileInfo(BSTR path, LPFILEINFO pFileInfo)
{
	CAtlFile file;
	CAtlFileMappingBase map;
	HRESULT hr;
	LPVOID pFile;

	if (path && pFileInfo)
	{
		// Open the file image referenced by the path parameter.
		hr = file.Create(CW2TEX<MAX_PATH>(path), GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING,
			FILE_ATTRIBUTE_READONLY | FILE_FLAG_RANDOM_ACCESS);
		if (FAILED(hr))
			return AtlReportError(GetObjectCLSID(), ERR_FILE_OPEN, IID_IAssemblyInfo, hr);

		// Map the file into memory. The destructor will unmap the file, which will
		// release the file handle when unmapped.
		hr = map.MapFile(file);
		if (FAILED(hr))
		{
			file.Close();
			return AtlReportError(GetObjectCLSID(), ERR_FILE_MAP, IID_IAssemblyInfo, hr);
		}

		pFile = map.GetData();

		// Determine if the COM+ header is found in the file.
		if (0 != (pFileInfo->fit & FILEINFOTYPE_CORHEADER))
		{
			hr = HasCorHeader(pFile);
			if (SUCCEEDED(hr))
			{
				pFileInfo->varfCorHeader = (S_OK == hr) ? VARIANT_TRUE : VARIANT_FALSE;
			}
		}

		// Get the machine architecture from the executable.
		if (SUCCEEDED(hr) && 0 != (pFileInfo->fit & FILEINFOTYPE_MACHINEARCH))
		{
			hr = GetMachineArch(pFile, &pFileInfo->wMachineArch);
		}

		// Close the file and return.
		file.Close();
	}
	else
	{
		hr = E_POINTER;
	}

	return hr;
}

HRESULT CAssemblyInfo::GetNtHeaders(LPVOID pFile, PIMAGE_NT_HEADERS* ppNtHeaders)
{
	HRESULT hr;
	PIMAGE_DOS_HEADER pDosHeader;

	if (pFile && ppNtHeaders)
	{
		// Get the PE header from the file.
		pDosHeader = reinterpret_cast<PIMAGE_DOS_HEADER>(pFile);
		if (IMAGE_DOS_SIGNATURE == pDosHeader->e_magic)
		{
			*ppNtHeaders = MAKEPTR(PIMAGE_NT_HEADERS, pDosHeader, pDosHeader->e_lfanew);
			hr = S_OK;
		}
		else
		{
			// The PE/COFF executable is not supported.
			hr = CO_E_NOT_SUPPORTED;
		}
	}
	else
	{
		hr = E_POINTER;
	}

	return hr;
}

HRESULT CAssemblyInfo::GetComDirectory(PIMAGE_NT_HEADERS pNtHeaders, PIMAGE_DATA_DIRECTORY pComDirectory)
{
	HRESULT hr;

	if (pNtHeaders && pComDirectory)
	{
		// Get the NT headers.
		if (IMAGE_NT_SIGNATURE == pNtHeaders->Signature)
		{
			// Get the data directories from the appropriate optional NT header.
			switch (pNtHeaders->OptionalHeader.Magic)
			{
			case IMAGE_NT_OPTIONAL_HDR32_MAGIC:
				*pComDirectory = reinterpret_cast<PIMAGE_NT_HEADERS32>
					(pNtHeaders)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR];
				hr = S_OK;
				break;

			case IMAGE_NT_OPTIONAL_HDR64_MAGIC:
				*pComDirectory = reinterpret_cast<PIMAGE_NT_HEADERS64>
					(pNtHeaders)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR];
				hr = S_OK;
				break;

			default:
				hr = CO_E_NOT_SUPPORTED;
			}
		}
		else
		{
			// The header signature is invalid or the header format is not supported.
			hr = CO_E_NOT_SUPPORTED;
		}
	}
	else
	{
		hr = E_POINTER;
	}

	return hr;
}

HRESULT CAssemblyInfo::HasCorHeader(LPVOID pFile)
{
	HRESULT hr;
	PIMAGE_NT_HEADERS pNtHeaders = NULL;
	IMAGE_DATA_DIRECTORY ComDirectory;

	hr = GetNtHeaders(pFile, &pNtHeaders);
	if (SUCCEEDED(hr))
	{
		hr = GetComDirectory(pNtHeaders, &ComDirectory);
		if (SUCCEEDED(hr))
		{
			// If the VirtualAddress is set, a valid COM+ header is present.
			hr = (ComDirectory.VirtualAddress && ComDirectory.Size) ? S_OK : S_FALSE;
		}
	}

	return hr;
}

HRESULT CAssemblyInfo::GetMachineArch(LPVOID pFile, LPWORD pwMachine)
{
	HRESULT hr;
	PIMAGE_NT_HEADERS pNtHeaders = NULL;

	if (pwMachine)
	{
		hr = GetNtHeaders(pFile, &pNtHeaders);
		if (SUCCEEDED(hr) && pNtHeaders)
		{
			*pwMachine = pNtHeaders->FileHeader.Machine;
		}
	}
	else
	{
		hr = E_POINTER;
	}

	return hr;
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Microsoft Public License (Ms-PL)

Share

About the Author

Heath Stewart
Program Manager Microsoft
United States United States
Heath Stewart is a happily married software engineer originally from the Midwest and a graduate of Iowa State University. Heath start programming early in life and enjoys continuous research and development in new languages, frameworks, and platforms. Fluent in many different programming languages, he has developed many large-scale software solutions for companies in different areas, such as Internet filtering, intrusion detection systems, production management systems, and web applications for various purposes. He also enjoys photography.

Currently, Heath is a Program Manager in the Visual Studio Professional Deployment Experience (VSPro DEX) team at Microsoft. Previous to his employment, he was a Microsoft MVP for Visual C#.

He is also a CodeProject protector and is happy to help the development community.

You may also be interested in...

| Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.160212.1 | Last Updated 17 Nov 2005
Article Copyright 2002 by Heath Stewart
Everything else Copyright © CodeProject, 1999-2016
Layout: fixed | fluid