Страницы

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

Показаны сообщения с ярлыком assembly. Показать все сообщения
Показаны сообщения с ярлыком assembly. Показать все сообщения

воскресенье, 15 марта 2020 г.

Статическое внедрение dll в сборку

#c_sharp #net #dll #assembly


Нужно при старте Win-приложения загружать dll, но делать это необходимо изнутри сборки
(именно потому статическая загрузка)!

Частичное решение уже есть: https://stackoverflow.com/questions/23971418/c-sharp-embed-dll-in-exe-filenotfoundexception

Но, что и закономерно, в моем случае также вылетает это же исключение: 


  An unhandled exception of type 'System.IO.FileNotFoundException' occurred in mscorlib.dll
  
  Additional information: Не удалось загрузить файл или сборку "SevenZipSharp, Version=0.64.3890.29348,
Culture=neutral, PublicKeyToken=20de82c62b055c88" либо одну из их зависимостей. Не
удается найти указанный файл.


Пробовал добавлять эту dll и через ресурсы (тогда программа даже отказывается стартовать
из-за того, что не находит в нужном месте эту dll, а именно в папке "bin/Debug/...")
и через "Сборка - Add - Existing Item..." (так стартует, но до загрузки формы получаю
вышеописанное исключение).

Код, находящийся в файле Program.cs, имеет вид (практически идентичен тому, который
рассматривается по ссылке и оставлен без ответа; также я пробовал изменять его, следуя
указаниям из различных источников):

namespace WindowsFormsApplication1
{
    static class Program
    {
        [STAThread]
        static void Main()
        {
            AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);

            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Form1());
        }

        static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs
args)
        {
            string assemblyName = args.Name.Split(',').First();
            using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("WindowsFormsApplication1."
+ assemblyName + ".dll"))
            {
                byte[] assemblyData = new byte[stream.Length];
                stream.Read(assemblyData, 0, assemblyData.Length);
                return Assembly.Load(assemblyData);
            }
        }
    }
}


Также пробовал в свойствах dll (добавленной через "Existing Item...") выбирать различные
варианты "Build Action". Результат отрицательный.

Также вариант с программой ILMerge (http://habrahabr.ru/post/126089/) не подходит.

Как указать системе, что я хочу обратиться и загрузить dll по пути изнутри сборки,
а не извне?
    


Ответы

Ответ 1



Вот полные шаги: Добавить SevenZipSharp.dll в проект через Add Existing Item, выставить Build Type = Embedded Resource Добавить SevenZipSharp.dll в References. Выставить у референса Copy Local = false - чтобы избежать копирования в bin. Упомнятуть класс где-то в Form_Load (чтобы произошла попытка подгрузки dll) - у вас это явно сделано: private void Form1_Load(object sender, EventArgs e) { SevenZip.SevenZipCompressor c = new SevenZip.SevenZipCompressor(); } Аккуратно обработать CurrentDomain_AssemblyResolve: using System; using System.Reflection; using System.Windows.Forms; namespace WindowsFormsApplication7 { static class Program { [STAThread] static void Main() { AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve; Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new Form1()); } private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) { var assemblyName = new AssemblyName(args.Name).Name; if (assemblyName == "SevenZipSharp") { using (var stream = typeof(Program).Assembly.GetManifestResourceStream( "WindowsFormsApplication7." + assemblyName + ".dll")) { byte[] assemblyData = new byte[stream.Length]; stream.Read(assemblyData, 0, assemblyData.Length); return Assembly.Load(assemblyData); } } else { return null; } } } } Это минимальный рабочий пример. Если не работает - запускайте под отладчиком. Скорее всего вы не угадали с именем ресурса, и GetManifestResourceStream возвращает null. Убедитесь, что тип у айтема выставлен именно в Embedded Resource (а не просто в Resource). Просмотреть имена всех доступных ресурсов можно прямо в отладчике, вызовом typeof(Program).Assembly.GetManifestResourceNames() Проект целиком на гитхабе: https://github.com/PashaPash/SevenZipSharp-Embedded

воскресенье, 8 декабря 2019 г.

Как сделать единую версию для всех проектов решения

#c_sharp #visual_studio_2012 #assembly


Есть решение, в нем несколько проектов.
Например, проекты: осн. прилож., сервис прилож., библиотека1, библиотека2.
У каждого проекта своя версия, свой файл assembly.cs.
Как сделать файл общим, или версию общую?

Видел вариант, когда у основного проекта файл, а у остальных файла физически нет,
но в дереве проектов есть и значок типа ярлыка.

Как это сделать?
    


Ответы

Ответ 1



