Вопрос был:
Как вернуть ответ от асинхронного вызова?
, который может быть интерпретирован как:
Как сделать синхронный асинхронный код синхронным?
Решение будет состоять в том, чтобы избежать обратных вызовов и использовать комбинацию Promises и async / await.
Я хотел бы привести пример для запроса Ajax.
(Хотя он может быть записан в Javascript, я предпочитаю писать его на Python и компилировать его в Javascript, используя Transcrypt . Это будет достаточно ясно.)
Позволяет сначала включить использование JQuery, чтобы
$
был доступен какS
:__pragma__ ('alias', 'S', '$')
Определить функцию, которая возвращает Promise, в этом случае вызов Ajax:
def read(url: str): deferred = S.Deferred() S.ajax({'type': "POST", 'url': url, 'data': { }, 'success': lambda d: deferred.resolve(d), 'error': lambda e: deferred.reject(e) }) return deferred.promise()
Использовать асинхронный код, как если бы он был синхронным:
async def readALot(): try: result1 = await read("url_1") result2 = await read("url_2") except Exception: console.warn("Reading a lot failed")
Я настоятельно рекомендую использовать Costura.Fody - безусловно лучший и самый простой способ встраивания ресурсов в вашу сборку. Он доступен как пакет NuGet.
Install-Package Costura.Fody
После добавления его в проект он автоматически вставляет все ссылки, скопированные в выходной каталог, в вашу главную сборку . Возможно, вы захотите очистить встроенные файлы, добавив цель в свой проект:
Install-CleanReferencesTarget
Вы также сможете указать, включать ли pdb, исключать определенные сборки или извлекать сборки на летать. Насколько мне известно, поддерживаются также неуправляемые сборки.
Обновление
В настоящее время некоторые люди пытаются добавить поддержку для DNX .
Ни подход ILMerge, ни Lars Holm Jensen, обрабатывающий событие AssemblyResolve, не будут работать для хоста плагина. Скажем, исполняемый файл H динамически загружает сборку P и обращается к ней через интерфейс IP , определенный в отдельной сборке. Чтобы вставить IP в H , потребуется небольшая модификация кода Ларса:
Dictionary<string, Assembly> loaded = new Dictionary<string,Assembly>();
AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
{ Assembly resAssembly;
string dllName = args.Name.Contains(",") ? args.Name.Substring(0, args.Name.IndexOf(',')) : args.Name.Replace(".dll","");
dllName = dllName.Replace(".", "_");
if ( !loaded.ContainsKey( dllName ) )
{ if (dllName.EndsWith("_resources")) return null;
System.Resources.ResourceManager rm = new System.Resources.ResourceManager(GetType().Namespace + ".Properties.Resources", System.Reflection.Assembly.GetExecutingAssembly());
byte[] bytes = (byte[])rm.GetObject(dllName);
resAssembly = System.Reflection.Assembly.Load(bytes);
loaded.Add(dllName, resAssembly);
}
else
{ resAssembly = loaded[dllName]; }
return resAssembly;
};
Трюк для обработки повторных попыток разрешить одну и ту же сборку и верните существующий, вместо создания нового экземпляра.
РЕДАКТИРОВАТЬ: чтобы он не испортил сериализацию .NET, обязательно верните значение null для всех сборок, не встроенных в ваш, тем самым не соблюдая стандартное поведение. Вы можете получить список этих библиотек:
static HashSet<string> IncludedAssemblies = new HashSet<string>();
string[] resources = System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceNames();
for(int i = 0; i < resources.Length; i++)
{ IncludedAssemblies.Add(resources[i]); }
и просто вернуть значение null, если переданная сборка не принадлежит IncludedAssemblies
.
Да, возможно объединить исполняемые файлы .NET с библиотеками. Для выполнения этой задачи доступны несколько инструментов:
Кроме того, это можно объединить с Mono Linker , который удаляет неиспользуемый код и, следовательно, уменьшает итоговую компоновку Другая возможность заключается в использовании
.NETZ - это инструмент с открытым исходным кодом, который сжимает и упаковывает Microsoft Исполняемые файлы .NET Framework (EXE, DLL), чтобы сделать их меньше.
Я опробовал это решение в коде-проекте, в который встроена DLL: http://www.codeproject.com/Articles/528178/Load-DLL-From-Embedded-Resource
И все получилось отлично.
Возможно, но не все так просто, создать гибридную сборку / управляемую сборку в C #. Если бы вы использовали C ++, это было бы намного проще, так как компилятор Visual C ++ может создавать гибридные сборки так же легко, как и все остальное.
Если у вас нет строгого требования о создании гибридной сборки, я бы согласитесь с MusiGenesis, что это не стоит того, чтобы делать с C #. Если вам нужно это сделать, возможно, посмотрите на переход на C ++ / CLI.
Чтобы увеличить @ Bobby's asnwer выше. Вы можете отредактировать свой .csproj, чтобы использовать IL-Repack , чтобы автоматически упаковывать все файлы в одну сборку при сборке.
Install-Package ILRepack.MSBuild.Task
Вот простой пример, который объединяет ExampleAssemblyToMerge.dll в ваш проект.
<!-- ILRepack -->
<Target Name="AfterBuild" Condition="'$(Configuration)' == 'Release'">
<ItemGroup>
<InputAssemblies Include="$(OutputPath)\$(AssemblyName).exe" />
<InputAssemblies Include="$(OutputPath)\ExampleAssemblyToMerge.dll" />
</ItemGroup>
<ILRepack
Parallel="true"
Internalize="true"
InputAssemblies="@(InputAssemblies)"
TargetKind="Exe"
OutputFile="$(OutputPath)\$(AssemblyName).exe"
/>
</Target>
Это может показаться упрощенным, но WinRar предоставляет возможность сжимать кучу файлов в самораспаковывающийся исполняемый файл. Он имеет множество настраиваемых параметров: финальный значок, извлечение файлов в заданный путь, файл для выполнения после извлечения, пользовательский логотип / тексты для всплывающего окна, отображаемого во время извлечения, всплывающее окно вообще, текст лицензионного соглашения и т. Д. Может быть полезным в некоторых случаях .
Просто щелкните свой проект правой кнопкой мыши в Visual Studio, выберите «Свойства проекта» -> «Ресурсы» -> «Добавить ресурс» - «Добавить существующий файл»). И добавьте код ниже в ваш App.xaml.cs или его эквивалент.
public App()
{
AppDomain.CurrentDomain.AssemblyResolve +=new ResolveEventHandler(CurrentDomain_AssemblyResolve);
}
System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
string dllName = args.Name.Contains(',') ? args.Name.Substring(0, args.Name.IndexOf(',')) : args.Name.Replace(".dll","");
dllName = dllName.Replace(".", "_");
if (dllName.EndsWith("_resources")) return null;
System.Resources.ResourceManager rm = new System.Resources.ResourceManager(GetType().Namespace + ".Properties.Resources", System.Reflection.Assembly.GetExecutingAssembly());
byte[] bytes = (byte[])rm.GetObject(dllName);
return System.Reflection.Assembly.Load(bytes);
}
Вот мой оригинальный пост в блоге: http://codeblog.larsholm.net/2011/06/embed-dlls-easily-in-a-net-assembly/
Я использую компилятор csc.exe, вызванный из сценария .vbs.
В вашем скрипте xyz.cs добавьте следующие строки после директив (мой пример для SSH Renci):
using System;
using Renci;//FOR THE SSH
using System.Net;//FOR THE ADDRESS TRANSLATION
using System.Reflection;//FOR THE Assembly
//+ref>"C:\Program Files (x86)\Microsoft\ILMerge\Renci.SshNet.dll"
//+res>"C:\Program Files (x86)\Microsoft\ILMerge\Renci.SshNet.dll"
//+ico>"C:\Program Files (x86)\Microsoft CAPICOM 2.1.0.2 SDK\Samples\c_sharp\xmldsig\resources\Traffic.ico"
Теги ref, res и ico будут подхвачены скриптом .vbs ниже, чтобы сформировать команду csc.
Затем добавьте вызывающего абонента для сборки в Main:
public static void Main(string[] args)
{
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
.
... и добавить сам резольвер где-то в классе:
static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) { String resourceName = new AssemblyName(args.Name).Name + ".dll"; using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName)) { Byte[] assemblyData = new Byte[stream.Length]; stream.Read(assemblyData, 0, assemblyData.Length); return Assembly.Load(assemblyData); } }
Я называю скрипт vbs в соответствии с именем .cs filename (например, ssh.vbs ищет ssh .cs); это делает запуск скрипта несколько раз намного проще, но если вы не идиот, как я, тогда общий сценарий может получить целевой файл .cs из перетаскивания:
Dim name_,oShell,fso Set oShell = CreateObject("Shell.Application") Set fso = CreateObject("Scripting.fileSystemObject") 'TAKE THE VBS SCRIPT NAME AS THE TARGET FILE NAME '################################################ name_ = Split(wscript.ScriptName, ".")(0) 'GET THE EXTERNAL DLL's AND ICON NAMES FROM THE .CS FILE '####################################################### Const OPEN_FILE_FOR_READING = 1 Set objInputFile = fso.OpenTextFile(name_ & ".cs", 1) 'READ EVERYTHING INTO AN ARRAY '############################# inputData = Split(objInputFile.ReadAll, vbNewline) For each strData In inputData if left(strData,7)="//+ref>" then csc_references = csc_references & " /reference:" & trim(replace(strData,"//+ref>","")) & " " end if if left(strData,7)="//+res>" then csc_resources = csc_resources & " /resource:" & trim(replace(strData,"//+res>","")) & " " end if if left(strData,7)="//+ico>" then csc_icon = " /win32icon:" & trim(replace(strData,"//+ico>","")) & " " end if Next objInputFile.Close 'COMPILE THE FILE '################ oShell.ShellExecute "c:\windows\microsoft.net\framework\v3.5\csc.exe", "/warn:1 /target:exe " & csc_references & csc_resources & csc_icon & " " & name_ & ".cs", "", "runas", 2 WScript.Quit(0)
Вы можете добавить библиотеки DLL в качестве встроенных ресурсов, а затем распаковать их в каталог приложения при запуске (после проверки, есть ли у них там уже).
Файлы настроек настолько просты чтобы, тем не менее, я не думаю, что это того стоило.
EDIT: Этот метод был бы легким в сборках .NET. С не-.NET DLL было бы намного больше работы (вам нужно было бы выяснить, где распаковать файлы и зарегистрировать их и т. Д.).
Другим продуктом, который может с этим справиться, является SmartAssembly, на SmartAssembly.com . Этот продукт в дополнение к объединению всех зависимостей в одну DLL (необязательно) обфускает ваш код, удаляет дополнительные метаданные, чтобы уменьшить результирующий размер файла, а также может фактически оптимизировать IL для повышения производительности во время выполнения. Существует также какая-то глобальная функция обработки / обработки исключений, которая добавляется к вашему программному обеспечению (если это необходимо), что я не нашел времени, чтобы понять, но может быть полезным. Я считаю, что у него также есть API командной строки, поэтому вы можете сделать его частью своего процесса сборки.
Я бы порекомендовал вам проверить утилиту .NETZ, которая также сжимает сборку со схемой по вашему выбору:
Выдержка Джеффри Рихтера очень хороша. Короче говоря, добавьте библиотеку в качестве встроенных ресурсов и добавьте обратный вызов перед чем-либо еще. Вот версия кода (найденная в комментариях его страницы), которую я поставил в начале метода Main для консольного приложения (просто убедитесь, что любые вызовы, которые используют библиотеку, находятся в другом методе Main).
AppDomain.CurrentDomain.AssemblyResolve += (sender, bargs) =>
{
String dllName = new AssemblyName(bargs.Name).Name + ".dll";
var assem = Assembly.GetExecutingAssembly();
String resourceName = assem.GetManifestResourceNames().FirstOrDefault(rn => rn.EndsWith(dllName));
if (resourceName == null) return null; // Not found, maybe another handler will find it
using (var stream = assem.GetManifestResourceStream(resourceName))
{
Byte[] assemblyData = new Byte[stream.Length];
stream.Read(assemblyData, 0, assemblyData.Length);
return Assembly.Load(assemblyData);
}
};
Если они действительно управляются сбоями, вы можете использовать ILMerge . Для родных DLL-файлов вам потребуется немного больше работы.
Кроме ILMerge , если вы не хотите беспокоиться о переключении командной строки, я действительно рекомендую ILMerge-Gui . Это проект с открытым исходным кодом, очень хороший!
Проверить boxedapp
Он может встраивать dll в любое приложение. Написано также на C #:)
Надеюсь, что это поможет.
Как правило, для выполнения слияния сборки, как вы описываете, вам понадобится форма сборки post build. Существует бесплатный инструмент Eazfuscator (eazfuscator.blogspot.com/), который предназначен для обработки байт-кода, который также обрабатывает слияние сборки. Вы можете добавить это в командную строку post build с помощью Visual Studio для объединения ваших сборок, но ваш пробег будет зависеть от проблем, возникающих в любых сценариях слияния неконкурентов.
Вы также можете проверить, чтобы увидеть если сборка делает невозможным, NANT имеет возможность объединять сборки после сборки, но я не достаточно знаком с самим NANT, чтобы сказать, встроена ли функция или нет.
Существует также много плагинов Visual Studio который будет выполнять слияние сборки как часть создания приложения.
Альтернативно, если вам не нужно, чтобы это было сделано автоматически, существует ряд инструментов, таких как ILMerge, которые будут объединять сборки .net в один файл .
Самая большая проблема, с которой я столкнулась при объединении сборок, - это использование каких-либо похожих пространств имен. Или, что еще хуже, ссылаться на разные версии одной и той же DLL (мои проблемы обычно были с файлами DLL NUnit).
ILMerge может комбинировать сборки с одной сборкой, если сборка имеет только управляемый код. Вы можете использовать приложение командной строки или добавить ссылку на exe и программно объединить. Для версии GUI существует Eazfuscator , а также .Netz , оба из которых являются свободными. Платные приложения включают BoxedApp и SmartAssembly .
Если вам нужно объединить сборки с неуправляемым кодом, я бы предложил SmartAssembly . У меня никогда не было икоты с SmartAssembly , но со всеми остальными. Здесь он может встраивать необходимые зависимости в качестве ресурсов в ваш основной exe.
Вы можете все это вручную не беспокоиться, если сборка управляется или в смешанном режиме, встраивая DLL в ваши ресурсы, а затем полагаясь на Сборка AppDomain ResolveHandler
. Это одностановленное решение, приняв наихудший случай, т. Е. Сборки с неуправляемым кодом.
static void Main()
{
AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
{
string assemblyName = new AssemblyName(args.Name).Name;
if (assemblyName.EndsWith(".resources"))
return null;
string dllName = assemblyName + ".dll";
string dllFullPath = Path.Combine(GetMyApplicationSpecificPath(), dllName);
using (Stream s = Assembly.GetEntryAssembly().GetManifestResourceStream(typeof(Program).Namespace + ".Resources." + dllName))
{
byte[] data = new byte[stream.Length];
s.Read(data, 0, data.Length);
//or just byte[] data = new BinaryReader(s).ReadBytes((int)s.Length);
File.WriteAllBytes(dllFullPath, data);
}
return Assembly.LoadFrom(dllFullPath);
};
}
Ключевым здесь является запись байтов в файл и загрузка из его местоположения. Чтобы избежать проблемы с курицей и яйцом, вы должны убедиться, что вы объявляете обработчик перед доступом к сборке и что вы не получаете доступа к членам сборки (или создаете экземпляр всего, что связано с сборкой) внутри части загрузки (сборки). Также будьте осторожны, чтобы гарантировать, что GetMyApplicationSpecificPath()
не является временным каталогом, поскольку временные файлы могут быть удалены другими программами или самим собой (а не то, что он будет удален, пока ваша программа обратится к DLL, но, по крайней мере, это неприятность. AppData - хорошее место). Также обратите внимание, что каждый раз вы должны писать байты, вы не можете загружать из местоположения только «потому что dll уже там существует».
Для управляемых dll вам не нужно писать байты, а непосредственно загружать из местоположения dll или просто прочитать байты и загрузить сборку из памяти. Например:
using (Stream s = Assembly.GetEntryAssembly().GetManifestResourceStream(typeof(Program).Namespace + ".Resources." + dllName))
{
byte[] data = new byte[stream.Length];
s.Read(data, 0, data.Length);
return Assembly.Load(data);
}
//or just
return Assembly.LoadFrom(dllFullPath); //if location is known.
Если сборка полностью неуправляема, вы можете увидеть эту ссылку или эту относительно того, как загрузить такие DLL .