У меня есть небольшая программа на 2 версии .NET, но новых версиях Windows она не запускается, но если создать файл настроек, то она запустится.
Есть какие-нибудь варианты, что можно сделать, чтоб было не 2 файла, а 1? Т.е внедрить этот файл настроек в exe или ещё как-нибудь.
Ответ
Встроить файл конфигурации в EXE-файл нельзя (так как весь смысл конфигурации - это возможность редактирования параметров без перекомпиляции программы, такой возможности не предусмотрели). Влиять на параметр supportedRuntime из кода на C# также нельзя, так этот параметр используется неуправляемым кодом загрузчика до того, как в процесс загружена CLR, и в этот момент управляемый код еще не может выполняться.
Если нужно управлять выбором версии CLR без файла конфигурации, единственный способ - написать свой собственный загрузчик на С++, пользуясь Unmanaged .NET API
Например, создадим такую программу на C#:
using System;
namespace ConsoleApplication1
{
class Program
{
static int Run(string arg)
{
Console.WriteLine("Hello from .NET " + Environment.Version.ToString());
Console.ReadKey();
return 0;
}
static void Main(string[] args)
{
Run("");
}
}
}
Скомпилируем ее, получаем файл Program.exe. Создадим проект С++, добавим в него файл Program.exe и создадим файл ресурсов resource.rc следующего содержания:
#define IDR_RCDATA1 101
IDR_RCDATA1 RCDATA "Program.exe"
Напишем на С++ код загрузчика, который находит первую установленную версию CLR, загружает ее, извлекает из ресурсов программу на C# во временную папку и запускает ее:
#include
#include
#include
#include
#pragma comment(lib, "mscoree.lib")
#define IDR_RCDATA1 101
int wmain(int argc, wchar_t* argv[])
{
LPCWSTR prog_name = L"Program.exe"; //имя программы на C#
//построим путь к временному файлу
WCHAR temppath[300] = L"c:\\temp\\";
GetTempPath(300,temppath);
wcscat(temppath,prog_name);
//извлечем программу из ресурсов
HRSRC myResource = ::FindResource(NULL, MAKEINTRESOURCE(IDR_RCDATA1), RT_RCDATA);
UINT Size = ::SizeofResource(NULL, myResource);
HGLOBAL myResourceData = ::LoadResource(NULL, myResource);
void* pMyBinaryData = ::LockResource(myResourceData);
FILE* f = _wfopen(temppath,L"wb");
fwrite(pMyBinaryData,Size,1,f);
fclose(f);
//инициализация CLR...
HRESULT hr;
ICLRMetaHost *pMetaHost = NULL;
ICLRRuntimeInfo *pRuntimeInfo = NULL;
ICLRRuntimeHost *pClrRuntimeHost = NULL;
IEnumUnknown* pEnum= NULL;
ICLRRuntimeInfo* pInfo= NULL;
IUnknown* pUnk = NULL;
hr = CLRCreateInstance(CLSID_CLRMetaHost, IID_PPV_ARGS(&pMetaHost));
if(FAILED(hr)){printf("CLRCreateInstance failed
");goto End;}
//поиск установленных версий CLR...
pMetaHost->EnumerateInstalledRuntimes(&pEnum);
if(FAILED(hr)){printf("EnumerateInstalledRuntimes failed
");goto End;}
ULONG c= 0;
WCHAR buffer[250];
DWORD cch = 250;
while(1){
if(pInfo!=NULL){pInfo->Release();pInfo = NULL;}
if(pUnk!=NULL){pUnk->Release();pUnk = NULL;}
if(pRuntimeInfo!=NULL){pRuntimeInfo->Release();pRuntimeInfo = NULL;}
hr = pEnum->Next(1,&pUnk,&c);
if(hr != S_OK)break;
pUnk->QueryInterface(IID_ICLRRuntimeInfo, (void**)&pInfo);
if(FAILED(hr)){printf("QueryInterface failed
");continue;}
pInfo->GetVersionString(buffer,&cch);
if(FAILED(hr)){printf("GetVersionString failed
");continue;}
hr = pMetaHost->GetRuntime(buffer, IID_PPV_ARGS(&pRuntimeInfo));
if(hr == S_OK){break;}
else {wprintf(L".NET %s: GetRuntime HRESULT 0x%x
",buffer,(UINT)hr);}
}
if(pRuntimeInfo == NULL){printf("Failed to initialize CLR
");goto End;}
/* Можно также указать версию явно, например:
pMetaHost->GetRuntime(L"v2.0.50727", IID_PPV_ARGS(&pRuntimeInfo));
pMetaHost->GetRuntime(L"v4.0.30319", IID_PPV_ARGS(&pRuntimeInfo));
и т.п.
*/
hr = pRuntimeInfo->GetInterface(CLSID_CLRRuntimeHost,
IID_PPV_ARGS(&pClrRuntimeHost));
if(FAILED(hr)){printf("GetInterface failed
");goto End;}
//запуск CLR
hr = pClrRuntimeHost->Start();
if(FAILED(hr)){printf("Start failed
");goto End;}
//Запуск программы на C#
DWORD pReturnValue;
hr = pClrRuntimeHost->ExecuteInDefaultAppDomain(
temppath,
L"ConsoleApplication1.Program", //класс
L"Run", //метод
L"", //параметр
&pReturnValue);
if(FAILED(hr)){printf("ExecuteInDefaultAppDomain failed 0x%x
",(UINT)hr);goto End;}
End:
//Освобождение ресурсов
if(pMetaHost != NULL) pMetaHost->Release();
if(pRuntimeInfo != NULL) pRuntimeInfo->Release();
if(pClrRuntimeHost != NULL) pClrRuntimeHost->Release();
if(pEnum != NULL) pEnum->Release();
if(pInfo != NULL) pInfo->Release();
if(pUnk != NULL) pUnk->Release();
return 0;
}
В результате программа, собранная под .NET 2.0, при его отсутствии будет запускаться на имеющейся версии .NET, как и при использовании параметра supportedRuntime
Источники:
Embedding supportedRuntime into exe file - ответ Ondrej Svejdar
How to load a custom binary resource in a VC++ static library as part of a dll?
- ответ LihO