Reputation: 50778
This is not a duplicate off following SO question: How do I tell if a win32 application uses the .NET runtime.
How can I find out programatically if a given exe file is a .net exe file or a regular WIN32/WIN64 exe file?
The question is not about interrogating a running process but it's about an exe file and as the tags suggest, a solution written in VB.net or C# is not required.
I need a function with a signature such as:
// return true if filename is a exe file for .Net
bool IsExeFileDotNet(LPCTSTR filename)
{
...
}
Upvotes: 9
Views: 4874
Reputation: 1286
The idea is to check if special PE directory IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR
exists.
I've written similar function recently, here the code, you can use it. In fact I've used a smart wrapper for handle, but it's omitted here, so expicit call of CloseHandle added. Also it's a good idea to check errors after ReadFile/SetFilePointer calls. Anyway I hope it can be useful:
BOOL IsDotNetApp(LPCWSTR szPath)
{
HANDLE hFile = CreateFileW(szPath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
if (INVALID_HANDLE_VALUE == hFile)
return FALSE;
DWORD temp;
IMAGE_DOS_HEADER IMAGE_DOS_HEADER_;
ReadFile(hFile, &IMAGE_DOS_HEADER_, sizeof(IMAGE_DOS_HEADER_), &temp, NULL);
SetFilePointer(hFile, IMAGE_DOS_HEADER_.e_lfanew, NULL, FILE_BEGIN);
const int nNtHeaderMaxSize = sizeof(IMAGE_NT_HEADERS64);
BYTE NT_HEADERS[nNtHeaderMaxSize];
ReadFile(hFile, NT_HEADERS, nNtHeaderMaxSize, &temp, NULL);
PIMAGE_NT_HEADERS pNT_HEADERS = (PIMAGE_NT_HEADERS)NT_HEADERS;
BOOL bRes;
if (pNT_HEADERS->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC)
{
bRes = 0 != ((PIMAGE_NT_HEADERS32)NT_HEADERS)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR].VirtualAddress;
}
else if (pNT_HEADERS->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC)
{
bRes = 0 != ((PIMAGE_NT_HEADERS64)NT_HEADERS)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR].VirtualAddress;
}
else
{
// Unknown header type
bRes = FALSE;
}
CloseHandle(hFile);
return bRes;
}
Upvotes: 5
Reputation: 30606
One approach is to interrogate the PE header etc. for the correct flags. There are several links for further reading; here and here (and an older MSDN article here).
MS documentation on the PE header is available (with a license agreement).
The easiest may just be to list the dependent dlls (since this is for the main exe only) and look for the presence of mscoree.dll. The import table will include mscoree.dll and include and entry for the function _CorExeMain
(from mscoree.dll). More links on this can be found here on SO and here on GitHub, that appears to be an extensive sample, and this article (on CodeGuru) with code for a function with the signature BOOL IsManaged(LPTSTR lpszImageName)
that you require (license seems to restrict re-publishing).
Upvotes: 8
Reputation: 941217
The olden standby was to read the target runtime version from the file with GetFileVersion(). It will fail with ERROR_BAD_FORMAT when the executable file does not contain the CLR header. Works in any bitness and any target architecture of the assembly. You'll have to use it like this:
#define USE_DEPRECATED_CLR_API_WITHOUT_WARNING
#include <mscoree.h>
#pragma comment(lib, "mscoree.lib")
#include <assert.h>
bool IsExeFileDotNet(LPCWSTR filename)
{
WCHAR buf[16];
HRESULT hr = GetFileVersion(filename, buf, 16, NULL);
assert(hr == S_OK || hr == HRESULT_FROM_WIN32(ERROR_BAD_FORMAT));
return hr == S_OK;
}
Do note the use of USE_DEPRECATED_CLR_API_WITHOUT_WARNING to suppress a deprecation error, the MSCorEE api is liable to disappear in a future major release of .NET.
The non-deprecated way is to use ICLRMetaHost::GetFileVersion(), disadvantage is that it can only work when the machine has .NET 4 installed. Not exactly a major problem today. Looks like this:
#include <Windows.h>
#include <metahost.h>
#include <assert.h>
#pragma comment(lib, "mscoree.lib")
bool IsExeFileDotNet(LPCWSTR filename)
{
ICLRMetaHost* host;
HRESULT hr = CLRCreateInstance(CLSID_CLRMetaHost, IID_ICLRMetaHost, (void**)&host);
assert(SUCCEEDED(hr));
if (hr == S_OK) {
WCHAR buf[16];
DWORD written;
hr = host->GetVersionFromFile(filename, buf, &written);
assert(hr == S_OK || hr == HRESULT_FROM_WIN32(ERROR_BAD_FORMAT));
host->Release();
}
return SUCCEEDED(hr);
}
Other techniques that poke the executable file directly to look for the CLR header in the file are mentioned in this Q+A. How future-proof they may be is very hard to guess.
Upvotes: 8