<?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 &#187; programming</title>
	<atom:link href="http://www.sharp-shooter.ru/tag/programming/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.sharp-shooter.ru</link>
	<description>Программирование на C# и все такое</description>
	<lastBuildDate>Sun, 11 Dec 2011 20:39:41 +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>BLToolkit появился в галерее NuGet</title>
		<link>http://www.sharp-shooter.ru/2011/06/04/bltoolkit-nuget/</link>
		<comments>http://www.sharp-shooter.ru/2011/06/04/bltoolkit-nuget/#comments</comments>
		<pubDate>Fri, 03 Jun 2011 23:46:15 +0000</pubDate>
		<dc:creator>yallie</dc:creator>
				<category><![CDATA[Программирование]]></category>
		<category><![CDATA[.NET]]></category>
		<category><![CDATA[linq]]></category>
		<category><![CDATA[programming]]></category>
		<category><![CDATA[tools]]></category>
		<category><![CDATA[tutorial]]></category>

		<guid isPermaLink="false">http://www.sharp-shooter.ru/?p=248</guid>
		<description><![CDATA[Отличная новость: для BLToolkit теперь есть NuGet package. В пакете три комплекта бинарников для разных платформ: FX3.5, FX4 и SL4.
На тот случай, если требуется инструкция — вот она:

Установите NuGet (это плагин для Visual Studio 2010).
В ваш проект добавьте ссылку с помощью пункта меню Add Library Package Reference&#8230;
В появившемся окне будет список пакетов NuGet, найдите там BLToolkit.
Нажмите кнопку Install — пакет [...]]]></description>
			<content:encoded><![CDATA[<p>Отличная новость: для BLToolkit теперь есть <a href="http://nuget.org/List/Packages/BLToolkit">NuGet package</a>. В пакете три комплекта бинарников для разных платформ: FX3.5, FX4 и SL4.</p>
<p>На тот случай, если требуется инструкция — вот она:</p>
<ol>
<li>Установите <a href="http://visualstudiogallery.msdn.microsoft.com/27077b70-9dad-4c64-adcf-c7cf6bc9970c">NuGet</a> (это плагин для Visual Studio 2010).</li>
<li>В ваш проект добавьте ссылку с помощью пункта меню Add Library Package Reference&#8230;</li>
<li>В появившемся окне будет список пакетов NuGet, найдите там BLToolkit.</li>
<li>Нажмите кнопку Install — пакет скачается и установится, а в проект добавится ссылка на библиотеку BLToolkit нужной версии.</li>
<li>Убедитесь, что в вашем проекте Target Framework указан полный (Client Profile для компиляции не подойдет).</li>
</ol>
<p>То же самое в виде комикса:</p>
<table cellspacing=8>
<tr>
<td><a href="http://www.sharp-shooter.ru/wp-content/uploads/2011/06/BLTNuget01.png" rel="lightbox"><img src="http://www.sharp-shooter.ru/wp-content/uploads/2011/06/BLTNuget01-150x150.png" alt="BLTNuget0" title="BLTNuget0" width="150" height="150" class="alignnone size-thumbnail wp-image-250" /></a></td>
<td><a href="http://www.sharp-shooter.ru/wp-content/uploads/2011/06/BLTNuget1.png" rel="lightbox"><img src="http://www.sharp-shooter.ru/wp-content/uploads/2011/06/BLTNuget1-150x150.png" alt="BLTNuget1" title="BLTNuget1" width="150" height="150" class="alignnone size-thumbnail wp-image-251" /></a></td>
<td><a href="http://www.sharp-shooter.ru/wp-content/uploads/2011/06/BLTNuget2.png" rel="lightbox"><img src="http://www.sharp-shooter.ru/wp-content/uploads/2011/06/BLTNuget2-150x150.png" alt="BLTNuget2" title="BLTNuget2" width="150" height="150" class="alignnone size-thumbnail wp-image-252" /></a></td>
<td><a href="http://www.sharp-shooter.ru/wp-content/uploads/2011/06/BLTNuget3.png" rel="lightbox"><img src="http://www.sharp-shooter.ru/wp-content/uploads/2011/06/BLTNuget3-150x150.png" alt="BLTNuget3" title="BLTNuget3" width="150" height="150" class="alignnone size-thumbnail wp-image-253" /></a></td>
</tr>
</table>
]]></content:encoded>
			<wfw:commentRss>http://www.sharp-shooter.ru/2011/06/04/bltoolkit-nuget/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Zyan Framework</title>
		<link>http://www.sharp-shooter.ru/2011/04/04/zyan-framework/</link>
		<comments>http://www.sharp-shooter.ru/2011/04/04/zyan-framework/#comments</comments>
		<pubDate>Sun, 03 Apr 2011 23:34:35 +0000</pubDate>
		<dc:creator>yallie</dc:creator>
				<category><![CDATA[Программирование]]></category>
		<category><![CDATA[.NET]]></category>
		<category><![CDATA[programming]]></category>
		<category><![CDATA[tutorial]]></category>
		<category><![CDATA[zyan]]></category>

		<guid isPermaLink="false">http://www.sharp-shooter.ru/?p=237</guid>
		<description><![CDATA[Близится выпуск второй версии проекта Zyan Framework, в котором я участвую в качестве разработчика. Думаю, к выпуску стоит приурочить перевод документации с немецкого языка. На английский я уже все перевел, осталось перевести на русский. Все статьи буду параллельно публиковать здесь. Итак, начнем со вступления:
Описание проекта
Zyan — это инфраструктура, упрощающая разработку распределенных приложений. Используя Zyan, вы [...]]]></description>
			<content:encoded><![CDATA[<p>Близится выпуск второй версии проекта <a href="http://zyan.codeplex.com/" title="Zyan Framework at CodePlex">Zyan Framework</a>, в котором я участвую в качестве разработчика. Думаю, к выпуску стоит приурочить перевод документации с немецкого языка. На английский я уже все <a href="http://zyan.codeplex.com/wikipage?title=English%20Documentation&amp;referringTitle=Documentation">перевел</a>, осталось перевести на русский. Все статьи буду параллельно публиковать здесь. Итак, начнем со вступления:</p>
<h3>Описание проекта</h3>
<p>Zyan — это инфраструктура, упрощающая разработку распределенных приложений. Используя Zyan, вы можете опубликовать любой .NET-класс для удаленного взаимодействия. Zyan гибко настраивается и предоставляет все необходимые инструменты для построения модульных распределенных приложений.</p>
<p><em>Zyan требует для работы Microsoft .NET Framework 3.5 Client Profile, либо Mono 2.8 или выше (поддерживаются платформы Linux &amp; Mac; см. <a href="http://zyan.codeplex.com/wikipage?title=Zyan%20auf%20Linux&amp;referringTitle=Home">скриншот</a>).</em></p>
<h3>Особенности</h3>
<ul>
<li>Интуитивный программный интерфейс</li>
<li>Широкий выбор вариантов хостинга .NET-компонентов (локальных или удаленных)</li>
<li>Два режима активации компонентов: сингльтон и режим одиночных вызовов</li>
<li>Поддержка TCP, HTTP и именованных каналов (для других протоколов возможно написать собственные плагины)</li>
<li>Дуплексный TCP-канал позволяет серверу взаимодействовать с клиентами, находящимся за NAT-ом или брандмауэром</li>
<li>Поддержка шифрованных каналов, не требующая сертификатов и наличия служб Active Directory</li>
<li>Простая в использовании расширяемая система аутентификации
<ul>
<li>Поддержка интегрированной аутентификации Windows (единый вход в систему)</li>
<li>Поддержка базовой аутентификации на серверах SAM (удобно при отсутствии служб Active Directory)</li>
</ul>
</li>
<li>Расширяемое управление сессиями
<ul>
<li>Быстрый и легкий однопроцессный менеджер сессий (сессии хранятся в памяти сервера)</li>
<li>Масштабируемый менеджер сессии на базе SQL-сервера (сессии хранятся в СУБД)</li>
<li>Переменные сессий поддерживаются обоими вариантами менеджера сессий</li>
</ul>
</li>
<li>Распределенные события обрабатываются так же просто, как button_Click в Windows.Forms-приложениях</li>
<li>Поддержка связывания распределенных EBC-компонентов (Event Based Components)</li>
</ul>
<h3>Простейший пример распределенного приложения</h3>
<h4>Публикация компонента:</h4>

<div class="wp_syntax"><div class="code"><pre class="csharp" style="font-family:monospace;"><span style="color: #008080; font-style: italic;">// Создаем сервер ZyanDemo и привязываем его к TCP-порту 8080</span>
var host <span style="color: #008000;">=</span> <span style="color: #008000;">new</span> ZyanComponentHost<span style="color: #000000;">&#40;</span><span style="color: #666666;">&quot;ZyanDemo&quot;</span>, <span style="color: #FF0000;">8080</span><span style="color: #000000;">&#41;</span><span style="color: #008000;">;</span>
&nbsp;
<span style="color: #008080; font-style: italic;">// Регистрируем тип и интерфейс компонента</span>
host.<span style="color: #0000FF;">RegisterComponent</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span><span style="color: #008000;">;</span></pre></div></div>

<p>Вот и все, что нужно, чтобы компонент HelloWorldService стал доступным для удаленного взаимодействия.</p>
<p>Классу HelloWorldService <strong>не требуется</strong> наследование от MarshalByRefObject. Кроме того, не требуется разметка класса атрибутами ServiceContract и OperationContract. Любой .NET-класс может быть опубликован для удаленного взаимодействия. Единственное требование к нему — наличие интерфейса, через который будут осуществляться вызовы методов.</p>
<h4>Получение доступа к службе с удаленного компьютера:</h4>

<div class="wp_syntax"><div class="code"><pre class="csharp" style="font-family:monospace;"><span style="color: #008080; font-style: italic;">// Соединяемся с сервером</span>
var connection <span style="color: #008000;">=</span> <span style="color: #008000;">new</span> ZyanConnection<span style="color: #000000;">&#40;</span><span style="color: #666666;">&quot;tcp://localhost:8080/ZyanDemo&quot;</span><span style="color: #000000;">&#41;</span><span style="color: #008000;">;</span>
&nbsp;
<span style="color: #008080; font-style: italic;">// Создаем прокси-объект для HelloWorldService</span>
var proxy <span style="color: #008000;">=</span> connection.<span style="color: #0000FF;">CreateProxy</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span><span style="color: #008000;">;</span>
&nbsp;
<span style="color: #008080; font-style: italic;">// Вызываем метод (точно так же, как метод локального объекта)</span>
proxy.<span style="color: #0000FF;">SayHello</span><span style="color: #000000;">&#40;</span><span style="color: #666666;">&quot;HelloWorld&quot;</span><span style="color: #000000;">&#41;</span><span style="color: #008000;">;</span></pre></div></div>

<p>Дальнейшие подробности см. в разделе <a href="http://zyan.codeplex.com/documentation">Документация</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.sharp-shooter.ru/2011/04/04/zyan-framework/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Entity Framework и Oracle</title>
		<link>http://www.sharp-shooter.ru/2011/03/25/entity-framework-oracle/</link>
		<comments>http://www.sharp-shooter.ru/2011/03/25/entity-framework-oracle/#comments</comments>
		<pubDate>Thu, 24 Mar 2011 21:12:52 +0000</pubDate>
		<dc:creator>yallie</dc:creator>
				<category><![CDATA[Программирование]]></category>
		<category><![CDATA[.NET]]></category>
		<category><![CDATA[entity framework]]></category>
		<category><![CDATA[linq]]></category>
		<category><![CDATA[oracle]]></category>
		<category><![CDATA[programming]]></category>
		<category><![CDATA[tutorial]]></category>

		<guid isPermaLink="false">http://www.sharp-shooter.ru/?p=220</guid>
		<description><![CDATA[Некоторое время назад вышла бета-версия провайдера ODP.NET с поддержкой Entity Framework (пока только 32-битная). Скачать провайдер можно на официальном сайте. Устанавливать ODP.NET нужно вместе с Oracle Developers Tools for Visual Studio (просто оставьте при установке выбранными все галочки, которые там стоят по умолчанию). Чтобы собрать простой проект и проверить работу провайдера в действии, понадобится рабочая [...]]]></description>
			<content:encoded><![CDATA[<p>Некоторое время назад вышла бета-версия провайдера ODP.NET с поддержкой Entity Framework (пока только 32-битная). Скачать провайдер можно <a href="http://www.oracle.com/technetwork/topics/dotnet/downloads/oracleefbeta-302521.html">на официальном сайте</a>. Устанавливать ODP.NET нужно вместе с Oracle Developers Tools for Visual Studio (просто оставьте при установке выбранными все галочки, которые там стоят по умолчанию). Чтобы собрать простой проект и проверить работу провайдера в действии, понадобится рабочая база Oracle. Если под рукой нет тестовой базы, можно поставить и настроить <a href="http://www.oracleimg.com/technetwork/database/express-edition/overview/index.html">Express-версию</a> Oracle на своем обычном рабочем компьютере.</p>
<h3>Создаем проект и подключаемся к серверу</h3>
<p>После установки ODAC первым делом, конечно, нужно настроить файл TNSNAMES.ORA. Проще всего взять пустую болванку, которая после установки будет лежать в папке %ORACLE_HOME%/Network/Admin/Sample/tnsnames.ora, подправить и переместить на одну папку вверх (в Network/Admin). Выглядеть этот файл будет примерно так:</p>
<p><span id="more-220"></span></p>

<div class="wp_syntax"><div class="code"><pre class="tns" style="font-family:monospace;">SANDBOX=
  (DESCRIPTION=
    (ADDRESS=
      (PROTOCOL=TCP)
      (HOST=192.168.xxx.xxx)
      (PORT=1521)
    )
    (CONNECT_DATA=
      (SID=xxxx)
    )
  )</pre></div></div>

<p>Дальше запускаем Visual Studio 2010 и создаем проект Windows Forms-приложения:</p>
<p><img class="size-full wp-image-221 alignnone" title="New Project" src="http://www.sharp-shooter.ru/wp-content/uploads/2011/03/01.png" alt="New Project" width="800" height="450" /></p>
<p>В окне Server Explorer создаем новое соединение, указывая в качестве Data Source Name имя соединения из файла TNSNAMES.ORA.</p>
<p><img class="size-full wp-image-222 alignnone" title="Data Source" src="http://www.sharp-shooter.ru/wp-content/uploads/2011/03/02.png" alt="Data Source" width="302" height="159" /></p>
<p><img class="size-full wp-image-223 alignnone" title="Add Connection" src="http://www.sharp-shooter.ru/wp-content/uploads/2011/03/03.png" alt="Add Connection" width="484" height="638" /></p>
<p>Здесь все как обычно, можно создавать/изменять таблицы, вьюшки, последовательности и прочие объекты БД. Я подключался к старой схеме, поэтому у меня уже были созданы таблицы AGENTS и USERS. Переименовывать их я не стал, чтобы продемонстрировать работу Entity Framework с классами, имена которых не совпадают с именами физических таблиц (классы будут называться Agent и User соответственно).</p>
<p><img class="size-full wp-image-224 alignnone" title="Server Explorer" src="http://www.sharp-shooter.ru/wp-content/uploads/2011/03/04.png" alt="Server Explorer" width="476" height="374" /></p>
<h3>Импортируем и настраиваем схему</h3>
<p>Добавляем модель Entity Data Model, генерируем классы из схемы БД (выбираем нужные таблицы, указываем пространство имен для модели и все прочее):</p>
<p><img class="size-full wp-image-225 alignnone" title="New Model" src="http://www.sharp-shooter.ru/wp-content/uploads/2011/03/05.png" alt="New Model" width="811" height="450" /></p>
<p><img class="size-full wp-image-226 alignnone" title="Generate from Database" src="http://www.sharp-shooter.ru/wp-content/uploads/2011/03/06.png" alt="Generate from Database" width="623" height="554" /></p>
<p><img class="size-full wp-image-227 alignnone" title="Choose Data Connection" src="http://www.sharp-shooter.ru/wp-content/uploads/2011/03/07.png" alt="Choose Data Connection" width="623" height="554" /></p>
<p><img class="size-full wp-image-228 alignnone" title="Choose Database Objects" src="http://www.sharp-shooter.ru/wp-content/uploads/2011/03/08.png" alt="Choose Database Objects" width="624" height="556" /></p>
<p>Переименовываем классы модели, которые нас не устраивают: AGENTS -&gt; Agent, USERS -&gt; User. Колонки тоже переименовываются: AGENT_ID -&gt; AgentID, и так далее.</p>
<p><img class="size-full wp-image-229 alignnone" title="Model Designer" src="http://www.sharp-shooter.ru/wp-content/uploads/2011/03/09.png" alt="Model Designer" width="478" height="494" /></p>
<h3>Выполняем LINQ-запросы</h3>
<p>Схема готова. Чтобы выполнить запрос к ней, нарисуем простое окошко:</p>
<p><img class="size-full wp-image-230 alignnone" title="Sample Form" src="http://www.sharp-shooter.ru/wp-content/uploads/2011/03/10.png" alt="Sample Form" width="386" height="368" /></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>var ctx <span style="color: #008000;">=</span> <span style="color: #008000;">new</span> Entities<span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span>
<span style="color: #000000;">&#123;</span>
  var query <span style="color: #008000;">=</span>
    from u <span style="color: #0600FF;">in</span> ctx.<span style="color: #0000FF;">Users</span>
    where u.<span style="color: #0000FF;">Name</span>.<span style="color: #0000FF;">StartsWith</span><span style="color: #000000;">&#40;</span><span style="color: #666666;">&quot;ya&quot;</span><span style="color: #000000;">&#41;</span>
    select u<span style="color: #008000;">;</span>
&nbsp;
  <span style="color: #0600FF;">foreach</span> <span style="color: #000000;">&#40;</span>var u <span style="color: #0600FF;">in</span> query<span style="color: #000000;">&#41;</span>
  <span style="color: #000000;">&#123;</span>
    var msg <span style="color: #008000;">=</span> <span style="color: #FF0000;">string</span>.<span style="color: #0000FF;">Format</span><span style="color: #000000;">&#40;</span><span style="color: #666666;">&quot;{0} → {1}&quot;</span>, u.<span style="color: #0000FF;">Name</span>, u.<span style="color: #0000FF;">Agent</span>.<span style="color: #0000FF;">Name</span><span style="color: #000000;">&#41;</span><span style="color: #008000;">;</span>
&nbsp;
    TextBox.<span style="color: #0000FF;">AppendText</span><span style="color: #000000;">&#40;</span>msg<span style="color: #000000;">&#41;</span><span style="color: #008000;">;</span>
    TextBox.<span style="color: #0000FF;">AppendText</span><span style="color: #000000;">&#40;</span>Environment.<span style="color: #0000FF;">NewLine</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>Вот так будет выглядеть результат:</p>
<p><img class="size-full wp-image-231 alignnone" title="Results" src="http://www.sharp-shooter.ru/wp-content/uploads/2011/03/12.png" alt="Results" width="300" height="300" /></p>
]]></content:encoded>
			<wfw:commentRss>http://www.sharp-shooter.ru/2011/03/25/entity-framework-oracle/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>NotifyIcon в консольном приложении</title>
		<link>http://www.sharp-shooter.ru/2011/02/27/notifyicon-in-console-app/</link>
		<comments>http://www.sharp-shooter.ru/2011/02/27/notifyicon-in-console-app/#comments</comments>
		<pubDate>Sun, 27 Feb 2011 20:31:35 +0000</pubDate>
		<dc:creator>yallie</dc:creator>
				<category><![CDATA[Программирование]]></category>
		<category><![CDATA[.NET]]></category>
		<category><![CDATA[console]]></category>
		<category><![CDATA[programming]]></category>
		<category><![CDATA[tutorial]]></category>
		<category><![CDATA[WinForms]]></category>
		<category><![CDATA[work]]></category>

		<guid isPermaLink="false">http://www.sharp-shooter.ru/?p=205</guid>
		<description><![CDATA[Я постоянно имею дело с консольными программами, которые используются для хостинга служб WCF или .NET Remoting. Для отладки это самый удобный вариант: запускается быстро, консоль при случае используется для отладочной печати, и так далее. Выглядит такая программа обычно как-нибудь так (в случае с Remoting):

static void Main&#40;string&#91;&#93; args&#41;
&#123;
    // Запускаем сервер
   [...]]]></description>
			<content:encoded><![CDATA[<p>Я постоянно имею дело с консольными программами, которые используются для хостинга служб WCF или .NET Remoting. Для отладки это самый удобный вариант: запускается быстро, консоль при случае используется для отладочной печати, и так далее. Выглядит такая программа обычно как-нибудь так (в случае с Remoting):</p>

<div class="wp_syntax"><div class="code"><pre class="csharp" style="font-family:monospace;"><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: #008080; font-style: italic;">// Запускаем сервер</span>
    var config <span style="color: #008000;">=</span> AppDomain.<span style="color: #0000FF;">CurrentDomain</span>.<span style="color: #0000FF;">SetupInformation</span>.<span style="color: #0000FF;">ConfigurationFile</span><span style="color: #008000;">;</span>
    RemotingConfiguration.<span style="color: #0000FF;">Configure</span><span style="color: #000000;">&#40;</span>config, <span style="color: #0600FF;">false</span><span style="color: #000000;">&#41;</span><span style="color: #008000;">;</span>
&nbsp;
    <span style="color: #008080; font-style: italic;">// Работаем, пока не нажмут Enter</span>
    Console.<span style="color: #0000FF;">WriteLine</span><span style="color: #000000;">&#40;</span><span style="color: #666666;">&quot;Server started. Press ENTER to quit.&quot;</span><span style="color: #000000;">&#41;</span><span style="color: #008000;">;</span>
    Console.<span style="color: #0000FF;">ReadLine</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span><span style="color: #008000;">;</span>
<span style="color: #000000;">&#125;</span></pre></div></div>

<p>Запустил, минимизировал такое окошко и крутится там себе сервер на фоне. Частенько бывает, что таких серверов запущено несколько (штуки три-четыре), и они постоянно торчат на панели задач. Изредка развернешь, чтобы прочитать, что там на консоли напечатано, а большую часть времени они только зря место занимают. Мне, честно говоря, это никогда особо не мешало. Пару недель назад один мой товарищ в разговоре заметил, что такие программки хорошо бы куда-нибудь прятать, оставляя одну иконку в системном трее. И с тех пор меня эти кнопки на панели задач стали раздражать.</p>
<p>Это такой мыслевирус, как кернинг: если человек не знает, что это такое, он живет себе спокойно. А если ему рассказать, да еще и показать на примере, чем плохой кернинг отличается от хорошего, — покой навеки утрачен. Теперь плохой кернинг постоянно будет ему бросаться в глаза на вывесках, объявлениях и рекламе в метро.</p>
<p>Короче говоря, пришлось-таки сделать небольшую библиотечку для сворачивания консоли в трей-иконку. Проблем тут, собственно, немного, но они есть (поэтому наивный вариант решения задачи — добавить компонент NotifyIcon с обработчиком DoubleClick, по которому что-нибудь там делать — не работает):</p>
<ul>
<li>В .NET Framework нету легального способа получить в свое распоряжение окно консоли</li>
<li>В консольных приложениях нет конвейера сообщений</li>
</ul>
<p>То есть, грубо говоря, добавить NotifyIcon можно (и в системном трее она будет нормально показываться), но обработчик события Click или DoubleClick вызываться не будет. Поскольку конвейера нет, событие просто некому переправить на обработку. А если и получится вызвать такой обработчик, непонятно, что в этом обработчике делать с консолью.</p>
<p>Чтобы справиться с этими проблемами, нужно 1) запустить свой конвейер сообщений в отдельном потоке и 2) заполучить хэндл окна консоли и прятать/показывать его средствами WinAPI. Выглядит так, как будто весь компонент будет состоять из сплошных вызовов p/invoke, то есть решение не будет блистать изяществом.</p>
<p>Однако выяснилось, что на деле не все так страшно. Оказывается, конвейеру WinForms — Application.Run(&#8230;) — вовсе не требуется главная форма. Он вполне сносно будет работать, если вместо этой формы ему подсунуть компонент NotifyIcon. То есть, первая часть задачи на самом деле сводится к запуску отдельного потока, в котором будет создаваться NotifyIcon и запускаться конвейер. И никакого p/invoke, что весьма приятно.</p>
<p>Хендл окна консоли, как выяснилось, иногда можно получить вот так: Process.GetCurrentProcess().MainWindowHandle. Здесь, разумеется, все портит слово «иногда»: на моем ноутбуке этот способ работает (Win7 x64), а на рабочем компьютере (WinXP x32) — нет. Вместо хендла возвращается IntPtr.Zero (MSDN говорит, что так задумано). </p>
<p>Так что для второй части задачи — прятать/показывать окно консоли — p/invoke все же нужен. Во-первых, нужен хендл консольного окна, во-вторых — метод ShowWindow(), который прячет или показывает окно по его хендлу. Весь WinAPI-мусор выносится в отдельный файл строчек примерно на 50 — ничего страшного. Жаль, правда, что портабельным этот вариант не будет, ну да хрен с ним, я и не претендовал на универсальность.</p>
<p>А дальше можно сделать приемлемую обертку, на иконку прицепить контекстное меню и заполнять его каким-нибудь fluent-интерфейсом:</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>var tools <span style="color: #008000;">=</span> <span style="color: #008000;">new</span> ConsoleTools<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: #008080; font-style: italic;">// настраиваем иконку в системном трее</span>
    tools.<span style="color: #0000FF;">SetNotifyIcon</span><span style="color: #000000;">&#40;</span>SystemIcons.<span style="color: #0000FF;">Shield</span><span style="color: #000000;">&#41;</span>
        .<span style="color: #0000FF;">SetTooltip</span><span style="color: #000000;">&#40;</span>Console.<span style="color: #0000FF;">Title</span> <span style="color: #008000;">+</span> <span style="color: #666666;">&quot;: double click to toggle visibility&quot;</span><span style="color: #000000;">&#41;</span>
&nbsp;
        <span style="color: #008080; font-style: italic;">// добавляем обработчики событий (конвейер их обработает)</span>
        .<span style="color: #0000FF;">OnClick</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#40;</span>s, e<span style="color: #000000;">&#41;</span> <span style="color: #008000;">=&gt;</span> Console.<span style="color: #0000FF;">WriteLine</span><span style="color: #000000;">&#40;</span><span style="color: #666666;">&quot;NotifyIcon clicked!&quot;</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span>
        .<span style="color: #0000FF;">OnDoubleClick</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#40;</span>s, e<span style="color: #000000;">&#41;</span> <span style="color: #008000;">=&gt;</span> tools.<span style="color: #0000FF;">ConsoleVisible</span> <span style="color: #008000;">=</span> <span style="color: #008000;">!</span>tools.<span style="color: #0000FF;">ConsoleVisible</span><span style="color: #000000;">&#41;</span>
&nbsp;
        <span style="color: #008080; font-style: italic;">// добавляем пункты контекстного меню</span>
        .<span style="color: #0000FF;">AddMenuItem</span><span style="color: #000000;">&#40;</span><span style="color: #666666;">&quot;About&quot;</span>, SystemIcons.<span style="color: #0000FF;">Question</span>, ShowAboutBox<span style="color: #000000;">&#41;</span>
        .<span style="color: #0000FF;">AddMenuSeparator</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span>
        .<span style="color: #0000FF;">AddMenuItem</span><span style="color: #000000;">&#40;</span><span style="color: #666666;">&quot;Hide console&quot;</span>, <span style="color: #000000;">&#40;</span>s, e<span style="color: #000000;">&#41;</span> <span style="color: #008000;">=&gt;</span> tools.<span style="color: #0000FF;">ConsoleVisible</span> <span style="color: #008000;">=</span> <span style="color: #0600FF;">false</span><span style="color: #000000;">&#41;</span>
        .<span style="color: #0000FF;">AddMenuItem</span><span style="color: #000000;">&#40;</span><span style="color: #666666;">&quot;Show console&quot;</span>, <span style="color: #000000;">&#40;</span>s, e<span style="color: #000000;">&#41;</span> <span style="color: #008000;">=&gt;</span> tools.<span style="color: #0000FF;">ConsoleVisible</span> <span style="color: #008000;">=</span> <span style="color: #0600FF;">true</span><span style="color: #000000;">&#41;</span>
        .<span style="color: #0000FF;">AddMenuSeparator</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span>
&nbsp;
        <span style="color: #008080; font-style: italic;">// метод CloseConsole() работает как нажатие кнопки [x] на окне консоли</span>
        .<span style="color: #0000FF;">AddMenuItem</span><span style="color: #000000;">&#40;</span><span style="color: #666666;">&quot;Close console&quot;</span>,
            SystemIcons.<span style="color: #0000FF;">Error</span>, <span style="color: #000000;">&#40;</span>s, e<span style="color: #000000;">&#41;</span> <span style="color: #008000;">=&gt;</span> tools.<span style="color: #0000FF;">CloseConsole</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span><span style="color: #008000;">;</span>
&nbsp;
    <span style="color: #008080; font-style: italic;">// А тут все как обычно, к примеру, RemotingConfiguration.Configure(...)</span>
    Console.<span style="color: #0000FF;">WriteLine</span><span style="color: #000000;">&#40;</span><span style="color: #666666;">&quot;Press Enter to exit...&quot;</span><span style="color: #000000;">&#41;</span><span style="color: #008000;">;</span>
    Console.<span style="color: #0000FF;">ReadLine</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span><span style="color: #008000;">;</span>
<span style="color: #000000;">&#125;</span></pre></div></div>

<p>Остается только причесать немножко библиотеку и запустить ее куда-нибудь на CodePlex.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.sharp-shooter.ru/2011/02/27/notifyicon-in-console-app/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<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>7</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>
	</channel>
</rss>