Очень просто. Заведите в общем для всех проектов месте файл CommonAssemblyInfo.cs, в котором укажите общую для всех проектов информацию (например, версию). Для каждого из проектов, который должен это использовать, удалите эту самую информацию из AssemblyInfo.cs (этот файл обычно находится в каталоге Properties в стандартном проекте). Добавьте CommonAssemblyInfo.cs в проект через Solution explorer → проект → Add... → Existing Item → CommonAssemblyInfo.cs, не забудьте выбрать «Add as link» Переместите добавленную ссылку в каталог Properties внутри проекта. Для ваших целей в CommonAssemblyInfo.cs имеет смысл указать [assembly: AssemblyVersion("10.1.0.0")] [assembly: AssemblyFileVersion("10.1.0.0")] // File version в свойствах DLL [assembly: AssemblyInformationalVersion("10.1.0.0")] // Product version в свойствах DLL

Ответ 2



Создайте новый cs файл с [assembly: AssemblyVersion("1.2.3.4")] [assembly: AssemblyFileVersion("1.2.3.4")] и добавьте его во вcе проекты через "Add Existing Item" - "Add As Link".

суббота, 6 июля 2019 г.

Компилирование и повторная загрузка сборки в runtime

Товарищи, кратко опишу ситуацию:
Пишу небольшое расширяемое приложение. Чтобы пользователи могли добавлять собственный функционал, в рамках программы создал интерфейсы, от которых они должны наследовать свои типы
При этом я сделал генерацию кода также и внутри самого приложения для большего удобства

Положим, есть такой интерфейс:
namespace MyAppNamespace { public interface INamed { string Name { get; } } }
Далее внутри приложения генерируется следующий код (и кладется в resultCode):
using MyAppNamespace;
public class Wrapper : INamed { public string Name { get { return "Test"; } } }
Компилирую это дело:
// Указываю, что на выходе мне не нужен исполняемый файл, а также что сборку нужно создать по указанному пути CompilerParameters options = new CompilerParameters { GenerateExecutable = false, GenerateInMemory = false, OutputAssembly = $"{SavePath}.dll" }; options.ReferencedAssemblies.Add(new Uri(GetType().Assembly.CodeBase, UriKind.Absolute).LocalPath); // Добавляю ссылку на текущую сборку для наследования интерфейса // Получаю результат компиляции CompilerResults results = new Microsoft.CSharp.CSharpCodeProvider().CompileAssemblyFromSource(options, resultCode); // Опустим проверки // Загружаю сборку из массива байт (так как сам файл потом, возможно, может быть удален) Assembly asm = Assembly.Load(File.ReadAllBytes(results.PathToAssembly)); // Создаю instance типа, который в сборке унаследован от нужного интерфейса INamed named = (INamed)Activator.CreateInstance(asm.DefinedTypes.First(x => x.ImplementedInterfaces.Contains(typeof(INamed))).AsType()); asm = null; GC.Collect(); // Вычищаю сборку из памяти. По крайней мере, я хочу в это верить return named;
После этого я могу спокойно получать доступ к named.Name. Через некоторое время объект "выбрасывается", пока пользователь явно не укажет, что хочет его использовать. В таком случае его нужно будет повторно достать из сборки
Но есть одно жирное но: если я попытаюсь тем же самым образом загрузить сборку и достать из нее тип во второй раз, то визуально процесс пройдет успешно, но при попытке доступа к named.Name вылетит ошибка, что сборка, в которой он определен, не найдена

Я могу поправить логику приложения и сделать считывание единоразовым (пожалуй, это будет даже правильнее), но сейчас для меня важно понимание, почему же так происходит: при первом считывании все работает как часы, а при втором считывании тем же самым способом из того же самого файла процесс проходит успешно, но объект оказывается "битым", так как при попытке доступа к его свойствам я получу ошибку о том, что сборка не может быть загружена


Ответ

Вычищаю сборку из памяти. По крайней мере, я хочу в это верить
Увы, эта вера не имеет оснований. Использованный вами способ загрузки сборки не только не позволяет выгрузить сборку из памяти без выгрузки всего домена приложений, но и при каждом повторном запуске будет грузить сборку с того же пути заново (иными словами, это хороший способ исчерпать память при длительной работе программы).
Создайте Dictionary (где string будет путем к файлу) и кэшируйте все загружаемые сборки в нем. Можно использовать вместо пути CRC/хэш файла, если вам нужно как-то учесть само содержимое файла. Или грузить каждую сборку в новый домен приложений, тогда их можно будет выгрузить (вообще, это обычная практика при создании приложений с расширениями).

вторник, 4 июня 2019 г.

Статическое внедрение dll в сборку

