Страницы

Поиск по вопросам

пятница, 30 ноября 2018 г.

App.config и приложение на C#

У меня есть небольшая программа на 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

Комментариев нет:

Отправить комментарий