<?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"
	>

<channel>
	<title>BoxedApp</title>
	<atom:link href="http://boxedapp.ru/blog/feed/" rel="self" type="application/rss+xml" />
	<link>http://boxedapp.ru/blog</link>
	<description>BoxedApp: примеры использования, советы, приемы</description>
	<pubDate>Tue, 16 Jun 2009 15:47:20 +0000</pubDate>
	<generator>http://wordpress.org/?v=2.5.1</generator>
	<language>en</language>
			<item>
		<title>BoxedApp SDK 2.1, BoxedApp Packer 2.2</title>
		<link>http://boxedapp.ru/blog/2009/06/16/boxedapp-sdk-21-boxedapp-packer-22/</link>
		<comments>http://boxedapp.ru/blog/2009/06/16/boxedapp-sdk-21-boxedapp-packer-22/#comments</comments>
		<pubDate>Tue, 16 Jun 2009 15:47:20 +0000</pubDate>
		<dc:creator>Artem A. Razin</dc:creator>
		
		<category><![CDATA[BoxedApp Packer]]></category>

		<category><![CDATA[BoxedApp Packer Release]]></category>

		<category><![CDATA[BoxedApp SDK]]></category>

		<category><![CDATA[BoxedApp SDK Release]]></category>

		<category><![CDATA[C++]]></category>

		<category><![CDATA[Delphi]]></category>

		<category><![CDATA[examples]]></category>

		<category><![CDATA[примеры]]></category>

		<guid isPermaLink="false">http://boxedapp.ru/blog/?p=13</guid>
		<description><![CDATA[Выпущены новый версии продуктов линейки BoxedApp: BoxedApp SDK 2.1, BoxedApp Packer 2.2.
[ Загрузить демо версии ]


Кратко о том, что сделано:

Редактор виртуального реестра в BoxedApp Packer
API для перехвата функций
Мелкие доработки


Редактор виртуального реестра в BoxedApp Packer
Прежде вся работа с виртуальным реестром проводилась в пользовательских плагинах. Если требовалось создать даже один виртуальный ключ реестра - надо было писать [...]]]></description>
			<content:encoded><![CDATA[<p>Выпущены новый версии продуктов линейки BoxedApp: BoxedApp SDK 2.1, BoxedApp Packer 2.2.<br />
<a href = "/download.html" target = "_blank"><b>[ Загрузить демо версии ]</b></a><br />
<a name = "post_begin"></a><br />
<span id="more-13"></span><br />
Кратко о том, что сделано:</p>
<ul>
<li><a href = "#virtual_registry_editor">Редактор виртуального реестра в BoxedApp Packer</a></li>
<li><a href = "#hooks">API для перехвата функций</a></li>
<li><a href = "#minor_changes">Мелкие доработки</a></li>
</ul>
<p><a name = "virtual_registry_editor"></a><br />
<h2>Редактор виртуального реестра в BoxedApp Packer</h2>
<p>Прежде вся работа с виртуальным реестром проводилась в пользовательских плагинах. Если требовалось создать даже один виртуальный ключ реестра - надо было писать плагин, который использует функции <a href = "http://boxedapp.com/boxedapppacker/help/index/plugin_api.html" target = "_blank">BoxedApp SDK</a>.</p>
<p>Теперь BoxedApp Packer содержит полноценный редактор виртуального реестра. Буквально за пару кликов можно создать требуемые виртуальные ключи и задать значения параметров.</p>
<p><small><a href = "#post_begin">[ назад к списку изменений ]</a></small><br />
<a name = "hooks"></a><br />
<h2>API для перехвата функций</h2>
<p>Для создания виртуальной файловой системы / реестра BoxedApp SDK (приложения, созданные BoxedApp Packer, кстати, используют именно его) использует технику перехвата системных функций. Несколько оригинальных идей позволили сделать систему перехвата совместимой с любым окружением, и теперь та часть SDK, которая отвечает за перехват, стала доступна для разработчиков - пользователей SDK.</p>
<p>Теперь BoxedApp SDK может рассматриваться как полноценная альтернатива <a href = "http://research.microsoft.com/en-us/projects/detours/" target = "_blank">Detours</a> от Microsoft.</p>
<p>Напомним, что BoxedApp SDK поддерживает как 32-битное, так и 64-битное окружение, а также может быть статически прилинкован к приложениям, использующим VC++ / Delphi любых версий. И, конечно же, SDK может быть использован в любой программной среде, которая поддерживает использование DLL.</p>
<p>Пара примеров на C++ и Delphi, блокирующих возможность создания и открытия файла с названием &#8220;1.txt&#8221;:</p>
<pre name="code" class="cpp" cols="60" rows="10">
typedef HANDLE (WINAPI *P_CreateFileW)(
	LPCWSTR lpFileName,
	DWORD dwDesiredAccess,
	DWORD dwShareMode,
	LPSECURITY_ATTRIBUTES lpSecurityAttributes,
	DWORD dwCreationDisposition,
	DWORD dwFlagsAndAttributes,
	HANDLE hTemplateFile);
P_CreateFileW g_pCreateFileW;

HANDLE WINAPI My_CreateFileW(
	LPCWSTR lpFileName,
	DWORD dwDesiredAccess,
	DWORD dwShareMode,
	LPSECURITY_ATTRIBUTES lpSecurityAttributes,
	DWORD dwCreationDisposition,
	DWORD dwFlagsAndAttributes,
	HANDLE hTemplateFile)
{
	if (0 == lstrcmpiW(lpFileName, L"1.txt"))
	{
		SetLastError(ERROR_FILE_EXISTS);
		return INVALID_HANDLE_VALUE;
	}
	else
		return g_pCreateFileW(
				lpFileName,
				dwDesiredAccess,
				dwShareMode,
				lpSecurityAttributes,
				dwCreationDisposition,
				dwFlagsAndAttributes,
				hTemplateFile);
}
...
BoxedAppSDK_Init();

PVOID pCreateFileW = GetProcAddress(GetModuleHandle(_T("kernel32.dll")), "CreateFileW");

HANDLE hHook__CreateFileW = BoxedAppSDK_HookFunction(pCreateFileW, &#038;My_CreateFileW, TRUE);

g_pCreateFileW = (P_CreateFileW)BoxedAppSDK_GetOriginalFunction(hHook__CreateFileW);

FILE* f = fopen("1.txt", "r");

// f is NULL
...

BoxedAppSDK_UnhookFunction(hHook__CreateFileW);
</pre>
<p>Аналогичный пример на Delphi:</p>
<pre name="code" class="delphi" cols="60" rows="10">
type
TCreateFileW = 
   function(lpFileName: PWideChar;
            dwDesiredAccess, dwShareMode: Integer;
            lpSecurityAttributes: PSecurityAttributes;
            dwCreationDisposition, dwFlagsAndAttributes: DWORD;
            hTemplateFile: THandle): THandle; stdcall;

var
   OriginalCreateFileW: TCreateFileW;

function My_CreateFileW(
         lpFileName: PWideChar;
         dwDesiredAccess, dwShareMode: Integer;
         lpSecurityAttributes: PSecurityAttributes;
         dwCreationDisposition, dwFlagsAndAttributes: DWORD;
         hTemplateFile: THandle): THandle; stdcall;
begin
   if 0 = lstrcmpiW(lpFileName, '1.txt') then
   begin
      Result := INVALID_HANDLE_VALUE;
      SetLastError(ERROR_ALREADY_EXISTS);
   end
   else
      Result := 
        OriginalCreateFileW(
           lpFileName, 
           dwDesiredAccess,
           dwShareMode, 
           lpSecurityAttributes, 
           dwCreationDisposition, 
           dwFlagsAndAttributes, 
           hTemplateFile);
end;

var
   pCreateFileW: Pointer;
   hHook__CreateFileW: THandle;

begin
  Application.Initialize;

  BoxedAppSDK_Init;

  pCreateFileW := GetProcAddress(GetModuleHandle('kernel32.dll'), 'CreateFileW');
  hHook__CreateFileW := BoxedAppSDK_HookFunction(pCreateFileW, @My_CreateFileW, TRUE);
  OriginalCreateFileW := BoxedAppSDK_GetOriginalFunction(hHook__CreateFileW);

  // This line produces an exception because we prevent creating / opening '1.txt'
  TFileStream.Create('1.txt', fmCreate or fmOpenRead);

  BoxedAppSDK_UnhookFunction(hHook__CreateFileW);
end.
</pre>
<p><small><a href = "#post_begin">[ назад к списку изменений ]</a></small><br />
<a name = "minor_changes"></a><br />
<h2>Мелкие доработки</h2>
<ul>
<li>Добавлена поддержка флага FILE_FLAG_DELETE_ON_CLOSE для виртуальных файлов</li>
<li>Исправлены небольшие проблемы с запуском виртуальных исполняемых файлов, созданных с помощью Delphi</li>
<li>Сделана более гибкая система хранения виртуальных файлов в памяти</li>
</ul>
<p><small><a href = "#post_begin">[ назад к списку изменений ]</a></small></p>
<p><a href = "/download.html" target = "_blank"><b>[ Загрузить демо версии ]</b></a></p>
]]></content:encoded>
			<wfw:commentRss>http://boxedapp.ru/blog/2009/06/16/boxedapp-sdk-21-boxedapp-packer-22/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Упаковываем MS PowerPoint презентацию в единственный EXE</title>
		<link>http://boxedapp.ru/blog/2009/05/24/pack_ms_powerpoint_presentation_single_exe/</link>
		<comments>http://boxedapp.ru/blog/2009/05/24/pack_ms_powerpoint_presentation_single_exe/#comments</comments>
		<pubDate>Sun, 24 May 2009 13:19:45 +0000</pubDate>
		<dc:creator>Artem A. Razin</dc:creator>
		
		<category><![CDATA[BoxedApp Packer]]></category>

		<category><![CDATA[примеры]]></category>

		<guid isPermaLink="false">http://boxedapp.ru/blog/?p=12</guid>
		<description><![CDATA[MS PowerPoint дает возможность подготовить презентацию для просмотра на любом компьютере, даже на котором не установлен PowerPoint. Но что неудобно - при старте надо нажать на кнопку &#8220;Принимаю&#8221;, да еще такое большое число файлов&#8230; Как было бы хорошо превратить весь этот набор в один единственный исполняемый файл. 
Итак, выбираем в главном меню Файл -> Подготовить [...]]]></description>
			<content:encoded><![CDATA[<p>MS PowerPoint дает возможность подготовить презентацию для просмотра на любом компьютере, даже на котором не установлен PowerPoint. Но что неудобно - при старте надо нажать на кнопку &#8220;Принимаю&#8221;, да еще такое большое число файлов&#8230; Как было бы хорошо превратить весь этот набор в один единственный исполняемый файл. <span id="more-12"></span></p>
<p>Итак, выбираем в главном меню Файл -> Подготовить для компакт-диска и жмем на кнопку &#8220;Копировать в папку&#8230;&#8221;.</p>
<p>Получается примерно такой набор файлов:</p>
<p><i><br />
gdiplus.dll<br />
intldate.dll<br />
play.bat<br />
playlist.txt<br />
pptview.exe<br />
ppvwintl.dll<br />
saext.dll<br />
unicows.dll<br />
Презентация1.ppt<br />
</i></p>
<p>Для запуска презентации надо воспользоваться play.bat, который содержит команду:</p>
<p><i>@pptview.exe /L &#8220;playlist.txt&#8221;</i></p>
<p>Содержимое playlist.txt при этом:</p>
<p><i>Презентация1.ppt</i></p>
<p>Запустим BoxedApp Packer, добавим файлы, настроим параметры командной строки:</p>
<p><img src='/blog/wp-content/uploads/2009/05/pptview_files.png' alt='Файлы' class='alignnone' /></p>
<p>А также можно сделать так, чтобы не возникал экран с вопросом при запуске:</p>
<p><img src='/blog/wp-content/uploads/2009/05/pptview_registry.png' alt='Реестр' class='alignnone' /></p>
<p>Жмем на &#8220;Generate EXE&#8230;&#8221; и получаем упакованный файл, которому ни нужны никакие дополнительные файлы. При запуске он сразу же начнет показ презентации.</p>
]]></content:encoded>
			<wfw:commentRss>http://boxedapp.ru/blog/2009/05/24/pack_ms_powerpoint_presentation_single_exe/feed/</wfw:commentRss>
		</item>
		<item>
		<title>BoxedApp SDK 2.0.1, BoxedApp Packer 2.1.1</title>
		<link>http://boxedapp.ru/blog/2009/01/31/boxedapp-sdk-201-boxedapp-packer-211/</link>
		<comments>http://boxedapp.ru/blog/2009/01/31/boxedapp-sdk-201-boxedapp-packer-211/#comments</comments>
		<pubDate>Sat, 31 Jan 2009 11:51:15 +0000</pubDate>
		<dc:creator>Artem A. Razin</dc:creator>
		
		<category><![CDATA[BoxedApp Packer]]></category>

		<category><![CDATA[BoxedApp Packer Release]]></category>

		<category><![CDATA[BoxedApp SDK]]></category>

		<category><![CDATA[BoxedApp SDK Release]]></category>

		<guid isPermaLink="false">http://boxedapp.ru/blog/?p=11</guid>
		<description><![CDATA[В последнее время мы активно тестировали BoxedApp под новой версией Windows, Windows 7. Обнаружено несколько моментов, которые были исправлены:
[FIXED] ShellExecute не запускал виртуальные файлы корректно под Windows 7
[FIXED] Регистрация ActiveX в виртуальном реестре иногда работала некорректно под Windows 7
А также еще одно небольшое исправление:
[FIXED] Запуск множества дочерних процессов, к которым аттачится BoxedApp, приводил к утечкам [...]]]></description>
			<content:encoded><![CDATA[<p>В последнее время мы активно тестировали BoxedApp под новой версией Windows, Windows 7. Обнаружено несколько моментов, которые были исправлены:</p>
<p>[FIXED] ShellExecute не запускал виртуальные файлы корректно под Windows 7<br />
[FIXED] Регистрация ActiveX в виртуальном реестре иногда работала некорректно под Windows 7</p>
<p>А также еще одно небольшое исправление:</p>
<p>[FIXED] Запуск множества дочерних процессов, к которым аттачится BoxedApp, приводил к утечкам памяти и дескрипторов.</p>
<p>Новая фича BoxedApp Packer&#8217;а:</p>
<p>[NEW] <a href = "/boxedapppacker/help/index/overview/virtual_path.html" target = "_blank">В виртуальном пути можно указывать переменные окружения.</a></p>
<p><a href = "/download.html" target = "_blank"><b>[ Загрузить демо версии ]</b></a></p>
]]></content:encoded>
			<wfw:commentRss>http://boxedapp.ru/blog/2009/01/31/boxedapp-sdk-201-boxedapp-packer-211/feed/</wfw:commentRss>
		</item>
		<item>
		<title>BoxedApp Packer 2.1 и BoxedApp SDK 2.0: новые возможности</title>
		<link>http://boxedapp.ru/blog/2009/01/26/boxedapppacker_2_1_boxedappsdk_2_0/</link>
		<comments>http://boxedapp.ru/blog/2009/01/26/boxedapppacker_2_1_boxedappsdk_2_0/#comments</comments>
		<pubDate>Mon, 26 Jan 2009 19:27:02 +0000</pubDate>
		<dc:creator>Artem A. Razin</dc:creator>
		
		<category><![CDATA[BoxedApp Packer]]></category>

		<category><![CDATA[BoxedApp Packer Release]]></category>

		<category><![CDATA[BoxedApp SDK]]></category>

		<category><![CDATA[BoxedApp SDK Release]]></category>

		<category><![CDATA[C++]]></category>

		<category><![CDATA[examples]]></category>

		<category><![CDATA[примеры]]></category>

		<guid isPermaLink="false">http://boxedapp.ru/blog/?p=10</guid>
		<description><![CDATA[Наконец мы выпустили новые релизы с двумя очень интересными фичами:

запуск процесса на основе виртуального файла
общая виртуальная файловая система для нескольких процессов



Запуск процесса на основе виртуального файла
Если кратко, то, создав виртуальный файл с содержимым exe, можно запускать процесс на основе этого файла:

BoxedAppSDK_Init();

HMODULE hModule = GetModuleHandle(NULL);
HRSRC hResInfo = FindResource(hModule, _T("BIN1"), _T("BIN"));
HGLOBAL hResData = LoadResource(hModule, hResInfo);
LPVOID lpData = [...]]]></description>
			<content:encoded><![CDATA[<p>Наконец мы выпустили новые релизы с двумя очень интересными фичами:</p>
<ul>
<li><a href = "#virtual_process">запуск процесса на основе виртуального файла</a></li>
<li><a href = "#shared_env">общая виртуальная файловая система для нескольких процессов</a></li>
</ul>
<p><span id="more-10"></span></p>
<p><a name = "virtual_process"></a></p>
<h3>Запуск процесса на основе виртуального файла</h3>
<p>Если кратко, то, создав виртуальный файл с содержимым exe, можно запускать процесс на основе этого файла:</p>
<pre name="code" class="cpp" cols="60" rows="10">
BoxedAppSDK_Init();

HMODULE hModule = GetModuleHandle(NULL);
HRSRC hResInfo = FindResource(hModule, _T("BIN1"), _T("BIN"));
HGLOBAL hResData = LoadResource(hModule, hResInfo);
LPVOID lpData = LockResource(hResData);
DWORD dwSize = SizeofResource(hModule, hResInfo);

HANDLE hFile = 
   BoxedAppSDK_CreateVirtualFile(
      _T("app1.exe"), 
      GENERIC_WRITE, 
      FILE_SHARE_READ, 
      NULL, 
      CREATE_NEW, 
      0, 
      NULL);

DWORD temp;
WriteFile(hFile, lpData, dwSize, &#038;temp, NULL);

CloseHandle(hFile);

ShellExecute(NULL, NULL, _T("app1.exe"), NULL, NULL, SW_SHOW);
</pre>
</p>
<p><a name = "shared_env"></a></p>
<h3>Общая виртуальная файловая система для нескольких процессов</h3>
<p>Прежде BoxedApp SDK был ограничен единственным процессом, однако мы получили множество запросов, суть которых сводилась к необходимости давать доступ стороннему приложению к созданным в основном процессе виртуальным файлам.</p>
<p>Мы доработали BoxedApp, добавив функцию <a href = "http://boxedapp.com/boxedappsdk/help/index/functions/boxedappsdk_attachtoprocess.html" target = "_blank">BoxedAppSDK_AttachToProcess</a>, которая внедряет BoxedApp в процесс. После внедрения процесс начинает видеть все виртуальные файлы, что созданы или создаются в основном процессе. Он может модифицировать их и т.д. Иными словами, оба процесса полностью разделяют общую виртуальную файловую систему. Можно вызвать BoxedAppSDK_AttachToProcess сразу для нескольких процессов, и все они будут иметь одну и ту же виртуальную файловую систему.</p>
<p>Кроме того, добавлена опция DEF_BOXEDAPPSDK_OPTION__EMBED_BOXEDAPP_IN_CHILD_PROCESSES, которая указывает, следует ли внедрять BoxedApp в каждый дочерний процесс (<b>по умолчанию, эта опция выключена</b>), который создается в программе:</p>
<pre name="code" class="cpp" cols="60" rows="10">
// да, внедрять в дочерние процессы
BoxedAppSDK_EnableOption(DEF_BOXEDAPPSDK_OPTION__EMBED_BOXEDAPP_IN_CHILD_PROCESSES, TRUE);
...
// нет, не внедрять в дочерние процессы
BoxedAppSDK_EnableOption(DEF_BOXEDAPPSDK_OPTION__EMBED_BOXEDAPP_IN_CHILD_PROCESSES, FALSE);
</pre>
</p>
<p><a href = "/download.html" target = "_blank"><b>[ Загрузить демонстрационные версии ]</b></a></p>
]]></content:encoded>
			<wfw:commentRss>http://boxedapp.ru/blog/2009/01/26/boxedapppacker_2_1_boxedappsdk_2_0/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Виртуальный файл на основе IStream</title>
		<link>http://boxedapp.ru/blog/2008/12/01/virtual_file_based_on_istream/</link>
		<comments>http://boxedapp.ru/blog/2008/12/01/virtual_file_based_on_istream/#comments</comments>
		<pubDate>Mon, 01 Dec 2008 16:24:42 +0000</pubDate>
		<dc:creator>Artem A. Razin</dc:creator>
		
		<category><![CDATA[.NET]]></category>

		<category><![CDATA[C++]]></category>

		<category><![CDATA[examples]]></category>

		<category><![CDATA[use cases]]></category>

		<category><![CDATA[примеры]]></category>

		<guid isPermaLink="false">http://boxedapp.ru/blog/?p=9</guid>
		<description><![CDATA[Кратко
В BoxedApp SDK появилась новая функция BoxedAppSDK_CreateVirtualFileBasedOnIStream.
Зачем?
Для большей гибкости в BoxedApp SDK теперь реализована возможность создания виртуальных файлов на основе IStream - стандартного COM-интерфейса. Теперь программист может сам определять поведение виртуального файла.
Новая функция объявлена следующим образом:

HANDLE BoxedAppSDK_CreateVirtualFileBasedOnIStream(   
    LPCTSTR szPath,    
    DWORD dwDesiredAccess,  [...]]]></description>
			<content:encoded><![CDATA[<p><strong>Кратко</strong></p>
<p>В BoxedApp SDK появилась новая функция BoxedAppSDK_CreateVirtualFileBasedOnIStream.</p>
<p><strong>Зачем?</strong></p>
<p>Для большей гибкости в BoxedApp SDK теперь реализована возможность создания виртуальных файлов на основе IStream - стандартного COM-интерфейса. <span id="more-9"></span>Теперь программист может сам определять поведение виртуального файла.</p>
<p>Новая функция объявлена следующим образом:</p>
<pre name="code" class="cpp" cols="60" rows="10">
HANDLE BoxedAppSDK_CreateVirtualFileBasedOnIStream(   
    LPCTSTR szPath,    
    DWORD dwDesiredAccess,   
    DWORD dwShareMode,   
    LPSECURITY_ATTRIBUTES lpSecurityAttributes,   
    DWORD dwCreationDisposition,   
    DWORD dwFlagsAndAttributes,   
    HANDLE hTemplateFile,    
  
    LPSTREAM pStream   
);
</pre>
<p>Все, кроме последнего, аргументы повторяют аргументы стандартной функции CreateFile. Последний аргумент - это указатель на IStream.</p>
<p>Чтобы посмотреть в действии, как работает BoxedApp SDK, скачайте демо-версию:<br />
<a href="http://boxedapp.com/download/boxedappsdk__demo.zip"  onClick="javascript: pageTracker._trackPageview('/tracker/boxedappsdk__download');" target = "_blank"><b><strong>[ Download demo version ]</strong></b></a><br />
Включены примеры для C++, C#, VB.Net, Delphi, VB6.</p>
<p><strong>Как BoxedApp работает с переданным IStream?</strong></p>
<p>При чтении будет вызываться IStream::Write, при записи &#8212; IStream::Read.<br />
Изменение текущей позиции в файле &#8212; IStream::Seek. Обратите внимание, получение размера файла тоже реализуется через IStream::Seek примерно следующим образом:</p>
<pre name="code" class="cpp" cols="60" rows="10">
IStream* pStream;
...
LARGE_INTEGER liZero = { 0 };
ULARGE_INTEGER CurPos;

// Сохраняем текущую позицию
pStream->Seek(liZero, STREAM_SEEK_CUR, &#038;CurPos);

// Двигаемся в конец файла
ULARGE_INTEGER SizeOfFile;
pStream->Seek(liZero, STREAM_SEEK_END, &#038;SizeOfFile);

// Восстанавливаем текущую позицию
LARGE_INTEGER Pos;
Pos.Quad = CurPos.Quad;

ULARGE_INTEGER temp;
pStream->Seek(Pos, STREAM_SEEK_SET, &#038;temp);
</pre>
<p>Когда создается новый дескриптор виртуального файла, созданного на основе IStream, вызывается IStream::Clone. Реализация этого метода должна создавать новый экземляр IStream, который имеет свой собственный указатель в файле.</p>
<p><strong>Пример Номер Один. C++. Реализация IStream на основе статического буфера.</strong></p>
<p>Кликните на &#8220;+ expand source&#8221;, чтобы посмотреть весь код целиком.</p>
<pre name="code" class="cpp:collapse" cols="60" rows="10">
class CVirtualFilePointer;
class CMemoryFile;

class CMemoryFileLock
{
private:
	CMemoryFile* m_pMemoryFile;
public:
	CMemoryFileLock(CMemoryFile* pMemoryFile);
	~CMemoryFileLock();
};

// A file based on fixed memory block
class CMemoryFile
{
	friend class CVirtualFilePointer;
	friend class CMemoryFileLock;

private:
	LONG m_nRefCount;
	PBYTE m_p;
	DWORD m_dwSize;
	CRITICAL_SECTION m_cs;

private:
	CMemoryFile(PVOID p, DWORD size);
	~CMemoryFile();

	IStream* CreateStream();
	void AddRef();
	void Release();
public:
	static IStream* Create(PVOID p, DWORD size);
};

class CVirtualFilePointer : public IStream
{
	friend class CVirtualFile;

private:
	LONG m_nRefCount;
	CMemoryFile* m_pFile;
	DWORD m_dwPosition;

public:
	CVirtualFilePointer(CMemoryFile* pMemoryFile) : 
		m_nRefCount(1), 
		m_dwPosition(0)
	{
		m_pFile = pMemoryFile;
		m_pFile->AddRef();
	}

	virtual ~CVirtualFilePointer()
	{
		m_pFile->Release();
	}

protected:
	// IUnknown
    virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject)
	{
		*ppvObject = NULL;

		if (IsEqualIID(IID_IUnknown, riid))
			*ppvObject = this;
		else if (IsEqualIID(IID_IStream, riid))
			*ppvObject = this;
		else if (IsEqualIID(IID_ISequentialStream, riid))
			*ppvObject = this;

		if (NULL != *ppvObject)
		{
			AddRef();
			return S_OK;
		}
		else
			return E_NOINTERFACE;
	}
    
    virtual ULONG STDMETHODCALLTYPE AddRef()
	{
		InterlockedIncrement(&#038;m_nRefCount);
		return m_nRefCount;
	}
    
    virtual ULONG STDMETHODCALLTYPE Release()
	{
		InterlockedDecrement(&#038;m_nRefCount);

		LONG nRefCount = m_nRefCount;

		if (0 == m_nRefCount)
			delete this;

		return nRefCount;
	}

	// ISequentialStream
    virtual HRESULT STDMETHODCALLTYPE Read(void* pv, ULONG cb, ULONG* pcbRead)
	{
		CMemoryFileLock lock(m_pFile);

		DWORD dwBytesToRead;

		if (m_dwPosition < 0 || m_dwPosition > m_pFile->m_dwSize)
			dwBytesToRead = 0;
		else
			dwBytesToRead = cb < m_pFile->m_dwSize - m_dwPosition ? 
cb : m_pFile->m_dwSize - m_dwPosition;

		CopyMemory(pv, m_pFile->m_p + m_dwPosition, dwBytesToRead);

		m_dwPosition += dwBytesToRead;

		if (NULL != pcbRead)
			*pcbRead = dwBytesToRead;

		return S_OK;
	}
    
    virtual HRESULT STDMETHODCALLTYPE Write(const void* pv, ULONG cb, ULONG* pcbWritten)
	{
		CMemoryFileLock lock(m_pFile);

		DWORD dwBytesToWrite;
		
		if (m_dwPosition < 0 || m_dwPosition > m_pFile->m_dwSize)
			dwBytesToWrite = 0;
		else
			dwBytesToWrite = cb < m_pFile->m_dwSize - m_dwPosition ? 
cb : m_pFile->m_dwSize - m_dwPosition;

		CopyMemory(m_pFile->m_p + m_dwPosition, pv, dwBytesToWrite);

		m_dwPosition += dwBytesToWrite;

		if (NULL != pcbWritten)
			*pcbWritten = dwBytesToWrite;

		return S_OK;
	}

	// IStream
    virtual HRESULT STDMETHODCALLTYPE Seek(LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER* plibNewPosition)
	{
		CMemoryFileLock lock(m_pFile);

		// Note: new position can be more than m_dwSize

		switch (dwOrigin)
		{
			case STREAM_SEEK_CUR:
			{
				m_dwPosition += dlibMove.QuadPart;
				break;
			}
			case STREAM_SEEK_END:
			{
				m_dwPosition = m_pFile->m_dwSize + dlibMove.QuadPart;
				break;
			}
			case STREAM_SEEK_SET:
			{
				m_dwPosition = dlibMove.QuadPart;
				break;
			}
			default:
			{
				return E_FAIL;
			}
		}

		if (NULL != plibNewPosition)
			plibNewPosition->QuadPart = m_dwPosition;

		return S_OK;
	}
    
    virtual HRESULT STDMETHODCALLTYPE SetSize(ULARGE_INTEGER libNewSize)
	{
		// TODO:

		return E_NOTIMPL;
	}

    virtual HRESULT STDMETHODCALLTYPE Clone(IStream** ppstm)
	{
		*ppstm = m_pFile->CreateStream();
		return S_OK;
	}
    
    virtual HRESULT STDMETHODCALLTYPE CopyTo(IStream* pstm, ULARGE_INTEGER cb, ULARGE_INTEGER* pcbRead, ULARGE_INTEGER* pcbWritten){return E_NOTIMPL;}    
    virtual HRESULT STDMETHODCALLTYPE Commit(DWORD grfCommitFlags){return E_NOTIMPL;}
    virtual HRESULT STDMETHODCALLTYPE Revert(){return E_NOTIMPL;}    
    virtual HRESULT STDMETHODCALLTYPE LockRegion(ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType){return E_NOTIMPL;}    
    virtual HRESULT STDMETHODCALLTYPE UnlockRegion(ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType){return E_NOTIMPL;}    
    virtual HRESULT STDMETHODCALLTYPE Stat(STATSTG* pstatstg, DWORD grfStatFlag){return E_NOTIMPL;}
};

CMemoryFileLock::CMemoryFileLock(CMemoryFile* pMemoryFile) : 
	m_pMemoryFile(pMemoryFile)
{
	EnterCriticalSection(&#038;pMemoryFile->m_cs);
}

CMemoryFileLock::~CMemoryFileLock()
{
	LeaveCriticalSection(&#038;m_pMemoryFile->m_cs);
}

CMemoryFile::CMemoryFile(PVOID p, DWORD size) : 
	m_p((PBYTE)p), 
	m_dwSize(size), 
	m_nRefCount(0)
{
	InitializeCriticalSection(&#038;m_cs);
}

CMemoryFile::~CMemoryFile()
{
	DeleteCriticalSection(&#038;m_cs);
}

IStream* CMemoryFile::CreateStream()
{
	return new CVirtualFilePointer(this);
}

void CMemoryFile::AddRef()
{
	InterlockedIncrement(&#038;m_nRefCount);
}

void CMemoryFile::Release()
{
	InterlockedDecrement(&#038;m_nRefCount);

	if (0 == m_nRefCount)
		delete this;
}

IStream* CMemoryFile::Create(PVOID p, DWORD size)
{
	CMemoryFile* pMemoryFile = new CMemoryFile(p, size);
	return pMemoryFile->CreateStream();
}
</pre>
<p><strong>Пример Номер Два. C#. Виртуальный файл, представляющий часть файла.</strong></p>
<p>Чтобы посмотреть этот пример в действии, скачайте демо-версию:<br />
<a href="http://boxedapp.com/download/boxedappsdk__demo.zip"  onClick="javascript: pageTracker._trackPageview('/tracker/boxedappsdk__download');" target = "_blank"><b><strong>[ Download demo version ]</strong></b></a></p>
<p>Пример расположен в папке samples\C#\Sample3_CustomVirtualFileSystem.</p>
<p>Кликните на &#8220;+ expand source&#8221;, чтобы посмотреть весь код целиком.</p>
<pre name="code" class="c-sharp:collapse" cols="60" rows="10">
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.IO;

namespace Sample3_CustomVirtualFileSystem
{
    class CustomFileStream : IStream
    {
        private long _Offset;
        private long _Length;
        private string _FilePath;
        private Stream _Stream;

        public CustomFileStream(string FilePath, long Offset, long Length)
        {
            _FilePath = FilePath;
            _Stream = new FileStream(FilePath, FileMode.Open, FileAccess.Read, FileShare.Read);
            _Offset = Offset;
            _Length = Length;
        }

        public CustomFileStream(string FilePath, long Offset)
        {
            _FilePath = FilePath;
            _Stream = new FileStream(FilePath, FileMode.Open, FileAccess.Read, FileShare.Read);
            _Offset = Offset;
            _Length = _Stream.Length - _Offset;
        }

        #region IStream Members

        public void Read(byte[] pv, int cb, IntPtr pcbRead)
        {
            if (_Stream.Position > _Offset + _Length)
                cb = 0;
            else if (_Stream.Position + cb > _Offset + _Length)
                cb = (int)(_Offset + _Length - _Stream.Position);

            int nReadBytes = _Stream.Read(pv, 0, cb);

            if (IntPtr.Zero != pcbRead)
                Marshal.WriteIntPtr(pcbRead, new IntPtr(nReadBytes));
        }

        public void Write(byte[] pv, int cb, IntPtr pcbWritten)
        {
            if (_Stream.Position > _Offset + _Length)
                cb = 0;
            else if (_Stream.Position + cb > _Offset + _Length)
                cb = (int)(_Offset + _Length - _Stream.Position);

            int nWrittenBytes = _Stream.Read(pv, 0, cb);

            if (IntPtr.Zero != pcbWritten)
                Marshal.WriteIntPtr(pcbWritten, new IntPtr(nWrittenBytes));
        }

        public void Clone(out IStream ppstm)
        {
            ppstm = new CustomFileStream(_FilePath, _Offset, _Length);
        }

        public void Seek(long dlibMove, int dwOrigin, IntPtr plibNewPosition)
        {
            SeekOrigin Origin = (SeekOrigin)dwOrigin;
            long NewPosition = 0;

            switch (Origin)
            {
                case SeekOrigin.Begin:
                    {
                        NewPosition = _Stream.Seek(_Offset + dlibMove, Origin);

                        break;
                    }
                case SeekOrigin.Current:
                    {
                        NewPosition = _Stream.Seek(dlibMove, Origin);

                        break;
                    }
                case SeekOrigin.End:
                    {
                        NewPosition = _Stream.Seek(_Offset + _Length + dlibMove, SeekOrigin.Begin);

                        break;
                    }
            }

            NewPosition -= _Offset;

            if (NewPosition < 0)
                NewPosition = 0;
            else if (NewPosition > _Length)
                NewPosition = _Length;

            if (IntPtr.Zero != plibNewPosition)
                Marshal.WriteInt64(plibNewPosition, NewPosition);
        }

        public void Commit(int grfCommitFlags)
        {
            throw new Exception("The method or operation is not implemented.");
        }

        public void CopyTo(IStream pstm, long cb, IntPtr pcbRead, IntPtr pcbWritten)
        {
            throw new Exception("The method or operation is not implemented.");
        }

        public void LockRegion(long libOffset, long cb, int dwLockType)
        {
            throw new Exception("The method or operation is not implemented.");
        }

        public void Revert()
        {
            throw new Exception("The method or operation is not implemented.");
        }

        public void SetSize(long libNewSize)
        {
            throw new Exception("The method or operation is not implemented.");
        }

        public void Stat(out System.Runtime.InteropServices.ComTypes.STATSTG pstatstg, int grfStatFlag)
        {
            throw new Exception("The method or operation is not implemented.");
        }

        public void UnlockRegion(long libOffset, long cb, int dwLockType)
        {
            throw new Exception("The method or operation is not implemented.");
        }

        #endregion
    }
}
</pre>
<p>Чтобы посмотреть в действии, как работает BoxedApp SDK, скачайте демо-версию:<br />
<a href="http://boxedapp.com/download/boxedappsdk__demo.zip"  onClick="javascript: pageTracker._trackPageview('/tracker/boxedappsdk__download');" target = "_blank"><b><strong>[ Download demo version ]</strong></b></a></p>
<p>Включены примеры для C++, C#, VB.Net, Delphi, VB6.</p>
]]></content:encoded>
			<wfw:commentRss>http://boxedapp.ru/blog/2008/12/01/virtual_file_based_on_istream/feed/</wfw:commentRss>
		</item>
		<item>
		<title>C++ / CLI - преодоление зависимости от Microsoft Visual C++ Redistributable</title>
		<link>http://boxedapp.ru/blog/2008/07/15/vc_cli_avoid_dependency_from_microsoft_visual_c_redistributable/</link>
		<comments>http://boxedapp.ru/blog/2008/07/15/vc_cli_avoid_dependency_from_microsoft_visual_c_redistributable/#comments</comments>
		<pubDate>Tue, 15 Jul 2008 16:38:56 +0000</pubDate>
		<dc:creator>Artem A. Razin</dc:creator>
		
		<category><![CDATA[.NET]]></category>

		<category><![CDATA[C++]]></category>

		<category><![CDATA[examples]]></category>

		<category><![CDATA[use cases]]></category>

		<category><![CDATA[примеры]]></category>

		<guid isPermaLink="false">http://boxedapp.ru/blog/?p=7</guid>
		<description><![CDATA[Если ваше .NET приложение использует компоненты, написанные на Managed C++, то вы сталкиваетесь с необходимостью распространять Microsoft Visual C++ Redistributable вместе с ним. Если попытаться запустить такое приложение в системе, где не установлен соответствующий Microsoft Visual C++ Redistributable, то будет получено сообщение &#8220;приложение не может быть запущено, так как оно неправильно настроено&#8221;. Почему так происходит, [...]]]></description>
			<content:encoded><![CDATA[<p>Если ваше .NET приложение использует компоненты, написанные на Managed C++, то вы сталкиваетесь с необходимостью распространять Microsoft Visual C++ Redistributable вместе с ним. Если попытаться запустить такое приложение в системе, где не установлен соответствующий Microsoft Visual C++ Redistributable, то будет получено сообщение &#8220;приложение не может быть запущено, так как оно неправильно настроено&#8221;. <span id="more-7"></span>Почему так происходит, и можно ли обойтись без установки Microsoft Visual C++ Redistributable?</p>
<p>При обычном подходе DLL, написанная на Managed C++, имеет зависимость от Microsoft Visual C++ Runtime, DLL-ки которого невозможно прилинковать статически. Традицонное решение - это включение Microsoft Visual C++ Redistributable в инсталлятор.</p>
<p>С помощью BoxedApp SDK можно эмулировать наличие Microsoft Visual C++ Runtime&#8217;а. Сразу после старта приложения, до использования собранных на Managed C++ компонентов, создайте те самые файлы, от которых зависит Managed C++ DLL (<a href = "#how_to_find_dependency">как узнать пути к зависимостям</a>):</p>
<pre name="code" class="c-sharp" cols="60" rows="10">
using System;
using System.Collections.Generic;
using System.Windows.Forms;
using System.IO;
using System.Runtime.InteropServices;
using System.Reflection;

namespace WindowsApplication1
{
    static class Program
    {
        [STAThread]
        static void Main()
        {
            BoxedAppSDK.NativeMethods.BoxedAppSDK_Init();

            string PathOfWinSxS = 
                Directory.GetParent(Environment.SystemDirectory).FullName + 
                @"\WinSxS";

            if (!Directory.Exists(PathOfWinSxS))
                // Win2k
                PathOfWinSxS = Application.StartupPath;

            Stream fromStream =
                Assembly.
                GetExecutingAssembly().
                GetManifestResourceStream("WindowsApplication1.res.msvcm80d.dll");

            CreateDLLInMemory(
                PathOfWinSxS + 
                @"\x86_Microsoft.VC80.DebugCRT_1fc8b3b9a1e18e3b_8.0.50727.42_x-ww_f75eb16c\msvcm80d.dll",
                fromStream);

            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Form1());

            BoxedAppSDK.NativeMethods.BoxedAppSDK_Exit();
        }

        [DllImport("kernel32.dll", SetLastError = true)]
        static extern bool CloseHandle(IntPtr hObject);

        static void CreateDLLInMemory(string strVirtualPath, Stream stream)
        {
            const int BufferSize = 1024;
            byte[] buffer = new byte[BufferSize];

            IntPtr hHandle =
                BoxedAppSDK.NativeMethods.BoxedAppSDK_CreateVirtualFile(
                strVirtualPath,
                BoxedAppSDK.NativeMethods.EFileAccess.GenericWrite,
                BoxedAppSDK.NativeMethods.EFileShare.Read,
                IntPtr.Zero,
                BoxedAppSDK.NativeMethods.ECreationDisposition.New,
                BoxedAppSDK.NativeMethods.EFileAttributes.Normal,
                IntPtr.Zero);
            CloseHandle(hHandle);

            int nReadBytes;

            using (FileStream VirtualFileStream = new FileStream(strVirtualPath, FileMode.Open))
            {
                while ((nReadBytes = stream.Read(buffer, 0, BufferSize)) > 0)
                    VirtualFileStream.Write(buffer, 0, nReadBytes);
            }
        }
    }
}
</pre>
<p><a name = "how_to_find_dependency"></a>Чтобы узнать, от каких именно DLL-ек имеет зависимость ваша Managed C++ DLL, воспользуйтесь приложением depends.exe из поставки Visual Studio. Открыв вашу Managed C++ DLL в depends.exe, вы легко установите от каких составляющих VC++ Runtime она зависит:</p>
<p><a href='http://boxedapp.ru/blog/wp-content/uploads/2008/07/how_to_find_dependency.png' target = "_blank"><img src="http://boxedapp.ru/blog/wp-content/uploads/2008/07/how_to_find_dependency.png" alt="Как посмотреть зависимости" title="how_to_find_dependency" width="500" height="400" class="aligncenter size-full wp-image-8" /></a></p>
]]></content:encoded>
			<wfw:commentRss>http://boxedapp.ru/blog/2008/07/15/vc_cli_avoid_dependency_from_microsoft_visual_c_redistributable/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Временные файлы полезно делать виртуальными</title>
		<link>http://boxedapp.ru/blog/2008/06/25/temp_files/</link>
		<comments>http://boxedapp.ru/blog/2008/06/25/temp_files/#comments</comments>
		<pubDate>Wed, 25 Jun 2008 19:10:09 +0000</pubDate>
		<dc:creator>Artem A. Razin</dc:creator>
		
		<category><![CDATA[C++]]></category>

		<category><![CDATA[примеры]]></category>

		<guid isPermaLink="false">http://boxedapp.ru/blog/?p=6</guid>
		<description><![CDATA[Приложения часто используют временные файлы. Но какие проблемы могут возникнуть с их использованием?
Основная проблема состоит в том, что у приложения не оказывается нужных прав на создание временного файла. Кроме того, если приложение завершится аварийно, и после него останется набор созданных временных файлов. А иногда то, что записывается во временные файлы, не должно попасть другим на [...]]]></description>
			<content:encoded><![CDATA[<p>Приложения часто используют временные файлы. <span id="more-6"></span>Но какие проблемы могут возникнуть с их использованием?</p>
<p>Основная проблема состоит в том, что у приложения не оказывается нужных прав на создание временного файла. Кроме того, если приложение завершится аварийно, и после него останется набор созданных временных файлов. А иногда то, что записывается во временные файлы, не должно попасть другим на глаза. Например, это может раскрыть логику работы программы.</p>
<p>С помощью BoxedApp SDK очень просто создать виртуальный временный файл:</p>
<pre name="code" class="cpp" cols="60" rows="10">
TCHAR szTempName[MAX_PATH];

GetTempFileName(
   _T("C:\\"), 
   _T("NEW"), // temp file name prefix 
   0, // create unique name 
   szTempName);

HANDLE hVirtualFile1 =    
   BoxedAppSDK_CreateVirtualFile(   
      szTempName, 
      GENERIC_WRITE,    
      FILE_SHARE_READ,    
      NULL,    
      CREATE_NEW,    
      0,    
      NULL);

// ...

CloseHandle(hVirtualFile1);
</pre>
]]></content:encoded>
			<wfw:commentRss>http://boxedapp.ru/blog/2008/06/25/temp_files/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Виртуальная регистрация ActiveX и других COM-библиотек</title>
		<link>http://boxedapp.ru/blog/2008/06/25/using_activex_without_real_registration/</link>
		<comments>http://boxedapp.ru/blog/2008/06/25/using_activex_without_real_registration/#comments</comments>
		<pubDate>Wed, 25 Jun 2008 18:41:43 +0000</pubDate>
		<dc:creator>Artem A. Razin</dc:creator>
		
		<category><![CDATA[activex]]></category>

		<guid isPermaLink="false">http://boxedapp.ru/blog/?p=5</guid>
		<description><![CDATA[Приложение может использовать ActiveX-компонент, при этом регистрировать его в реестре может быть нежелательно по ряду причин:

требуются администраторские права;
регистрация может затронуть другие приложения;
например, вы желаете использовать Flash 7, а сейчас в реестре прописан Flash 9
ActiveX нужен только на время работы приложения;
например, вы пишете portable приложение


Как решить эту задачу с помощью BoxedApp SDK?
BoxedApp SDK предлагает любопытный механизм, [...]]]></description>
			<content:encoded><![CDATA[<p>Приложение может использовать ActiveX-компонент, при этом регистрировать его в реестре может быть нежелательно по ряду причин:</p>
<ul>
<li>требуются администраторские права;</li>
<li>регистрация может затронуть другие приложения;<br />
<i>например, вы желаете использовать Flash 7, а сейчас в реестре прописан Flash 9</i></li>
<li>ActiveX нужен только на время работы приложения;<br />
<i>например, вы пишете portable приложение</i></li>
</ul>
<p><span id="more-5"></span></p>
<p>Как решить эту задачу с помощью BoxedApp SDK?</p>
<p>BoxedApp SDK предлагает любопытный механизм, реализованный функцией <a href = "http://www.boxedapp.com/boxedappsdk/help/index/functions/boxedappsdk_registercomlibraryinvirtualregistry.html" target = "_blank">BoxedAppSDK_RegisterCOMLibraryInVirtualRegistry</a>. Эта функция делает ровно тоже, что и утилита <a href = "http://en.wikipedia.org/wiki/Regsvr32" target = "_blank">regsvr32</a>. BoxedAppSDK_RegisterCOMLibraryInVirtualRegistry загружает файл (неважно, виртуальный или реальный) и вызывает функцию <a href = "http://msdn.microsoft.com/en-us/library/ms682162(VS.85).aspx" target = "_blank">DllRegisterServer</a>. Но что важно: при этом все изменения, которые затрагивают реестр, сохраняются в виртуальном реестре!</p>
<p>Таким образом, когда приложение создает ActiveX-компонент, подсистема COM находит все, что ей требуется, в виртуальном реестре.</p>
<p>Как результат, реальный (системный) реестр не меняется, а ActiveX создается ровно так же, как если бы он был реально зарегистрирован в системе.</p>
]]></content:encoded>
			<wfw:commentRss>http://boxedapp.ru/blog/2008/06/25/using_activex_without_real_registration/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Внедрение (aka статическая линковка) .NET Runtime</title>
		<link>http://boxedapp.ru/blog/2008/06/17/dotnet_embedding/</link>
		<comments>http://boxedapp.ru/blog/2008/06/17/dotnet_embedding/#comments</comments>
		<pubDate>Tue, 17 Jun 2008 14:36:37 +0000</pubDate>
		<dc:creator>Artem A. Razin</dc:creator>
		
		<category><![CDATA[.NET]]></category>

		<category><![CDATA[C++]]></category>

		<category><![CDATA[примеры]]></category>

		<guid isPermaLink="false">http://boxedapp.ru/blog/?p=3</guid>
		<description><![CDATA[Если вы разрабатываете .NET приложения, то вы вероятно знаете, что для их успешного запуска требуется .NET Runtime. Если рантайм не установлен, то при запуске показывается неприятный messagebox о том, что mscoree.dll не найден. Можно ли внедрить рантайм в приложение? Да!
Итак, у нас есть .net приложение WindowsApplication1.exe, которое показывает простую форму с прогресс-баром и парой линков. [...]]]></description>
			<content:encoded><![CDATA[<p>Если вы разрабатываете .NET приложения, то вы вероятно знаете, что для их успешного запуска требуется .NET Runtime. Если рантайм не установлен, то при запуске показывается неприятный messagebox о том, что mscoree.dll не найден. <span id="more-3"></span>Можно ли внедрить рантайм в приложение? Да!</p>
<p>Итак, у нас есть .net приложение WindowsApplication1.exe, которое показывает простую форму с прогресс-баром и парой линков. Требуется создать условия, чтобы WindowsApplication1.exe запускалась на системах, в которых нет .net рантайма.</p>
<p>BoxedApp SDK создает инфраструктуру виртуальной файловой системы и виртуального реестра. Поэтому, создав соответствующие виртуальные ключи и виртуальные файлы, .NET приложение может быть успешно запущено.</p>
<p>Как известно, раздел HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework содержит ключ InstallRoot, который указывает на каталог, в котором лежат подкаталоги с .net рантаймами различных версий. Например, у меня этот ключ содержит значение &#8220;C:\WINDOWS\Microsoft.NET\Framework&#8221;. В этом каталоге лежат следующие версии рантаймов:</p>
<p>v1.0.3705<br />
v1.1.4322<br />
v2.0.50727<br />
v3.0<br />
v3.5</p>
<p>В данном примере мы предполагаем, что версия рантайма 2.0.50727. В качестве каталога, в котором будут лежать файлы рантайма, выберем какой-нибудь путь, например C:\DotNet. Далее мы создадим виртуальные файлы внутри этого каталога, и для приложения все будет выглядеть так, будто бы .net рантайм действительно установлен в системе.</p>
<p>Итак, создаем виртуальный ключ:</p>
<pre name="code" class="cpp" cols="60" rows="10">
// Initialize BoxedAppSDK
BoxedAppSDK_Init();

tstring strDotNetRoot = _T("C:\\DotNet");

DWORD dwDisposition;
HKEY hKey__DotNet;
LONG lRes = 
BoxedAppSDK_CreateVirtualRegKey(
	HKEY_LOCAL_MACHINE, 
	_T("SOFTWARE\\Microsoft\\.NETFramework"), 
	0, 
	NULL, 
	REG_OPTION_NON_VOLATILE, 
	KEY_ALL_ACCESS, 
	NULL, 
	&#038;hKey__DotNet, 
	&#038;dwDisposition);

LPCTSTR szValue = _T("C:\\DotNet\\");
RegSetValueEx(hKey__DotNet, _T("InstallRoot"), 0, REG_SZ, (CONST BYTE*)szValue, (lstrlen(szValue) + 1) * sizeof(TCHAR));
</pre>
<p>Нам потребуются пути к системным директориям:</p>
<pre name="code" class="cpp" cols="60" rows="10">
TCHAR szWinDir[MAX_PATH] = { 0 };
GetWindowsDirectory(szWinDir, MAX_PATH);
tstring strWinDir = szWinDir;

TCHAR szSystemDir[MAX_PATH] = { 0 };
GetSystemDirectory(szSystemDir, MAX_PATH);
tstring strSystemDir = szSystemDir;
</pre>
<p>В файле ресурсов укажем, какие файлы мы внедрим в приложение. Мы внедряем не только файлы из .net рантайма, но и WindowsApplication1.exe, а также сборку AppLauncher.dll, о которой чуть ниже:</p>
<pre name="code" class="cpp" cols="60" rows="10">
WindowsApplication1.exe BIN "WindowsApplication1.exe"
AppLauncher.dll BIN "AppLauncher.dll"

mscoree.dll BIN "system32\\mscoree.dll"
mscorjit.dll BIN "v2.0.50727\\mscorjit.dll"
mscorwks.dll BIN "v2.0.50727\\mscorwks.dll"
comctl32.dll BIN "WinSxS\\x86_Microsoft.Windows.Common-Controls_6595b64144ccf1df_6.0.2600.2982_x-ww_ac3f9c03\\comctl32.dll"
msvcr80.dll BIN "WinSxS\\x86_Microsoft.VC80.CRT_1fc8b3b9a1e18e3b_8.0.50727.1433_x-ww_5cf844d2\\msvcr80.dll"
msvcr80d.dll BIN "WinSxS\\x86_Microsoft.VC80.DebugCRT_1fc8b3b9a1e18e3b_8.0.50727.42_x-ww_f75eb16c\\msvcr80d.dll"
System.Windows.Forms.dll BIN "assembly\\GAC_MSIL\\System.Windows.Forms\\2.0.0.0__b77a5c561934e089\\System.Windows.Forms.dll"
mscorlib.dll BIN "assembly\\GAC_32\\mscorlib\\2.0.0.0__b77a5c561934e089\\mscorlib.dll"
mscorrc.dll BIN "v2.0.50727\\mscorrc.dll"
sorttbls.nlp BIN "assembly\\GAC_32\\mscorlib\\2.0.0.0__b77a5c561934e089\\sorttbls.nlp"
sortkey.nlp BIN "assembly\\GAC_32\\mscorlib\\2.0.0.0__b77a5c561934e089\\sortkey.nlp"
System.dll BIN "v2.0.50727\\System.dll"
System.Data.dll BIN "v2.0.50727\\System.Data.dll"
System.Drawing.dll BIN "v2.0.50727\\System.Drawing.dll"
Culture.dll BIN "v2.0.50727\\Culture.dll"
Accessibility.dll BIN "v2.0.50727\\Accessibility.dll"
GdiPlus.dll BIN "system32\\GdiPlus.dll"
</pre>
<p>В свойствах этого rc-файла (right-click по rc-файлу, затем Properties, Configuration Properties -> Resources -> General, Additional Include Directories) укажем дополнительные директории, в которых осуществляется поиск файлов:</p>
<p><code><br />
"$(FrameworkDir)";"$(SystemRoot)";"..\WindowsApplication1\bin\x86\$(ConfigurationName)";"..\AppLauncher\bin\x86\$(ConfigurationName)"<br />
</code></p>
<p>Небольшая функция-helper для работы с ресурсами:</p>
<pre name="code" class="cpp" cols="60" rows="10">
void LoadResourceHelper( /* in */ LPCTSTR lpszName, 
                         /* in */ LPCTSTR lpszType, 
                         /* out */ LPVOID&#038; lpData, 
                         /* out */ DWORD&#038; dwSize)
{
    HMODULE hModule = GetModuleHandle(NULL);
    HRSRC hResInfo = FindResource(hModule, lpszName, lpszType);
    HGLOBAL hResData = LoadResource(hModule, hResInfo);
    lpData = LockResource(hResData);
    dwSize = SizeofResource(hModule, hResInfo);
}
</pre>
<p>Функция для создания виртуального файла, контент берется из указанного ресурса:</p>
<pre name="code" class="cpp" cols="60" rows="10">
void CreateVirtualFileFromResource(LPCTSTR szVirtualPath, LPCTSTR szResName, LPCTSTR szResType)
{
	DWORD temp;

	LPVOID pData;
	DWORD dwSize;

	LoadResourceHelper(szResName, szResType, pData, dwSize);

	HANDLE hFile = BoxedAppSDK_CreateVirtualFile(szVirtualPath, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_NEW, 0, NULL);
	WriteFile(hFile, pData, dwSize, &#038;temp, NULL);
	CloseHandle(hFile);
}
</pre>
<p>Теперь можно создать виртуальные файлы, которые требуются для успешной работы .net рантайма:</p>
<pre name="code" class="cpp" cols="60" rows="10">
struct SFileResInfo
{
	tstring strVirtualPath;
	LPCTSTR szResName;
};

const SFileResInfo ResInfo[] = 
{
		{ strSystemDir + _T("\\mscoree.dll"), _T("mscoree.dll") }, 

		{ strDotNetRoot + _T("\\v2.0.50727\\mscorjit.dll"), _T("mscorjit.dll") }, 

		{ strDotNetRoot + _T("\\v2.0.50727\\mscorwks.dll"), _T("mscorwks.dll") }, 

		{ strWinDir + _T("\\WinSxS\\x86_Microsoft.Windows.Common-Controls_6595b64144ccf1df_6.0.2600.2982_x-ww_ac3f9c03\\comctl32.dll"), _T("comctl32.dll") }, 
		{ strSystemDir + _T("\\comctl32.dll"), _T("comctl32.dll") }, 

		{ strWinDir + _T("\\WinSxS\\x86_Microsoft.VC80.CRT_1fc8b3b9a1e18e3b_8.0.50727.1433_x-ww_5cf844d2\\msvcr80.dll"), _T("msvcr80.dll") }, 
		{ strSystemDir + _T("\\msvcr80.dll"), _T("msvcr80.dll") }, 

		{ strWinDir + _T("\\assembly\\GAC_MSIL\\System.Windows.Forms\\2.0.0.0__b77a5c561934e089\\System.Windows.Forms.dll"), _T("System.Windows.Forms.dll") }, 

		{ strWinDir + _T("\\assembly\\GAC_32\\mscorlib\\2.0.0.0__b77a5c561934e089\\mscorlib.dll"), _T("mscorlib.dll") }, 

		{ strDotNetRoot + _T("\\v2.0.50727\\mscorrc.dll"), _T("mscorrc.dll") }, 

		{ strWinDir + _T("\\assembly\\GAC_32\\mscorlib\\2.0.0.0__b77a5c561934e089\\sorttbls.nlp"), _T("sorttbls.nlp") }, 
		{ strWinDir + _T("\\assembly\\GAC_32\\mscorlib\\2.0.0.0__b77a5c561934e089\\sortkey.nlp"), _T("sortkey.nlp") }, 

		{ strDotNetRoot + _T("\\v2.0.50727\\System.dll"), _T("System.dll") }, 

		{ strDotNetRoot + _T("\\v2.0.50727\\System.Data.dll"), _T("System.Data.dll") }, 
		{ strDotNetRoot + _T("\\v2.0.50727\\System.Drawing.dll"), _T("System.Drawing.dll") }, 

		{ strDotNetRoot + _T("\\v2.0.50727\\Culture.dll"), _T("Culture.dll") }, 

		{ strWinDir + _T("\\assembly\\GAC_MSIL\\System\\2.0.0.0__b77a5c561934e089\\System.dll"), _T("System.dll") }, 
		{ strWinDir + _T("\\assembly\\GAC_MSIL\\System.Drawing\\2.0.0.0__b03f5f7f11d50a3a\\System.Drawing.dll"), _T("System.Drawing.dll") }, 

		{ strWinDir + _T("\\Microsoft.NET\\Framework\\v2.0.50727\\Accessibility.dll"), _T("Accessibility.dll") }, 
		{ strWinDir + _T("\\assembly\\GAC_MSIL\\Accessibility\\2.0.0.0__b03f5f7f11d50a3a\\Accessibility.dll"), _T("Accessibility.dll") }, 

		{ strSystemDir + _T("\\GdiPlus.dll"), _T("GdiPlus.dll") }, 


		{ _T("C:\\AppLauncher.dll"), _T("AppLauncher.dll") }, 
		{ _T("C:\\WindowsApplication1.exe"), _T("WindowsApplication1.exe") }
};

for (int i = 0; i < sizeof(ResInfo) / sizeof(ResInfo[0]); i++)
	CreateVirtualFileFromResource(ResInfo[i].strVirtualPath.c_str(), ResInfo[i].szResName, _T("BIN"));
</pre>
<p>Для работы с .net рантаймом будем использовать библиотеку mscoree.dll (обратите внимание, mscoree.dll внедряется в приложение). mscoree.dll экспортирует функцию CorBindToRuntimeEx, с помощью которой мы получим интерфейс ICLRRuntimeHost. Метод ICLRRuntimeHost::ExecuteInDefaultAppDomain загружает сборку и вызывает заданный метод. Правда, этот метод должен иметь вполне конкретную сигнатуру; произвольный метод вызвать нельзя &#8212; но об этом позже. Итак, получаем ICLRRuntimeHost:</p>
<pre name="code" class="cpp" cols="60" rows="10">
HMODULE hMSCoree = LoadLibrary((strSystemDir + _T("\\mscoree.dll")).c_str());

typedef HRESULT (__stdcall *P_CorBindToRuntimeEx)(LPCWSTR pwszVersion, LPCWSTR pwszBuildFlavor, DWORD startupFlags, REFCLSID rclsid, REFIID riid, LPVOID FAR *ppv);
P_CorBindToRuntimeEx pCorBindToRuntimeEx = (P_CorBindToRuntimeEx)GetProcAddress(hMSCoree, "CorBindToRuntimeEx");

ICLRRuntimeHost* pCLRRuntimeHost = NULL;
pCorBindToRuntimeEx(L"v2.0.50727", NULL, 0, CLSID_CLRRuntimeHost, IID_ICLRRuntimeHost, (void**)&#038;pCLRRuntimeHost);
</pre>
<p>ICLRRuntimeHost::ExecuteInDefaultAppDomain может вызвать только метод, имеющий следующую сигнатуру:</p>
<pre name="code" class="c-sharp" cols="60" rows="10">
int SomeMethod (string Argument);
</pre>
<p>Наша задача: загрузить сборку .net приложения и вызвать ее точку входа. Но точка входа имеет другую сигнатуру:</p>
<pre name="code" class="c-sharp" cols="60" rows="10">
static void Main();
</pre>
<p>Поэтому мы создадим отдельную сборку, которая будет предоставлять класс со статическим методом, который, в свою очередь, будет загружать сборку основного приложения и вызывать ее точку входа:</p>
<pre name="code" class="c-sharp" cols="60" rows="10">
using System;
using System.Reflection;
using System.Text;

namespace AppLauncher
{
    public class Launcher
    {
        public static int Launch(string strPath)
        {
            Assembly assembly = Assembly.LoadFile(strPath);
            assembly.EntryPoint.Invoke(null, null);

            return 0;
        }
    }
}
</pre>
<p>Все готово для запуска приложения:</p>
<pre name="code" class="cpp" cols="60" rows="10">
pCLRRuntimeHost->Start();

DWORD nRetValue;
pCLRRuntimeHost->ExecuteInDefaultAppDomain(
	L"C:\\AppLauncher.dll", 
	L"AppLauncher.Launcher", 
	L"Launch", 
	L"C:\\WindowsApplication1.exe", 
	&#038;nRetValue);

pCLRRuntimeHost->Stop();

pCLRRuntimeHost->Release();

FreeLibrary(hMSCoree);
</pre>
<p>Ссылки по теме:</p>
<ul>
<li><a href = "http://boxedapp.com/download/boxedappsdk__demo.zip" target = "_blank">Скачать пример, смотрите папку samples\Misc\DotNetEmbedding</a></li>
<li><a href = "http://msdn.microsoft.com/en-us/library/99sz37yh(VS.71).aspx" target = "_blank">Описание CorBindToRuntimeEx</a></li>
<li><a href = "http://boxedapp.com/boxedappsdk/help/index/functions/boxedappsdk_createvirtualregkey.html" target = "_blank">BoxedAppSDK_CreateVirtualRegKey</a></li>
<li><a href = "http://boxedapp.com/boxedappsdk/help/index/functions/boxedappsdk_createvirtualfile.html" target = "_blank">BoxedAppSDK_CreateVirtualFile</a></li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://boxedapp.ru/blog/2008/06/17/dotnet_embedding/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Как внедрить Flash Player ActiveX с помощью BoxedApp SDK</title>
		<link>http://boxedapp.ru/blog/2008/06/01/how-to-embed-flash-player-activex-using-boxedapp-sdk/</link>
		<comments>http://boxedapp.ru/blog/2008/06/01/how-to-embed-flash-player-activex-using-boxedapp-sdk/#comments</comments>
		<pubDate>Sun, 01 Jun 2008 17:28:09 +0000</pubDate>
		<dc:creator>Artem A. Razin</dc:creator>
		
		<category><![CDATA[BoxedAppSDK_RegisterCOMLibraryInVirtualRegistry]]></category>

		<category><![CDATA[C++]]></category>

		<category><![CDATA[activex]]></category>

		<category><![CDATA[flash]]></category>

		<category><![CDATA[примеры]]></category>

		<guid isPermaLink="false">http://boxedapp.ru/blog/?p=4</guid>
		<description><![CDATA[Начнем.
Одна из важнейших фич SDK - это возможность внедрения ActiveX в приложение. BoxedAppSDK_RegisterCOMLibraryInVirtualRegistry регистрирует виртуальный файл в виртуальном реестре. Более точно, эта функция загружает указанный файл (LoadLibrary) и вызывает DllRegisterServer (экспортируемый из этой DLL). Все изменения, сделанные этой функцией, сохраняются в виртуальном реестре. . Системный реестр остается нетронутым.
После этого CoCreateInstance (и прочие функции такого рода) [...]]]></description>
			<content:encoded><![CDATA[<p>Начнем.</p>
<p>Одна из важнейших фич SDK - это возможность внедрения ActiveX в приложение. <a href="http://boxedapp.com/boxedappsdk/help/index/functions/boxedappsdk_registercomlibraryinvirtualregistry.html" target="_blank">BoxedAppSDK_RegisterCOMLibraryInVirtualRegistry</a> регистрирует виртуальный файл в виртуальном реестре. Более точно, эта функция загружает указанный файл (LoadLibrary) и вызывает DllRegisterServer (экспортируемый из этой DLL). Все изменения, сделанные этой функцией, сохраняются в виртуальном реестре. <span id="more-4"></span>. Системный реестр остается нетронутым.</p>
<p>После этого CoCreateInstance (и прочие функции такого рода) будут получать корректную информацию из системного реестра &#8212; такую, будто бы ActiveX действительно зарегистрирован в системе!</p>
<p>Внедрение Flash ActiveX:</p>
<p>1. Инициализация BoxedApp SDK:</p>
<pre name="code" class="cpp" cols="60" rows="10">
BoxedAppSDK_Init();
</pre>
<p>2. Создаем виртуальный файл с Flash.ocx:</p>
<pre name="code" class="cpp" cols="60" rows="10">
LPVOID pBuffer;
DWORD dwSize;

// LoadResourceHelper получает указатель на начало ресурса и его размер
LoadResourceHelper(
   MAKEINTRESOURCE(IDR_BIN_FLASH_OCX), 
   _T("BIN"), 
   pBuffer, 
   dwSize);

HANDLE hVirtualFile1 = 
   BoxedAppSDK_CreateVirtualFile(
      _T("C:\\Flash9e.ocx"), 
      GENERIC_READ, 
      FILE_SHARE_READ, 
      NULL, 
      CREATE_NEW, 
      0, 
      NULL);  

DWORD dwTemp; 
WriteFile(hVirtualFile1, pBuffer, dwSize, &amp;dwTemp, NULL);

CloseHandle(hVirtualFile1);
</pre>
<p>3. Регистрируем COM библиотеку в виртуальном реестре:</p>
<pre name="code" class="cpp" cols="60" rows="10">
BoxedAppSDK_RegisterCOMLibraryInVirtualRegistry(_T("C:\\Flash9e.ocx"));
</pre>
<p>Теперь, когда бы вы не создавали экземпляр Flash ActiveX, подсистема COM (CoCreateInstance) будет читать информацию из виртуального реестра и создавать ActiveX, используя виртуальную DLL.</p>
<p>В частности, используя этот метод вы можете создавать приложения, которые готовы работать без инсталляции (т.н. &#8220;portable applications&#8221;), даже если они используют ActiveX-ы или другие COM-объекты. Просто создавайте виртуальный файл с DLL-кой и вызывайте <a href="http://boxedapp.com/boxedappsdk/help/index/functions/boxedappsdk_registercomlibraryinvirtualregistry.html" target="_blank">BoxedAppSDK_RegisterCOMLibraryInVirtualRegistry</a>.</p>
<hr />
<p>Related links:</p>
<ol>
<li><a href = "http://boxedapp.com/download/boxedappsdk__demo.zip" target = "_blank">Загрузка примеров BoxedApp SDK</a></li>
<li><a href = "http://f-in-box.com/" target = "_blank">Лучшее решение для работы с Flash ActiveX (внедрение также поддерживается)</a></li>
<li><a href="http://boxedapp.com/boxedappsdk/help/index/functions/boxedappsdk_registercomlibraryinvirtualregistry.html" target="_blank">Функция BoxedAppSDK_RegisterCOMLibraryInVirtualRegistry</a></li>
</ol>
]]></content:encoded>
			<wfw:commentRss>http://boxedapp.ru/blog/2008/06/01/how-to-embed-flash-player-activex-using-boxedapp-sdk/feed/</wfw:commentRss>
		</item>
	</channel>
</rss>