Нужно при старте Win-приложения загружать dll, но делать это необходимо изнутри сборки (именно потому статическая загрузка)!
Частичное решение уже есть: https://stackoverflow.com/questions/23971418/c-sharp-embed-dll-in-exe-filenotfoundexception
Но, что и закономерно, в моем случае также вылетает это же исключение:
An unhandled exception of type 'System.IO.FileNotFoundException' occurred in mscorlib.dll Additional information: Не удалось загрузить файл или сборку "SevenZipSharp, Version=0.64.3890.29348, Culture=neutral, PublicKeyToken=20de82c62b055c88" либо одну из их зависимостей. Не удается найти указанный файл.
Пробовал добавлять эту dll и через ресурсы (тогда программа даже отказывается стартовать из-за того, что не находит в нужном месте эту dll, а именно в папке "bin/Debug/...") и через "Сборка - Add - Existing Item..." (так стартует, но до загрузки формы получаю вышеописанное исключение).
Код, находящийся в файле Program.cs, имеет вид (практически идентичен тому, который рассматривается по ссылке и оставлен без ответа; также я пробовал изменять его, следуя указаниям из различных источников):
namespace WindowsFormsApplication1 { static class Program { [STAThread] static void Main() { AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new Form1()); }
static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) { string assemblyName = args.Name.Split(',').First(); using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("WindowsFormsApplication1." + assemblyName + ".dll")) { byte[] assemblyData = new byte[stream.Length]; stream.Read(assemblyData, 0, assemblyData.Length); return Assembly.Load(assemblyData); } } } }
Также пробовал в свойствах dll (добавленной через "Existing Item...") выбирать различные варианты "Build Action". Результат отрицательный.
Также вариант с программой ILMerge (http://habrahabr.ru/post/126089/) не подходит.
Как указать системе, что я хочу обратиться и загрузить dll по пути изнутри сборки, а не извне?


Ответ

Вот полные шаги:
Добавить SevenZipSharp.dll в проект через Add Existing Item, выставить Build Type = Embedded Resource Добавить SevenZipSharp.dll в References. Выставить у референса Copy Local = false - чтобы избежать копирования в bin. Упомнятуть класс где-то в Form_Load (чтобы произошла попытка подгрузки dll) - у вас это явно сделано:
private void Form1_Load(object sender, EventArgs e) { SevenZip.SevenZipCompressor c = new SevenZip.SevenZipCompressor(); } Аккуратно обработать CurrentDomain_AssemblyResolve
using System; using System.Reflection; using System.Windows.Forms;
namespace WindowsFormsApplication7 { static class Program { [STAThread] static void Main() { AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new Form1()); }
private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) { var assemblyName = new AssemblyName(args.Name).Name; if (assemblyName == "SevenZipSharp") { using (var stream = typeof(Program).Assembly.GetManifestResourceStream( "WindowsFormsApplication7." + assemblyName + ".dll")) { byte[] assemblyData = new byte[stream.Length]; stream.Read(assemblyData, 0, assemblyData.Length); return Assembly.Load(assemblyData); } } else { return null; } } } }
Это минимальный рабочий пример. Если не работает - запускайте под отладчиком. Скорее всего вы не угадали с именем ресурса, и GetManifestResourceStream возвращает null. Убедитесь, что тип у айтема выставлен именно в Embedded Resource (а не просто в Resource). Просмотреть имена всех доступных ресурсов можно прямо в отладчике, вызовом
typeof(Program).Assembly.GetManifestResourceNames()
Проект целиком на гитхабе: https://github.com/PashaPash/SevenZipSharp-Embedded

среда, 17 октября 2018 г.

Как сделать единую версию для всех проектов решения

Есть решение, в нем несколько проектов. Например, проекты: осн. прилож., сервис прилож., библиотека1, библиотека2. У каждого проекта своя версия, свой файл assembly.cs Как сделать файл общим, или версию общую?
Видел вариант, когда у основного проекта файл, а у остальных файла физически нет, но в дереве проектов есть и значок типа ярлыка.
Как это сделать?


Ответ

Очень просто.
Заведите в общем для всех проектов месте файл CommonAssemblyInfo.cs, в котором укажите общую для всех проектов информацию (например, версию).
Для каждого из проектов, который должен это использовать, удалите эту самую информацию из AssemblyInfo.cs (этот файл обычно находится в каталоге Properties в стандартном проекте). Добавьте CommonAssemblyInfo.cs в проект через Solution explorer → проект → Add... → Existing Item → CommonAssemblyInfo.cs, не забудьте выбрать «Add as link»

Переместите добавленную ссылку в каталог Properties внутри проекта.
Для ваших целей в CommonAssemblyInfo.cs имеет смысл указать
[assembly: AssemblyVersion("10.1.0.0")] [assembly: AssemblyFileVersion("10.1.0.0")] // File version в свойствах DLL [assembly: AssemblyInformationalVersion("10.1.0.0")] // Product version в свойствах DLL