Archive for the ‘.NET’ Category
NBox и проблема с App.config
Как известно, CLR при запуске .NET-приложения пытается прочитать config-файл с именем запускаемой сборки + расширение “.config”. Но ведь если запускаемый exe-файл переименовать, то config-файл не подгрузится – и приложение, возможно, перестанет работать. С этим частенько сталкиваются разработчики при работе над деплоингом приложения. И, к сожалению, .NET не содержит способов, позволяющих самому приложению задать имя config-файла во время некой процедуры “прединициализации”. Это возможно лишь для опций, которые читаются классом Settings путем написания собственного SettingsProvider’a, реализующего интерфейс IApplicationSettingsProvider (см Application Settings Architecture для более подробной информации).
Но мы можем сделать самостоятельно из нашего приложения некий “микрозагрузчик”, который создает AppDomain, конфигурирует его и запускает сам себя, используя сконфигурированный AppDomain. Это действительно работает, причем в самом примитивном случае код очень компактен :
AppDomainSetup setupInfo = new AppDomainSetup(); string entryAssemblyPath = Assembly.GetEntryAssembly().Location; string entryAssemblyDir = Path.GetDirectoryName(entryAssemblyPath); setupInfo.ApplicationBase = Path.GetDirectoryName(entryAssemblyPath); setupInfo.ConfigurationFile = Path.Combine(entryAssemblyDir, "MyApp.exe.config"); AppDomain forkedDomain = AppDomain.CreateDomain(Guid.NewGuid().ToString(), null, setupInfo); forkedDomain.ExecuteAssembly(Assembly.GetEntryAssembly().Location); AppDomain.Unload(forkedDomain);
Однако, необходимо следить за тем, чтобы такие ухищрения не приводили к сильной деградации производительности на этапе загрузки. Возможно, стоит написать отдельную сборку, которая будет загружать уже то, что нужно нам. Эта сборка будет очень малого размера, и работать будет практически без потери производительности.
А теперь подумаем, что произойдет, если у приложения, обработанного с помощью NBox (а это, как правило, один-единственный исполняемый файл), изменить имя файла. Если приложение не использовало config-файлы, то все будет ок. Но в противном случае – при запуске – CLR просто не подцепит старый конфиг, и приложение, скорее всего, работать не будет. Но, используя вышеприведенные приемы, мы можем заставить NBox распознать config-файл, и перезапустить самого себя, предварительно сконфигурировав AppDomain нужным config-файлом. А чтобы работа по разворачиванию файлов и сборок не производилась дважды, NBox может извлечь файлы во время начального запуска, и, перезапустившись, уже не извлекать файлы, работая со сборками. Таким образом можно минимизировать потери производительности, одновременно получая еще одну степень свободы в том, как сконфигурировать запускаемое приложение.
В принципе, теперь даже не обязательно извлекать config-файл рядом с exe-файлом упакованного приложения. NBox может закешировать извлекаемые config-файлы во временной директории, извлекая их заново в случае необходимости. Для пользователя это означает минус один файл, создаваемый в каталоге приложения, а для разработчика – уверенность в том, что его конфигурация будет применена независимо от названия exe-файла.
PS. Интересно, что метод AppDomainSetup.SetConfigurationBytes, который бы мог избавить нас от использования файлов в таких случаях, похоже, просто не работает
Здесь тред об этом : AppDomainSetup.SetConfigurationBytes has no visible effect on configuration…
minifmod4net
Не так давно мутил я на дотнете воспроизведение трекерных файлов, а именно, XM. Громоздкие либы типа BASS явно для этой задачи не подходили, широко известный в узких кругах ufmod почему-то в связке с дотнет интеропом не заработал, зато нашлась библиотечка minifmod, которая обладала всеми необходимыми качествами (интересовала возможность загрузки из файла и из памяти) и небольшими габаритами. Для того, чтобы можно было удобно указывать способ загрузки (из файла или из памяти), я дописал маленький адаптер, который обрабатывал callback’и минифмода, и засунул его в DLL. Соответственно, нативная DLL к .net-проекту была подцеплена через простейший interop.
Сегодня наконец собрался с мыслями, почистил файлы проекта, и выложил все на гуглокод, благо sourceforge и codeplex уже попробовал. Google code очень понравился своей беспроблемностью в залитии файлов и работе с SVN. Вспоминая, как я трахался с sourceforge через SFTP чтобы залить туда пару архивов, могу сказать, что здесь все очень и очень удобно.
Итак, сайт проекта находится здесь http://code.google.com/p/minifmod4net/
Код использования библиотеки прост как пять копеек, достаточно взглянуть на тестовый пример, чтобы все стало ясно. Только один момент – если вы желаете использовать ее в своей программе, то вам придется сменить режим генерации кода с AnyCPU на x86, поскольку имеется нативная DLL с 32-битным кодом, и в режиме AnyCPU на какой-нибудь 64-битной винде программа не запустится, мотивировав отказ исключением наподобие ImageBadFormatException. Если же х86 специфицировать явно, то все будет работать и на 64-битных Windows.
PS. Поздравляю всех с наступившим 2010 годом, надеюсь, он принесет всем нам немало радости и профессиональных успехов
Идея насчет синтаксиса обработки исключений
Часто приходится писать обработчики исключений, которые делают одно и то же. Например, в следующем кусочке кода нам необходимо среагировать на исключения типа Exception1 и Exception2 записью в лог-файл :
try { // Блок, который может вызвать исключения Exception1, Exception2 // или исключение любого другого типа } catch (Exception1 exc) { logger.WarnException("An exception has been occured : {0}", exc); } catch (Exception2 exc) { logger.WarnException("An exception has been occured : {0}", exc); }
Проблема в том, что мы не можем никак избежать дублирования кода. Единственный выход – создать отдельный метод-обработчик, в котором и инкапсулировать логику. Но – во-первых, это может оказаться неудобным, поскольку локальные переменные, которые могут понадобиться в обработчике, придется передавать при вызове функции, а во-вторых, сам по себе вызов функции – это еще несколько тактов процессора.
Хотелось бы иметь способ, который бы позволял писать, скажем, следующим образом :
try { // } catch (Exception1, Exception2 exc as Exception) { // exc имеет тип Exception }