<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>#Shooter</title>
	<atom:link href="http://www.sharp-shooter.ru/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.sharp-shooter.ru</link>
	<description>Программирование на C# и все такое</description>
	<lastBuildDate>Mon, 25 Jan 2010 10:31:00 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.8.4</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Exepack.NET 0.02 alpha</title>
		<link>http://www.sharp-shooter.ru/2010/01/25/exepack-net-0-02-alpha/</link>
		<comments>http://www.sharp-shooter.ru/2010/01/25/exepack-net-0-02-alpha/#comments</comments>
		<pubDate>Sun, 24 Jan 2010 21:55:06 +0000</pubDate>
		<dc:creator>yallie</dc:creator>
				<category><![CDATA[Программирование]]></category>
		<category><![CDATA[Программы]]></category>
		<category><![CDATA[.NET]]></category>
		<category><![CDATA[programming]]></category>
		<category><![CDATA[tools]]></category>

		<guid isPermaLink="false">http://www.sharp-shooter.ru/?p=186</guid>
		<description><![CDATA[Не прошло и года, как я доделал Windows-версию упаковщика Exepack.NET. Правда, помимо интерфейса WinForms тут добавлена еще целая куча разных улучшений.
Консольную и GUI-версии программы вместе с исходниками можно взять на сайте проекта. Среди прочего в исходниках лежит и концептуальная версия (минимальный скелет программы без каких-либо наворотов), по которой можно разобраться, как все это работает. Концептуальная [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://antiquatoria.ru/yallie/miscellaneous/exepack.net-0.02-alpha.png" rel="lightbox"><img class="alignleft" title="Exepack.NET 0.02 alpha" src="http://antiquatoria.ru/yallie/miscellaneous/exepack.net-0.02-alpha-thumb.png" alt="" width="200" height="158" /></a>Не прошло и года, как я доделал Windows-версию упаковщика Exepack.NET. Правда, помимо интерфейса WinForms тут добавлена еще целая куча разных улучшений.</p>
<p>Консольную и GUI-версии программы вместе с исходниками можно взять <a href="http://exepack.codeplex.com/">на сайте проекта</a>. Среди прочего в исходниках лежит и концептуальная версия (минимальный скелет программы без каких-либо наворотов), по которой можно разобраться, как все это работает. Концептуальная версия описана в нескольких прошлых постах: <a href="http://www.sharp-shooter.ru/2009/01/24/exepack1/">часть 1</a>, <a href="http://www.sharp-shooter.ru/2009/01/31/exepack2/">часть 2</a>, <a href="http://www.sharp-shooter.ru/2009/02/07/exepack3/">часть 3</a>.</p>
<p>Системные требования:</p>
<ul>
<li>.NET Framework версии 2.0 или выше (используется DeflateStream)</li>
<li>Windows XP или выше (используются системные процедуры для загрузки ресурсов)</li>
</ul>
<p>Новые возможности версии 0.02 alpha:</p>
<ul style="clear: both;">
<li>Исполняемый файл генерируется с помощью Reflection.Emit</li>
<li>Ресурсы иконок и VersionInfo копируются из исходного файла</li>
<li>В Windows-версии можно добавлять в список дополнительные сборки для упаковки в тот же исполняемый файл</li>
<li>Добавлена защита от дизассемблирования Red Gate .NET Reflector-ом</li>
<li>Ну и, разумеется, <span style="text-decoration: line-through;">добавлены новые</span> исправлены старые ошибки.</li>
</ul>
<p><a href="http://antiquatoria.ru/yallie/miscellaneous/reflector-crash-exepack-0.02-alpha.png" rel="lightbox"><img class="alignright" title="Reflector crashes" src="http://antiquatoria.ru/yallie/miscellaneous/reflector-crash-exepack-0.02-alpha-thumb.png" alt="" width="200" height="147" /></a>Справа — скриншот Рефлектора, который пытается декомпилировать упакованный файл. Попытка декомпилировать в C# (и любой другой высокоуровневый язык) приводит к ошибке. Дизассемблирование в IL, разумеется, работает, как и ILDasm (любую программу, которая загружается и запускается под .NET Framework, можно дизассемблировать в IL-код).</p>
<p>P.S. Стоит иметь в виду, что это по-прежнему альфа-версия. Любое использование исключительно на свой страх и риск. Замечания об ошибках и неполадках приветствуются.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.sharp-shooter.ru/2010/01/25/exepack-net-0-02-alpha/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Непредвиденная ошибка 0x8ffe2740</title>
		<link>http://www.sharp-shooter.ru/2009/08/01/0x8ffe2740/</link>
		<comments>http://www.sharp-shooter.ru/2009/08/01/0x8ffe2740/#comments</comments>
		<pubDate>Fri, 31 Jul 2009 23:09:39 +0000</pubDate>
		<dc:creator>yallie</dc:creator>
				<category><![CDATA[Ошибки]]></category>

		<guid isPermaLink="false">http://www.sharp-shooter.ru/?p=165</guid>
		<description><![CDATA[Это мой любимый формат сообщений об ошибках. Первая реакция человека непосвященного — испугаться и решить, что все пропало (как же, ошибка непредвиденная! Да еще такая ужасная, 0x8ffe2740).
На самом деле такие ошибки исправлять проще всего. Надо только не ошибиться, набирая в гугле ругательное слово 0x8ffe2740.
На скриншоте представлена моя неудачная попытка запустить службу W3SCV (это веб-сервер IIS). Не ища [...]]]></description>
			<content:encoded><![CDATA[<p><img class="alignleft size-full wp-image-166" title="Непредвиденная ошибка 0x8ffe2740" src="http://www.sharp-shooter.ru/wp-content/uploads/2009/08/0x8ffe2740.png" alt="Непредвиденная ошибка 0x8ffe2740" width="266" height="126" />Это мой любимый формат сообщений об ошибках. Первая реакция человека непосвященного — испугаться и решить, что все пропало (как же, ошибка непредвиденная! Да еще такая ужасная, <span>0x</span>8ffe2740).</p>
<p>На самом деле такие ошибки исправлять проще всего. Надо только не ошибиться, набирая в гугле ругательное слово <span>0</span>x8ffe2740.</p>
<p>На скриншоте представлена моя неудачная попытка запустить службу W3SCV (это веб-сервер IIS). Не ища легких путей (бросаться сразу в гугл — сами понимаете, неспортивно), я для начала полез посмотреть подробности в EventViewer. Подробности оказались еще более загадочными: «Службе не удается создать привязку экземпляра 1. Данные содержат код ошибки». И код ошибки в шестнадцатиричном виде: 40 27 00 00. Потрясающе.</p>
<p>На этом месте я все-таки плюнул и полез в гугл. Ошибка, которую в данном случае не смогли предвидеть разработчики IIS — это всего-навсего <a title="Служба поддержки Microsoft — Непредвиденная ошибка 0x8ffe2740" href="http://support.microsoft.com/kb/816944/ru">занятый 80-й порт</a>.</p>
<p>Для тех, кому не терпится справиться с ошибкой, сообщаю: скорее всего, у вас (как и у меня) этот порт занял Skype. Чтобы исправить ситуацию, зайдите в меню скайпа: Инструменты -&gt; Настройки -&gt; Дополнительно -&gt; Соединение. Снимите флажок «Использовать порты 80 и 443 в качестве входящих альтернативных» и перезапустите скайп. После этого служба IIS стартует нормально.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.sharp-shooter.ru/2009/08/01/0x8ffe2740/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Хранилище для временных файлов</title>
		<link>http://www.sharp-shooter.ru/2009/03/06/tempfilemanager/</link>
		<comments>http://www.sharp-shooter.ru/2009/03/06/tempfilemanager/#comments</comments>
		<pubDate>Thu, 05 Mar 2009 22:41:29 +0000</pubDate>
		<dc:creator>yallie</dc:creator>
				<category><![CDATA[Программирование]]></category>
		<category><![CDATA[.NET]]></category>
		<category><![CDATA[programming]]></category>
		<category><![CDATA[tools]]></category>
		<category><![CDATA[tutorial]]></category>

		<guid isPermaLink="false">http://www.sharp-shooter.ru/?p=130</guid>
		<description><![CDATA[Мне частенько приходится использовать хранилище для временных файлов (например, чтобы сохранить файлы во временную папку и передать на обработку внешней программе, а в конце работы все почистить). Обычно я каждый раз пишу подобную ерунду заново, чтобы не искать того, что было написано в прошлый раз. Искать — это, во-первых долго, во-вторых лень, а в-третьих иногда [...]]]></description>
			<content:encoded><![CDATA[<p>Мне частенько приходится использовать хранилище для временных файлов (например, чтобы сохранить файлы во временную папку и передать на обработку внешней программе, а в конце работы все почистить). Обычно я каждый раз пишу подобную ерунду заново, чтобы не искать того, что было написано в прошлый раз. Искать — это, во-первых долго, во-вторых лень, а в-третьих иногда оказывается, что искомое лежит не на домашнем компе, а на рабочем. Словом, обычно написать заново выходит быстрее <img src='http://www.sharp-shooter.ru/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<p>Сегодня я очередной раз написал такой класс-хранилище, и получилось вполне симпатично. Пользоваться им нужно вот так:</p>

<div class="wp_syntax"><div class="code"><pre class="csharp" style="font-family:monospace;"><span style="color: #0600FF;">using</span> <span style="color: #000000;">&#40;</span>TempFileManager temp <span style="color: #008000;">=</span> <span style="color: #008000;">new</span> TempFileManager<span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span>
<span style="color: #000000;">&#123;</span>
  <span style="color: #0600FF;">using</span> <span style="color: #000000;">&#40;</span>FileStream fs <span style="color: #008000;">=</span> File.<span style="color: #0000FF;">Create</span><span style="color: #000000;">&#40;</span>temp<span style="color: #000000;">&#91;</span><span style="color: #666666;">&quot;my-temp-file.txt&quot;</span><span style="color: #000000;">&#93;</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span>
  <span style="color: #000000;">&#123;</span>
    <span style="color: #008080; font-style: italic;">// файл my-temp-file.txt создался во временной папке</span>
    <span style="color: #008080; font-style: italic;">// и будет удален вместе с ней</span>
  <span style="color: #000000;">&#125;</span>
<span style="color: #000000;">&#125;</span></pre></div></div>

<p><span id="more-130"></span> Класс TempFileManager создает временную папку со случайным именем в подпапке системного временного каталога (обычно этот путь хранится в переменной окружения %TEMP% или %TMP% и расположен где-нибудь в C:\WINDOWS\Temp).</p>
<p>TempFileManager реализует интерфейс IDisposable, и в методе Dispose удаляет временные файлы и папку, в которой они хранятся. Временные файлы, кстати, вовсе необязательно должны быть в этой папке: можно добавить в список файлы с указанием полного пути, и они тоже будут чиститься в конце работы.</p>
<p>Файл регистрируется как временный, когда его имя передается в индексатор класса TempFileManager (индексатор — это свойство this[string fileName], возвращает он тоже строку). Индексатор TempFileManager всегда возвращает полный путь к переданному файлу: если имя файла указано без пути, файл считается помещенным во временную папку, иначе остается как есть. Например:</p>

<div class="wp_syntax"><div class="code"><pre class="csharp" style="font-family:monospace;"><span style="color: #0600FF;">using</span> <span style="color: #000000;">&#40;</span>TempFileManager t <span style="color: #008000;">=</span> <span style="color: #008000;">new</span> TempFileManager<span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span>
<span style="color: #000000;">&#123;</span>
  <span style="color: #FF0000;">string</span> s <span style="color: #008000;">=</span> t<span style="color: #000000;">&#91;</span><span style="color: #666666;">@&quot;1.txt&quot;</span><span style="color: #000000;">&#93;</span><span style="color: #008000;">;</span>    <span style="color: #008080; font-style: italic;">// s = @&quot;c:\Windows\Temp\...\1.txt&quot;</span>
  s <span style="color: #008000;">=</span> t<span style="color: #000000;">&#91;</span><span style="color: #666666;">@&quot;c:\autoexec.bat&quot;</span><span style="color: #000000;">&#93;</span><span style="color: #008000;">;</span> <span style="color: #008080; font-style: italic;">// s = @&quot;c:\autoexec.bat&quot;;</span>
<span style="color: #000000;">&#125;</span></pre></div></div>

<p>Исходный текст можно взять на странице проекта <a title="Exepack.NET project homepage" href="http://www.codeplex.com/exepack">Exepack.NET</a> или здесь: <a title="TempFileManager.cs" href="http://www.sharp-shooter.ru/wp-content/uploads/2009/03/TempFileManager.cs">TempFileManager.cs</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.sharp-shooter.ru/2009/03/06/tempfilemanager/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Декларативный интерфейс</title>
		<link>http://www.sharp-shooter.ru/2009/02/14/declarative/</link>
		<comments>http://www.sharp-shooter.ru/2009/02/14/declarative/#comments</comments>
		<pubDate>Fri, 13 Feb 2009 22:40:12 +0000</pubDate>
		<dc:creator>yallie</dc:creator>
				<category><![CDATA[Программирование]]></category>
		<category><![CDATA[.NET]]></category>
		<category><![CDATA[programming]]></category>
		<category><![CDATA[tutorial]]></category>

		<guid isPermaLink="false">http://www.sharp-shooter.ru/?p=111</guid>
		<description><![CDATA[Для большинства небольших программ на WinForms отлично подходит дизайнер меню и панелей инструментов. Самый подходящий случай — это небольшая монолитная программка с более-менее фиксированным набором функций. Например, какой-нибудь простой текстовый редактор.
А самый неподходящий вариант — модульная программа, построенная на плагинах. Что если плагин должен иметь возможность добавлять команды в главное меню или кнопки на панель [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.sharp-shooter.ru/wp-content/uploads/2009/02/declarative_ui.png" rel="lightbox"><img class="alignleft size-full wp-image-113" title="Декларативный интерфейс" src="http://www.sharp-shooter.ru/wp-content/uploads/2009/02/declarative_ui_thumb.gif" alt="Декларативный интерфейс" width="204" height="146" /></a>Для большинства небольших программ на WinForms отлично подходит дизайнер меню и панелей инструментов. Самый подходящий случай — это небольшая монолитная программка с более-менее фиксированным набором функций. Например, какой-нибудь простой текстовый редактор.</p>
<p>А самый неподходящий вариант — модульная программа, построенная на плагинах. Что если плагин должен иметь возможность добавлять команды в главное меню или кнопки на панель инструментов? Давать плагину доступ к меню небезопасно, поэтому обычно поступают наоборот: программа спрашивает у плагина, какие команды он поддерживает и сама добавляет их в свое меню. Для этого плагин должен поддерживать метод типа GetPluginCommands(), возвращающий коллекцию доступных команд.</p>
<p><span id="more-111"></span>В результате в плагине приходится писать здоровенный такой метод, который формирует некую древовидную структуру команд. При добавлении в плагин новых функций нужно каждый раз лезть в этот метод и что-то туда дописывать. В .NET есть гораздо более изящный вариант решения подобной задачи — с помощью атрибутов.</p>
<p>Посмотрите на картинку: панель инструментов и меню описываются атрибутами ToolButton и MenuItem. Чтобы добавить новую команду-кнопку на панель инструментов, я пишу метод-обработчик, реализующий эту команду, и помечаю его атрибутом [ToolButton("Название команды")]. Добавление команд в главное меню сделано почти так же, только атрибут MenuItem позволяет указывать полный путь для добавляемой команды. Например, пункт File -&gt; Exit описывается атрибутом [MenuItem("File", "Exit")]. Уровень вложенности может быть любой. При старте программа строит список помеченных атрибутами методов и добавляет пункты в меню и кнопки на панель инструментов.</p>
<p>Чем такой подход хорош? Во-первых, при добавлении новых функций не нужно лезть в дизайнер, чтобы что-нибудь там найти. Во-вторых, команды можно вытаскивать из любого класса (из самой программы или из плагинов) совершенно одинаковым способом. Плагину не нужен будет монструозный метод GetPluginCommands(), не несущий никакой полезной нагрузки.</p>
<p>Мне очень нравится такое описание интерфейсов, по-моему, оно удобно даже в маленьких проектах вроде <a title="Exepack.NET" href="http://www.codeplex.com/exepack">Exepack.NET</a>. Я далеко не всегда пишу в VisualStudio, и дизайнер форм иногда запустить просто негде, так что этот способ меня здорово выручает.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.sharp-shooter.ru/2009/02/14/declarative/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Exepack.NET, часть 3</title>
		<link>http://www.sharp-shooter.ru/2009/02/07/exepack3/</link>
		<comments>http://www.sharp-shooter.ru/2009/02/07/exepack3/#comments</comments>
		<pubDate>Sat, 07 Feb 2009 02:01:58 +0000</pubDate>
		<dc:creator>yallie</dc:creator>
				<category><![CDATA[Программирование]]></category>
		<category><![CDATA[.NET]]></category>
		<category><![CDATA[programming]]></category>
		<category><![CDATA[tools]]></category>
		<category><![CDATA[tutorial]]></category>

		<guid isPermaLink="false">http://www.sharp-shooter.ru/?p=92</guid>
		<description><![CDATA[С загрузчиком все понятно, осталось сделать сам упаковщик. Задача упаковщика в двух словах такова: определить список файлов, упаковать их, скомпилировать загрузчик и приклеить к нему упакованные файлы в виде ресурсов. Пока я писал загрузчик, мне нужно было делать это вручную, теперь же моя задача это автоматизировать.
Итак, упаковщик работает по такой схеме:

Загружает главную исполняемую сборку (*.exe)
Составляет [...]]]></description>
			<content:encoded><![CDATA[<p><img class="alignleft size-full wp-image-93" title="ilmerge_packed" src="http://www.sharp-shooter.ru/wp-content/uploads/2009/02/ilmerge_packed1.png" alt="ilmerge_packed" width="186" height="127" />С загрузчиком все понятно, осталось сделать сам упаковщик. Задача упаковщика в двух словах такова: определить список файлов, упаковать их, скомпилировать загрузчик и приклеить к нему упакованные файлы в виде ресурсов. Пока я писал загрузчик, мне нужно было делать это вручную, теперь же моя задача это автоматизировать.</p>
<p>Итак, упаковщик работает по такой схеме:</p>
<ol>
<li>Загружает главную исполняемую сборку (*.exe)</li>
<li>Составляет список используемых библиотек классов (*.dll)</li>
<li>Упаковывает все сборки, сохраняет во временные файлы</li>
<li>Готовит исходник для загрузчика (loader.cs)</li>
<li>Компилирует загрузчик, добавляя временные файлы как ресурсы</li>
<li>Удаляет временные файлы</li>
</ol>
<p>Список библиотек для упаковки, конечно, можно было бы задавать явно (например, в командной строке) . Но согласитесь, гораздо приятнее, если упаковщик сам его составит, тем более, что это сделать совсем несложно. Стандартный класс-обертка Assembly, который позволяет более-менее успешно манипулировать .NET-сборками, дает возможность получить список модулей, на которые ссылается загруженная сборка: Assembly.GetReferencedAssemblies().</p>
<p><span id="more-92"></span> Повторяя этот процесс рекурсивно, мы получим полный список библиотек, которые используются нашим пакуемым приложением. Правда, в этом списке будут присутствовать всякие системные сборки типа mscorlib.dll и System.dll. При упаковке их, конечно, нужно будет пропускать.</p>
<p>Упаковки данных делается тем же самым классом DeflateStream, что и распаковка в загрузчике. При упаковке файлов я меняю их расширение на .deflated, в ресурсах файлы так и будут называться (было MyLib.dll, стало MyLib.deflated). Тут есть еще одна небольшая хитрость. Сборка-библиотека может быть спрятана где-нибудь далеко, например, в глобальном кэше сборок (GAC). Чтобы не искать ее самостоятельно, можно загрузить сборку стандартным механизмом загрузки — с помощью Assembly.LoadFrom(string name), — а затем посмотреть на свойство Assembly.Location. Получается весьма компактный метод:</p>

<div class="wp_syntax"><div class="code"><pre class="csharp" style="font-family:monospace;"><span style="color: #0600FF;">static</span> <span style="color: #FF0000;">string</span> CompressFile<span style="color: #000000;">&#40;</span><span style="color: #FF0000;">string</span> asmName<span style="color: #000000;">&#41;</span>
<span style="color: #000000;">&#123;</span>
  <span style="color: #008080; font-style: italic;">// load assembly</span>
  Assembly asm <span style="color: #008000;">=</span> Assembly.<span style="color: #0000FF;">LoadFrom</span><span style="color: #000000;">&#40;</span>asmName<span style="color: #000000;">&#41;</span><span style="color: #008000;">;</span>
  <span style="color: #FF0000;">string</span> compressedName <span style="color: #008000;">=</span> Path.<span style="color: #0000FF;">ChangeExtension</span><span style="color: #000000;">&#40;</span>asm.<span style="color: #0000FF;">Location</span>, <span style="color: #666666;">&quot;deflated&quot;</span><span style="color: #000000;">&#41;</span><span style="color: #008000;">;</span>
&nbsp;
  <span style="color: #0600FF;">using</span> <span style="color: #000000;">&#40;</span>FileStream inFile <span style="color: #008000;">=</span> File.<span style="color: #0000FF;">OpenRead</span><span style="color: #000000;">&#40;</span>asm.<span style="color: #0000FF;">Location</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span>
  <span style="color: #0600FF;">using</span> <span style="color: #000000;">&#40;</span>FileStream outFile <span style="color: #008000;">=</span> File.<span style="color: #0000FF;">Create</span><span style="color: #000000;">&#40;</span>compressedName<span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span>
  <span style="color: #0600FF;">using</span> <span style="color: #000000;">&#40;</span>DeflateStream ds <span style="color: #008000;">=</span> <span style="color: #008000;">new</span> DeflateStream<span style="color: #000000;">&#40;</span>outFile, CompressionMode.<span style="color: #0000FF;">Compress</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span>
  <span style="color: #000000;">&#123;</span>
    <span style="color: #008000;">new</span> BinaryWriter<span style="color: #000000;">&#40;</span>ds<span style="color: #000000;">&#41;</span>.<span style="color: #0000FF;">Write</span><span style="color: #000000;">&#40;</span><span style="color: #008000;">new</span> BinaryReader<span style="color: #000000;">&#40;</span>inFile<span style="color: #000000;">&#41;</span>.<span style="color: #0000FF;">ReadBytes</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#40;</span><span style="color: #FF0000;">int</span><span style="color: #000000;">&#41;</span>inFile.<span style="color: #0000FF;">Length</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span><span style="color: #008000;">;</span>
  <span style="color: #000000;">&#125;</span>
&nbsp;
  <span style="color: #0600FF;">return</span> compressedName<span style="color: #008000;">;</span>
<span style="color: #000000;">&#125;</span></pre></div></div>

<p>При загрузке каждой сборки надо еще проверить: вдруг она уже была загружена (например, по ссылке из другой сборки). Здесь я опустил такую проверку, но в исходных текстах Exepack.NET она, конечно, есть.</p>
<p>Дальше у нас по плану — исходник загрузчика. Можно его держать в отдельном файле loader.cs на диске, но я считаю, что это неудобно. Вдруг потеряется. Гораздо лучше, если он будет храниться в ресурсах самого упаковщика. Непосредственно перед компиляцией его нужно извлечь и сохранить на диск, чтобы компилятор C# смог до него добраться. Попутно нужно в нем исправить кое-какие мелочи (в частности, прописать имя ресурса, из которого будут загружены упакованные данные):</p>

<div class="wp_syntax"><div class="code"><pre class="csharp" style="font-family:monospace;"><span style="color: #0600FF;">static</span> <span style="color: #FF0000;">string</span> GetLoaderSource<span style="color: #000000;">&#40;</span><span style="color: #FF0000;">string</span> exeResourceName<span style="color: #000000;">&#41;</span>
<span style="color: #000000;">&#123;</span>
  <span style="color: #0600FF;">using</span> <span style="color: #000000;">&#40;</span>Stream stream <span style="color: #008000;">=</span>
    Assembly.<span style="color: #0000FF;">GetExecutingAssembly</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span>.<span style="color: #0000FF;">GetManifestResourceStream</span><span style="color: #000000;">&#40;</span><span style="color: #666666;">&quot;loader.cs&quot;</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span>
  <span style="color: #000000;">&#123;</span>
    <span style="color: #FF0000;">string</span> loader <span style="color: #008000;">=</span> Encoding.<span style="color: #0000FF;">UTF8</span>.<span style="color: #0000FF;">GetString</span><span style="color: #000000;">&#40;</span>
      <span style="color: #008000;">new</span> BinaryReader<span style="color: #000000;">&#40;</span>stream<span style="color: #000000;">&#41;</span>.<span style="color: #0000FF;">ReadBytes</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#40;</span><span style="color: #FF0000;">int</span><span style="color: #000000;">&#41;</span>stream.<span style="color: #0000FF;">Length</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span><span style="color: #008000;">;</span>
    loader <span style="color: #008000;">=</span> loader.<span style="color: #0000FF;">Replace</span><span style="color: #000000;">&#40;</span><span style="color: #666666;">&quot;app.deflated&quot;</span>, exeResourceName<span style="color: #000000;">&#41;</span><span style="color: #008000;">;</span>
    <span style="color: #0600FF;">return</span> loader<span style="color: #008000;">;</span>
  <span style="color: #000000;">&#125;</span>
<span style="color: #000000;">&#125;</span></pre></div></div>

<p>Осталось рассказать о том, как запустить C#, чтобы скомпилировать загрузчик с упакованными ресурсами. Я не советую париться по поводу путей установки .NET Framework и собирания директив компиляции в RSP-файл: есть куда более легкий путь. Можно воспользоваться CodeDom-провайдером для C# (это такая подсистема .NET Framework, которая отвечает за генерацию кода — например, в дизайнере форм). Провайдер CodeDom самостоятельно определит, где находится компилятор C# (csc.exe), подготовит ключи компиляции, запустит и сохранит готовый EXE-файл. Очень удобно:</p>

<div class="wp_syntax"><div class="code"><pre class="csharp" style="font-family:monospace;"><span style="color: #0600FF;">static</span> <span style="color: #FF0000;">string</span> CreateExecutable<span style="color: #000000;">&#40;</span>List files,
  <span style="color: #FF0000;">string</span> iconName, <span style="color: #FF0000;">bool</span> isConsole<span style="color: #000000;">&#41;</span>
<span style="color: #000000;">&#123;</span>
  <span style="color: #008080; font-style: italic;">// prepare loader source code</span>
  <span style="color: #FF0000;">string</span> exeName <span style="color: #008000;">=</span> Path.<span style="color: #0000FF;">GetFileName</span><span style="color: #000000;">&#40;</span>files<span style="color: #000000;">&#91;</span><span style="color: #FF0000;">0</span><span style="color: #000000;">&#93;</span><span style="color: #000000;">&#41;</span><span style="color: #008000;">;</span>
  <span style="color: #FF0000;">string</span> loader <span style="color: #008000;">=</span> GetLoaderSource<span style="color: #000000;">&#40;</span>exeName<span style="color: #000000;">&#41;</span><span style="color: #008000;">;</span>
&nbsp;
  <span style="color: #008080; font-style: italic;">// prepare compiler parameters</span>
  CompilerParameters cp <span style="color: #008000;">=</span> <span style="color: #008000;">new</span> CompilerParameters<span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span><span style="color: #008000;">;</span>
  cp.<span style="color: #0000FF;">GenerateExecutable</span> <span style="color: #008000;">=</span> true<span style="color: #008000;">;</span>
  cp.<span style="color: #0000FF;">OutputAssembly</span> <span style="color: #008000;">=</span> Path.<span style="color: #0000FF;">ChangeExtension</span><span style="color: #000000;">&#40;</span>exeName, <span style="color: #666666;">&quot;packed.exe&quot;</span><span style="color: #000000;">&#41;</span><span style="color: #008000;">;</span>
  cp.<span style="color: #0000FF;">IncludeDebugInformation</span> <span style="color: #008000;">=</span> false<span style="color: #008000;">;</span>
  cp.<span style="color: #0000FF;">GenerateInMemory</span> <span style="color: #008000;">=</span> false<span style="color: #008000;">;</span>
  cp.<span style="color: #0000FF;">ReferencedAssemblies</span>.<span style="color: #0000FF;">Add</span><span style="color: #000000;">&#40;</span><span style="color: #666666;">&quot;System.dll&quot;</span><span style="color: #000000;">&#41;</span><span style="color: #008000;">;</span>
  cp.<span style="color: #0000FF;">CompilerOptions</span> <span style="color: #008000;">=</span> <span style="color: #666666;">&quot;/o /filealign:512&quot;</span><span style="color: #008000;">;</span>
  cp.<span style="color: #0000FF;">CompilerOptions</span> <span style="color: #008000;">+=</span> isConsole <span style="color: #008000;">?</span> <span style="color: #666666;">&quot; /target:exe&quot;</span> <span style="color: #008000;">:</span> <span style="color: #666666;">&quot; /target:winexe&quot;</span><span style="color: #008000;">;</span>
  cp.<span style="color: #0000FF;">CompilerOptions</span> <span style="color: #008000;">+=</span> iconName <span style="color: #008000;">!=</span> <span style="color: #0600FF;">null</span> <span style="color: #008000;">?</span> <span style="color: #666666;">&quot; /win32icon:<span style="color: #008080; font-weight: bold;">\&quot;</span>&quot;</span> <span style="color: #008000;">+</span> iconName <span style="color: #008000;">+</span> <span style="color: #666666;">&quot;<span style="color: #008080; font-weight: bold;">\&quot;</span>&quot;</span> <span style="color: #008000;">:</span> <span style="color: #666666;">&quot;&quot;</span><span style="color: #008000;">;</span>
&nbsp;
  <span style="color: #008080; font-style: italic;">// add compressed resources</span>
  <span style="color: #0600FF;">foreach</span> <span style="color: #000000;">&#40;</span><span style="color: #FF0000;">string</span> fileName <span style="color: #0600FF;">in</span> files<span style="color: #000000;">&#41;</span>
  <span style="color: #000000;">&#123;</span>
    <span style="color: #0600FF;">if</span> <span style="color: #000000;">&#40;</span>fileName <span style="color: #008000;">==</span> exeName<span style="color: #000000;">&#41;</span>
      continue<span style="color: #008000;">;</span>
&nbsp;
    cp.<span style="color: #0000FF;">EmbeddedResources</span>.<span style="color: #0000FF;">Add</span><span style="color: #000000;">&#40;</span>fileName<span style="color: #000000;">&#41;</span><span style="color: #008000;">;</span>
  <span style="color: #000000;">&#125;</span>
&nbsp;
  <span style="color: #008080; font-style: italic;">// compile and check for errors</span>
  CompilerResults cr <span style="color: #008000;">=</span> <span style="color: #008000;">new</span> CSharpCodeProvider<span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span>.<span style="color: #0000FF;">CompileAssemblyFromSource</span><span style="color: #000000;">&#40;</span>cp, loader<span style="color: #000000;">&#41;</span><span style="color: #008000;">;</span>
  <span style="color: #0600FF;">if</span> <span style="color: #000000;">&#40;</span>cr.<span style="color: #0000FF;">Errors</span>.<span style="color: #0000FF;">Count</span> <span style="color: #008000;">&amp;</span>gt<span style="color: #008000;">;</span> <span style="color: #FF0000;">0</span><span style="color: #000000;">&#41;</span>
  <span style="color: #000000;">&#123;</span>
    <span style="color: #008080; font-style: italic;">// Display compilation errors.</span>
    Console.<span style="color: #0000FF;">WriteLine</span><span style="color: #000000;">&#40;</span><span style="color: #666666;">&quot;Errors building loader into {0}&quot;</span>, cr.<span style="color: #0000FF;">PathToAssembly</span><span style="color: #000000;">&#41;</span><span style="color: #008000;">;</span>
    <span style="color: #0600FF;">foreach</span> <span style="color: #000000;">&#40;</span>CompilerError ce <span style="color: #0600FF;">in</span> cr.<span style="color: #0000FF;">Errors</span><span style="color: #000000;">&#41;</span>
    <span style="color: #000000;">&#123;</span>
      Console.<span style="color: #0000FF;">WriteLine</span><span style="color: #000000;">&#40;</span><span style="color: #666666;">&quot;  {0}&quot;</span>, ce.<span style="color: #0000FF;">ToString</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span><span style="color: #008000;">;</span>
      Console.<span style="color: #0000FF;">WriteLine</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span><span style="color: #008000;">;</span>
    <span style="color: #000000;">&#125;</span>
  <span style="color: #000000;">&#125;</span>
&nbsp;
  <span style="color: #008080; font-style: italic;">// return new executable name</span>
  <span style="color: #0600FF;">return</span> cp.<span style="color: #0000FF;">OutputAssembly</span><span style="color: #008000;">;</span>
<span style="color: #000000;">&#125;</span></pre></div></div>

<p>К сожалению, класс CompilerParameters поддерживает не все возможности компилятора C#. Поэтому кое-что приходится задавать ключами CompilerOptions. Это плохо, потому что не переносимо (то есть, не будет работать с другими компиляторами C# — Mono, Portable.NET и т. д.) Лично я с этим пока смирился: если придется портировать упаковщик для Linux — вероятно, это будет наименьшей проблемой из всех, что у меня появятся.</p>
<p>Ну вот, собственно, и все. Оказалось неожиданно просто. Весь процесс от задумки до работающей программы занял три часа (или чуть больше), зато написание статей растянулось на несколько дней. Кстати, на скриншоте в самом начале статьи показан файл ILMerge.exe после упаковки (было 828, стало 373 килобайта).</p>
<p>Я создал проект <a title="Exepack.NET at CodePlex" href="http://www.codeplex.com/exepack">Exepack.NET</a> на сайте CodePlex. Исходники для упаковщика из этой статьи — это <a title="Exepack.NET version 0,00 alpha" href="http://www.codeplex.com/exepack/Release/ProjectReleases.aspx?ReleaseId=22851">релиз версии 0.00 alpha</a>. Проект выпущен под лицензией MIT (в двух словах: используйте как угодно, в коммерческих или некоммерческих целях, только оставьте упоминание оригинального авторства). Если захотите принять участие в дальнейшей разработке проекта — присоединяйтесь!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.sharp-shooter.ru/2009/02/07/exepack3/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Exepack.NET, часть 2</title>
		<link>http://www.sharp-shooter.ru/2009/01/31/exepack2/</link>
		<comments>http://www.sharp-shooter.ru/2009/01/31/exepack2/#comments</comments>
		<pubDate>Fri, 30 Jan 2009 23:19:45 +0000</pubDate>
		<dc:creator>yallie</dc:creator>
				<category><![CDATA[Программирование]]></category>
		<category><![CDATA[.NET]]></category>
		<category><![CDATA[programming]]></category>
		<category><![CDATA[tools]]></category>
		<category><![CDATA[tutorial]]></category>

		<guid isPermaLink="false">http://www.sharp-shooter.ru/?p=74</guid>
		<description><![CDATA[
Попробуем усложнить задачу. Возьмем какое-нибудь реальное .NET-приложение, которое состоит из нескольких сборок. Как правило, это один EXE-файл и несколько дополнительных DLL-библиотек.
Модули (файлы *.netmodule) я рассматривать не буду, никогда не видел, чтобы ими кто-то пользовался. Я могу ошибаться, но по-моему, в Visual Studio нет для них полноценной поддержки: проекты компилируются в монолитные сборки, а не в набор [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.sharp-shooter.ru/wp-content/uploads/2009/01/app_ildasm.png" rel="lightbox"><img class="size-thumbnail wp-image-75 alignleft" title="Программа app.exe в дизассемблере ILDASM" src="http://www.sharp-shooter.ru/wp-content/uploads/2009/01/app_ildasm-150x150.png" alt="Программа app.exe в дизассемблере ILDASM" width="150" height="150" /></a></p>
<p>Попробуем усложнить задачу. Возьмем какое-нибудь реальное .NET-приложение, которое состоит из нескольких сборок. Как правило, это один EXE-файл и несколько дополнительных DLL-библиотек.</p>
<p>Модули (файлы *.netmodule) я рассматривать не буду, никогда не видел, чтобы ими кто-то пользовался. Я могу ошибаться, но по-моему, в Visual Studio нет для них полноценной поддержки: проекты компилируются в монолитные сборки, а не в набор модулей. Теоретически, конечно, это может быть реализовано по-разному на разных платформах, но я пока не ставил себе цели написать полностью переносимый EXE-упаковщик.</p>
<p>Чтобы не искать готовое приложение, я за минуту написал небольшую программку из двух файлов: app.cs и applib.cs. На картинке показано, как такая программа выглядит в дизассемблере ILDASM (красным выделена ссылка на сборку-библиотеку).</p>
<p><span id="more-74"></span>Теперь я модифицирую загрузчик так, чтобы он мог распаковать и запустить программу вместе с библиотекой. Файлы app.exe и applib.dll я присоединяю к ресурсам моего загрузчика, чтобы получить один EXE-файл. Загрузчик работает по старой схеме: вытаскивает ресурс с именем app.exe и выполняет метод Main() с помощью методов отражения. Но теперь при загрузке происходит еще кое-что интересное.</p>
<p>Файл app.exe невозможно загрузить отдельно от applib.dll. Поэтому среда исполнения .NET перед загрузкой сборки app.exe начинает усиленно искать applib.dll везде, где только можно: сначала в текущем каталоге, потом в GAC. Поскольку файла нигде нет (он ведь теперь упакован), среда генерирует исключение: файл не найден.</p>
<p>А раз есть исключение, мы можем написать для него свой обработчик. Обработчик (у меня он называется ExtractAssembly) подключается одной строчкой:</p>

<div class="wp_syntax"><div class="code"><pre class="csharp" style="font-family:monospace;">AppDomain.<span style="color: #0000FF;">CurrentDomain</span>.<span style="color: #0000FF;">AssemblyResolve</span> <span style="color: #008000;">+=</span> ExtractAssembly<span style="color: #008000;">;</span></pre></div></div>

<p>Обработчик работает по той же схеме, что и загрузчик: он вытаскивает из ресурсов файл applib.dll и подсовывает его исполняющей среде .NET вместо файла на диске. Вот как это делается:</p>

<div class="wp_syntax"><div class="code"><pre class="csharp" style="font-family:monospace;"><span style="color: #0600FF;">static</span> Assembly ExtractAssembly<span style="color: #000000;">&#40;</span><span style="color: #FF0000;">object</span> sender,
  ResolveEventArgs args<span style="color: #000000;">&#41;</span>
<span style="color: #000000;">&#123;</span>
    <span style="color: #0600FF;">return</span> GetResourceAssembly<span style="color: #000000;">&#40;</span><span style="color: #008000;">new</span> AssemblyName<span style="color: #000000;">&#40;</span>
      args.<span style="color: #0000FF;">Name</span>.<span style="color: #0000FF;">ToLowerInvariant</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span>.<span style="color: #0000FF;">Name</span> <span style="color: #008000;">+</span> <span style="color: #666666;">&quot;.dll&quot;</span><span style="color: #000000;">&#41;</span><span style="color: #008000;">;</span>
<span style="color: #000000;">&#125;</span>
&nbsp;
<span style="color: #0600FF;">static</span> Assembly GetResourceAssembly<span style="color: #000000;">&#40;</span><span style="color: #FF0000;">string</span> name<span style="color: #000000;">&#41;</span>
<span style="color: #000000;">&#123;</span>
    <span style="color: #0600FF;">using</span> <span style="color: #000000;">&#40;</span>Stream stream <span style="color: #008000;">=</span> Assembly.<span style="color: #0000FF;">GetExecutingAssembly</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span>.
      <span style="color: #0000FF;">GetManifestResourceStream</span><span style="color: #000000;">&#40;</span>name<span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span>
    <span style="color: #000000;">&#123;</span>
        Assembly asm <span style="color: #008000;">=</span> Assembly.<span style="color: #0000FF;">Load</span><span style="color: #000000;">&#40;</span>
          <span style="color: #008000;">new</span> BinaryReader<span style="color: #000000;">&#40;</span>stream<span style="color: #000000;">&#41;</span>.<span style="color: #0000FF;">ReadBytes</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#40;</span><span style="color: #FF0000;">int</span><span style="color: #000000;">&#41;</span>stream.<span style="color: #0000FF;">Length</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span><span style="color: #008000;">;</span>
        <span style="color: #0600FF;">return</span> asm<span style="color: #008000;">;</span>
    <span style="color: #000000;">&#125;</span>
<span style="color: #000000;">&#125;</span></pre></div></div>

<p>Тут есть один маленький нюанс. Обработчику передается не имя файла, а имя сборки, в данном случае — «applib». Вообще говоря, тут нужно сохранить табличку соответствия имен сборок именам их файлов, но пока я ограничился самым простым вариантом: к имени сборки я добавляю расширение «.dll».</p>
<p>Собственно, это уже практический нормальный загрузчик, которым уже можно пользоваться. Осталось только добавить к нему распаковку ресурсов перед загрузкой (это делается одной строкой кода, с помощью DeflateStream) и еще пару мелочей:</p>

<div class="wp_syntax"><div class="code"><pre class="csharp" style="font-family:monospace;"><span style="color: #008080; font-style: italic;">// csc loader.cs /res:app.deflated /res:applib.deflated</span>
<span style="color: #008080; font-style: italic;">// Written by Y [10-01-09]</span>
&nbsp;
<span style="color: #0600FF;">using</span> <span style="color: #008080;">System</span><span style="color: #008000;">;</span>
<span style="color: #0600FF;">using</span> <span style="color: #008080;">System.IO</span><span style="color: #008000;">;</span>
<span style="color: #0600FF;">using</span> <span style="color: #008080;">System.IO.Compression</span><span style="color: #008000;">;</span>
<span style="color: #0600FF;">using</span> <span style="color: #008080;">System.Reflection</span><span style="color: #008000;">;</span>
&nbsp;
<span style="color: #FF0000;">class</span> Loader
<span style="color: #000000;">&#123;</span>
  <span style="color: #000000;">&#91;</span>STAThread<span style="color: #000000;">&#93;</span>
  <span style="color: #0600FF;">static</span> <span style="color: #0600FF;">void</span> Main<span style="color: #000000;">&#40;</span><span style="color: #FF0000;">string</span><span style="color: #000000;">&#91;</span><span style="color: #000000;">&#93;</span> args<span style="color: #000000;">&#41;</span>
  <span style="color: #000000;">&#123;</span>
    AppDomain.<span style="color: #0000FF;">CurrentDomain</span>.<span style="color: #0000FF;">AssemblyResolve</span> <span style="color: #008000;">+=</span> ExtractAssembly<span style="color: #008000;">;</span>
&nbsp;
    Assembly asm <span style="color: #008000;">=</span> GetResourceAssembly<span style="color: #000000;">&#40;</span><span style="color: #666666;">&quot;app.exe&quot;</span><span style="color: #000000;">&#41;</span><span style="color: #008000;">;</span>
&nbsp;
    <span style="color: #0600FF;">if</span> <span style="color: #000000;">&#40;</span>asm.<span style="color: #0000FF;">EntryPoint</span>.<span style="color: #0000FF;">GetParameters</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span>.<span style="color: #0000FF;">Length</span> <span style="color: #008000;">==</span> <span style="color: #FF0000;">0</span><span style="color: #000000;">&#41;</span>
      asm.<span style="color: #0000FF;">EntryPoint</span>.<span style="color: #0000FF;">Invoke</span><span style="color: #000000;">&#40;</span><span style="color: #0600FF;">null</span>, <span style="color: #008000;">new</span> <span style="color: #FF0000;">object</span><span style="color: #000000;">&#91;</span><span style="color: #FF0000;">0</span><span style="color: #000000;">&#93;</span><span style="color: #000000;">&#41;</span><span style="color: #008000;">;</span>
    <span style="color: #0600FF;">else</span>
      asm.<span style="color: #0000FF;">EntryPoint</span>.<span style="color: #0000FF;">Invoke</span><span style="color: #000000;">&#40;</span><span style="color: #0600FF;">null</span>, <span style="color: #008000;">new</span> <span style="color: #FF0000;">object</span><span style="color: #000000;">&#91;</span><span style="color: #000000;">&#93;</span> <span style="color: #000000;">&#123;</span> args <span style="color: #000000;">&#125;</span><span style="color: #000000;">&#41;</span><span style="color: #008000;">;</span>
  <span style="color: #000000;">&#125;</span>
&nbsp;
  <span style="color: #0600FF;">static</span> Assembly ExtractAssembly<span style="color: #000000;">&#40;</span><span style="color: #FF0000;">object</span> sender,
    ResolveEventArgs args<span style="color: #000000;">&#41;</span>
  <span style="color: #000000;">&#123;</span>
    <span style="color: #0600FF;">return</span> GetResourceAssembly<span style="color: #000000;">&#40;</span>
      <span style="color: #008000;">new</span> AssemblyName<span style="color: #000000;">&#40;</span>args.<span style="color: #0000FF;">Name</span>.<span style="color: #0000FF;">ToLowerInvariant</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span>.<span style="color: #0000FF;">Name</span> <span style="color: #008000;">+</span> <span style="color: #666666;">&quot;.dll&quot;</span><span style="color: #000000;">&#41;</span><span style="color: #008000;">;</span>
  <span style="color: #000000;">&#125;</span>
&nbsp;
  <span style="color: #0600FF;">static</span> Assembly GetResourceAssembly<span style="color: #000000;">&#40;</span><span style="color: #FF0000;">string</span> name<span style="color: #000000;">&#41;</span>
  <span style="color: #000000;">&#123;</span>
    <span style="color: #0600FF;">using</span> <span style="color: #000000;">&#40;</span>Stream stream <span style="color: #008000;">=</span> Assembly.<span style="color: #0000FF;">GetExecutingAssembly</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span>.
      <span style="color: #0000FF;">GetManifestResourceStream</span><span style="color: #000000;">&#40;</span>name<span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span>
    <span style="color: #0600FF;">using</span> <span style="color: #000000;">&#40;</span>DeflateStream ds <span style="color: #008000;">=</span>
      <span style="color: #008000;">new</span> DeflateStream<span style="color: #000000;">&#40;</span>stream, CompressionMode.<span style="color: #0000FF;">Decompress</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span>
    <span style="color: #000000;">&#123;</span>
      <span style="color: #008080; font-style: italic;">// (int)ds.Length не поддерживается,</span>
      <span style="color: #008080; font-style: italic;">// пока сделаем ограничение на 4 мегабайта</span>
      Assembly asm <span style="color: #008000;">=</span> Assembly.<span style="color: #0000FF;">Load</span><span style="color: #000000;">&#40;</span>
        <span style="color: #008000;">new</span> BinaryReader<span style="color: #000000;">&#40;</span>ds<span style="color: #000000;">&#41;</span>.<span style="color: #0000FF;">ReadBytes</span><span style="color: #000000;">&#40;</span><span style="color: #FF0000;">4000000</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span><span style="color: #008000;">;</span>
      <span style="color: #0600FF;">return</span> asm<span style="color: #008000;">;</span>
    <span style="color: #000000;">&#125;</span>
  <span style="color: #000000;">&#125;</span>
<span style="color: #000000;">&#125;</span></pre></div></div>

<p>Теперь, когда загрузчик готов, остается сделать не так уж много. Нужно написать программу, которая упаковывает сборки и компонует их вместе с загрузчиком в монолитные EXE-файлы. Пускай это остается на следующий раз.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.sharp-shooter.ru/2009/01/31/exepack2/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Exepack.NET, часть 1</title>
		<link>http://www.sharp-shooter.ru/2009/01/24/exepack1/</link>
		<comments>http://www.sharp-shooter.ru/2009/01/24/exepack1/#comments</comments>
		<pubDate>Sat, 24 Jan 2009 01:49:17 +0000</pubDate>
		<dc:creator>yallie</dc:creator>
				<category><![CDATA[Программирование]]></category>
		<category><![CDATA[.NET]]></category>
		<category><![CDATA[programming]]></category>
		<category><![CDATA[tools]]></category>
		<category><![CDATA[tutorial]]></category>

		<guid isPermaLink="false">http://www.sharp-shooter.ru/?p=54</guid>
		<description><![CDATA[Давно мечтал написать exe-packer — упаковщик для исполняемых файлов. Дурацкая мечта, прямо скажем, но у меня в запасе еще много таких.
Как недавно мне удалось выяснить, написать такой упаковщик для .NET-программ можно за три часа. Наверное, можно и быстрее, но у меня получилось за три с копейками. Это такая приятная особенность .NET: почти все уже сделано [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.sharp-shooter.ru/wp-content/uploads/2009/01/loader_concept.gif" rel="lightbox"><img class="size-thumbnail wp-image-55 alignleft" title="Концепция загрузчика" src="http://www.sharp-shooter.ru/wp-content/uploads/2009/01/loader_concept-150x150.gif" alt="Концепция загрузчика" width="150" height="150" /></a>Давно мечтал написать exe-packer — упаковщик для исполняемых файлов. Дурацкая мечта, прямо скажем, но у меня в запасе еще много таких.</p>
<p>Как недавно мне удалось выяснить, написать такой упаковщик для .NET-программ можно за три часа. Наверное, можно и быстрее, но у меня получилось за три с копейками. Это такая приятная особенность .NET: почти все уже сделано до нас.</p>
<p><span id="more-54"></span> А сделано до нас вот что:</p>
<ul>
<li>управляемый загрузчик Assembly.Load() или Assembly.LoadFrom()</li>
<li>загрузчик ресурсов Assembly.GetManifestResourceStream()</li>
<li>класс DeflateStream для упаковки и распаковки данных.</li>
</ul>
<p>В простейшем случае .NET-программа — это один EXE-файл, который ссылается на системные сборки типа mscorlib, System.dll и так далее. Когда я обработаю этот файл упаковщиком, у меня должен получиться другой EXE-файл, который делает то же самое, но места занимает меньше. Естественно, это будет тоже .NET-программа, так называемый загрузчик.</p>
<p>Для начала я прикинул, как будет выглядеть этот самый загрузчик. Он будет загружать откуда-нибудь исходную сборку и запускать ее в своей же виртуальной машине CLR, в дефолтовом домене приложений. Сказано — сделано:</p>

<div class="wp_syntax"><div class="code"><pre class="csharp" style="font-family:monospace;"><span style="color: #0600FF;">using</span> <span style="color: #008080;">System</span><span style="color: #008000;">;</span>
<span style="color: #0600FF;">using</span> <span style="color: #008080;">System.Reflection</span><span style="color: #008000;">;</span>
&nbsp;
<span style="color: #FF0000;">class</span> Loader
<span style="color: #000000;">&#123;</span>
  <span style="color: #0600FF;">static</span> <span style="color: #0600FF;">void</span> Main<span style="color: #000000;">&#40;</span><span style="color: #FF0000;">string</span><span style="color: #000000;">&#91;</span><span style="color: #000000;">&#93;</span> args<span style="color: #000000;">&#41;</span>
  <span style="color: #000000;">&#123;</span>
    Assembly asm <span style="color: #008000;">=</span> Assembly.<span style="color: #0000FF;">LoadFrom</span><span style="color: #000000;">&#40;</span><span style="color: #666666;">&quot;app.exe&quot;</span><span style="color: #000000;">&#41;</span><span style="color: #008000;">;</span>
&nbsp;
    <span style="color: #0600FF;">if</span> <span style="color: #000000;">&#40;</span>asm.<span style="color: #0000FF;">EntryPoint</span>.<span style="color: #0000FF;">GetParameters</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span>.<span style="color: #0000FF;">Length</span> <span style="color: #008000;">==</span> <span style="color: #FF0000;">0</span><span style="color: #000000;">&#41;</span>
      asm.<span style="color: #0000FF;">EntryPoint</span>.<span style="color: #0000FF;">Invoke</span><span style="color: #000000;">&#40;</span><span style="color: #0600FF;">null</span>, <span style="color: #008000;">new</span> <span style="color: #FF0000;">object</span><span style="color: #000000;">&#91;</span><span style="color: #FF0000;">0</span><span style="color: #000000;">&#93;</span><span style="color: #000000;">&#41;</span><span style="color: #008000;">;</span>
    <span style="color: #0600FF;">else</span>
      asm.<span style="color: #0000FF;">EntryPoint</span>.<span style="color: #0000FF;">Invoke</span><span style="color: #000000;">&#40;</span><span style="color: #0600FF;">null</span>, <span style="color: #008000;">new</span> <span style="color: #FF0000;">object</span><span style="color: #000000;">&#91;</span><span style="color: #000000;">&#93;</span> <span style="color: #000000;">&#123;</span> args <span style="color: #000000;">&#125;</span><span style="color: #000000;">&#41;</span><span style="color: #008000;">;</span>
  <span style="color: #000000;">&#125;</span>
<span style="color: #000000;">&#125;</span></pre></div></div>

<p>В данном случае сборка грузится из файла app.exe. Единственная тонкость, на которую тут надо обратить внимание: метод Main может быть с параметрами, а может — без. Поэтому перед запуском приходится проверять, с каким вариантом мы имеем дело. Более того, этот метод может быть void, а может возвращать значение типа int, это тоже надо бы учитывать (у меня тут, как видите, не учитывается).</p>
<p>Идем дальше. Загружать сборку из файла, понятно, некрасиво. У нас в результате должен быть один-единственный EXE-шник, в котором упаковано все. Поэтому сборку нужно грузить из наших же ресурсов. Когда я буду компилировать новую версию загрузчика, я попрошу компилятор C# присобачить файлик app.exe к моей программе в качестве ресурса. Есть такой удобный ключик у консольного компилятора — /res:app.exe. Ресурс в результате так и будет называться — app.exe.</p>
<p>Осталось разобраться, как загрузить сборку из ресурса. Выясняется, что это тоже довольно просто: вытаскиваем нужный Stream из ресурса с именем hello.exe, считываем в буфер (массив byte[]) и загружаем из него сборку с помощью Assembly.Load():</p>

<div class="wp_syntax"><div class="code"><pre class="csharp" style="font-family:monospace;"><span style="color: #008080; font-style: italic;">// csc loader2.cs /res:app.exe</span>
&nbsp;
<span style="color: #0600FF;">using</span> <span style="color: #008080;">System</span><span style="color: #008000;">;</span>
<span style="color: #0600FF;">using</span> <span style="color: #008080;">System.IO</span><span style="color: #008000;">;</span>
<span style="color: #0600FF;">using</span> <span style="color: #008080;">System.Reflection</span><span style="color: #008000;">;</span>
&nbsp;
<span style="color: #FF0000;">class</span> Loader
<span style="color: #000000;">&#123;</span>
  <span style="color: #0600FF;">static</span> <span style="color: #0600FF;">void</span> Main<span style="color: #000000;">&#40;</span><span style="color: #FF0000;">string</span><span style="color: #000000;">&#91;</span><span style="color: #000000;">&#93;</span> args<span style="color: #000000;">&#41;</span>
  <span style="color: #000000;">&#123;</span>
    <span style="color: #0600FF;">using</span> <span style="color: #000000;">&#40;</span>Stream stream <span style="color: #008000;">=</span> Assembly.<span style="color: #0000FF;">GetExecutingAssembly</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span>.
      <span style="color: #0000FF;">GetManifestResourceStream</span><span style="color: #000000;">&#40;</span><span style="color: #666666;">&quot;app.exe&quot;</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span>
    <span style="color: #000000;">&#123;</span>
      Assembly asm <span style="color: #008000;">=</span> Assembly.<span style="color: #0000FF;">Load</span><span style="color: #000000;">&#40;</span>
        <span style="color: #008000;">new</span> BinaryReader<span style="color: #000000;">&#40;</span>stream<span style="color: #000000;">&#41;</span>.<span style="color: #0000FF;">ReadBytes</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#40;</span><span style="color: #FF0000;">int</span><span style="color: #000000;">&#41;</span>stream.<span style="color: #0000FF;">Length</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span><span style="color: #008000;">;</span>
&nbsp;
      <span style="color: #0600FF;">if</span> <span style="color: #000000;">&#40;</span>asm.<span style="color: #0000FF;">EntryPoint</span>.<span style="color: #0000FF;">GetParameters</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span>.<span style="color: #0000FF;">Length</span> <span style="color: #008000;">==</span> <span style="color: #FF0000;">0</span><span style="color: #000000;">&#41;</span>
        asm.<span style="color: #0000FF;">EntryPoint</span>.<span style="color: #0000FF;">Invoke</span><span style="color: #000000;">&#40;</span><span style="color: #0600FF;">null</span>, <span style="color: #008000;">new</span> <span style="color: #FF0000;">object</span><span style="color: #000000;">&#91;</span><span style="color: #FF0000;">0</span><span style="color: #000000;">&#93;</span><span style="color: #000000;">&#41;</span><span style="color: #008000;">;</span>
      <span style="color: #0600FF;">else</span>
        asm.<span style="color: #0000FF;">EntryPoint</span>.<span style="color: #0000FF;">Invoke</span><span style="color: #000000;">&#40;</span><span style="color: #0600FF;">null</span>, <span style="color: #008000;">new</span> <span style="color: #FF0000;">object</span><span style="color: #000000;">&#91;</span><span style="color: #000000;">&#93;</span> <span style="color: #000000;">&#123;</span> args <span style="color: #000000;">&#125;</span><span style="color: #000000;">&#41;</span><span style="color: #008000;">;</span>
    <span style="color: #000000;">&#125;</span>
  <span style="color: #000000;">&#125;</span>
<span style="color: #000000;">&#125;</span></pre></div></div>

<p>Проверяем — работает! Как нетрудно догадаться, если перед компиляцией загрузчика файл app.exe упаковать, а после загрузки, соответственно, распаковать — это и будет практически готовым упаковщиком.</p>
<p>В реальной жизни .NET-программы редко представляют собой единственный EXE-файл. Обычно это целая пачка сборок — файлов EXE и DLL. Что делать с такими программами, я расскажу в следующий раз.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.sharp-shooter.ru/2009/01/24/exepack1/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Коммандозер для всех :)</title>
		<link>http://www.sharp-shooter.ru/2008/11/10/ultima-talk/</link>
		<comments>http://www.sharp-shooter.ru/2008/11/10/ultima-talk/#comments</comments>
		<pubDate>Mon, 10 Nov 2008 18:05:53 +0000</pubDate>
		<dc:creator>yallie</dc:creator>
				<category><![CDATA[Программы]]></category>
		<category><![CDATA[.NET]]></category>
		<category><![CDATA[tools]]></category>
		<category><![CDATA[ultima]]></category>
		<category><![CDATA[work]]></category>

		<guid isPermaLink="false">http://www.sharp-shooter.ru/?p=34</guid>
		<description><![CDATA[Есть у нашей компании такой продукт — мессенджер для локальной сети, который мы устанавливаем своим клиентам впридачу к информационной системе. 
Крут этот мессенджер тем, что, во-первых, берет список контактов из ActiveDirectory (т. е. в контакт-листе автоматом находятся все доменные пользователи компании), а во-вторых — не требует никакой настройки на клиенте, берешь и запускаешь.  Можно даже с сетевого диска запускать, все равно [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.sharp-shooter.ru/wp-content/uploads/2008/11/ultimatalk.gif" rel="lightbox"><img class="alignleft size-thumbnail wp-image-44" style="margin-right: 16px; margin-bottom: 16px;" title="Скриншот коммандозера" src="http://www.sharp-shooter.ru/wp-content/uploads/2008/11/ultimatalkthumb.gif" alt="" width="117" height="170" align="left" /></a>Есть у нашей компании такой продукт — мессенджер для локальной сети, который мы устанавливаем своим клиентам впридачу к информационной системе. </p>
<p>Крут этот мессенджер тем, что, во-первых, берет список контактов из ActiveDirectory (т. е. в контакт-листе автоматом находятся все доменные пользователи компании), а во-вторых — не требует никакой настройки на клиенте, берешь и запускаешь.  Можно даже с сетевого диска запускать, все равно пользователь автоматом авторизуется под своим доменным логином и будет иметь доступ к контакт-листу и своей личной истории сообщений.</p>
<p>Первый год своего существования (а появился он в конце 2003 года) назывался он Kommandozentrum, и старые пользователи до сих пор его называют коммандозером, или еще проще — дозером. Ну, типа, «Скинь мне в дозер ссылочку на тот сайт».</p>
<p><span id="more-34"></span></p>
<p>Месяц назад наконец решились мы раздавать корпоративный мессенджер вместе с сервером <a title="Скачать Коммандозер" href="http://www.ultima.ru/talk">на халяву всем желающим</a>. Поскольку желающие теперь время от времени скачивают его с нашего сайта и задают всякие вопросы по установке сервера, я решил написать к нему побольше всякой документации и, возможно, провести что-то типа онлайн-лекции по администрированию сервера. Хотя, строго говоря, никакого там администрирования нет, нужно только один раз поставить, чтобы заработало, и забыть.</p>
<p>Так что, в ближайшие дни опубликую на этом блоге, что у меня получилось.</p>
<p>UPD. Написал два документа, но не стал публиковать в этом блоге:</p>
<ul>
<li><a title="Руководство по установке сервера Ultima::Talk" href="http://www.ultima.ru/products/satellites/talk/install/index.html">Установка сервера</a></li>
<li><a title="Конфигурация сервера Ultima::Talk" href="http://www.ultima.ru/products/satellites/talk/server_setup/index.html">Описание файла конфигурации</a></li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://www.sharp-shooter.ru/2008/11/10/ultima-talk/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Сменю-ка я тему</title>
		<link>http://www.sharp-shooter.ru/2008/09/01/switch/</link>
		<comments>http://www.sharp-shooter.ru/2008/09/01/switch/#comments</comments>
		<pubDate>Sun, 31 Aug 2008 22:20:13 +0000</pubDate>
		<dc:creator>yallie</dc:creator>
				<category><![CDATA[Без рубрики]]></category>

		<guid isPermaLink="false">http://www.sharp-shooter.ru/?p=29</guid>
		<description><![CDATA[Поскольку тема аспирантуры за рубежом, судя по всему, оказалась не слишком востребованной, буду теперь писать о более актуальных вещах  
]]></description>
			<content:encoded><![CDATA[<p>Поскольку тема аспирантуры за рубежом, судя по всему, оказалась не слишком востребованной, буду теперь писать о более актуальных вещах <img src='http://www.sharp-shooter.ru/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
]]></content:encoded>
			<wfw:commentRss>http://www.sharp-shooter.ru/2008/09/01/switch/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>Конспект тренинга</title>
		<link>http://www.sharp-shooter.ru/2008/04/11/abstract/</link>
		<comments>http://www.sharp-shooter.ru/2008/04/11/abstract/#comments</comments>
		<pubDate>Fri, 11 Apr 2008 17:59:49 +0000</pubDate>
		<dc:creator>yallie</dc:creator>
				<category><![CDATA[Аспирантура]]></category>
		<category><![CDATA[Тренинги]]></category>

		<guid isPermaLink="false">http://www.sharp-shooter.ru/?p=27</guid>
		<description><![CDATA[Сегодня наконец доделал конспект! Помимо транскрипции того, что было в аудиозаписях, туда вошли некоторые фрагменты книги, которую сейчас пишет д-р Михаил Красавин. Отсылаю всем участникам, которые прошли тренинг и оставили отзыв:

paddfoot
Roquentin
Денис
de_leo

Если вы тоже участвовали и хотите увидеть себя в этом списке — еще не все потеряно. Только нужно успеть сделать все это до&#160;понедельника, 14&#160;апреля. Тот, кто не дослушал [...]]]></description>
			<content:encoded><![CDATA[<p>Сегодня наконец доделал конспект! Помимо транскрипции того, что было в аудиозаписях, туда вошли некоторые фрагменты книги, которую сейчас пишет д-р Михаил Красавин. Отсылаю всем участникам, которые прошли тренинг и <a href="http://www.sharp-shooter.ru/2008/03/31/phd-usa-final/">оставили отзыв</a>:</p>
<ul>
<li>paddfoot</li>
<li>Roquentin</li>
<li>Денис</li>
<li>de_leo</li>
</ul>
<p>Если вы тоже участвовали и хотите увидеть себя в этом списке — еще не все потеряно. Только нужно успеть сделать все это <strong>до&nbsp;понедельника, 14&nbsp;апреля</strong>. Тот, кто не дослушал курс и не <a href="http://www.sharp-shooter.ru/2008/03/31/phd-usa-final/">оставил отзыв</a>, во вторник навсегда потеряет возможность получить этот конспект :)</p>
]]></content:encoded>
			<wfw:commentRss>http://www.sharp-shooter.ru/2008/04/11/abstract/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
