<?xml version="1.0" encoding="utf-8"?>

<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
	<channel>
		<title>Форум программистов и сисадминов Киберфорум - Блоги - The trick</title>
		<link>https://www.cyberforum.ru/blogs/354370/</link>
		<description>КиберФорум - форум программистов, системных администраторов, администраторов баз данных, компьютерный форум, форум по электронике и бытовой технике, обсуждение софта. Бесплатная помощь в решении задач по программированию и наукам, решение проблем с компьютером, операционными системам</description>
		<language>ru</language>
		<lastBuildDate>Sat, 09 May 2026 07:39:15 GMT</lastBuildDate>
		<generator>vBulletin</generator>
		<ttl>60</ttl>
		<image>
			<url>https://www.cyberforum.ru//cyberstatic.net/images/misc/rss.jpg</url>
			<title>Форум программистов и сисадминов Киберфорум - Блоги - The trick</title>
			<link>https://www.cyberforum.ru/blogs/354370/</link>
		</image>
		<item>
			<title>Генератор сигналов с визуализацией спектра</title>
			<link>https://www.cyberforum.ru/blogs/354370/6370.html</link>
			<pubDate>Wed, 12 Feb 2020 06:36:05 GMT</pubDate>
			<description>Всем привет! 
 
В данном проекте реализован простой генератор нескольких звуковых сигналов с...</description>
			<content:encoded><![CDATA[<div>Всем привет!<br />
<br />
В данном проекте реализован простой генератор нескольких звуковых сигналов с визуализацией их спектра. Список сигналов включает в себя:<br />
<ol style="list-style-type: decimal"><li>Белый шум;</li>
<li>Розовый шум;</li>
<li>Коричневый/красный шум;</li>
<li>Синий шум;</li>
<li>Фиолетовый шум;</li>
<li>Синусоида с изменяющейся частотой;</li>
<li>Прямоугольная волна (нечетные гармоники убывающие на 6db/oct);</li>
<li>Пилообразная волна (все гармоники убывающие на 6db/oct);</li>
<li>Треугольная волна (нечетные гармоники убывающие на 12db/oct);</li>
<li>Сигнал только с четными гармониками;</li>
<li>Сложная частотная модуляция;</li>
<li>Амплитудная модуляция;</li>
</ol><br />
Визуализатор спектра поддерживает разрешение до 32768 точек, а также усреднение спектра по нескольким выборкам. Визуализатор оформлен в виде отдельного контрола и может быть использован в других проектах. Для визуализации спектра применяется быстрое преобразование Фурье (FFT). Данная процедура оптимизирована для обработки реального сигнала так что за раз обрабатывается сразу 2 реальных сигнала которые затем преобразуются в 2 спектра <a rel="nofollow noopener noreferrer" href="https://books.google.ru/books?id=CdtQBAAAQBAJ&amp;pg=PA102" target="_blank" title="https://books.google.ru/books?id=CdtQBAAAQBAJ&amp;pg=PA102">декомпозицией на основе сигналов с четной и нечетной симметрией</a>. Дальнейший синтез единого спектра из двух основан на сдвиге во временной области и соответствующих поворотах в частотной. Комментарии в коде подробно описывают процесс.<br />
<br />
<div align="center"><a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=5910&amp;d=1581490278" rel="Lightbox" id="attachment5910" ><img src="https://www.cyberforum.ru/blog_attachment.php?attachmentid=5910&amp;thumb=1&amp;d=1581490278" class="thumbnail" border="0" alt="Нажмите на изображение для увеличения
Название: spectrum.png
Просмотров: 1170
Размер:	23.2 Кб
ID:	5910" style="margin: 5px" /></a></div><br />
Спасибо за внимание!<br />
The trick.</div>


<!-- attachments -->
	<div style="margin-top:10px">

		
		
		
		
			<fieldset class="fieldset">
				<legend>Вложения</legend>
				<table cellpadding="0" cellspacing="3" border="0">
				<tr>
	<td><img loading="lazy" decoding="async" class="inlineimg" src="http://www.cyberforum.ru//cyberstatic.net/images/attach/zip.gif" alt="Тип файла: zip" width="16" height="16" border="0" style="vertical-align:baseline" /></td>
	<td><a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=5909&amp;d=1581490268">SignalSpectrum.zip</a> (13.4 Кб, 1208 просмотров)</td>
</tr>
				</table>
			</fieldset>
		

	</div>
<!-- / attachments -->
]]></content:encoded>
			<dc:creator>The trick</dc:creator>
			<guid isPermaLink="true">https://www.cyberforum.ru/blogs/354370/6370.html</guid>
		</item>
		<item>
			<title>PNG/ICO/CUR/ANI изображения в стандартных контролах VB6.</title>
			<link>https://www.cyberforum.ru/blogs/354370/5639.html</link>
			<pubDate>Wed, 23 Jan 2019 20:55:40 GMT</pubDate>
			<description>ivmYQeyIDr8 
 
ОПИСАНИЕ УСТАРЕЛО. АКТУАЛЬНОЕ ОПИСАНИЕ НА GITHUB. 
 
Всем привет. 
 
Как известно...</description>
			<content:encoded><![CDATA[<div><iframe width="640" height="360" src="https://www.youtube.com/embed/ivmYQeyIDr8" frameborder="0" allowfullscreen></iframe><br />
<br />
<font color="Red">ОПИСАНИЕ УСТАРЕЛО. АКТУАЛЬНОЕ ОПИСАНИЕ НА GITHUB.</font><br />
<br />
Всем привет.<br />
<br />
Как известно встроенные средства <b>Visual Basic 6.0</b> не поддерживают возможности работы с PNG изображениями, т.е. к примеру нельзя ипользовать Png картинку в качестве свойства <b>Form.Picture</b>. Я представляю небольшую библиотеку и Add-in которые позволяют обойти эти ограничения. Данная библиотека позволяет загружать и сохранять Png изображения (с альфа каналом) стандартными средствами (<b>LoadPicture</b> /<b> SavePicture</b>), а также включает поддержку Png изображений (с альфа каналом) в контролы. Любой контрол который в своей работе использует стандарнтые Ole Picture объекты будет поддерживать загрузку Png изображений. В свою очередь если изображение выводится посредством <b>IPicture::Render</b> то картинка будет отрисовываться с учетом альфа канала. Данная библиотека должна работать на всех версиях Windows начиная с XP:<br />
<br />
<div align="center"><a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=5169&amp;d=1548276785" rel="Lightbox" id="attachment5169" ><img src="https://www.cyberforum.ru/blog_attachment.php?attachmentid=5169&amp;thumb=1&amp;d=1548276785" class="thumbnail" border="0" alt="Нажмите на изображение для увеличения
Название: png_.png
Просмотров: 911
Размер:	434.3 Кб
ID:	5169" style="margin: 5px" /></a></div><br />
<font size="5">Как использовать?</font><br />
<br />
Библиотека может быть использована как внешняя DLL либо быть прилинкована к исполняемому файлу (только native code). Для использования в качестве Dll необходимо вызвать функцию <b>Initialize</b> которая вернет 1 в случае успеха. После этого можно пользоваться возможностями библиотеки. Если необходимо выгрузить библиотеку то нужно вызвать функцию <b>CanUnloadNow</b> которая сообщит можно ли в данный момент выгрузить библиотеку. Если библиотека готова к выгрузке функция вернет <b>S_OK</b> после которой нужно вызвать <b>Uninitialize</b>. Если функция возвращает <b>S_FALSE</b> то библиотеку нельзя выгружать т.к. имеются активные Picture объекты которые еще не выгружены и они используют библиотеку. Для IDE создан специальный Add-in который автоматически загружает библиотеку при старте среды. В скомпилированном варианте можно к примеру в событии <b>Initialize</b> или в процедуре <b>Main</b> вызывать <b>Initialize</b>, а при завершении <b>Uninitialize</b>:<br />
<div class="codeblock"><table class="vb"><thead><tr><td colspan="2" id="733037939"  class="head">Visual Basic</td></tr></thead><tbody><tr class="li1"><td><div id="733037939" style="height: 254px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
5
6
7
8
9
10
11
12
13
14
</pre></td><td class="de1"><pre class="de1"><span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Function</span> Initialize Lib <span class="st0">&quot;VBPng.dll&quot;</span> () <span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Sub</span> Uninitialize Lib <span class="st0">&quot;VBPng.dll&quot;</span> ()
&nbsp;
<span class="kw2">Private</span> <span class="kw2">Sub</span> Form_Initialize()
&nbsp;
&nbsp; &nbsp; <span class="kw3">If</span> Initialize() = 0 <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; MsgBox <span class="st0">&quot;Unable to initialize png dll&quot;</span>, vbCritical
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; 
<span class="kw3">End</span> <span class="kw2">Sub</span>
&nbsp;
<span class="kw2">Private</span> <span class="kw2">Sub</span> Form_Terminate()
&nbsp; &nbsp; Uninitialize
<span class="kw3">End</span> <span class="kw2">Sub</span></pre></td></tr></table></div></td></tr></tbody></table></div>Для статической линковки необходимо использовать более новый линкер (в своих примерах я использовал линкер из <b>Visual Studio 2010</b>), поскольку оригинальный имеет баги при использовании опции <b>/OPT:REF</b>, а также в секцию <b>VBCompiler</b> файла проекта (vbp) необходимо добавить параметры:<br />
Для EXE:<br />
<div class="codeblock"><table class="unknown"><thead><tr><td colspan="2" id="799180395"  class="head">Code</td></tr></thead><tbody><tr class="li1"><td><div id="799180395" style="height: 46px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
</pre></td><td class="de1"><pre class="de1">LinkSwitches= ..\Libs\msvcrt_winxp.obj ..\Libs\VBPng.lib -ENTRY:mainCRTStartup</pre></td></tr></table></div></td></tr></tbody></table></div>Для DLL:<br />
<div class="codeblock"><table class="unknown"><thead><tr><td colspan="2" id="31357423"  class="head">Code</td></tr></thead><tbody><tr class="li1"><td><div id="31357423" style="height: 46px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
</pre></td><td class="de1"><pre class="de1">LinkSwitches= ..\Libs\msvcrt_winxp.obj ..\Libs\VBPng.lib -ENTRY:VBDllMain -EXPORT:Initialize -EXPORT:Uninitialize</pre></td></tr></table></div></td></tr></tbody></table></div>В случае DLL, в скомпилированном виде необходимо сделать инициализацию, вызвав <b>Initialize</b> из себя же при первом запуске.<br />
<br />
<font size="5">Как это работает?</font><br />
<br />
Библиотека написана на C++. Принцип работы библиотеки основан на перехвате функций <b>OleLoadPictureEx</b> и <b>OleLoadPicture</b>. Данные функции не поддерживают загрузку PNG изображений, поэтому если загружается PNG файл, библиотека <b>VbPng</b> пытается загрузить файл с помощью <b>GDI+</b>. При успехе создается аналогичный <b>StdPicture</b> объект который и возвращается функцией. Для вызывающей стороны все это выглядит как-будто она работает с оригинальным объектом. Сам объект поддерживает интерфейсы <b>IPicture</b>, <b>IPictureDisp</b>, <b>IPersistStream</b>, <b>IConnectionPointContainer</b> (не поддерживает connection point'ы возвращает <b>E_NOTIMPL</b>), <b>IDispatch</b>, поэтому может быть присвоен <b>Object</b> переменной или к примеру быть сохраненным в <b>PropertyBag</b>.<br />
<br />
Перехватчик функций реализован в классе <b>CHooker</b>. Данный класс использует дизассемблер длин (<b>ldasm</b>) от <b>Ms-Rem</b> с небольшой доработкой. Доработка заключается в добавление флага <b>OP_REL32</b> к некоторым инструкциям (к примеру <b>JMP SHORT</b>), поскольку в оригинале на некоторых относительных инструкциях этот флаг отсутствовал. Для перехвата функции используется простейший метод сплайсинга при котором в начало функции всталяется инструкция <b>JMP</b> которая переводит поток исполнения на функцию-перехватчик. Поскольку в начале оригинальной функции содержатся инструкции которые мы перезаписываем, необходимо правильно перенести инструкции для того чтобы была возможность вызвать оригинальную функцию. При вызове метода <b>Hook</b> с помощью дизассемблера длин определяется целое количество инструкций которое будет перезаписано инструкцией <b>JMP</b> (5 байт). После этого выделяется временный буфер (с разрешением на исполнение данных) в который будут скопированы данные инструкции + <b>JMP</b> на инструкцию следующую за перезаписываемой. Это позволит, передав управление на этот буфер, вызвать оригинальную функцию как-будто перехвата не было. Тут существует одна сложность заключающаяся в том, что мы не можем просто так скопировать инструкции, поскольку существуют относительные инструкции типа <b>JMP</b>, <b>CALL</b>, <b>JNE</b> которые &quot;прыгают&quot; относительно своего адреса. Для определения типа инструкции как раз и служит флаг <b>OP_REL32</b> который показывает является ли инструкция относительной или нет. Другая сложность заключается в том что существуют &quot;короткие&quot; относительные инструкции которые &quot;прыгают&quot; в пределах 255 байт, а при переносе кода в буфер расстояние может значительно увеличится. Поэтому после определения количества перезаписываемых инструкций выделяется буфер размером как минимум чтобы обеспечить транслирование из коротких в длинные инструкции. После этого производится анализ каждой инструкции и при необходимости происходит корректировка смещения и типа. В конце буфера добавляется инструкция <b>JMP</b> со смещением на инструкцию следующую за последней перезаписаной. Наконец начало функции перезаписывается на безусловный <b>JMP</b> на функцию-перехватчик. <br />
<br />
<b>CHooker</b> объекты используют в качестве буфера кода кучу (<b>Heap</b>) с разрешением на исполнение, поэтому код является <b>DEP</b> безопасным. Куча автоматически создается при создании первого перехватчика и удаляется при уничтожении последнего. В проекте используются 2 таких объекта для перехвата 2-х функций <b>OleLoadPictureEx</b> и <b>OleLoadPicture</b>, с соответствующим перехватчиками <b>OleLoadPictureEx_user</b> и <b>OleLoadPicture_user</b>. В системах до <b>Windows 8</b> можно было перехватывать только одну <b>OleLoadPictureEx</b> функцию которая вызывается из <b>OleLoadPicture</b>, но начиная с <b>Windows 8</b> <b>OleLoadPicture</b> вызывает уже недокументированную <b>OleLoadPictureExt</b>, поэтому для обеспечения правильной работы некоторых контролов (к примеру <b>ImageList</b>) нужно перехватывать 2 этих функции. Конечно можно пробовать перехватывать <b>OleLoadPictureExt</b>, но эта функция недокументирована и не факт что в новых версиях <b>Microsoft</b> не изменят эту функцию на другую. В перехватчиках вызывается оригинальная функция и если вызов окончился неудачей вызывается наша реализация. Чтобы обеспечить возможность узнать был ли перехват уже осуществлен (к примеру подгруженная DLL уже перехватила и нет смысла делать это еще раз) используется переменна окружения <b>&quot;VBPng&quot;</b>.<br />
<br />
Основа библиотеки - класс <b>CPicture</b> который и реализует всю логику работы изображений. Данный класс создавался на основе реверс-инжиниринга библиотеки <b>oleaut32</b> некоторые функции возможно реализованы не точно. Данный класс позволяет загружать PNG изображения из <b>COM</b> потока (<b>IStream</b>), а также сохранять их в него. Библиотека ведет учет созданных объектов в глобальной переменной <b>g_lCountOfObject</b> для того чтобы обеспечить контроль при выгрузке библиотеки вызовом <b>CanUnloadNow</b>. В противном случае не было бы способа узнать можно ли выгрузить библиотеку или нет. Соответственно при выгрузке библиотеки которой пользуются активные объекты происходило бы падение.<br />
<br />
Загрузка изображения выполняется в методе <b>LoadFromStream</b>. Поскольку при загрузке из потока <b>GDI+</b> автоматически устанавливает указатель в начало, приходится создавать поток в коотром содержатся только данные <b>PNG</b> файла. Эта задача выполняется методом <b>CreatePngStream</b> в котором происходит также первичная валидация PNG чанков. Далее с помощью <b>GDI+</b> происходит создание объекта Bitmap из данных временного потока. Далее создается <b>DIB</b>-секция и в нее копируются данные PNG пикселей в формате <b>PixelFormat32bppPARGB</b>. Это позволяет выводить изображение с альфа-каналом посредством функции <b>AlphaBlend</b>, а также имеется возможность доступа к <b>GDI</b>-совместимому HBITMAP. <b>Далее</b>, если установлено свойство <b>KeepOriginalFormat</b> равным <b>true</b>, происходит сохранение PNG потока (это позволяет легко сохранять PNG файл без перекодировки). <br />
<br />
Второй по важности метод - это <b>Render</b>. Тут все просто, происходит подготовка координат для вывода изображения в <b>HIMETRIC</b> и происходит вывод с помощью <b>AlphaBlend</b>. Т.к. свойство <b>get_Attributes</b> возвращает <b>PICTURE_TRANSPARENT</b> то пользователь перед выводом изображения сам заботится о восстановлении фона за изображением.<br />
<br />
Метод <b>SaveAsFile</b> сохраняет изображение в поток. Тут все тоже самое только наоборот. Также стоит отметить что если использовалось сохранение оригинального формата то данные изображение берутся из сохраненного PNG потока. В противном случае создается временный <b>GDI+</b> битмап из пикселей <b>DIB</b>-секции, извлекается <b>CLSID</b> PNG кодека и происходит сохранение изображения во временный поток. Далее из этого потока данные копируются в поток назначения.<br />
<br />
Следующая группа методов это реализация интерфейса <b>IDispatch</b>. Поскольку данные о типе <b>IPicture</b> хранятся в стандартной библиотеке <b>stdole2.tlb</b> то в методе <b>GetTypeInfo</b> происходит загрузка этой библиотеки с извлечением нужного интерфейса типа через <b>ITypeLib::GetTypeInfoOfGuid</b>. Тоже самое относится к методу <b>GetIDsOfNames</b>, тут просто происходит транслирование вызова стандартному <b>ITypeInfo::GetIDsOfNames</b>. Метод Invoke реализован напрямую с проверкой параметров.<br />
<br />
Для того чтобы можно было статически прилинковать библиотеку к <b>VB6 EXE</b> файлу необходимо инициализировать сишный рантайм передачей управления на функцию <b>mainCRTStartup</b> и передать управление на метку <b>___vbaS</b>. Для этой цели служит файл <b>gostartup.asm</b> написаный на <b>fasm'е</b>. Для <b>EXE</b> файла выполняются строчки:<br />
<div class="codeblock"><table class="asm"><thead><tr><td colspan="2" id="22395416"  class="head">Assembler</td></tr></thead><tbody><tr class="li1"><td><div id="22395416" style="height: 78px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
</pre></td><td class="de1"><pre class="de1">_main<span class="sy1">:</span>
<span class="kw1">call</span> Initialize
<span class="kw1">jmp</span> ___vbaS</pre></td></tr></table></div></td></tr></tbody></table></div>Сишный рантайм вызывает функцию <b>main</b>, а она в свою очередь инициализирует библиотеку <b>VbPng</b>. Тут существует проблема со старым линкером, поскольку то ли из-за бага, то ли из-за чего то еще, ликер отбрасывает весь VB-шный импорт из результирующего файла при использовании опции <b>-OPT:REF</b>. Решается данная проблема просто - заменой линкера на современный.<br />
Для DLL выполняются похожие действия, только в этом случае необходимо указать в качестве точки входа <b>_VBDllMain</b>:<br />
<div class="codeblock"><table class="asm"><thead><tr><td colspan="2" id="426345141"  class="head">Assembler</td></tr></thead><tbody><tr class="li1"><td><div id="426345141" style="height: 206px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
5
6
7
8
9
10
11
</pre></td><td class="de1"><pre class="de1">_VBDllMain<span class="sy1">:</span>
&nbsp;
<span class="kw1">push</span> <span class="kw6">dword</span> <span class="br0">&#91;</span><span class="kw4">esp</span> <span class="sy1">+</span> <span class="nu0">12</span><span class="br0">&#93;</span>
<span class="kw1">push</span> <span class="kw6">dword</span> <span class="br0">&#91;</span><span class="kw4">esp</span> <span class="sy1">+</span> <span class="nu0">12</span><span class="br0">&#93;</span>
<span class="kw1">push</span> <span class="kw6">dword</span> <span class="br0">&#91;</span><span class="kw4">esp</span> <span class="sy1">+</span> <span class="nu0">12</span><span class="br0">&#93;</span>
&nbsp;
<span class="co1">; // Init CRT</span>
<span class="kw1">call</span> &nbsp;__DllMainCRTStartup@<span class="nu0">12</span>
&nbsp;
<span class="co1">; // Init runtime</span>
<span class="kw1">jmp</span> ___vbaS</pre></td></tr></table></div></td></tr></tbody></table></div>В этом случае сначала вызывается инициализации сишного рантайма, а затем происходит переход на функцию <b>DllMain ActiveX Dll</b>.<br />
<br />
Для олегчения работы в IDE был написан Add-in который автоматически загружает <b>VbPng.dll</b> для того чтобы было удобно работать с проектами. Для отключения библиотеки просто нужно отключить Add-in. Тут есть ньюанс, если есть активные PNG-изображения, то Add-in выгрузится, но <b>VbPng</b> нет, при этом покажется предупреждение. В любой момент можно будет включить Add-in, найти изображения, удалить их, и заново отключить Add-in, тогда DLL выгрузится.<br />
<br />
________________________________________ ________________________________________ _____________________________<br />
<br />
Некоторые контролы, к примеру <b>ListView</b>, не будут отображать альфа канал, поскольку отрисовывают себя не методом <b>Render</b>, а через <b>StretchBlt</b>, для них <b>premultiplied</b> фон будет черный. Это следует иметь в виду при работе с библиотекой. Также не поддерживаются уведомления <b>IPropertyNotifySink</b> (при желании можно реализовать). Ресурсы в <b>FRX</b> файлах и скомпилированных файлах также хранятся в PNG поэтому проекты не будут открываться и работать без библиотеки. Для комфортной работы рекомендуется установить Add-in с автоматическим запуском при загрузке IDE.<br />
<br />
В директории содержатся также несколько примеров работы:<ul><li><b>Test_EXE_Linked</b> - демонстрация <b>32bpp</b> PNG изображений на стандартных контролах с использованием статической линковки;</li>
<li><b>Test_EXE_Dll</b> - тоже самое только с использованием dll;</li>
<li><b>Test_AXDll</b> - ActiveX DLL библиотека с использованием PNG ресурсов на форме;</li>
<li><b>Test_SavePng</b> - пример сохранения изображения посредством <b>SavePicture</b>.</li>
</ul><br />
Также в директории содержатся PNG файлы, собраные мной еще давно посредством спутниковой рыбалки.<br />
<br />
Модуль слабо тестировался, поэтому возможны баги. Буду очень рад любым замечаниям, по мере возможности буду их исправлять.<br />
Всем спасибо за внимание, надеюсь модуль кому-то будет полезен.<br />
<br />
<a rel="nofollow noopener noreferrer" href="https://github.com/thetrik/VbPng" target="_blank" title="https://github.com/thetrik/VbPng">Проект на GitHub.</a><br />
<br />
<b>The trick</b>,<br />
2019.</div>

]]></content:encoded>
			<dc:creator>The trick</dc:creator>
			<guid isPermaLink="true">https://www.cyberforum.ru/blogs/354370/5639.html</guid>
		</item>
		<item>
			<title>Модуль для работы с многопоточностью на VB6</title>
			<link>https://www.cyberforum.ru/blogs/354370/5296.html</link>
			<pubDate>Tue, 12 Jun 2018 17:12:45 GMT</pubDate>
			<description>Всем привет! 
Представляю модуль для работы с многопоточностью на VB6 для Standard EXE проектов....</description>
			<content:encoded><![CDATA[<div>Всем привет!<br />
Представляю модуль для работы с многопоточностью на VB6 для Standard EXE проектов. Данный модуль разработан на основе <a href="https://www.cyberforum.ru/blogs/354370/blog3262.html">этого</a> решения в котором исправлены некоторые баги и добавлен дополнительный функционал. Модуль не требует никаких дополнительных зависимостей и библиотек типов, работает как в IDE (все функции работают в главном потоке) так и в скомпилированном виде.<br />
<br />
<div align="center"><iframe width="640" height="360" src="https://www.youtube.com/embed/D1-3PlAoEnk" frameborder="0" allowfullscreen></iframe></div><br />
Для начала работы с модулем нужно вызывать функцию <b>Initialize</b>, которая инициализирует данные необходимые для работы (инициализирует критические секции для монопольного доступа к куче хидеров и потокам маршалинга, модифицирует <b>VBHeader</b> (<a href="https://www.cyberforum.ru/blogs/354370/blog3262.html">тут</a> написано для чего), выделяет <b>TLS</b> слот для передачи параметров потоку).<br />
<br />
Основная функция создания потока - <b>vbCreateThread</b> которая является аналогом функции <a rel="nofollow noopener noreferrer" href="https://msdn.microsoft.com/en-us/library/windows/desktop/ms682453(v=vs.85).aspx" target="_blank" title="https://msdn.microsoft.com/en-us/library/windows/desktop/ms682453(v=vs.85).aspx">CreateThread</a>.<br />
<div class="codeblock"><table class="vb"><thead><tr><td colspan="2" id="723664775"  class="head">Visual Basic</td></tr></thead><tbody><tr class="li1"><td><div id="723664775" style="height: 158px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
5
6
7
8
</pre></td><td class="de1"><pre class="de1"><span class="co1">' // Create a new thread</span>
<span class="kw2">Public</span> <span class="kw2">Function</span> vbCreateThread(<span class="kw4">ByVal</span> lpThreadAttributes <span class="kw4">As</span> <span class="kw1">Long</span>, _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">ByVal</span> dwStackSize <span class="kw4">As</span> <span class="kw1">Long</span>, _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">ByVal</span> lpStartAddress <span class="kw4">As</span> <span class="kw1">Long</span>, _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">ByVal</span> lpParameter <span class="kw4">As</span> <span class="kw1">Long</span>, _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">ByVal</span> dwCreationFlags <span class="kw4">As</span> <span class="kw1">Long</span>, _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">ByRef</span> lpThreadId <span class="kw4">As</span> <span class="kw1">Long</span>, _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">Optional</span> <span class="kw4">ByVal</span> bIDEInSameThread <span class="kw4">As</span> <span class="kw1">Boolean</span> = <span class="kw5">True</span>) <span class="kw4">As</span> <span class="kw1">Long</span></pre></td></tr></table></div></td></tr></tbody></table></div>Функция создает поток и вызывает функцию переданную в параметре <b>lpStartAddress</b> с параметром <b>lpParameter</b>. <br />
В IDE вызов сводится к простому вызову по указателю реализованному через <a rel="nofollow noopener noreferrer" href="https://msdn.microsoft.com/en-us/library/windows/desktop/ms221473(v=vs.85).aspx" target="_blank" title="https://msdn.microsoft.com/en-us/library/windows/desktop/ms221473(v=vs.85).aspx">DispCallFunc</a>. В скомпилированном варианте данная функция работает по-другому. Т.к. для работы потока требуется инициализация проектно-специфичных данных, а также инициализация рантайма, параметры переданные в <b>lpStartAddress</b> и <b>lpParameter</b> временно сохраняются в куче посредством функции <b>PrepareData</b>, а поток создается в функции <b>ThreadProc</b>, которая занимается непосредственно инициализацией и вызовом уже пользовательской функции с пользовательским параметром. Данная функция создает копию структуры <b>VBHeader</b> через <b>CreateVBHeaderCopy</b> и изменяет данные размещения публичных переменных в структурах <b>VbPublicObjectDescriptor.lpPublicBytes, VbPublicObjectDescriptor.lpStaticBytes</b> (что не было сделано в предыдущем варианте) так чтобы глобальные переменные не затрагивались при инициализации. Далее <b>VBDllGetClassObject</b> вызывает функцию <b>FakeMain</b> (адрес которой записан в модифицированную структуру <b>VBHeader</b>). Для передачи пользовательских параметров используется <b>TLS</b> слот (т.к. функция <b>Main</b> не принимает параметры, подробности <a href="https://www.cyberforum.ru/blogs/354370/blog3262.html">тут</a>). В <b>FakeMain</b> уже параметры извлекаются из <b>TLS</b> и вызывается пользовательская процедура. Возвращаемое значение функции также передается обратно через <b>TLS</b>. Тут есть один интересный момент связанный с копией хидера, который был не учтен в предыдущей версии. Т.к. рантайм использует хидер после завершения потока (при <b>DLL_THREAD_DETACH</b>) мы не можем освободить хидер в процедуре <b>ThreadProc</b>, и произойдет утечка памяти. Для предотвращения утечки памяти используется куча фиксированного размера, хидеры не очищаются пока есть место в этой куче. Как только место заканчивается (а оно выделяется в функции <b>CreateVBHeaderCopy</b>) происходит очистка ресурсов. Первый <b>DWORD</b> хидера на самом деле хранит <b>ID</b> потока в котором был создан и в функции <b>FreeUnusedHeaders</b> происходит проверка всех хидеров в куче. Если поток завершен - место освобождается (хотя ID может повторятся, но это не играет особой роли т.к. в любом случае в куче будут свободные места и если хидер не освободился в одном случае то он будет освобожден позже). Из-за того что очистка может быть вызвана сразу из нескольких потоков доступ к очистке разделяется критической секцией <b>tLockHeap.tWinApiSection</b> и если какой-то поток уже занимается очисткой функция возвратит <b>True</b> что означает что вызывающий поток должен немного подождать и память будет доступна.<br />
<br />
Еще одной из возможностей модуля является возможность инициализации рантайма и проекта и вызова <b>callback</b>-фнкции. Это может пригодится для <b>сallback</b>-функций которые могут быть вызваны в контексте произвольного потока (к примеру <a rel="nofollow noopener noreferrer" href="https://msdn.microsoft.com/en-us/library/windows/desktop/aa385121(v=vs.85).aspx" target="_blank" title="https://msdn.microsoft.com/en-us/library/windows/desktop/aa385121(v=vs.85).aspx">InternetStatusCallback</a>). Для этого служат функции <b>InitCurrentThreadAndCallFunction</b> и <b>InitCurrentThreadAndCallFunctionIDEProc</b>. Первая функция используется в скомпилированном приложении и принимает адрес функции обратного вызова которая будет вызвана после инициализации рантайма, а также параметра который будет передан в эту функцию. В <b>callback</b>-процедуре передается адрес первого параметра чтобы потом в пользовательской процедуре ссылаться на него:<br />
<div class="codeblock"><table class="vb"><thead><tr><td colspan="2" id="2174669"  class="head">Visual Basic</td></tr></thead><tbody><tr class="li1"><td><div id="2174669" style="height: 254px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
5
6
7
8
9
10
11
12
13
14
</pre></td><td class="de1"><pre class="de1"><span class="co1">' // This function is used in compiled form</span>
<span class="kw2">Public</span> <span class="kw2">Function</span> CallbackProc( _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">ByVal</span> lThreadId <span class="kw4">As</span> <span class="kw1">Long</span>, _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">ByVal</span> sKey <span class="kw4">As</span> <span class="kw1">String</span>, _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">ByVal</span> fTimeFromLastTick <span class="kw4">As</span> <span class="kw1">Single</span>) <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="co1">' // Init runtime and call CallBackProc_user with VarPtr(lThreadId) parameter</span>
 &nbsp; &nbsp;InitCurrentThreadAndCallFunction AddressOf CallBackProc_user, VarPtr(lThreadId), CallbackProc
<span class="kw3">End</span> <span class="kw2">Function</span>
&nbsp;
<span class="co1">' // Callback function is called by runtime/window proc (in IDE)</span>
<span class="kw2">Public</span> <span class="kw2">Function</span> CallBackProc_user( _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">ByRef</span> tParam <span class="kw4">As</span> tCallbackParams) <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp;
<span class="kw3">End</span> <span class="kw2">Function</span></pre></td></tr></table></div></td></tr></tbody></table></div><b>CallBackProc_user</b> - будет вызвана уже с инициализированным рантаймом. <br />
<br />
Для работы в <b>IDE</b> данная функция не подойдет, потому что в <b>IDE</b> все работает в главном потоке. Для отладки в <b>IDE</b> используется функция <b>InitCurrentThreadAndCallFunctionIDEProc</b> которая возвращает адрес ассемблерного переходника который транслирует вызов в главный поток и вызовет пользовательскую функцию в контексте главного потока. Данная функция принимает адрес пользовательской <b>callback</b>-функции и размер параметров в байтах. В качестве параметра пользовательской функции она всегда передает адрес первого параметра. Немного расскажу о работе этого механизма в <b>IDE</b>. Для трансляции вызова из вызывающего потока в главный поток используется <b>message-only</b> окно. Данное окно создается посредством вызова функции <b>InitializeMessageWindow</b>. При первом вызове создается <b>WindowProc</b> процедура со следующим кодом:<br />
<div class="codeblock"><table class="asm"><thead><tr><td colspan="2" id="461808414"  class="head">Assembler</td></tr></thead><tbody><tr class="li1"><td><div id="461808414" style="height: 126px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
5
6
</pre></td><td class="de1"><pre class="de1">&nbsp; &nbsp; <span class="kw1">CMP</span> <span class="kw6">DWORD</span> <span class="br0">&#91;</span><span class="kw4">ESP</span><span class="sy1">+</span><span class="nu0">8</span><span class="br0">&#93;</span><span class="sy1">,</span> WM_ONCALLBACK
&nbsp; &nbsp; <span class="kw1">JE</span> SHORT L
&nbsp; &nbsp; <span class="kw1">JMP</span> DefWindowProcW
&nbsp;L<span class="sy1">:</span> <span class="kw1">PUSH</span> <span class="kw6">DWORD</span> PTR <span class="kw4">SS</span><span class="sy1">:</span><span class="br0">&#91;</span><span class="kw4">ESP</span><span class="sy1">+</span><span class="nu0">10</span><span class="br0">&#93;</span>
&nbsp; &nbsp; <span class="kw1">CALL</span> <span class="kw6">DWORD</span> PTR <span class="kw4">SS</span><span class="sy1">:</span><span class="br0">&#91;</span><span class="kw4">ESP</span><span class="sy1">+</span><span class="nu0">10</span><span class="br0">&#93;</span>
&nbsp; &nbsp; RETN <span class="nu0">10</span></pre></td></tr></table></div></td></tr></tbody></table></div>Как видно из кода данная процедура &quot;слушает&quot; сообщение <b>WM_ONCALLBACK</b> которое содержит в параметре <b>wParam</b> - адрес функции, а в параметре <b>lParam</b> параметры. При получении этого сообщение она вызывает данную процедуру с данным параметром, остальные сообщения игнорируются. Данное сообщение отправляется как-раз ассемблерным переходником из <b>caller</b>-потока.<br />
Далее создается окно и хендл этого окна и хендл кучи сохраняются в данных класса окна. Это используется для устранения утечки памяти в IDE, т.к. если класс один раз зарегистрирован то потом данные параметры можно получить в любой сессии отладки. Сама <b>callback</b>-функция генерируется в <b>InitCurrentThreadAndCallFunctionIDEProc</b>, но сначала проверяется не была ли уже создана подобная процедура (чтобы не создавать одинаковые переходники). Сам переходник имеет следющий код:<br />
<div class="codeblock"><table class="asm"><thead><tr><td colspan="2" id="207429048"  class="head">Assembler</td></tr></thead><tbody><tr class="li1"><td><div id="207429048" style="height: 142px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
5
6
7
</pre></td><td class="de1"><pre class="de1"><span class="kw1">LEA</span> <span class="kw4">EAX</span><span class="sy1">,</span> <span class="br0">&#91;</span><span class="kw4">ESP</span><span class="sy1">+</span><span class="nu0">4</span><span class="br0">&#93;</span>
<span class="kw1">PUSH</span> <span class="kw4">EAX</span>
<span class="kw1">PUSH</span> pfnCallback
<span class="kw1">PUSH</span> WM_ONCALLBACK
<span class="kw1">PUSH</span> hMsgWindow
<span class="kw1">Call</span> SendMessageW
RETN lParametersSize</pre></td></tr></table></div></td></tr></tbody></table></div>Как видно из ода, что при вызове <b>callback</b>-функции вызов транслируется через <b>SendMessage</b> в главный поток. Параметр <b>lParametersSize</b> используется для правильного восстановления стека.<br />
<br />
Следующая особенность модуля - это создание объектов в отдельном потоке причем создавать можно как приватные объекты (для этого используется метод основанный на коде модуля <a rel="nofollow noopener noreferrer" href="http://bbs.vbstreets.ru/viewtopic.php?f=28&amp;t=43201" target="_blank" title="http://bbs.vbstreets.ru/viewtopic.php?f=28&amp;t=43201">NameBasedObjectFactory by firehacker</a>). Для создания проектных классов используется функция <b>CreatePrivateObjectByNameInNewThread</b> для <b>ActiveX</b>-публичных классов <b>CreateActiveXObjectInNewThread</b> и <b>CreateActiveXObjectInNewThread2</b>. Прежде чем создавать экземпляры проектных классов нужно сначала разрешить маршалинг данных объектов посредством вызова функции <b>EnablePrivateMarshaling</b>. Данные функции принимают идентификатор класса (<b>ProgID/CLSID</b> для <b>ActiveX</b> и имя для проектных классов), идентификатор интерфейса (по умолчанию используется <b>IDispatch/Object</b>). При успешном вызове функции возвращают отмаршаленый объект и <b>ID</b> асинхронного вызова. Для скомпилированного варианта это <b>ID</b> потока для <b>IDE</b> - указатель на объект. Объекты создаются и &quot;живут&quot; в функции <b>ActiveXThreadProc</b>. Жизнь объектов контролируется через счетчик ссылок (когда он равен 1 значит только <b>ActiveXThreadProc</b> ссылается на объект и можно его удалять и завершать поток. <br />
Вызывать методы можно синхронно - просто вызывать метод как обычно, либо асинхронно - используя процедуру <b>AsynchDispMethodCall</b>. Данная процедура принимает <b>ID</b> асинхронного вызова, имя метода, тип вызова, объект который получит уведомление о вызове, имя метода уведомления и список параметров. Процедура копирует параметры во временную область, маршалит объект уведомления и отправляет данные потоку объекта через <b>WM_ASYNCH_CALL</b>. Стоит отметить что здесь не поддерживается маршалинг параметров, поэтому следует с осторожностью передавать ссылки на объекты. Если нужно отмаршалить объектную ссылку то следует использовать синхронный метод для маршалинга объектов и далее вызвать асинхронный метод. Процедура возвращается немедленно. В потоке <b>ActiveXThreadProc</b> данные извлекаются и выполняется синхронный вызов посредством <b>MakeAsynchCall</b>. Тут все просто, вызывается <b>CallByName</b> для объекта потока и <b>CallByName</b> для уведомления. Метод уведомления имеет следующий прототип:<br />
<div class="codeblock"><table class="vb"><thead><tr><td colspan="2" id="688155086"  class="head">Visual Basic</td></tr></thead><tbody><tr class="li1"><td><div id="688155086" style="height: 46px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
</pre></td><td class="de1"><pre class="de1"><span class="kw2">Public</span> <span class="kw2">Sub</span> CallBack(<span class="kw4">ByVal</span> vRet <span class="kw4">As</span> <span class="kw1">Variant</span>)</pre></td></tr></table></div></td></tr></tbody></table></div>, где <b>vRet</b> принимает возвращаемое значение метода.<br />
<br />
Следующие функции предназначены для маршалинга: <b>Marshal</b>, <b>Marshal2</b>, <b>UnMarshal</b>, <b>FreeMarshalData</b>. Первая создает информацию о маршалинге (<b>Proxy</b>) интерфейса и заносит ее в поток который возвращает. Принимает в параметре <b>pInterface</b> идентификатор интерфейса (по умолчанию <b>IDispatch/Object</b>). Функция <b>UnMarshal</b> наоборот принимает поток и на основе информации создает <b>Proxy</b>-объект. Опционально можно освободить объект потока. <b>Marshal2</b> делает тоже самое что и <b>Marshal</b> за исключением того что она позволяет создавать <b>Proxy</b>-объект множество раз в разных потоках. <b>FreeMarshalData</b> соответственно освобождает данные и поток.<br />
Если к примеру требуется перенести ссылку на объект между двумя потоками то достаточно вызвать пару <b>Marshal</b>/<b>UnMarshal</b> соответственно в потоке создавшем объект и потоке принимающем ссылку. В другом случае если к примеру есть один глобальный объект и нужно передать ссылку на него во множество потоков (к примеру объект логирования) то в потоке объекта вызывается <b>Marshal2</b>, а в клиентских потоках вызывается <b>UnMarshal</b> с параметром <b>bReleaseStream</b> равным <b>False</b>. Когда данные больше не нужны вызывается <b>FreeMarshalData</b>.<br />
<br />
Функция <b>WaitForObjectThreadCompletion</b> предназначена для ожидания завершения объектного потока и принимает <b>ID</b> асинхронного вызова. Эту функцию желательно вызывать всегда при завершении основного процесса, поскольку объектный поток так или иначе может взаимодействовать с основным потоком и его объектами (к примеру если объектный поток имеет маршаленую ссылку на интерфейс основного потока). <br />
<br />
Функция <b>SuspendResume</b> предназначена для приостановления/возобновления объектного потока. <b>bSuspend</b> определяет усыпить либо возобновить поток.<br />
<br />
Помимо модуля также в архиве есть несколько примеров работы с ним:<ol style="list-style-type: decimal"><li><b>Callback</b> - проект в котором продемонстрирована работа с <b>callback</b>-функцией вызываемой из разных потоков. Также там содержится дополнительный проект нативной dll (на VB6) которая вызывает периодически функцию в разных потоках;</li>
<li><b>JuliaSet</b> - генерация фрактала <b>Julia</b> параллельно в нескольких потоках (задается пользователем);</li>
<li><b>CopyProgress</b> - Копирование папки в отдельном потоке с отображением прогресса копирования;</li>
<li><b>PublicMarshaling</b> - Создание публичных объектов (<b>Dictionary</b>) в разных потоках и вызов их методов (синхронно/асинхронно);</li>
<li><b>PrivateMarshaling</b> - Создание приватных объектов в разных потоках и вызов их методов (синхронно/асинхронно);</li>
<li><b>MarshalUserInterface</b> - Создание приватных объектов в разных потоках и вызов их методов (синхронно/асинхронно) на основе пользовательских интерфейсов (содержит tlb и Reg-Free манифест). </li>
</ol><br />
<a rel="nofollow noopener noreferrer" href="https://github.com/thetrik/VbTrickThreading" target="_blank" title="https://github.com/thetrik/VbTrickThreading">Проект на GitHub.</a><br />
<br />
Модуль слабо тестировался, поэтому возможны баги. Буду очень рад любым замечаниям, по мере возможности буду их исправлять.<br />
Всем спасибо за внимание!</div>

]]></content:encoded>
			<dc:creator>The trick</dc:creator>
			<guid isPermaLink="true">https://www.cyberforum.ru/blogs/354370/5296.html</guid>
		</item>
		<item>
			<title>Direct2D, DirectWrite, WIC</title>
			<link>https://www.cyberforum.ru/blogs/354370/5273.html</link>
			<pubDate>Sat, 26 May 2018 08:35:39 GMT</pubDate>
			<description>Всем привет. 
 
Представляю библиотеки типов для работы с Direct2D, DirectWrite и Windows Imaging...</description>
			<content:encoded><![CDATA[<div>Всем привет.<br />
<br />
Представляю библиотеки типов для работы с Direct2D, DirectWrite и Windows Imaging Component. Также в архиве содержатся вспомогательные модули и примеры работы с данными библиотеками типов. Библиотеки довольно &quot;сырые&quot; поэтому будут дополнятся (новые сущности доступные в Win8 и позже, вспомогательные функции и т.д.) и исправляться различные баги. <br />
Для работы некоторых методов и интерфейсов необходимо подключить библиотеку <a rel="nofollow noopener noreferrer" href="http://www.vbforums.com/showthread.php?786079-VB6-Modern-Shell-Interface-Type-Library-oleexp-tlb" target="_blank" title="http://www.vbforums.com/showthread.php?786079-VB6-Modern-Shell-Interface-Type-Library-oleexp-tlb">OLEEXP</a>.<br />
<br />
<a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=4827&amp;d=1527323701" rel="Lightbox" id="attachment4827" ><img src="https://www.cyberforum.ru/blog_attachment.php?attachmentid=4827&amp;thumb=1&amp;d=1527323701" class="thumbnail" border="0" alt="Нажмите на изображение для увеличения
Название: b9ec1c30dcf49cb6423b8d74ecef7112.png
Просмотров: 1905
Размер:	19.5 Кб
ID:	4827" style="margin: 5px" /></a><a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=4828&amp;d=1527323701" rel="Lightbox" id="attachment4828" ><img src="https://www.cyberforum.ru/blog_attachment.php?attachmentid=4828&amp;thumb=1&amp;d=1527323701" class="thumbnail" border="0" alt="Нажмите на изображение для увеличения
Название: 77b5fc308794a6a4ddd6228c9771bcfc.png
Просмотров: 1244
Размер:	116.9 Кб
ID:	4828" style="margin: 5px" /></a><a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=4829&amp;d=1527323701" rel="Lightbox" id="attachment4829" ><img src="https://www.cyberforum.ru/blog_attachment.php?attachmentid=4829&amp;thumb=1&amp;d=1527323701" class="thumbnail" border="0" alt="Нажмите на изображение для увеличения
Название: 9742efcf1e884b6b6395ba5e195d4712.png
Просмотров: 1074
Размер:	11.6 Кб
ID:	4829" style="margin: 5px" /></a><a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=4830&amp;d=1527323701" rel="Lightbox" id="attachment4830" ><img src="https://www.cyberforum.ru/blog_attachment.php?attachmentid=4830&amp;thumb=1&amp;d=1527323701" class="thumbnail" border="0" alt="Нажмите на изображение для увеличения
Название: b41b563f25e1aafee8da59caa468f132.png
Просмотров: 1113
Размер:	48.3 Кб
ID:	4830" style="margin: 5px" /></a></div>


<!-- attachments -->
	<div style="margin-top:10px">

		
		
		
		
			<fieldset class="fieldset">
				<legend>Вложения</legend>
				<table cellpadding="0" cellspacing="3" border="0">
				<tr>
	<td><img loading="lazy" decoding="async" class="inlineimg" src="http://www.cyberforum.ru//cyberstatic.net/images/attach/zip.gif" alt="Тип файла: zip" width="16" height="16" border="0" style="vertical-align:baseline" /></td>
	<td><a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=7952&amp;d=1677767330">Direct2D_V2_10.zip</a> (1.01 Мб, 272 просмотров)</td>
</tr>
				</table>
			</fieldset>
		

	</div>
<!-- / attachments -->
]]></content:encoded>
			<dc:creator>The trick</dc:creator>
			<guid isPermaLink="true">https://www.cyberforum.ru/blogs/354370/5273.html</guid>
		</item>
		<item>
			<title><![CDATA["Hello world" в машинных кодах.]]></title>
			<link>https://www.cyberforum.ru/blogs/354370/4505.html</link>
			<pubDate>Fri, 09 Dec 2016 01:47:02 GMT</pubDate>
			<description>Всем привет. Как известно большинство из нас создают программы используя языки высокого уровня,...</description>
			<content:encoded><![CDATA[<div>Всем привет. Как известно большинство из нас создают программы используя языки высокого уровня, некоторые также используют ассемблер. Сегодня мы с вами напишем программу используя только HEX редактор. Подразумевается что читатель знает строение исполняемых файлов хотя бы поверхностно, поэтому я не буду углубляться в детали, к тому же я уже приводил небольшой обзор загрузчика EXE файлов на VB6. Итак поехали...<br />
Для начала определимся с функциональностью приложения и используемыми инструментами. Для простоты создадим 64-битное приложение которое показывает сообщение &quot;Hello World!&quot; и завершает свою работу. В качестве HEX - редактора будем использовать 010 Editor.<br />
Для начала создадим схему чтобы определить все смещения и размеры внутри PE файла. Для начала определимся с количеством секций. Т.к. наше приложение будет вызывать внешние API функции, то нам нужна будет таблица импорта (вариант с получением через <b>PEB</b> я не рассматриваю). Для показа сообщения мы будем использовать функцию <b>MassageBoxA</b>, а для завершения приложения <b>ExitProcess</b>, т.е. нам уже нужно 2 библиотеки - <b>kernel32.dll</b> и <b>user32.dll</b>. Давайте подсчитаем размер таблицы импорта. Для 2-х библиотек нужно разместить 3 структуры <b>IMAGE_IMPORT_DESCRIPTOR</b> (две с данными и одну забитую нулями), получаем <i>0x14 * 3 = 0x3C</i>. Также нужно место для размещения имен библиотек в формате <b>ASCIIZ</b>: <i>0x3C + sizeof(&quot;kernel32.dll&quot;) + sizeof(&quot;user32.dll&quot;) = 0x54</i>. Далее нужно расчитать размеры таблиц имен и таблиц адресов, по одной функции из каждой библиотеки получается <i>0x54 + sizeof(IMAGE_THUNK_DATA) * 4 + sizeof(IMAGE_THUNK_DATA) * 2 = 0x84</i>. Теперь прибавляем размер имен функций: <i>0x84 + sizeof(&quot;MessageBoxA&quot;) + 2 + sizeof(&quot;ExitProcess&quot;) + 2 = 0xA0</i>. Итак таблица импорта занимает у нас <i>0xA0</i> байт. Первую секцию разместим по первому доступному RVA выровненному на размер страницы, т.е. <b>0x1000</b>. Таблицу импорта разместим в самом начале секции (не забывая что данные должны быть в <b>little-endian</b> формате (младший байт по младшему адресу)):<br />
<div class="codeblock"><table class="unknown"><thead><tr><td colspan="2" id="449087133"  class="head">Code</td></tr></thead><tbody><tr class="li1"><td><div id="449087133" style="height: 350px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
</pre></td><td class="de1"><pre class="de1">+-----------------+----------+------------------------+---------------------------------------------+
| метка &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; | смещение | &nbsp; &nbsp; &nbsp; &nbsp; данные &nbsp; &nbsp; &nbsp; &nbsp; | &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; описание &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;|
+-----------------+----------+------------------------+---------------------------------------------+
| таблица импорта | &nbsp; 0x00 &nbsp; | 0x00001054 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; | OriginalFirstThunk -----------------------+ |
| &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; | &nbsp; 0x04 &nbsp; | 0x00000000 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; | TimeDateStamp &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; | |
| &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; | &nbsp; 0x08 &nbsp; | 0x00000000 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; | ForwarderChain &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;| |
| &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; | &nbsp; 0x0c &nbsp; | 0x0000103c &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; | Name ------------------+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;| |
| &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; | &nbsp; 0x10 &nbsp; | 0x00001074 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; | FirstThunk ------------+---------------+ &nbsp;| |
| &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; | &nbsp; 0x14 &nbsp; | 0x00001064 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; | OriginalFirstThunk ----+--------------+| &nbsp;| |
| &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; | &nbsp; 0x18 &nbsp; | 0x00000000 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; | TimeDateStamp &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;| &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;|| &nbsp;| |
| &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; | &nbsp; 0x1c &nbsp; | 0x00000000 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; | ForwarderChain &nbsp; &nbsp; &nbsp; &nbsp; | &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;|| &nbsp;| |
| &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; | &nbsp; 0x20 &nbsp; | 0x00001049 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; | Name ----------------+ | &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;|| &nbsp;| |
| &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; | &nbsp; 0x24 &nbsp; | 0x0000107c &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; | FirstThunk ----------+-+-----------+ &nbsp;|| &nbsp;| |
| &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; | &nbsp; 0x28 &nbsp; | 0x00000000 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; | OriginalFirstThunk &nbsp; | | &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; | &nbsp;|| &nbsp;| |
| &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; | &nbsp; 0x2c &nbsp; | 0x00000000 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; | TimeDateStamp &nbsp; &nbsp; &nbsp; &nbsp;| | &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; | &nbsp;|| &nbsp;| |
| &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; | &nbsp; 0x30 &nbsp; | 0x00000000 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; | ForwarderChain &nbsp; &nbsp; &nbsp; | | &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; | &nbsp;|| &nbsp;| |
| &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; | &nbsp; 0x34 &nbsp; | 0x00000000 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; | Name &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; | | &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; | &nbsp;|| &nbsp;| |
| &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; | &nbsp; 0x38 &nbsp; | 0x00000000 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; | FirstThunk &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; | | &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; | &nbsp;|| &nbsp;| |
| имена библиотек | &nbsp; 0x3c &nbsp; | kernel32.dll, <span class="nu0">0</span> &nbsp; &nbsp; &nbsp; &nbsp;| &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;----+-+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; | &nbsp;|| &nbsp;| |
| &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; | &nbsp; 0x49 &nbsp; | user32.dll, <span class="nu0">0</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;| &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;----+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; | &nbsp;|| &nbsp;| |
| таблица имен <span class="nu0">1</span> &nbsp;| &nbsp; 0x54 &nbsp; | 0x0000000000001084 &nbsp; &nbsp; | IMAGE_THUNK_DATA ---------------+ &nbsp;| &nbsp;||&lt;-+ |
| &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; | &nbsp; 0x5c &nbsp; | 0x0000000000000000 &nbsp; &nbsp; | IMAGE_THUNK_DATA завершающая &nbsp; &nbsp;| &nbsp;| &nbsp;|| &nbsp; &nbsp;|
| таблица имен <span class="nu0">2</span> &nbsp;| &nbsp; 0x64 &nbsp; | 0x0000000000001092 &nbsp; &nbsp; | IMAGE_THUNK_DATA ------------+ &nbsp;| &nbsp;|&lt;-+| &nbsp; &nbsp;|
| &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; | &nbsp; 0x6c &nbsp; | 0x0000000000000000 &nbsp; &nbsp; | IMAGE_THUNK_DATA завершающая | &nbsp;| &nbsp;| &nbsp; | &nbsp; &nbsp;|
| таблица адресов | &nbsp; 0x74 &nbsp; | 0x0000000000001084 &nbsp; &nbsp; | IMAGE_THUNK_DATA -+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;| &nbsp;| &nbsp;|&lt;--+ &nbsp; &nbsp;|
| &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; | &nbsp; 0x7c &nbsp; | 0x0000000000001092 &nbsp; &nbsp; | IMAGE_THUNK_DATA &nbsp;|--+ &nbsp; &nbsp; &nbsp; | &nbsp;|&lt;-+ &nbsp; &nbsp; &nbsp; &nbsp;|
| &nbsp; &nbsp; &nbsp;имя <span class="nu0">1</span> &nbsp; &nbsp; &nbsp;| &nbsp; 0x84 &nbsp; | 0x0000, ExitProcess, <span class="nu0">0</span> | &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;-+ &nbsp;| &nbsp; &nbsp; &nbsp; |&lt;-+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; |
| &nbsp; &nbsp; &nbsp;имя <span class="nu0">2</span> &nbsp; &nbsp; &nbsp;| &nbsp; 0x92 &nbsp; | 0x0000, MessageBoxA, <span class="nu0">0</span> | &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;----+ &nbsp; &nbsp; &lt;-+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;|
+-----------------+----------+------------------------+---------------------------------------------+</pre></td></tr></table></div></td></tr></tbody></table></div>Вставляем эти данные в новый файл - это будет у нас таблица импорта:<br />
<div class="codeblock"><table class="unknown"><thead><tr><td colspan="2" id="573006370"  class="head">Code</td></tr></thead><tbody><tr class="li1"><td><div id="573006370" style="height: 190px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
5
6
7
8
9
10
</pre></td><td class="de1"><pre class="de1"><span class="nu0">54</span> <span class="nu0">10</span> 00 00 00 00 00 00 00 00 00 00 3C <span class="nu0">10</span> 00 00 &nbsp;T...........&lt;... 
<span class="nu0">74</span> <span class="nu0">10</span> 00 00 <span class="nu0">64</span> <span class="nu0">10</span> 00 00 00 00 00 00 00 00 00 00 &nbsp;t...d........... 
<span class="nu0">49</span> <span class="nu0">10</span> 00 00 7C <span class="nu0">10</span> 00 00 00 00 00 00 00 00 00 00 &nbsp;I...|........... 
00 00 00 00 00 00 00 00 00 00 00 00 6B <span class="nu0">65</span> <span class="nu0">72</span> 6E &nbsp;............kern 
<span class="nu0">65</span> 6C <span class="nu0">33</span> <span class="nu0">32</span> 2E <span class="nu0">64</span> 6C 6C 00 <span class="nu0">75</span> <span class="nu0">73</span> <span class="nu0">65</span> <span class="nu0">72</span> <span class="nu0">33</span> <span class="nu0">32</span> 2E &nbsp;el32.dll.user32. 
<span class="nu0">64</span> 6C 6C 00 <span class="nu0">84</span> <span class="nu0">10</span> 00 00 00 00 00 00 00 00 00 00 &nbsp;dll.„........... 
00 00 00 00 <span class="nu0">92</span> <span class="nu0">10</span> 00 00 00 00 00 00 00 00 00 00 &nbsp;....’........... 
00 00 00 00 <span class="nu0">84</span> <span class="nu0">10</span> 00 00 00 00 00 00 <span class="nu0">92</span> <span class="nu0">10</span> 00 00 &nbsp;....„.......’... 
00 00 00 00 00 00 <span class="nu0">45</span> <span class="nu0">78</span> <span class="nu0">69</span> <span class="nu0">74</span> <span class="nu0">50</span> <span class="nu0">72</span> 6F <span class="nu0">63</span> <span class="nu0">65</span> <span class="nu0">73</span> &nbsp;......ExitProces 
<span class="nu0">73</span> 00 00 00 4D <span class="nu0">65</span> <span class="nu0">73</span> <span class="nu0">73</span> <span class="nu0">61</span> <span class="nu0">67</span> <span class="nu0">65</span> <span class="nu0">42</span> 6F <span class="nu0">78</span> <span class="nu0">41</span> 00 &nbsp;s...MessageBoxA.</pre></td></tr></table></div></td></tr></tbody></table></div>Для отображения сообщения нужно само сообщение где-то хранить. Будем хранить его непосредственно за таблицей импорта т.е. по смещению <i>0xA0</i>:<br />
<div class="codeblock"><table class="unknown"><thead><tr><td colspan="2" id="235772341"  class="head">Code</td></tr></thead><tbody><tr class="li1"><td><div id="235772341" style="height: 46px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
</pre></td><td class="de1"><pre class="de1">00A0h: <span class="nu0">48</span> <span class="nu0">65</span> 6C 6C 6F <span class="nu0">20</span> <span class="nu0">77</span> 6F <span class="nu0">72</span> 6C <span class="nu0">64</span> <span class="nu0">21</span> 00 &nbsp;Hello world!.</pre></td></tr></table></div></td></tr></tbody></table></div>Сам код у нас будет начинаться сразу после данного сообщения, т.е. по смещению <i>0xA0 + sizeof(&quot;Hello world!&quot;) = 0xAD</i>. Все API функции в x64 используют одноименное соглашение: первые 4 параметра передаются в регистрах RCX, RDX, R8, R9, остальные в стека, также в стеке выделяется 32 байта теневой области. Также стек должен быть выровнен на 16 байтовую границу. Теперь используя относительное смещение напишем код на ассемблере, который далее мы переведем непосредственно в машинный код:<br />
<div class="codeblock"><table class="asm"><thead><tr><td colspan="2" id="279434546"  class="head">Assembler</td></tr></thead><tbody><tr class="li1"><td><div id="279434546" style="height: 190px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
5
6
7
8
9
10
</pre></td><td class="de1"><pre class="de1">MSG <span class="kw5">db</span> <span class="st0">&quot;Hello world!&quot;</span><span class="sy1">,</span> <span class="nu0">0</span>
&nbsp; &nbsp; 
<span class="kw1">sub</span> <span class="kw4">RSP</span><span class="sy1">,</span> <span class="nu0">0x28</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">; Резервируем теневую область</span>
<span class="kw1">mov</span> <span class="kw4">R9</span><span class="sy1">,</span> <span class="nu0">0x00000040</span> &nbsp; &nbsp; &nbsp;<span class="co1">; MB_ICONINFORMATION</span>
<span class="kw1">xor</span> <span class="kw4">R8</span><span class="sy1">,</span> <span class="kw4">R8</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">; lpCaption = NULL</span>
<span class="kw1">lea</span> <span class="kw4">RDX</span><span class="sy1">,</span> <span class="br0">&#91;</span>MSG<span class="br0">&#93;</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">; lpText = 'Hello world!'</span>
<span class="kw1">xor</span> <span class="kw4">RCX</span><span class="sy1">,</span> <span class="kw4">RCX</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">; HWND = NULL</span>
<span class="kw1">Call</span> MessageBoxA
<span class="kw1">xor</span> <span class="kw4">RCX</span><span class="sy1">,</span> <span class="kw4">RCX</span>
<span class="kw1">Call</span> ExitProcess</pre></td></tr></table></div></td></tr></tbody></table></div>Т.к. в x64 используется RIP адресация (все смещения считаются отнгосительно адреса следующей команды) то немного перепишем код с использованием меток:<br />
<div class="codeblock"><table class="asm"><thead><tr><td colspan="2" id="293591994"  class="head">Assembler</td></tr></thead><tbody><tr class="li1"><td><div id="293591994" style="height: 206px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
5
6
7
8
9
10
11
</pre></td><td class="de1"><pre class="de1">MSG <span class="kw5">db</span> <span class="st0">&quot;Hello world!&quot;</span><span class="sy1">,</span> <span class="nu0">0</span>
&nbsp; &nbsp; 
<span class="kw1">sub</span> <span class="kw4">RSP</span><span class="sy1">,</span> <span class="nu0">0x28</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">; Резервируем теневую область</span>
<span class="kw1">mov</span> <span class="kw4">R9</span><span class="sy1">,</span> <span class="nu0">0x00000040</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">; MB_ICONINFORMATION</span>
<span class="kw1">xor</span> <span class="kw4">R8</span><span class="sy1">,</span> <span class="kw4">R8</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">; lpCaption = NULL</span>
<span class="kw1">lea</span> <span class="kw4">RDX</span><span class="sy1">,</span> <span class="br0">&#91;</span>RIP <span class="sy1">+</span> <span class="br0">&#40;</span>MSG <span class="sy1">-</span> L1<span class="br0">&#41;</span><span class="br0">&#93;</span> &nbsp; &nbsp; <span class="co1">; lpText = 'Hello world!'</span>
L1<span class="sy1">:</span> <span class="kw1">xor</span> <span class="kw4">RCX</span><span class="sy1">,</span> <span class="kw4">RCX</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">; HWND = NULL</span>
<span class="kw1">Call</span> <span class="br0">&#91;</span>RIP <span class="sy1">+</span> <span class="br0">&#40;</span>MessageBoxA <span class="sy1">-</span> L2<span class="br0">&#41;</span><span class="br0">&#93;</span>
L2<span class="sy1">:</span> <span class="kw1">xor</span> <span class="kw4">RCX</span><span class="sy1">,</span> <span class="kw4">RCX</span>
<span class="kw1">Call</span> <span class="br0">&#91;</span>RIP <span class="sy1">+</span> <span class="br0">&#40;</span>ExitProcess <span class="sy1">-</span> L3<span class="br0">&#41;</span><span class="br0">&#93;</span>
L3<span class="sy1">:</span></pre></td></tr></table></div></td></tr></tbody></table></div>Теперь приступим непосредственно к трансляции в машинный код. Для этого я буду использовать вот <a rel="nofollow noopener noreferrer" href="http://ref.x86asm.net/coder64.html" target="_blank" title="http://ref.x86asm.net/coder64.html">эту таблицу</a>. Первая инструкция <i>sub RSP, 0x28</i> оперирует с 64 битным регистром <b>RSP</b> поэтому опкод должен содержать префикс <b>REX.W</b>(<i>0x48</i>), далее смотрим по списку инструкцию <b>SUB</b> чтобы первым операндом был 64 битный регистр, а вторым непосредственное однобайтовое значение и это - <i>0x83</i>. теперь нужно определится с <b>mod/rm</b> байтом. Т.к. мы используем непосредственно регистр <b>RSP</b> то поле <b>mod</b> будет равно <i>0b11</i>, а поле <b>r/m</b> будет равно <i>0b100</i> что соответствует регистрам <b>AH/SP/ESP/RSP</b>. В таблице указано что для нашей команды поле <b>Register/ Opcode Field</b> должно быть равно 5 (<i>0b101</i> в двоичной форме). Собираем все вместе, и получаем <i>0b11-101-100</i> = <i>0xEC</i>. Непосредственный операнд идет сразу же после <b>mod/rm</b> байта, в итоге полный код команды <i>48 83 EC 28</i>. Следующая инструкция <b>mov R9, 0x00000040</b> также имеет <b>REX</b> префикс, поскольку использует регистр недоступный в 32 битном режиме, а именно комбинацию <b>REX.W</b> и <b>REX.B</b> = <i>0x49</i>. Префикс <b>REX.B</b> говорит о том что наша инструкция имеет расширенное поле <b>rm</b>. В 32 битном режиме мы могли бы использовать однобайтовую <i>0xB8 + r</i>, в 64-битном нам придется использовать <i>0xC7</i>. Также определяем поле <b>mod/rm</b>, т.к. у нас операнд непосредственный регистр, то <b>mod</b> = <i>0b11</i>, а <b>rm</b> = <i>0b001</i> что соответствует регистру <b>R9</b>. По таблице поле <b>Register/ Opcode Field</b> должно быть равно 0, собирая все вместе получим <i>0b11-000-001</i> = <i>0xC1</i>. Непосредственный операнд размещается за <b>mod/rm</b> полем. В итоге получаем полный код команды = <i>49 C7 C1 40 00 00 00</i>. Следующая инструкция также работает с расширенными регистрами (двумя) поэтому она также содержит расширенный префикс с комбинацией <b>REX.W</b>, <b>REX.B</b> и <b>REX.R</b> = <i>0x4D</i>. Префикс <b>REX.R</b> говорит о том что поле <b>reg</b> байта <b>mod/rm</b> также является расширенным. Далее ищем опкод команды <b>XOR</b>, здесь мы можем выбрать любой из двух либо <i>0x31</i> либо <i>0x33</i>, я возьму первое. Также определяемся с полем <b>mod/rm</b>. Опять-таки т.к. мы используем непосредственно регистры то поле <b>mod</b> будет равно <i>0b11</i>, по таблице регистров смотрим что расширеное поле для регистра <b>R8</b> = <i>0b000</i>. Собираем все вместе - <i>0b11-000-000</i> = <i>0xC0</i>, а полный код команды - <i>4D 31 C0</i>. Следующая инструкция - <b>lea RDX, [RIP + (MSG - L1)]</b>, второй операнд у нас является непосредственным значением, т.к. мы работаем в 64 битном режиме и адресация у нас идет относительно адреса следующей команды. Т.е. нам нужна инструкция вида <b>lea reg64, imm32</b>, но сначала определимся с префиксом. Т.к. команда работает с 64 битным регистром то префикс будет <b>REX.W</b>(<i>0x48</i>). Опкод команды <b>LEA</b> - <i>0x8D</i>. В качестве <b>mod/rm</b> у нас должно быть <b>mod</b> = <i>0b000</i>, а <b>rm</b> = <i>0b101</i> что соответствует <b>[RIP + disp32]</b>. Для регистра <b>RDX</b> номер равен <i>0b010</i>. Компонуем вместе - <i>0b00-010-101</i> = <i>0x15</i>. После идет 4-байтное смещение. Теперь давайте посчитаем смещение до нашей строки относительно следующей команды:<br />
<i>disp = -(sizeof(&quot;Hello world!&quot;) + sizeof({48 83 EC 28}) + sizeof({49 C7 C1 40 00 00 00}) + sizeof({4D 31 C0}) + sizeof({48 8D 15 00 00 00 00})) = 0xFFFFFFDE</i><br />
Т.е. полный код будет тогда = <i>48 8D 15 DE FF FF FF</i>. Следующий <b>XOR</b> расчитывается также как и предыдущий: <b>REX.W</b> + <i>0x31</i> + <i>0b11-001-001</i> = <i>48 31 C9</i>. Дальше у нас идет вызов из таблицы импорта, поэтому нужно посчитать смещение относительно следующей команды до 2-го элемента таблицы адресов (там у нас содержится адрес функции <b>MessageBoxA</b>), которое равно в данном случае -79 (<i>0xFFFFFFB1</i>). Теперь нам нужно найти опкод инструкции <b>CALL</b> которая позволяет вызывать по адресу расположенному в памяти. По таблице находим <i>FF</i>, <b>Register/ Opcode Field</b> должно быть равно 2. Теперь также посчитаем <b>mod/rm</b> байт. <i>0b00-010-101</i> = <i>0x15</i>. Полный код команды = <i>FF 15 B1 FF FF FF</i>. Код следующей команды нам уже известен, поэтому переходим к последнему опкоду - <b>CALL</b>. Опять считаем смещение, оно равно -96 <i>0xFFFFFFA0</i>, подставляем и получаем опкод команды <i>FF 15 A0 FF FF FF</i>. Все! Ничего сложного, только очень кропотливо. Давайте соберем все данные секции вместе:<br />
<div class="codeblock"><table class="unknown"><thead><tr><td colspan="2" id="748299276"  class="head">Code</td></tr></thead><tbody><tr class="li1"><td><div id="748299276" style="height: 254px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
5
6
7
8
9
10
11
12
13
14
</pre></td><td class="de1"><pre class="de1">0000h: <span class="nu0">54</span> <span class="nu0">10</span> 00 00 00 00 00 00 00 00 00 00 3C <span class="nu0">10</span> 00 00 &nbsp;T...........&lt;... 
0010h: <span class="nu0">74</span> <span class="nu0">10</span> 00 00 <span class="nu0">64</span> <span class="nu0">10</span> 00 00 00 00 00 00 00 00 00 00 &nbsp;t...d........... 
0020h: <span class="nu0">49</span> <span class="nu0">10</span> 00 00 7C <span class="nu0">10</span> 00 00 00 00 00 00 00 00 00 00 &nbsp;I...|........... 
0030h: 00 00 00 00 00 00 00 00 00 00 00 00 6B <span class="nu0">65</span> <span class="nu0">72</span> 6E &nbsp;............kern 
0040h: <span class="nu0">65</span> 6C <span class="nu0">33</span> <span class="nu0">32</span> 2E <span class="nu0">64</span> 6C 6C 00 <span class="nu0">75</span> <span class="nu0">73</span> <span class="nu0">65</span> <span class="nu0">72</span> <span class="nu0">33</span> <span class="nu0">32</span> 2E &nbsp;el32.dll.user32. 
0050h: <span class="nu0">64</span> 6C 6C 00 <span class="nu0">84</span> <span class="nu0">10</span> 00 00 00 00 00 00 00 00 00 00 &nbsp;dll.„........... 
0060h: 00 00 00 00 <span class="nu0">92</span> <span class="nu0">10</span> 00 00 00 00 00 00 00 00 00 00 &nbsp;....’........... 
0070h: 00 00 00 00 <span class="nu0">84</span> <span class="nu0">10</span> 00 00 00 00 00 00 <span class="nu0">92</span> <span class="nu0">10</span> 00 00 &nbsp;....„.......’... 
0080h: 00 00 00 00 00 00 <span class="nu0">45</span> <span class="nu0">78</span> <span class="nu0">69</span> <span class="nu0">74</span> <span class="nu0">50</span> <span class="nu0">72</span> 6F <span class="nu0">63</span> <span class="nu0">65</span> <span class="nu0">73</span> &nbsp;......ExitProces 
0090h: <span class="nu0">73</span> 00 00 00 4D <span class="nu0">65</span> <span class="nu0">73</span> <span class="nu0">73</span> <span class="nu0">61</span> <span class="nu0">67</span> <span class="nu0">65</span> <span class="nu0">42</span> 6F <span class="nu0">78</span> <span class="nu0">41</span> 00 &nbsp;s...MessageBoxA. 
00A0h: <span class="nu0">48</span> <span class="nu0">65</span> 6C 6C 6F <span class="nu0">20</span> <span class="nu0">77</span> 6F <span class="nu0">72</span> 6C <span class="nu0">64</span> <span class="nu0">21</span> 00 <span class="nu0">48</span> <span class="nu0">83</span> EC &nbsp;Hello world!.Hƒì 
00B0h: <span class="nu0">28</span> <span class="nu0">49</span> C7 C1 <span class="nu0">40</span> 00 00 00 4D <span class="nu0">31</span> C0 <span class="nu0">48</span> 8D <span class="nu0">15</span> DE FF &nbsp;<span class="br0">&#40;</span>IÇÁ@...M1ÀH
00C0h: FF FF <span class="nu0">48</span> <span class="nu0">31</span> C9 FF <span class="nu0">15</span> B1 FF FF FF <span class="nu0">48</span> <span class="nu0">31</span> C9 FF <span class="nu0">15</span> &nbsp;ÿÿH1Éÿ.±ÿÿÿH1Éÿ. 
00D0h: A0 FF FF FF &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;*ÿÿÿ</pre></td></tr></table></div></td></tr></tbody></table></div>Итоговый размер секции у нас занимает <i>0xD4</i> байт. Точка входа у нас равна <i>0x10AD</i>. Теперь приступим к непосредственному созданию EXE файла. В самом начале любого PE файла располагается <b>IMAGE_DOS_HEADER</b> заголовок:<br />
<div class="codeblock"><table class="cpp"><thead><tr><td colspan="2" id="626459421"  class="head">C++</td></tr></thead><tbody><tr class="li1"><td><div id="626459421" style="height: 350px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
</pre></td><td class="de1"><pre class="de1"><span class="kw4">typedef</span> <span class="kw4">struct</span> _IMAGE_DOS_HEADER
<span class="br0">&#123;</span>
&nbsp; &nbsp; &nbsp;WORD e_magic<span class="sy4">;</span>
&nbsp; &nbsp; &nbsp;WORD e_cblp<span class="sy4">;</span>
&nbsp; &nbsp; &nbsp;WORD e_cp<span class="sy4">;</span>
&nbsp; &nbsp; &nbsp;WORD e_crlc<span class="sy4">;</span>
&nbsp; &nbsp; &nbsp;WORD e_cparhdr<span class="sy4">;</span>
&nbsp; &nbsp; &nbsp;WORD e_minalloc<span class="sy4">;</span>
&nbsp; &nbsp; &nbsp;WORD e_maxalloc<span class="sy4">;</span>
&nbsp; &nbsp; &nbsp;WORD e_ss<span class="sy4">;</span>
&nbsp; &nbsp; &nbsp;WORD e_sp<span class="sy4">;</span>
&nbsp; &nbsp; &nbsp;WORD e_csum<span class="sy4">;</span>
&nbsp; &nbsp; &nbsp;WORD e_ip<span class="sy4">;</span>
&nbsp; &nbsp; &nbsp;WORD e_cs<span class="sy4">;</span>
&nbsp; &nbsp; &nbsp;WORD e_lfarlc<span class="sy4">;</span>
&nbsp; &nbsp; &nbsp;WORD e_ovno<span class="sy4">;</span>
&nbsp; &nbsp; &nbsp;WORD e_res<span class="br0">&#91;</span><span class="nu0">4</span><span class="br0">&#93;</span><span class="sy4">;</span>
&nbsp; &nbsp; &nbsp;WORD e_oemid<span class="sy4">;</span>
&nbsp; &nbsp; &nbsp;WORD e_oeminfo<span class="sy4">;</span>
&nbsp; &nbsp; &nbsp;WORD e_res2<span class="br0">&#91;</span><span class="nu0">10</span><span class="br0">&#93;</span><span class="sy4">;</span>
&nbsp; &nbsp; &nbsp;DWORD e_lfanew<span class="sy4">;</span>
<span class="br0">&#125;</span> IMAGE_DOS_HEADER, <span class="sy2">*</span>PIMAGE_DOS_HEADER<span class="sy4">;</span></pre></td></tr></table></div></td></tr></tbody></table></div>В этой структуре нас интересуют только поля <b>e_magic</b> и <b>e_lfanew</b>, находящихся по смещениям <i>0x00</i> и <i>0x3C</i> соответственно. Первое поле содержит сигнатуру <i>MZ</i>, а второе смещение на NT заголовки. Т.к. мы не используем заглушку DOS, мы расположим NT заголовки сразу за ней, т.е. смещение будет равно <i>0x40</i>. Это очень удобно поскольку NT заголовки должны быть выровнены на 8 байтовую границу, а структура <b>IMAGE_DOS_HEADER</b> имеет размер <i>0x40</i> байт. Итак создаем новый файл и вписываем наши данные:<br />
<div class="codeblock"><table class="unknown"><thead><tr><td colspan="2" id="345355797"  class="head">Code</td></tr></thead><tbody><tr class="li1"><td><div id="345355797" style="height: 94px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
</pre></td><td class="de1"><pre class="de1">0000h: 4D 5A 00 00 00 00 00 00 00 00 00 00 00 00 00 00 &nbsp;MZ.............. 
0010h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 &nbsp;................ 
0020h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 &nbsp;................ 
0030h: 00 00 00 00 00 00 00 00 00 00 00 00 <span class="nu0">40</span> 00 00 00 &nbsp;............@...</pre></td></tr></table></div></td></tr></tbody></table></div>Далее вставляем структуру <b>IMAGE_NT_HEADERS</b>:<br />
<div class="codeblock"><table class="cpp"><thead><tr><td colspan="2" id="996388865"  class="head">C++</td></tr></thead><tbody><tr class="li1"><td><div id="996388865" style="height: 350px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
</pre></td><td class="de1"><pre class="de1"><span class="kw4">typedef</span> <span class="kw4">struct</span> _IMAGE_NT_HEADERS
<span class="br0">&#123;</span>
&nbsp; &nbsp; &nbsp;DWORD Signature<span class="sy4">;</span>
&nbsp; &nbsp; &nbsp;IMAGE_FILE_HEADER FileHeader<span class="sy4">;</span>
&nbsp; &nbsp; &nbsp;IMAGE_OPTIONAL_HEADER64 OptionalHeader<span class="sy4">;</span>
<span class="br0">&#125;</span> IMAGE_NT_HEADERS<span class="sy4">;</span>
<span class="kw4">typedef</span> <span class="kw4">struct</span> _IMAGE_FILE_HEADER
<span class="br0">&#123;</span>
&nbsp; &nbsp; &nbsp;WORD Machine<span class="sy4">;</span>
&nbsp; &nbsp; &nbsp;WORD NumberOfSections<span class="sy4">;</span>
&nbsp; &nbsp; &nbsp;DWORD TimeDateStamp<span class="sy4">;</span>
&nbsp; &nbsp; &nbsp;DWORD PointerToSymbolTable<span class="sy4">;</span>
&nbsp; &nbsp; &nbsp;DWORD NumberOfSymbols<span class="sy4">;</span>
&nbsp; &nbsp; &nbsp;WORD SizeOfOptionalHeader<span class="sy4">;</span>
&nbsp; &nbsp; &nbsp;WORD Characteristics<span class="sy4">;</span>
<span class="br0">&#125;</span> IMAGE_FILE_HEADER<span class="sy4">;</span>
<span class="kw4">typedef</span> <span class="kw4">struct</span> _IMAGE_OPTIONAL_HEADER64
<span class="br0">&#123;</span>
&nbsp; &nbsp; &nbsp;WORD Magic<span class="sy4">;</span>
&nbsp; &nbsp; &nbsp;UCHAR MajorLinkerVersion<span class="sy4">;</span>
&nbsp; &nbsp; &nbsp;UCHAR MinorLinkerVersion<span class="sy4">;</span>
&nbsp; &nbsp; &nbsp;DWORD SizeOfCode<span class="sy4">;</span>
&nbsp; &nbsp; &nbsp;DWORD SizeOfInitializedData<span class="sy4">;</span>
&nbsp; &nbsp; &nbsp;DWORD SizeOfUninitializedData<span class="sy4">;</span>
&nbsp; &nbsp; &nbsp;DWORD AddressOfEntryPoint<span class="sy4">;</span>
&nbsp; &nbsp; &nbsp;DWORD BaseOfCode<span class="sy4">;</span>
&nbsp; &nbsp; &nbsp;DWORD64 ImageBase<span class="sy4">;</span>
&nbsp; &nbsp; &nbsp;DWORD SectionAlignment<span class="sy4">;</span>
&nbsp; &nbsp; &nbsp;DWORD FileAlignment<span class="sy4">;</span>
&nbsp; &nbsp; &nbsp;WORD MajorOperatingSystemVersion<span class="sy4">;</span>
&nbsp; &nbsp; &nbsp;WORD MinorOperatingSystemVersion<span class="sy4">;</span>
&nbsp; &nbsp; &nbsp;WORD MajorImageVersion<span class="sy4">;</span>
&nbsp; &nbsp; &nbsp;WORD MinorImageVersion<span class="sy4">;</span>
&nbsp; &nbsp; &nbsp;WORD MajorSubsystemVersion<span class="sy4">;</span>
&nbsp; &nbsp; &nbsp;WORD MinorSubsystemVersion<span class="sy4">;</span>
&nbsp; &nbsp; &nbsp;DWORD Win32VersionValue<span class="sy4">;</span>
&nbsp; &nbsp; &nbsp;DWORD SizeOfImage<span class="sy4">;</span>
&nbsp; &nbsp; &nbsp;DWORD SizeOfHeaders<span class="sy4">;</span>
&nbsp; &nbsp; &nbsp;DWORD CheckSum<span class="sy4">;</span>
&nbsp; &nbsp; &nbsp;WORD Subsystem<span class="sy4">;</span>
&nbsp; &nbsp; &nbsp;WORD DllCharacteristics<span class="sy4">;</span>
&nbsp; &nbsp; &nbsp;DWORD64 SizeOfStackReserve<span class="sy4">;</span>
&nbsp; &nbsp; &nbsp;DWORD64 SizeOfStackCommit<span class="sy4">;</span>
&nbsp; &nbsp; &nbsp;DWORD64 SizeOfHeapReserve<span class="sy4">;</span>
&nbsp; &nbsp; &nbsp;DWORD64 SizeOfHeapCommit<span class="sy4">;</span>
&nbsp; &nbsp; &nbsp;DWORD LoaderFlags<span class="sy4">;</span>
&nbsp; &nbsp; &nbsp;DWORD NumberOfRvaAndSizes<span class="sy4">;</span>
&nbsp; &nbsp; &nbsp;IMAGE_DATA_DIRECTORY DataDirectory<span class="br0">&#91;</span><span class="nu0">16</span><span class="br0">&#93;</span><span class="sy4">;</span>
<span class="br0">&#125;</span> IMAGE_OPTIONAL_HEADER64<span class="sy4">;</span></pre></td></tr></table></div></td></tr></tbody></table></div>В качестве <b>Signature</b> вставляем строку из 4-х символов <i>PE\0\0</i>. Т.к. у нас 64 битное приложение то в качестве <b>Machine</b> устанавливаем значение <b>IMAGE_FILE_MACHINE_AMD64</b> равное <i>0x8664</i>. В качестве <b>NumberOfSections</b> укажем 1, т.к. у нас одна секция. Три следующих поля нам не нужны, поэтому забиваем их нулями. Размер необязательного заголовка установим в <b>IMAGE_SIZEOF_NT_OPTIONAL64_HEADER</b> (<i>0x00F0</i>). Для <b>Characteristics</b> зададим комбинацию флагов <b>IMAGE_FILE_EXECUTABLE_IMAGE</b> и <b>IMAGE_FILE_LARGE_ADDRESS_AWARE</b> (<i>0x0022</i>). Далее начнем заполнять необязательный заголовок. В качестве <b>Magic</b> указываем <b>IMAGE_NT_OPTIONAL_HDR64_MAGIC</b> (<i>0x020B</i>). Версия линкера нам не нужна, поэтому забиваем туда нули. Размер кода указываем равным <i>0x1000</i>, потому что тут указывается выровненный размер данных на размер одной страницы. Размер инициализированных и неинициализированных данных забиваем нулями. Как мы выше вычислили, точка входа у нас равна <i>0x10AD</i>, в <b>BaseOfCode</b> забиваем RVA нашей секции, т.к. она содержит код. В качестве <b>ImageBase</b> задаем <i>0x0000000000400000</i> - это наш базовый адрес, тут можно в принципе указать любое значение, т.к. наш модуль не содержит абсолютных ссылок. В качестве <b>SectionAlignment</b> указываем <i>0x1000</i> - размер одной страницы памяти, а в качестве <b>FileAlignment</b> - <i>0x200</i> (стандартное значение для PE файлов). Версии операционной системы и образа мы не используем, а вот в качестве <b>MajorSubsystemVersion</b> и <b>MinorSubsystemVersion</b> укажем <i>0x0005</i> и <i>0x0002</i> (аналогично <b>/SUBSYSTEM[,major[.minor]]</b> ключу линкера). В качестве <b>SizeOfImage</b> укажем значение <i>0x2000</i>, поскольку наш файл будет располагаться на двух страницах памяти (заголовки и одна секция). Значение <b>SizeOfHeaders</b> нужно посчитать сложением всех заголовков и выравниванием на границу <b>FileAlignment</b>:<br />
<i>align(sizeof(IMAGE_DOS_HEADER) + sizeof(IMAGE_NT_HEADERS) + sizeof(IMAGE_SECTION_HEADER), 0x200) = 0x200</i><br />
Контрольную сумму также оставляем без внимания, а вот в качестве <b>Subsystem</b> вбиваем <b>IMAGE_SUBSYSTEM_WINDOWS_GUI</b> (<i>0x0002</i>). В поле <b>DllCharacteristics</b> забиваем комбинацию флагов <b>IMAGE_DLLCHARACTERISTICS_NO_SEH</b> и <b>IMAGE_DLLCHARACTERISTICS_NO_BIND</b> = <i>0x0C00</i>. Размер резервируемой памяти стека оставим по умолчанию <i>0x100000</i> байт, тоже самое и с начальным размером - <i>0x1000</i> байт. Теже самые значения забъем и для кучи. <b>LoaderFlags</b> - устаревшее поле и нас не интересует. <b>NumberOfRvaAndSizes</b> - забиваем <i>0x10</i>. В каталоге директорий нам понадобится только таблица импорта под индексом 1. Забиваем туда <i>0x1000</i> в качестве виртуального адреса, а размер равен (как мы ранее вычислили) <i>0xA0</i>. Вот что у нас получилось:<br />
<div class="codeblock"><table class="unknown"><thead><tr><td colspan="2" id="163236684"  class="head">Code</td></tr></thead><tbody><tr class="li1"><td><div id="163236684" style="height: 350px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
</pre></td><td class="de1"><pre class="de1">0000h: 4D 5A 00 00 00 00 00 00 00 00 00 00 00 00 00 00 &nbsp;MZ.............. 
0010h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 &nbsp;................ 
0020h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 &nbsp;................ 
0030h: 00 00 00 00 00 00 00 00 00 00 00 00 <span class="nu0">40</span> 00 00 00 &nbsp;............@... 
0040h: <span class="nu0">50</span> <span class="nu0">45</span> 00 00 <span class="nu0">64</span> <span class="nu0">86</span> 01 00 00 00 00 00 00 00 00 00 &nbsp;PE..d†.......... 
0050h: 00 00 00 00 F0 00 <span class="nu0">22</span> 00 0B 02 00 00 00 <span class="nu0">10</span> 00 00 &nbsp;....ð.&quot;......... 
0060h: 00 00 00 00 00 00 00 00 AD <span class="nu0">10</span> 00 00 00 <span class="nu0">10</span> 00 00 &nbsp;........*....... 
0070h: 00 00 <span class="nu0">40</span> 00 00 00 00 00 00 <span class="nu0">10</span> 00 00 00 02 00 00 &nbsp;..@............. 
0080h: 00 00 00 00 00 00 00 00 05 00 02 00 00 00 00 00 &nbsp;................ 
0090h: 00 <span class="nu0">20</span> 00 00 00 02 00 00 00 00 00 00 02 00 00 0C &nbsp;. .............. 
00A0h: 00 00 <span class="nu0">10</span> 00 00 00 00 00 00 <span class="nu0">10</span> 00 00 00 00 00 00 &nbsp;................ 
00B0h: 00 00 <span class="nu0">10</span> 00 00 00 00 00 00 <span class="nu0">10</span> 00 00 00 00 00 00 &nbsp;................ 
00C0h: 00 00 00 00 <span class="nu0">10</span> 00 00 00 00 00 00 00 00 00 00 00 &nbsp;................ 
00D0h: 00 <span class="nu0">10</span> 00 00 A0 00 00 00 00 00 00 00 00 00 00 00 &nbsp;....*........... 
00E0h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 &nbsp;................ 
00F0h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 &nbsp;................ 
0100h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 &nbsp;................ 
0110h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 &nbsp;................ 
0120h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 &nbsp;................ 
0130h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 &nbsp;................ 
0140h: 00 00 00 00 00 00 00 00 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;........</pre></td></tr></table></div></td></tr></tbody></table></div>Далее следует вставить описатель секции:<br />
<div class="codeblock"><table class="cpp"><thead><tr><td colspan="2" id="309840995"  class="head">C++</td></tr></thead><tbody><tr class="li1"><td><div id="309840995" style="height: 238px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
5
6
7
8
9
10
11
12
13
</pre></td><td class="de1"><pre class="de1"><span class="kw4">typedef</span> <span class="kw4">struct</span> _IMAGE_SECTION_HEADER
<span class="br0">&#123;</span>
&nbsp; &nbsp; &nbsp;BYTE Name<span class="br0">&#91;</span><span class="nu0">8</span><span class="br0">&#93;</span><span class="sy4">;</span>
&nbsp; &nbsp; &nbsp;DWORD VirtualSize<span class="sy4">;</span>
&nbsp; &nbsp; &nbsp;DWORD VirtualAddress<span class="sy4">;</span>
&nbsp; &nbsp; &nbsp;DWORD SizeOfRawData<span class="sy4">;</span>
&nbsp; &nbsp; &nbsp;DWORD PointerToRawData<span class="sy4">;</span>
&nbsp; &nbsp; &nbsp;DWORD PointerToRelocations<span class="sy4">;</span>
&nbsp; &nbsp; &nbsp;DWORD PointerToLinenumbers<span class="sy4">;</span>
&nbsp; &nbsp; &nbsp;WORD NumberOfRelocations<span class="sy4">;</span>
&nbsp; &nbsp; &nbsp;WORD NumberOfLinenumbers<span class="sy4">;</span>
&nbsp; &nbsp; &nbsp;DWORD Characteristics<span class="sy4">;</span>
<span class="br0">&#125;</span> IMAGE_SECTION_HEADER<span class="sy4">;</span></pre></td></tr></table></div></td></tr></tbody></table></div>В качестве имени забиваем стандартное '.text\0\0\0'. <b>VirtualSize</b> устанавливаем равным <i>0x1000</i> (округляем на границу выравнивания секций). <b>VirtualAdress</b> как мы в самом начале определили как <i>0x1000</i>. Поле <b>SizeOfRawData</b> устанавливаем равным <i>0x200</i> байт поскольку размер данных секции равен <i>0xD4</i> байт, но его нужно округлить на границу <b>FileAlignment</b>, а в оставшееся место секции забить нули или произвольные данные. Поле <b>PointerToRawData</b> у нас равно значению из <b>IMAGE_OPTIONAL_HEADER64.SizeOfHeaders</b>, т.е. <i>0x200</i>. Поля до поля <b>Characteristics</b> забиваем нулями, а вот это поле будет равно комбинации флагов <b>IMAGE_SCN_CNT_CODE</b>, <b>IMAGE_SCN_MEM_EXECUTE</b> и <b>IMAGE_SCN_MEM_READ</b>, т.е. <i>0x60000020</i>. Все, добиваем нулями до границы 512 байт и прикрепляем секцию которую тоже добиваем до 512 байт нулями. В итоге у нас получается вот такой файл:<br />
<div class="codeblock"><table class="unknown"><thead><tr><td colspan="2" id="817190566"  class="head">Code</td></tr></thead><tbody><tr class="li1"><td><div id="817190566" style="height: 350px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
</pre></td><td class="de1"><pre class="de1">0000h: 4D 5A 00 00 00 00 00 00 00 00 00 00 00 00 00 00 &nbsp;MZ.............. 
0010h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 &nbsp;................ 
0020h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 &nbsp;................ 
0030h: 00 00 00 00 00 00 00 00 00 00 00 00 <span class="nu0">40</span> 00 00 00 &nbsp;............@... 
0040h: <span class="nu0">50</span> <span class="nu0">45</span> 00 00 <span class="nu0">64</span> <span class="nu0">86</span> 01 00 00 00 00 00 00 00 00 00 &nbsp;PE..d†.......... 
0050h: 00 00 00 00 F0 00 <span class="nu0">22</span> 00 0B 02 00 00 00 <span class="nu0">10</span> 00 00 &nbsp;....ð.&quot;......... 
0060h: 00 00 00 00 00 00 00 00 AD <span class="nu0">10</span> 00 00 00 <span class="nu0">10</span> 00 00 &nbsp;........*....... 
0070h: 00 00 <span class="nu0">40</span> 00 00 00 00 00 00 <span class="nu0">10</span> 00 00 00 02 00 00 &nbsp;..@............. 
0080h: 00 00 00 00 00 00 00 00 05 00 02 00 00 00 00 00 &nbsp;................ 
0090h: 00 <span class="nu0">20</span> 00 00 00 02 00 00 00 00 00 00 02 00 00 0C &nbsp;. .............. 
00A0h: 00 00 <span class="nu0">10</span> 00 00 00 00 00 00 <span class="nu0">10</span> 00 00 00 00 00 00 &nbsp;................ 
00B0h: 00 00 <span class="nu0">10</span> 00 00 00 00 00 00 <span class="nu0">10</span> 00 00 00 00 00 00 &nbsp;................ 
00C0h: 00 00 00 00 <span class="nu0">10</span> 00 00 00 00 00 00 00 00 00 00 00 &nbsp;................ 
00D0h: 00 <span class="nu0">10</span> 00 00 A0 00 00 00 00 00 00 00 00 00 00 00 &nbsp;....*........... 
00E0h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 &nbsp;................ 
00F0h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 &nbsp;................ 
0100h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 &nbsp;................ 
0110h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 &nbsp;................ 
0120h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 &nbsp;................ 
0130h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 &nbsp;................ 
0140h: 00 00 00 00 00 00 00 00 2E <span class="nu0">74</span> <span class="nu0">65</span> <span class="nu0">78</span> <span class="nu0">74</span> 00 00 00 &nbsp;.........text... 
0150h: 00 <span class="nu0">10</span> 00 00 00 <span class="nu0">10</span> 00 00 00 02 00 00 00 02 00 00 &nbsp;................ 
0160h: 00 00 00 00 00 00 00 00 00 00 00 00 <span class="nu0">20</span> 00 00 <span class="nu0">60</span> &nbsp;............ ..` 
0170h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 &nbsp;................ 
0180h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 &nbsp;................ 
0190h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 &nbsp;................ 
01A0h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 &nbsp;................ 
01B0h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 &nbsp;................ 
01C0h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 &nbsp;................ 
01D0h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 &nbsp;................ 
01E0h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 &nbsp;................ 
01F0h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 &nbsp;................ 
0200h: <span class="nu0">54</span> <span class="nu0">10</span> 00 00 00 00 00 00 00 00 00 00 3C <span class="nu0">10</span> 00 00 &nbsp;T...........&lt;... 
0210h: <span class="nu0">74</span> <span class="nu0">10</span> 00 00 <span class="nu0">64</span> <span class="nu0">10</span> 00 00 00 00 00 00 00 00 00 00 &nbsp;t...d........... 
0220h: <span class="nu0">49</span> <span class="nu0">10</span> 00 00 7C <span class="nu0">10</span> 00 00 00 00 00 00 00 00 00 00 &nbsp;I...|........... 
0230h: 00 00 00 00 00 00 00 00 00 00 00 00 6B <span class="nu0">65</span> <span class="nu0">72</span> 6E &nbsp;............kern 
0240h: <span class="nu0">65</span> 6C <span class="nu0">33</span> <span class="nu0">32</span> 2E <span class="nu0">64</span> 6C 6C 00 <span class="nu0">75</span> <span class="nu0">73</span> <span class="nu0">65</span> <span class="nu0">72</span> <span class="nu0">33</span> <span class="nu0">32</span> 2E &nbsp;el32.dll.user32. 
0250h: <span class="nu0">64</span> 6C 6C 00 <span class="nu0">84</span> <span class="nu0">10</span> 00 00 00 00 00 00 00 00 00 00 &nbsp;dll.„........... 
0260h: 00 00 00 00 <span class="nu0">92</span> <span class="nu0">10</span> 00 00 00 00 00 00 00 00 00 00 &nbsp;....’........... 
0270h: 00 00 00 00 <span class="nu0">84</span> <span class="nu0">10</span> 00 00 00 00 00 00 <span class="nu0">92</span> <span class="nu0">10</span> 00 00 &nbsp;....„.......’... 
0280h: 00 00 00 00 00 00 <span class="nu0">45</span> <span class="nu0">78</span> <span class="nu0">69</span> <span class="nu0">74</span> <span class="nu0">50</span> <span class="nu0">72</span> 6F <span class="nu0">63</span> <span class="nu0">65</span> <span class="nu0">73</span> &nbsp;......ExitProces 
0290h: <span class="nu0">73</span> 00 00 00 4D <span class="nu0">65</span> <span class="nu0">73</span> <span class="nu0">73</span> <span class="nu0">61</span> <span class="nu0">67</span> <span class="nu0">65</span> <span class="nu0">42</span> 6F <span class="nu0">78</span> <span class="nu0">41</span> 00 &nbsp;s...MessageBoxA. 
02A0h: <span class="nu0">48</span> <span class="nu0">65</span> 6C 6C 6F <span class="nu0">20</span> <span class="nu0">77</span> 6F <span class="nu0">72</span> 6C <span class="nu0">64</span> <span class="nu0">21</span> 00 <span class="nu0">48</span> <span class="nu0">83</span> EC &nbsp;Hello world!.Hƒì 
02B0h: <span class="nu0">28</span> <span class="nu0">49</span> C7 C1 <span class="nu0">40</span> 00 00 00 4D <span class="nu0">31</span> C0 <span class="nu0">48</span> 8D <span class="nu0">15</span> DE FF &nbsp;<span class="br0">&#40;</span>IÇÁ@...M1ÀH
02C0h: FF FF <span class="nu0">48</span> <span class="nu0">31</span> C9 FF <span class="nu0">15</span> B1 FF FF FF <span class="nu0">48</span> <span class="nu0">31</span> C9 FF <span class="nu0">15</span> &nbsp;ÿÿH1Éÿ.±ÿÿÿH1Éÿ. 
02D0h: A0 FF FF FF 00 00 00 00 00 00 00 00 00 00 00 00 &nbsp;*ÿÿÿ............ 
02E0h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 &nbsp;................ 
02F0h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 &nbsp;................ 
0300h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 &nbsp;................ 
0310h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 &nbsp;................ 
0320h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 &nbsp;................ 
0330h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 &nbsp;................ 
0340h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 &nbsp;................ 
0350h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 &nbsp;................ 
0360h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 &nbsp;................ 
0370h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 &nbsp;................ 
0380h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 &nbsp;................ 
0390h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 &nbsp;................ 
03A0h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 &nbsp;................ 
03B0h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 &nbsp;................ 
03C0h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 &nbsp;................ 
03D0h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 &nbsp;................ 
03E0h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 &nbsp;................ 
03F0h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 &nbsp;................</pre></td></tr></table></div></td></tr></tbody></table></div>Теперь если попробовать запустить его, то у нас появится сообщение, как мы и ожидали ).<br />
<img src="https://www.cyberforum.ru/blog_attachment.php?attachmentid=4059&amp;d=1481247895" border="0" alt="Название: preview.png
Просмотров: 11291

Размер: 6.1 Кб" style="margin: 5px" /><br />
Также можно поиграться с параметром <b>FileAlignment</b> дабы уменьшить размер файла.<br />
Надеюсь вам было интересно, спасибо за внимание!<br />
С уважением,<br />
Кривоус Анатолий (The trick).</div>


<!-- attachments -->
	<div style="margin-top:10px">

		
		
		
		
			<fieldset class="fieldset">
				<legend>Вложения</legend>
				<table cellpadding="0" cellspacing="3" border="0">
				<tr>
	<td><img loading="lazy" decoding="async" class="inlineimg" src="http://www.cyberforum.ru//cyberstatic.net/images/attach/zip.gif" alt="Тип файла: zip" width="16" height="16" border="0" style="vertical-align:baseline" /></td>
	<td><a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=4058&amp;d=1481247895">HEX_exe_by_The_trick.zip</a> (350 байт, 661 просмотров)</td>
</tr>
				</table>
			</fieldset>
		

	</div>
<!-- / attachments -->
]]></content:encoded>
			<dc:creator>The trick</dc:creator>
			<guid isPermaLink="true">https://www.cyberforum.ru/blogs/354370/4505.html</guid>
		</item>
		<item>
			<title>Создаем VST эффект на ассемблере</title>
			<link>https://www.cyberforum.ru/blogs/354370/4494.html</link>
			<pubDate>Sun, 27 Nov 2016 00:08:09 GMT</pubDate>
			<description>e-qc-oFxcu0 
Всем привет, сегодня я хотел бы рассказать как написать простейший VST плагин на...</description>
			<content:encoded><![CDATA[<div><iframe width="640" height="360" src="https://www.youtube.com/embed/e-qc-oFxcu0" frameborder="0" allowfullscreen></iframe><br />
Всем привет, сегодня я хотел бы рассказать как написать простейший VST плагин на ассемблере. Те кто создает музыку на компьютере, или занимается обработкой звука хорошо знакомы с этими плагинами и часто используют их как для генерации звука так и для обработки. Основное достоинство таких плагинов - это простота подключения к большинству аудио или музыкальных редакторов. Существуют два типа плагинов VST эффекты и VST инструменты (которые также называют VSTi). В данной статье мы рассмотрим создание VST эффекта, на основе стандарта VST 2.4 который поддерживают большинство редакторов. Программировать будем на FASM'е.<br />
Итак, для начала нужно определится с самим эффектом и для простоты я решил использовать биткрашер. Суть эффекта состоит в понижении разрешения звука как по частоте так и по амплитуде без всякой фильтрации, что дает характерное звучание из-за шумов квантования. Такой эффект нередко можно встретить в электронной музыке, я и сам его очень часто использую. Наглядно эффект продемонстрирован на рисунке:<br />
<a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=4030&amp;d=1480204851" rel="Lightbox" id="attachment4030" ><img src="https://www.cyberforum.ru/blog_attachment.php?attachmentid=4030&amp;thumb=1&amp;d=1480204851" class="thumbnail" border="0" alt="Нажмите на изображение для увеличения
Название: demonstr.gif
Просмотров: 774
Размер:	223.7 Кб
ID:	4030" style="margin: 5px" /></a><br />
Для начала определимся с параметрами эффекта - это частота среза, количество уровней громкости (битность) и дополнительно добавим регулировку выходной громкости. Частота среза у нас может регулироваться от половины частоты дискретизации до нуля, битность от 1 до 16 бит (от 2 до 65536 уровней соответственно), громкость от 0 до 100%. Звук в VST стандарте представляет из себя буффер с семплами, где каждый семпл представлен либо 32-битным числом с плавающей точкой, либо 64 битным числом с плавающей точкой. Частота дискретизации задает количество таких семплов в секунду; максимальная частота которая может быть воспроизведена равна половине частоты дискретизации (обычно 22050Гц). Амплитуда варьируется от -1 до 1, но может также выходить за пределы, что влечет за собой перегруз и клиппинг. Также следует учитывать количество каналов звука, для стерео звука это два канала и каждый обрабатывается независимо. Для того чтобы понизить разрядность звука нужно применить простую формулу: <br />
<br />
<div align="center"><i>newValue = int(oldValue * levels) / levels</i></div><br />
В итоге из-за округления мы получим дискретный шаг который зависит от величины <b>levels</b>. С ограничением частоты также все просто, для этого найдем сначала количество семплов которое следует пропустить для получения нужной частоты по формуле:<br />
<br />
<div align="center"><i>numSamples = sampleRate / downSamplingFreq / 2</i></div><br />
Нужно отметить что это число должно быть вещественное для плавной регулировки. Далее нужно просто завести счетчик семлов и периодически сравнивать его значение с <b>numSamples</b>, если оно больше или равно то следует прогрузить следующий семпл в выходной буфер иначе прогружать семпл из предыдущего такого прогруза. Т.к. мы будем использовать стерео обработку, то нужно иметь 2 независимых канала обработки. Из всего этого можно уже примерно накидать структуру эффекта:<br />
<div class="codeblock"><table class="asm"><thead><tr><td colspan="2" id="987380862"  class="head">Assembler</td></tr></thead><tbody><tr class="li1"><td><div id="987380862" style="height: 174px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
5
6
7
8
9
</pre></td><td class="de1"><pre class="de1">struct 
&nbsp; &nbsp; sampleRate &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw5">dd</span> ? &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">; // Частота дискретизации</span>
&nbsp; &nbsp; volume &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw5">dd</span> ? &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">; // Громкость 0..1 (0..100%)</span>
&nbsp; &nbsp; downsampling &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw5">dd</span> ? &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">; // Частота среза 0..1 (0..SampleRate)</span>
&nbsp; &nbsp; quantize &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw5">dd</span> ? &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">; // Битность 0..1 (2 ^ (value * 15 + 1))</span>
&nbsp; &nbsp; lValue &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw5">dd</span> ? &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">; // Текущее значение семпла левого канала</span>
&nbsp; &nbsp; rValue &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw5">dd</span> ? &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">; // Текущее значение семпла правого канала</span>
&nbsp; &nbsp; sampleCounter &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw5">dd</span> ? &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">; // Счетчик семплов фильтра</span>
ends</pre></td></tr></table></div></td></tr></tbody></table></div>Теперь, если просмотреть VST SDK, то можно увидеть что VST плагин представляет собой обычную DLL которая экспортирует функцию <b>VSTPluginMain</b> или <b>main</b> (<b>Main</b>, <b>MAIN</b>, и т.д.). Хост вызывает эту функцию когда создается новый экземпляр VST эффекта. Эта функция должна при успехе возвратить указатель на объект дескриптора эффекта <b>AEEffect</b>, который имеет следующую структуру:<br />
<div class="codeblock"><table class="asm"><thead><tr><td colspan="2" id="371842001"  class="head">Assembler</td></tr></thead><tbody><tr class="li1"><td><div id="371842001" style="height: 350px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
</pre></td><td class="de1"><pre class="de1">struct AEEffect
&nbsp; &nbsp; magic &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw5">dd</span> ? &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">; // Сигнатура 'VstP'</span>
&nbsp; &nbsp; dispatcher &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw5">dd</span> ? &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">; // Процедура диспечеризации</span>
&nbsp; &nbsp; process &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw5">dd</span> ?
&nbsp; &nbsp; setParameter &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw5">dd</span> ? &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">; // Установка параметра</span>
&nbsp; &nbsp; getParameter &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw5">dd</span> ? &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">; // Получение параметра</span>
&nbsp; &nbsp; numPrograms &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw5">dd</span> ?
&nbsp; &nbsp; numParams &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw5">dd</span> ?
&nbsp; &nbsp; numInputs &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw5">dd</span> ? &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">; // Количество входных каналов</span>
&nbsp; &nbsp; numOutputs &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw5">dd</span> ? &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">; // Количество выходных каналов</span>
&nbsp; &nbsp; flags &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw5">dd</span> ? &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">; // Флаги</span>
&nbsp; &nbsp; resvd1 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw5">dd</span> ?
&nbsp; &nbsp; resvd2 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw5">dd</span> ?
&nbsp; &nbsp; initialDelay &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw5">dd</span> ?
&nbsp; &nbsp; realQualities &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw5">dd</span> ?
&nbsp; &nbsp; offQualities &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw5">dd</span> ?
&nbsp; &nbsp; ioRatio &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw5">dd</span> ? &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; object &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw5">dd</span> ? &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">; // Указатель на объект эффекта</span>
&nbsp; &nbsp; user &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw5">dd</span> ?
&nbsp; &nbsp; uniqueId &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw5">dd</span> ? &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">; // Уникальный ИД эффекта</span>
&nbsp; &nbsp; version &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw5">dd</span> ? &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">; // Версия эффекта</span>
&nbsp; &nbsp; processReplacing &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw5">dd</span> ? &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">; // Процедура обработки звука</span>
&nbsp; &nbsp; processDoubleReplacing &nbsp;<span class="kw5">dd</span> ?
&nbsp; &nbsp; future &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw5">db</span> <span class="nu0">56</span> dup <span class="br0">&#40;</span>?<span class="br0">&#41;</span>
ends</pre></td></tr></table></div></td></tr></tbody></table></div>Как видно структура содержит множество полей, но нас интересуют только некоторые. Самое важное поле это <b>dispatcher</b> - указатель на функцию которая принимает различные запросы от хоста (чем-то похоже на <b>WindowProc</b>); <b>setParameter</b>/<b>getParameter </b>- задают указатели на функции установки/получения параметров от элементов управления или автоматизации. В <b>numInputs</b>/<b>numOutputs</b> мы задаем количество поддерживаемых каналов, в нашем случае 2. Поле <b>object</b> - содержит указатель на связанный пользовательский объект эффекта, т.е. там мы будем хранить указатель на структуру объекта что мы привели ранее. <b>processReplacing</b> и <b>processDoubleReplacing</b> содержат процедуры обработки звуковых данных для 32-float и 64-double соответственно. Для нашего примера мы будем использовать только <b>32-float</b> Обработку. Флаги задают некоторые характеристики эффекта, мы будем использовать два значения: <b>effFlagsCanReplacing</b> и <b>effFlagsNoSoundInStop</b>. Первый говорит нам что плагин имеет функцию  <b>processReplacing</b> и должен быть всегда установлен в VST 2.4 эффекте, а <b>effFlagsNoSoundInStop</b> что плагин ничего не делает если нет входного звука или там тишина. Итак чтобы связать <b>AEEffect</b> и наш эффект соберем их в одну структуру ASMCrusher которая будет олецетворять наш эффект:<br />
<div class="codeblock"><table class="asm"><thead><tr><td colspan="2" id="835918343"  class="head">Assembler</td></tr></thead><tbody><tr class="li1"><td><div id="835918343" style="height: 206px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
5
6
7
8
9
10
11
</pre></td><td class="de1"><pre class="de1"><span class="co1">; // Объект эффекта ASMCrusher</span>
struct ASMCrusher
&nbsp; &nbsp; ae &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;AEEffect ? &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">; // Базовый интерфейс AEEffect</span>
&nbsp; &nbsp; sampleRate &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw5">dd</span> ? &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">; // Частота дискретизации</span>
&nbsp; &nbsp; volume &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw5">dd</span> ? &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">; // Громкость 0..1 (0..100%)</span>
&nbsp; &nbsp; downsampling &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw5">dd</span> ? &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">; // Частота среза 0..1 (0..SampleRate)</span>
&nbsp; &nbsp; quantize &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw5">dd</span> ? &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">; // Битность 0..1 (2 ^ (value * 15 + 1))</span>
&nbsp; &nbsp; lValue &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw5">dd</span> ? &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">; // Текущее значение семпла левого канала</span>
&nbsp; &nbsp; rValue &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw5">dd</span> ? &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">; // Текущее значение семпла правого канала</span>
&nbsp; &nbsp; sampleCounter &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw5">dd</span> ? &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">; // Счетчик семплов фильтра</span>
ends</pre></td></tr></table></div></td></tr></tbody></table></div>Итак для начала зададим формат файла, декларации типов и констант, импорт, экспорт и зададим релокации:<br />
<div class="codeblock"><table class="asm"><thead><tr><td colspan="2" id="151071981"  class="head">Assembler</td></tr></thead><tbody><tr class="li1"><td><div id="151071981" style="height: 350px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
</pre></td><td class="de1"><pre class="de1">format PE GUI <span class="nu0">4.0</span> DLL <span class="kw5">at</span> <span class="nu0">11000000h</span>
&nbsp;
include <span class="st0">'win32wx.inc'</span>
&nbsp;
<span class="co1">; // Базовый интерфейс VST эффекта</span>
struct AEEffect
&nbsp; &nbsp; magic &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw5">dd</span> ? &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">; // Сигнатура 'VstP'</span>
&nbsp; &nbsp; dispatcher &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw5">dd</span> ? &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">; // Процедура диспечеризации</span>
&nbsp; &nbsp; process &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw5">dd</span> ?
&nbsp; &nbsp; setParameter &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw5">dd</span> ? &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">; // Установка параметра</span>
&nbsp; &nbsp; getParameter &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw5">dd</span> ? &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">; // Получение параметра</span>
&nbsp; &nbsp; numPrograms &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw5">dd</span> ?
&nbsp; &nbsp; numParams &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw5">dd</span> ?
&nbsp; &nbsp; numInputs &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw5">dd</span> ? &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">; // Количество входных каналов</span>
&nbsp; &nbsp; numOutputs &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw5">dd</span> ? &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">; // Количество выходных каналов</span>
&nbsp; &nbsp; flags &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw5">dd</span> ? &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">; // Флаги</span>
&nbsp; &nbsp; resvd1 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw5">dd</span> ?
&nbsp; &nbsp; resvd2 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw5">dd</span> ?
&nbsp; &nbsp; initialDelay &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw5">dd</span> ?
&nbsp; &nbsp; realQualities &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw5">dd</span> ?
&nbsp; &nbsp; offQualities &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw5">dd</span> ?
&nbsp; &nbsp; ioRatio &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw5">dd</span> ?
&nbsp; &nbsp; object &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw5">dd</span> ? &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">; // Указатель на объект эффекта</span>
&nbsp; &nbsp; user &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw5">dd</span> ?
&nbsp; &nbsp; uniqueId &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw5">dd</span> ? &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">; // Уникальный ИД эффекта</span>
&nbsp; &nbsp; version &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw5">dd</span> ? &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">; // Версия эффекта</span>
&nbsp; &nbsp; processReplacing &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw5">dd</span> ? &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">; // Процедура обработки звука</span>
&nbsp; &nbsp; processDoubleReplacing &nbsp;<span class="kw5">dd</span> ?
&nbsp; &nbsp; future &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw5">db</span> <span class="nu0">56</span> dup <span class="br0">&#40;</span>?<span class="br0">&#41;</span>
ends
&nbsp;
<span class="co1">; // Объект эффекта ASMCrusher</span>
struct ASMCrusher
&nbsp; &nbsp; ae &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;AEEffect ? &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">; // Базовый интерфейс AEEffect</span>
&nbsp; &nbsp; sampleRate &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw5">dd</span> ? &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">; // Частота дискретизации</span>
&nbsp; &nbsp; volume &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw5">dd</span> ? &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">; // Громкость 0..1 (0..100%)</span>
&nbsp; &nbsp; downsampling &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw5">dd</span> ? &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">; // Частота среза 0..1 (0..SampleRate)</span>
&nbsp; &nbsp; quantize &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw5">dd</span> ? &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">; // Битность 0..1 (2 ^ (value * 15 + 1))</span>
&nbsp; &nbsp; lValue &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw5">dd</span> ? &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">; // Текущее значение семпла левого канала</span>
&nbsp; &nbsp; rValue &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw5">dd</span> ? &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">; // Текущее значение семпла правого канала</span>
&nbsp; &nbsp; sampleCounter &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw5">dd</span> ? &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">; // Счетчик семплов фильтра</span>
ends
&nbsp;
NUMBER_OF_PARAMETERS &nbsp; &nbsp;= <span class="nu0">3</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">; // Количество параметров эффекта</span>
UNIQUE_ID &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; = <span class="nu0">1234567</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">; // Уникальный ИД эффекта</span>
VERSION &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; = <span class="nu0">1</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">; // Версия эффекта</span>
PAR_VOLUME &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;= <span class="nu0">0</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">; // Индексы параметров ...</span>
PAR_DOWNSAMPLING &nbsp; &nbsp; &nbsp; &nbsp;= <span class="nu0">1</span>
PAR_QUANTIZE &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;= <span class="nu0">2</span>
&nbsp;
kEffectMagic &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;= <span class="nu0">0x56737450</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">; // Сигнатура AEEffect</span>
audioMasterVersion &nbsp; &nbsp; &nbsp;= <span class="nu0">1</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">; // Версия хоста</span>
&nbsp;
<span class="co1">; // Максимальные размеры строк</span>
kVstMaxParamStrLen &nbsp; &nbsp; &nbsp;= <span class="nu0">8</span>
kVstMaxVendorStrLen &nbsp; &nbsp; = <span class="nu0">64</span>
kVstMaxProductStrLen &nbsp; &nbsp;= <span class="nu0">64</span>
kVstMaxEffectNameLen &nbsp; &nbsp;= <span class="nu0">32</span>
&nbsp;
effClose &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;= <span class="nu0">1</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">; // Событие вызывается когда эффект уничтожается</span>
effSetSampleRate &nbsp; &nbsp; &nbsp; &nbsp;= <span class="nu0">10</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">; // Событие установки частоты дискретизации</span>
effGetParamName &nbsp; &nbsp; &nbsp; &nbsp; = <span class="nu0">8</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">; // Событие получения имени параметра</span>
effGetParamLabel &nbsp; &nbsp; &nbsp; &nbsp;= <span class="nu0">6</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">; // Событие получения метки параметра</span>
effGetParamDisplay &nbsp; &nbsp; &nbsp;= <span class="nu0">7</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">; // Событие получения метки значения параметра</span>
effGetEffectName &nbsp; &nbsp; &nbsp; &nbsp;= <span class="nu0">45</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">; // Событие получения имени эффекта</span>
effGetVendorString &nbsp; &nbsp; &nbsp;= <span class="nu0">47</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">; // Событие получения имени производителя</span>
effGetProductString &nbsp; &nbsp; = <span class="nu0">48</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">; // Событие получения имени продукта</span>
effGetVendorVersion &nbsp; &nbsp; = <span class="nu0">49</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">; // Событие получения версии</span>
&nbsp;
effFlagsCanReplacing &nbsp; &nbsp;= <span class="nu0">16</span>
effFlagsNoSoundInStop &nbsp; = <span class="nu0">512</span>
&nbsp;
<span class="kw5">section</span> <span class="st0">'.idata'</span> <span class="kw5">import</span> <span class="kw5">data</span> readable writeable
&nbsp;
library kernel<span class="sy1">,</span> <span class="st0">'kernel32.dll'</span><span class="sy1">,</span> \
&nbsp; &nbsp; &nbsp; &nbsp; msvcrt<span class="sy1">,</span> <span class="st0">'msvcrt.dll'</span>
&nbsp;
<span class="kw5">import</span> kernel<span class="sy1">,</span>\
&nbsp; &nbsp; &nbsp; &nbsp;GetProcessHeap<span class="sy1">,</span> <span class="st0">'GetProcessHeap'</span><span class="sy1">,</span> \
&nbsp; &nbsp; &nbsp; &nbsp;HeapAlloc<span class="sy1">,</span> <span class="st0">'HeapAlloc'</span><span class="sy1">,</span> \
&nbsp; &nbsp; &nbsp; &nbsp;HeapFree<span class="sy1">,</span> <span class="st0">'HeapFree'</span><span class="sy1">,</span> \
&nbsp; &nbsp; &nbsp; &nbsp;lstrcpynA<span class="sy1">,</span> <span class="st0">'lstrcpynA'</span>
&nbsp;
<span class="kw5">import</span> msvcrt<span class="sy1">,</span> \
&nbsp; &nbsp; &nbsp; &nbsp;sprintf<span class="sy1">,</span> <span class="st0">'sprintf'</span>
&nbsp;
<span class="kw5">data</span> <span class="kw5">export</span>
<span class="kw5">export</span> <span class="st0">'AsmCrusher.DLL'</span><span class="sy1">,</span> Main<span class="sy1">,</span> <span class="st0">'Main'</span>
end <span class="kw5">data</span>
&nbsp;
<span class="kw5">section</span> <span class="st0">'.reloc'</span> <span class="kw5">data</span> readable discardable fixups</pre></td></tr></table></div></td></tr></tbody></table></div>Одной замечательной особенностью VST стандарта является то что можно вообще не реализовывать пользовательский интерфейс, нужно лишь сообщить хосту количество параметров и их свойства и каждый хост сам предоставит нужные регуляторы и свяжет их с параметрами эффекта. Поэтому далее задаем таблицу строк и список указателей на необходимые строки для каждого параметра, а также точку входа DLL. Таблицу разместим в секции <i>.text</i>:<br />
<div class="codeblock"><table class="asm"><thead><tr><td colspan="2" id="663763401"  class="head">Assembler</td></tr></thead><tbody><tr class="li1"><td><div id="663763401" style="height: 350px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
</pre></td><td class="de1"><pre class="de1"><span class="kw5">section</span> <span class="st0">'.text'</span> <span class="kw5">code</span> readable executable
&nbsp;
EFFECT_NAME &nbsp; &nbsp; &nbsp;<span class="kw5">db</span> &nbsp; &nbsp; &nbsp;<span class="st0">'ASMCrusher'</span><span class="sy1">,</span> <span class="nu0">0</span>
VENDOR_NAME &nbsp; &nbsp; &nbsp;<span class="kw5">db</span> &nbsp; &nbsp; &nbsp;<span class="st0">'Кривоус Анатолий Анатольевич (The trick)'</span><span class="sy1">,</span> <span class="nu0">0</span>
PRODUCT_NAME &nbsp; &nbsp; <span class="kw5">db</span> &nbsp; &nbsp; &nbsp;<span class="st0">'ASMCrusher'</span><span class="sy1">,</span> <span class="nu0">0</span>
PARAM_NAME_1 &nbsp; &nbsp; <span class="kw5">db</span> &nbsp; &nbsp; &nbsp;<span class="st0">'Volume'</span><span class="sy1">,</span> <span class="nu0">0</span>
PARAM_NAME_2 &nbsp; &nbsp; <span class="kw5">db</span> &nbsp; &nbsp; &nbsp;<span class="st0">'Frequency'</span><span class="sy1">,</span> <span class="nu0">0</span>
PARAM_NAME_3 &nbsp; &nbsp; <span class="kw5">db</span> &nbsp; &nbsp; &nbsp;<span class="st0">'Quantize'</span><span class="sy1">,</span> <span class="nu0">0</span>
PARAM_LABEL_1 &nbsp; &nbsp;<span class="kw5">db</span> &nbsp; &nbsp; &nbsp;<span class="st0">'%'</span><span class="sy1">,</span> <span class="nu0">0</span>
PARAM_LABEL_2 &nbsp; &nbsp;<span class="kw5">db</span> &nbsp; &nbsp; &nbsp;<span class="st0">'Hz'</span><span class="sy1">,</span> <span class="nu0">0</span>
PARAM_LABEL_3 &nbsp; &nbsp;<span class="kw5">db</span> &nbsp; &nbsp; &nbsp;<span class="st0">'Levels'</span><span class="sy1">,</span> <span class="nu0">0</span>
PARAM_FORMAT_1 &nbsp; <span class="kw5">db</span> &nbsp; &nbsp; &nbsp;<span class="st0">'%d%%'</span><span class="sy1">,</span> <span class="nu0">0</span>
PARAM_FORMAT_2 &nbsp; <span class="kw5">db</span> &nbsp; &nbsp; &nbsp;<span class="st0">'%dHz'</span><span class="sy1">,</span> <span class="nu0">0</span>
PARAM_FORMAT_3 &nbsp; <span class="kw5">db</span> &nbsp; &nbsp; &nbsp;<span class="st0">'%d'</span><span class="sy1">,</span> <span class="nu0">0</span>
&nbsp;
PARAMS_LIST &nbsp; &nbsp; &nbsp;<span class="kw5">dd</span> &nbsp; &nbsp; &nbsp;PARAM_NAME_1<span class="sy1">,</span> PARAM_NAME_2<span class="sy1">,</span> PARAM_NAME_3 &nbsp; &nbsp; &nbsp; <span class="co1">; // Имена параметров</span>
LABELS_LIST &nbsp; &nbsp; &nbsp;<span class="kw5">dd</span> &nbsp; &nbsp; &nbsp;PARAM_LABEL_1<span class="sy1">,</span> PARAM_LABEL_2<span class="sy1">,</span> PARAM_LABEL_3 &nbsp; &nbsp;<span class="co1">; // Метки единиц измерения параметров</span>
FORMATS_LIST &nbsp; &nbsp; <span class="kw5">dd</span> &nbsp; &nbsp; &nbsp;PARAM_FORMAT_1<span class="sy1">,</span> PARAM_FORMAT_2<span class="sy1">,</span> PARAM_FORMAT_3 <span class="co1">; // Форматы параметров</span>
&nbsp;
entry EntryPoint
&nbsp;
<span class="co1">; // Точка входа DLL</span>
proc EntryPoint<span class="sy1">,</span> hinstDLL<span class="sy1">,</span> fdwReason<span class="sy1">,</span> lpvReserved
&nbsp; &nbsp; <span class="kw1">mov</span> <span class="kw4">eax</span><span class="sy1">,</span> <span class="nu0">1</span>
&nbsp; &nbsp; <span class="kw1">ret</span>
endp</pre></td></tr></table></div></td></tr></tbody></table></div>В <b>PARAMS_LIST</b> мы храним указатели на строки имен параметров, в <b>LABELS_LIST</b> на соответствующие единицы измерений для них, а в <b>FORMATS_LIST</b> строки формата для функции <b>sprintf</b>. Каждый экземпляр объекта мы будем хранить в куче процесса, для выделения и освобождения памяти в ней создадим две процедуры:<br />
<div class="codeblock"><table class="asm"><thead><tr><td colspan="2" id="692672053"  class="head">Assembler</td></tr></thead><tbody><tr class="li1"><td><div id="692672053" style="height: 206px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
5
6
7
8
9
10
11
</pre></td><td class="de1"><pre class="de1"><span class="co1">; // Выделить память</span>
proc MemAlloc<span class="sy1">,</span> size
&nbsp; &nbsp; invoke HeapAlloc<span class="sy1">,</span> &lt;invoke GetProcessHeap&gt;<span class="sy1">,</span> HEAP_NO_SERIALIZE <span class="kw1">OR</span> HEAP_ZERO_MEMORY<span class="sy1">,</span> <span class="br0">&#91;</span>size<span class="br0">&#93;</span>
&nbsp; &nbsp; <span class="kw1">ret</span>
endp
&nbsp;
<span class="co1">; // Освободить память</span>
proc MemFree<span class="sy1">,</span> pMem
&nbsp; &nbsp; invoke HeapFree<span class="sy1">,</span> &lt;invoke GetProcessHeap&gt;<span class="sy1">,</span> <span class="br0">&#91;</span>pMem<span class="br0">&#93;</span><span class="sy1">,</span> HEAP_NO_SERIALIZE
&nbsp; &nbsp; <span class="kw1">ret</span>
endp</pre></td></tr></table></div></td></tr></tbody></table></div>Теперь можно приступать к непосредственно к реализации стандартных функций VST формата. Первая самая важная функция которую мы также будем экспортировать из DLL будет <b>Main</b>. В ней мы сначала проверяем версию VST хоста, и если она не равна нулю то переходим к созданию эффекта. Создание эффекта - это просто выделение памяти под структуру <b>ASMCrusher</b> и заполнение некоторых ее полей, а также установка свойств по умолчанию:<br />
<div class="codeblock"><table class="asm"><thead><tr><td colspan="2" id="132926623"  class="head">Assembler</td></tr></thead><tbody><tr class="li1"><td><div id="132926623" style="height: 350px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
</pre></td><td class="de1"><pre class="de1"><span class="co1">; // Вызывается при создании нового экземпляра VST эффекта</span>
proc Main c audioMaster
&nbsp;
&nbsp; &nbsp; <span class="co1">; // Проверяем версию</span>
&nbsp; &nbsp; cinvoke audioMaster<span class="sy1">,</span> <span class="nu0">0</span><span class="sy1">,</span> audioMasterVersion<span class="sy1">,</span> <span class="nu0">0</span><span class="sy1">,</span> <span class="nu0">0</span><span class="sy1">,</span> <span class="nu0">0</span><span class="sy1">,</span> <span class="nu0">0</span>
&nbsp; &nbsp; <span class="sy1">.</span>if <span class="kw4">eax</span> = <span class="nu0">0</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">ret</span>
&nbsp; &nbsp; <span class="sy1">.</span>endif
&nbsp;
&nbsp; &nbsp; stdcall CreateASMCrusher
&nbsp;
&nbsp; &nbsp; <span class="kw1">ret</span>
&nbsp;
endp
&nbsp;
<span class="co1">; // Создать объект ASMCrusher</span>
proc CreateASMCrusher uses <span class="kw4">ebx</span>
&nbsp;
&nbsp; &nbsp; stdcall MemAlloc<span class="sy1">,</span> sizeof<span class="sy1">.</span>ASMCrusher
&nbsp;
&nbsp; &nbsp; <span class="sy1">.</span>if <span class="kw4">eax</span> = <span class="nu0">0</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">ret</span>
&nbsp; &nbsp; <span class="sy1">.</span>endif
&nbsp;
&nbsp; &nbsp; <span class="kw1">mov</span> <span class="kw4">ebx</span><span class="sy1">,</span> <span class="kw4">eax</span>
&nbsp; &nbsp; <span class="kw1">lea</span> <span class="kw4">eax</span><span class="sy1">,</span> <span class="br0">&#91;</span><span class="kw4">ebx</span> <span class="sy1">+</span> ASMCrusher<span class="sy1">.</span>ae<span class="br0">&#93;</span>
&nbsp;
&nbsp; &nbsp; <span class="kw1">mov</span> <span class="br0">&#91;</span><span class="kw4">eax</span> <span class="sy1">+</span> ASMCrusher<span class="sy1">.</span>ae<span class="sy1">.</span>magic<span class="br0">&#93;</span><span class="sy1">,</span> kEffectMagic
&nbsp; &nbsp; <span class="kw1">mov</span> <span class="br0">&#91;</span><span class="kw4">eax</span> <span class="sy1">+</span> ASMCrusher<span class="sy1">.</span>ae<span class="sy1">.</span>dispatcher<span class="br0">&#93;</span><span class="sy1">,</span> Dispatcher
&nbsp; &nbsp; <span class="kw1">mov</span> <span class="br0">&#91;</span><span class="kw4">eax</span> <span class="sy1">+</span> ASMCrusher<span class="sy1">.</span>ae<span class="sy1">.</span>setParameter<span class="br0">&#93;</span><span class="sy1">,</span> SetParameter
&nbsp; &nbsp; <span class="kw1">mov</span> <span class="br0">&#91;</span><span class="kw4">eax</span> <span class="sy1">+</span> ASMCrusher<span class="sy1">.</span>ae<span class="sy1">.</span>getParameter<span class="br0">&#93;</span><span class="sy1">,</span> GetParameter
&nbsp; &nbsp; <span class="kw1">mov</span> <span class="br0">&#91;</span><span class="kw4">eax</span> <span class="sy1">+</span> ASMCrusher<span class="sy1">.</span>ae<span class="sy1">.</span>processReplacing<span class="br0">&#93;</span><span class="sy1">,</span> ProcessReplacing
&nbsp; &nbsp; <span class="kw1">mov</span> <span class="br0">&#91;</span><span class="kw4">eax</span> <span class="sy1">+</span> ASMCrusher<span class="sy1">.</span>ae<span class="sy1">.</span>numInputs<span class="br0">&#93;</span><span class="sy1">,</span> <span class="nu0">2</span>
&nbsp; &nbsp; <span class="kw1">mov</span> <span class="br0">&#91;</span><span class="kw4">eax</span> <span class="sy1">+</span> ASMCrusher<span class="sy1">.</span>ae<span class="sy1">.</span>numOutputs<span class="br0">&#93;</span><span class="sy1">,</span> <span class="nu0">2</span>
&nbsp; &nbsp; <span class="kw1">mov</span> <span class="br0">&#91;</span><span class="kw4">eax</span> <span class="sy1">+</span> ASMCrusher<span class="sy1">.</span>ae<span class="sy1">.</span>numParams<span class="br0">&#93;</span><span class="sy1">,</span> NUMBER_OF_PARAMETERS
&nbsp; &nbsp; <span class="kw1">mov</span> <span class="br0">&#91;</span><span class="kw4">eax</span> <span class="sy1">+</span> ASMCrusher<span class="sy1">.</span>ae<span class="sy1">.</span>flags<span class="br0">&#93;</span><span class="sy1">,</span> effFlagsCanReplacing <span class="kw1">OR</span> effFlagsNoSoundInStop
&nbsp; &nbsp; <span class="kw1">mov</span> <span class="br0">&#91;</span><span class="kw4">eax</span> <span class="sy1">+</span> ASMCrusher<span class="sy1">.</span>ae<span class="sy1">.</span>uniqueId<span class="br0">&#93;</span><span class="sy1">,</span> UNIQUE_ID
&nbsp; &nbsp; <span class="kw1">mov</span> <span class="br0">&#91;</span><span class="kw4">eax</span> <span class="sy1">+</span> ASMCrusher<span class="sy1">.</span>ae<span class="sy1">.</span>version<span class="br0">&#93;</span><span class="sy1">,</span> VERSION
&nbsp; &nbsp; <span class="kw1">mov</span> <span class="br0">&#91;</span><span class="kw4">eax</span> <span class="sy1">+</span> ASMCrusher<span class="sy1">.</span>ae<span class="sy1">.</span>object<span class="br0">&#93;</span><span class="sy1">,</span> <span class="kw4">ebx</span>
&nbsp;
&nbsp; &nbsp; <span class="co1">; // Загрузка значений по умолчанию</span>
&nbsp; &nbsp; <span class="kw1">mov</span> <span class="br0">&#91;</span><span class="kw4">eax</span> <span class="sy1">+</span> ASMCrusher<span class="sy1">.</span>sampleRate<span class="br0">&#93;</span><span class="sy1">,</span> <span class="nu0">44100</span>
&nbsp; &nbsp; <span class="kw1">mov</span> <span class="br0">&#91;</span><span class="kw4">eax</span> <span class="sy1">+</span> ASMCrusher<span class="sy1">.</span>volume<span class="br0">&#93;</span><span class="sy1">,</span> <span class="nu0">1.0</span>
&nbsp; &nbsp; <span class="kw1">mov</span> <span class="br0">&#91;</span><span class="kw4">eax</span> <span class="sy1">+</span> ASMCrusher<span class="sy1">.</span>downsampling<span class="br0">&#93;</span><span class="sy1">,</span> <span class="nu0">1.0</span>
&nbsp; &nbsp; <span class="kw1">mov</span> <span class="br0">&#91;</span><span class="kw4">eax</span> <span class="sy1">+</span> ASMCrusher<span class="sy1">.</span>quantize<span class="br0">&#93;</span><span class="sy1">,</span> <span class="nu0">1.0</span>
&nbsp; &nbsp; <span class="kw1">mov</span> <span class="br0">&#91;</span><span class="kw4">eax</span> <span class="sy1">+</span> ASMCrusher<span class="sy1">.</span>lValue<span class="br0">&#93;</span><span class="sy1">,</span> <span class="nu0">0.0</span>
&nbsp; &nbsp; <span class="kw1">mov</span> <span class="br0">&#91;</span><span class="kw4">eax</span> <span class="sy1">+</span> ASMCrusher<span class="sy1">.</span>rValue<span class="br0">&#93;</span><span class="sy1">,</span> <span class="nu0">0.0</span>
&nbsp; &nbsp; <span class="kw1">mov</span> <span class="br0">&#91;</span><span class="kw4">eax</span> <span class="sy1">+</span> ASMCrusher<span class="sy1">.</span>sampleCounter<span class="br0">&#93;</span><span class="sy1">,</span> <span class="nu0">0.0</span>
&nbsp;
&nbsp; &nbsp; <span class="kw1">mov</span> <span class="kw4">eax</span><span class="sy1">,</span> <span class="kw4">ebx</span>
&nbsp;
&nbsp; &nbsp; <span class="kw1">ret</span>
&nbsp;
endp</pre></td></tr></table></div></td></tr></tbody></table></div>В качестве параметра функция <b>Main</b> принимает указатель на функцию обратного вызова <b>audioMaster</b>, которую мы вызываем для того чтобы определить версию хоста. При создании объекта сначала выделяется память и заполняется члены базового интерфейса <b>AEEffect</b>, затем заполняются поля значений по умолчанию. <b>Dispatcher</b>, <b>SetParameter</b>, <b>GetParameter</b>, <b>ProcessReplacing</b> являются указателями на функции которые будут рассмотрены далее. Следующей важной функцией является функция диспетчеризации - <b>Dispatcher</b>, которая принимает  различные события от хоста:<br />
<div class="codeblock"><table class="asm"><thead><tr><td colspan="2" id="858826696"  class="head">Assembler</td></tr></thead><tbody><tr class="li1"><td><div id="858826696" style="height: 350px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
</pre></td><td class="de1"><pre class="de1"><span class="co1">; // Процедура диспетчеризации</span>
proc Dispatcher c<span class="sy1">,</span> pEffect<span class="sy1">,</span> uOpcode<span class="sy1">,</span> uIndex<span class="sy1">,</span> value<span class="sy1">,</span> lpPtr<span class="sy1">,</span> opt
&nbsp;
&nbsp; &nbsp; <span class="kw1">mov</span> <span class="kw4">ecx</span><span class="sy1">,</span> <span class="br0">&#91;</span>pEffect<span class="br0">&#93;</span>
&nbsp; &nbsp; <span class="kw1">mov</span> <span class="kw4">ecx</span><span class="sy1">,</span> <span class="br0">&#91;</span><span class="kw4">ecx</span> <span class="sy1">+</span> AEEffect<span class="sy1">.</span>object<span class="br0">&#93;</span>
&nbsp;
&nbsp; &nbsp; <span class="sy1">.</span>if <span class="br0">&#91;</span>uOpcode<span class="br0">&#93;</span> = effClose
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">; // Удалить VST эффект</span>
&nbsp; &nbsp; &nbsp; &nbsp; stdcall MemFree<span class="sy1">,</span> <span class="kw4">ecx</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">xor</span> <span class="kw4">eax</span><span class="sy1">,</span> <span class="kw4">eax</span>
&nbsp; &nbsp; <span class="sy1">.</span>elseif <span class="br0">&#91;</span>uOpcode<span class="br0">&#93;</span> = effSetSampleRate
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">; // Установить частоту дискретизации</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">mov</span> <span class="kw4">eax</span><span class="sy1">,</span> <span class="br0">&#91;</span>opt<span class="br0">&#93;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">mov</span> <span class="br0">&#91;</span><span class="kw4">ecx</span> <span class="sy1">+</span> ASMCrusher<span class="sy1">.</span>sampleRate<span class="br0">&#93;</span><span class="sy1">,</span> <span class="kw4">eax</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">xor</span> <span class="kw4">eax</span><span class="sy1">,</span> <span class="kw4">eax</span>
&nbsp; &nbsp; <span class="sy1">.</span>elseif <span class="br0">&#91;</span>uOpcode<span class="br0">&#93;</span> = effGetParamName
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">; // Получить имя параметра</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">mov</span> <span class="kw4">eax</span><span class="sy1">,</span> <span class="br0">&#91;</span>uIndex<span class="br0">&#93;</span>
&nbsp; &nbsp; &nbsp; &nbsp; invoke lstrcpynA<span class="sy1">,</span> <span class="br0">&#91;</span>lpPtr<span class="br0">&#93;</span><span class="sy1">,</span> <span class="br0">&#91;</span>PARAMS_LIST <span class="sy1">+</span> <span class="kw4">eax</span> <span class="sy1">*</span> <span class="nu0">4</span><span class="br0">&#93;</span><span class="sy1">,</span> kVstMaxParamStrLen
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">xor</span> <span class="kw4">eax</span><span class="sy1">,</span> <span class="kw4">eax</span>
&nbsp; &nbsp; <span class="sy1">.</span>elseif <span class="br0">&#91;</span>uOpcode<span class="br0">&#93;</span> = effGetParamLabel
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">; // Получить имя параметра в окне (надпись)</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">mov</span> <span class="kw4">eax</span><span class="sy1">,</span> <span class="br0">&#91;</span>uIndex<span class="br0">&#93;</span>
&nbsp; &nbsp; &nbsp; &nbsp; invoke lstrcpynA<span class="sy1">,</span> <span class="br0">&#91;</span>lpPtr<span class="br0">&#93;</span><span class="sy1">,</span> <span class="br0">&#91;</span>PARAMS_LIST <span class="sy1">+</span> <span class="kw4">eax</span> <span class="sy1">*</span> <span class="nu0">4</span><span class="br0">&#93;</span><span class="sy1">,</span> kVstMaxParamStrLen
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">xor</span> <span class="kw4">eax</span><span class="sy1">,</span> <span class="kw4">eax</span>
&nbsp; &nbsp; <span class="sy1">.</span>elseif <span class="br0">&#91;</span>uOpcode<span class="br0">&#93;</span> = effGetEffectName
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">; // Получить имя эффекта</span>
&nbsp; &nbsp; &nbsp; &nbsp; invoke lstrcpynA<span class="sy1">,</span> <span class="br0">&#91;</span>lpPtr<span class="br0">&#93;</span><span class="sy1">,</span> EFFECT_NAME<span class="sy1">,</span> kVstMaxEffectNameLen
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">xor</span> <span class="kw4">eax</span><span class="sy1">,</span> <span class="kw4">eax</span>
&nbsp; &nbsp; <span class="sy1">.</span>elseif <span class="br0">&#91;</span>uOpcode<span class="br0">&#93;</span> = effGetVendorString
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">; // Получить имя производителя</span>
&nbsp; &nbsp; &nbsp; &nbsp; invoke lstrcpynA<span class="sy1">,</span> <span class="br0">&#91;</span>lpPtr<span class="br0">&#93;</span><span class="sy1">,</span> VENDOR_NAME<span class="sy1">,</span> kVstMaxVendorStrLen
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">xor</span> <span class="kw4">eax</span><span class="sy1">,</span> <span class="kw4">eax</span>
&nbsp; &nbsp; <span class="sy1">.</span>elseif <span class="br0">&#91;</span>uOpcode<span class="br0">&#93;</span> = effGetProductString
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">; // Получить имя продукта</span>
&nbsp; &nbsp; &nbsp; &nbsp; invoke lstrcpynA<span class="sy1">,</span> <span class="br0">&#91;</span>lpPtr<span class="br0">&#93;</span><span class="sy1">,</span> PRODUCT_NAME<span class="sy1">,</span> kVstMaxProductStrLen
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">xor</span> <span class="kw4">eax</span><span class="sy1">,</span> <span class="kw4">eax</span>
&nbsp; &nbsp; <span class="sy1">.</span>elseif <span class="br0">&#91;</span>uOpcode<span class="br0">&#93;</span> = effGetVendorVersion
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">; // Получить версию</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">mov</span> <span class="kw4">eax</span><span class="sy1">,</span> VERSION
&nbsp; &nbsp; <span class="sy1">.</span>elseif <span class="br0">&#91;</span>uOpcode<span class="br0">&#93;</span> = effGetParamDisplay
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">; // Получить значение параметра (надпись)</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="sy1">.</span>if <span class="br0">&#91;</span>uIndex<span class="br0">&#93;</span> = PAR_VOLUME
&nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">; // volume * 100</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">mov</span> <span class="kw4">eax</span><span class="sy1">,</span> <span class="nu0">100.0</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">movd</span> <span class="kw4">xmm0</span><span class="sy1">,</span> <span class="kw4">eax</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">mulss</span> <span class="kw4">xmm0</span><span class="sy1">,</span> <span class="br0">&#91;</span><span class="kw4">ecx</span> <span class="sy1">+</span> ASMCrusher<span class="sy1">.</span>volume<span class="br0">&#93;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">cvtss2si</span> <span class="kw4">eax</span><span class="sy1">,</span> <span class="kw4">xmm0</span>
&nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; cinvoke sprintf<span class="sy1">,</span> <span class="br0">&#91;</span>lpPtr<span class="br0">&#93;</span><span class="sy1">,</span> <span class="br0">&#91;</span>FORMATS_LIST <span class="sy1">+</span> PAR_VOLUME <span class="sy1">*</span> <span class="nu0">4</span><span class="br0">&#93;</span><span class="sy1">,</span> <span class="kw4">eax</span>
&nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; <span class="sy1">.</span>elseif <span class="br0">&#91;</span>uIndex<span class="br0">&#93;</span> = PAR_DOWNSAMPLING
&nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; stdcall CalcDownsamplingFreq<span class="sy1">,</span> <span class="br0">&#91;</span><span class="kw4">ecx</span> <span class="sy1">+</span> ASMCrusher<span class="sy1">.</span>sampleRate<span class="br0">&#93;</span><span class="sy1">,</span> <span class="br0">&#91;</span><span class="kw4">ecx</span> <span class="sy1">+</span> ASMCrusher<span class="sy1">.</span>downsampling<span class="br0">&#93;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; cinvoke sprintf<span class="sy1">,</span> <span class="br0">&#91;</span>lpPtr<span class="br0">&#93;</span><span class="sy1">,</span> <span class="br0">&#91;</span>FORMATS_LIST <span class="sy1">+</span> PAR_DOWNSAMPLING <span class="sy1">*</span> <span class="nu0">4</span><span class="br0">&#93;</span><span class="sy1">,</span> <span class="kw4">eax</span>
&nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; <span class="sy1">.</span>elseif <span class="br0">&#91;</span>uIndex<span class="br0">&#93;</span> = PAR_QUANTIZE
&nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; stdcall CalcLevels<span class="sy1">,</span> <span class="br0">&#91;</span><span class="kw4">ecx</span> <span class="sy1">+</span> ASMCrusher<span class="sy1">.</span>quantize<span class="br0">&#93;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; cinvoke sprintf<span class="sy1">,</span> <span class="br0">&#91;</span>lpPtr<span class="br0">&#93;</span><span class="sy1">,</span> <span class="br0">&#91;</span>FORMATS_LIST <span class="sy1">+</span> PAR_QUANTIZE <span class="sy1">*</span> <span class="nu0">4</span><span class="br0">&#93;</span><span class="sy1">,</span> <span class="kw4">eax</span>
&nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; <span class="sy1">.</span>endif
&nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">xor</span> <span class="kw4">eax</span><span class="sy1">,</span> <span class="kw4">eax</span>
&nbsp; &nbsp; <span class="sy1">.</span>else
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">xor</span> <span class="kw4">eax</span><span class="sy1">,</span> <span class="kw4">eax</span>
&nbsp; &nbsp; <span class="sy1">.</span>endif
&nbsp;
&nbsp; &nbsp; <span class="kw1">ret</span>
&nbsp;
endp</pre></td></tr></table></div></td></tr></tbody></table></div>Процедура диспетчеризации принимает несколько параметров, в качестве <b>pEffect</b> передается указатель на <b>AEEffect</b> нашего VST эффекта. В параметре <b>uOpcode</b> передается идентификатор события. <b>uIndex</b> содержит индексный параметр, в нашем случае здесь содержится индекс параметра о котором хост желает получить те или иные сведения. Параметр <b>value</b> и <b>opt</b> содержат целочисленные значения специфичные для события, в параметре <b>lpPtr</b> передается указатель на данные также специфичные для события. Анализируя исходный код видим что процедура состоит из большого switch в котором перебираются идентификаторы события. При получении события <b>effClose </b>мы просто освобождаем память выделенную для нашего объекта. При получении события <b>effSetSampleRate</b> мы устанавливаем частоту дискретизации, которая используется в расчетах; параметр opt содержит <b>float</b> значение частоты дискретизации. События <b>effGetParamName</b> и <b>effGetParamLabel</b> извлекают данные из таблицы строк и записывают данные в выходной параметр <b>lpPtr</b>. Стоит отметить что длина строки ограничена <b>kVstMaxParamStrLen</b> символами. Аналогично <b>effGetEffectName</b>, <b>effGetVendorString</b>, <b>effGetProductString</b> извлекают соответствующие данные из таблиц строк. <b>effGetVendorVersion</b> просто возвращает версию. При получении события <b>effGetParamDisplay</b> мы уже анализируем индекс эффекта, для того чтобы привести значения из логического диапазона 0..1 в реальный текстового вида, который используется в качестве надписи на элементах управления VST. Если это регулятор громкости то мы просто умножаем это число на 100 и добавляем знак процента; если это частота то мы вызываем функцию <b>CalcDownsamplingFreq</b> которая преобразует частоту из диапазона <i>0..1</i> в диапазон <i>0Гц..SampleRate/2</i>, далее формируется строка с добавлением смволов <i>Hz</i>; наконец если это регулятор квантования то вызывается функция <b>CalcLevels</b> которая возвращает количество уровней исходя из диапазона <i>0..1</i> (<i>2..65536</i>). Давайте рассмотрим исходный код этих функций:<br />
<div class="codeblock"><table class="asm"><thead><tr><td colspan="2" id="694168628"  class="head">Assembler</td></tr></thead><tbody><tr class="li1"><td><div id="694168628" style="height: 350px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
</pre></td><td class="de1"><pre class="de1"><span class="co1">; // Получить реальную частоту ресемплинга на основании значения downsampling</span>
<span class="co1">; // Вычисляем по формуле int(downsampling * samplerate * 0.5)</span>
proc CalcDownsamplingFreq<span class="sy1">,</span> sampleRate<span class="sy1">,</span> downsampling
&nbsp;
&nbsp; &nbsp; <span class="kw1">mov</span> <span class="kw4">eax</span><span class="sy1">,</span> <span class="nu0">0.5</span>
&nbsp; &nbsp; <span class="kw3">movd</span> <span class="kw4">xmm0</span><span class="sy1">,</span> <span class="kw4">eax</span>
&nbsp; &nbsp; <span class="kw3">mulss</span> <span class="kw4">xmm0</span><span class="sy1">,</span> <span class="br0">&#91;</span>downsampling<span class="br0">&#93;</span>
&nbsp; &nbsp; <span class="kw3">mulss</span> <span class="kw4">xmm0</span><span class="sy1">,</span> <span class="br0">&#91;</span>sampleRate<span class="br0">&#93;</span>
&nbsp; &nbsp; <span class="kw3">cvtss2si</span> <span class="kw4">eax</span><span class="sy1">,</span> <span class="kw4">xmm0</span>
&nbsp;
&nbsp; &nbsp; <span class="kw1">ret</span>
&nbsp;
endp
&nbsp;
<span class="co1">; // Посчитать количество уровней сигнала на основании значения quantize</span>
<span class="co1">; // Вычисляем по формуле int(2 ^ (quantize * 15 + 1)))</span>
proc CalcLevels<span class="sy1">,</span> quantize
&nbsp;
&nbsp; &nbsp; <span class="kw1">mov</span> <span class="kw4">eax</span><span class="sy1">,</span> <span class="nu0">2</span>
&nbsp; &nbsp; <span class="kw1">mov</span> <span class="kw4">ecx</span><span class="sy1">,</span> <span class="nu0">15.0</span>
&nbsp; &nbsp; <span class="kw3">movss</span> <span class="kw4">xmm0</span><span class="sy1">,</span> <span class="br0">&#91;</span>quantize<span class="br0">&#93;</span>
&nbsp; &nbsp; <span class="kw3">movd</span> <span class="kw4">xmm1</span><span class="sy1">,</span> <span class="kw4">ecx</span>
&nbsp; &nbsp; <span class="kw3">mulss</span> <span class="kw4">xmm0</span><span class="sy1">,</span> <span class="kw4">xmm1</span>
&nbsp; &nbsp; <span class="kw3">cvtss2si</span> <span class="kw4">ecx</span><span class="sy1">,</span> <span class="kw4">xmm0</span>
&nbsp; &nbsp; <span class="kw1">shl</span> <span class="kw4">eax</span><span class="sy1">,</span> <span class="kw4">cl</span>
&nbsp;
&nbsp; &nbsp; <span class="kw1">ret</span>
&nbsp;
endp</pre></td></tr></table></div></td></tr></tbody></table></div>Первая функция вычисляет частоту по формуле <i>int(downsampling * samplerate * 0.5)</i>, где <b>downsampling</b> находится в диапазоне <i>[0..1]</i>. Вторая функция получает количество уровней сигнала по формуле <i>int(2<sup>quantize * 15 + 1</sup>)</i>, где <b>quantize</b> также располагается в диапазоне <i>[0..1]</i>. Эта функция оперирует 16 битными значениями, т.е. максимум получается 65536, а минимум 2. Далее рассмотрим функцию установки и получения параметров:<br />
<div class="codeblock"><table class="asm"><thead><tr><td colspan="2" id="799714357"  class="head">Assembler</td></tr></thead><tbody><tr class="li1"><td><div id="799714357" style="height: 350px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
</pre></td><td class="de1"><pre class="de1"><span class="co1">; // Получить параметр</span>
proc GetParameter c<span class="sy1">,</span> pEffect<span class="sy1">,</span> uIndex
&nbsp;
&nbsp; &nbsp; <span class="kw1">mov</span> <span class="kw4">ecx</span><span class="sy1">,</span> <span class="br0">&#91;</span>pEffect<span class="br0">&#93;</span>
&nbsp; &nbsp; <span class="kw1">mov</span> <span class="kw4">ecx</span><span class="sy1">,</span> <span class="br0">&#91;</span><span class="kw4">ecx</span> <span class="sy1">+</span> AEEffect<span class="sy1">.</span>object<span class="br0">&#93;</span>
&nbsp;
&nbsp; &nbsp; <span class="sy1">.</span>if <span class="br0">&#91;</span>uIndex<span class="br0">&#93;</span> = PAR_VOLUME
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">fld</span> <span class="br0">&#91;</span><span class="kw4">ecx</span> <span class="sy1">+</span> ASMCrusher<span class="sy1">.</span>volume<span class="br0">&#93;</span>
&nbsp; &nbsp; <span class="sy1">.</span>elseif <span class="br0">&#91;</span>uIndex<span class="br0">&#93;</span> = PAR_DOWNSAMPLING
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">fld</span> <span class="br0">&#91;</span><span class="kw4">ecx</span> <span class="sy1">+</span> ASMCrusher<span class="sy1">.</span>downsampling<span class="br0">&#93;</span>
&nbsp; &nbsp; <span class="sy1">.</span>elseif <span class="br0">&#91;</span>uIndex<span class="br0">&#93;</span> = PAR_QUANTIZE
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">fld</span> <span class="br0">&#91;</span><span class="kw4">ecx</span> <span class="sy1">+</span> ASMCrusher<span class="sy1">.</span>quantize<span class="br0">&#93;</span>
&nbsp; &nbsp; <span class="sy1">.</span>else
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">fldz</span>
&nbsp; &nbsp; <span class="sy1">.</span>endif
&nbsp;
&nbsp; &nbsp; <span class="kw1">ret</span>
endp
&nbsp;
<span class="co1">; // Установить параметр</span>
proc SetParameter c<span class="sy1">,</span> pEffect<span class="sy1">,</span> uIndex<span class="sy1">,</span> fValue
&nbsp;
&nbsp; &nbsp; <span class="kw1">mov</span> <span class="kw4">ecx</span><span class="sy1">,</span> <span class="br0">&#91;</span>pEffect<span class="br0">&#93;</span>
&nbsp; &nbsp; <span class="kw1">mov</span> <span class="kw4">ecx</span><span class="sy1">,</span> <span class="br0">&#91;</span><span class="kw4">ecx</span> <span class="sy1">+</span> AEEffect<span class="sy1">.</span>object<span class="br0">&#93;</span>
&nbsp; &nbsp; <span class="kw1">mov</span> <span class="kw4">eax</span><span class="sy1">,</span> <span class="kw6">dword</span> <span class="br0">&#91;</span>fValue<span class="br0">&#93;</span>
&nbsp;
&nbsp; &nbsp; <span class="sy1">.</span>if <span class="br0">&#91;</span>uIndex<span class="br0">&#93;</span> = PAR_VOLUME
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">mov</span> <span class="br0">&#91;</span><span class="kw4">ecx</span> <span class="sy1">+</span> ASMCrusher<span class="sy1">.</span>volume<span class="br0">&#93;</span><span class="sy1">,</span> <span class="kw4">eax</span>
&nbsp; &nbsp; <span class="sy1">.</span>elseif <span class="br0">&#91;</span>uIndex<span class="br0">&#93;</span> = PAR_DOWNSAMPLING
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">mov</span> <span class="br0">&#91;</span><span class="kw4">ecx</span> <span class="sy1">+</span> ASMCrusher<span class="sy1">.</span>downsampling<span class="br0">&#93;</span><span class="sy1">,</span> <span class="kw4">eax</span>
&nbsp; &nbsp; <span class="sy1">.</span>elseif <span class="br0">&#91;</span>uIndex<span class="br0">&#93;</span> = PAR_QUANTIZE
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">mov</span> <span class="br0">&#91;</span><span class="kw4">ecx</span> <span class="sy1">+</span> ASMCrusher<span class="sy1">.</span>quantize<span class="br0">&#93;</span><span class="sy1">,</span> <span class="kw4">eax</span>
&nbsp; &nbsp; <span class="sy1">.</span>endif
&nbsp;
&nbsp; &nbsp; <span class="kw1">ret</span>
&nbsp;
endp</pre></td></tr></table></div></td></tr></tbody></table></div>Каждый параметр в VST кодируется <b>32 bit - float </b>значением в диапазоне от 0 до 1. Здесь все просто, нужно только отметить что возвращаемое значение возвращается на вершине стека FPU. <br />
При обработке звука вызывается функция ProcessReplacing которая принимает указатель на объект, два указателя на указатели семплов и количество семплов:<br />
<div class="codeblock"><table class="asm"><thead><tr><td colspan="2" id="430629725"  class="head">Assembler</td></tr></thead><tbody><tr class="li1"><td><div id="430629725" style="height: 318px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
</pre></td><td class="de1"><pre class="de1">proc ProcessReplacing c uses <span class="kw4">esi</span> <span class="kw4">edi</span> <span class="kw4">ebx</span><span class="sy1">,</span> pEffect<span class="sy1">,</span> pInputs<span class="sy1">,</span> pOutputs<span class="sy1">,</span> sampleFrames
&nbsp;
&nbsp; &nbsp; <span class="kw1">mov</span> <span class="kw4">esi</span><span class="sy1">,</span> <span class="br0">&#91;</span>pInputs<span class="br0">&#93;</span>
&nbsp; &nbsp; <span class="kw1">mov</span> <span class="kw4">edi</span><span class="sy1">,</span> <span class="br0">&#91;</span>pOutputs<span class="br0">&#93;</span>
&nbsp; &nbsp; <span class="kw1">mov</span> <span class="kw4">ebx</span><span class="sy1">,</span> <span class="br0">&#91;</span>pEffect<span class="br0">&#93;</span>
&nbsp; &nbsp; <span class="kw1">mov</span> <span class="kw4">ebx</span><span class="sy1">,</span> <span class="br0">&#91;</span><span class="kw4">ebx</span> <span class="sy1">+</span> AEEffect<span class="sy1">.</span>object<span class="br0">&#93;</span>
&nbsp;
&nbsp; &nbsp; <span class="co1">; // Обрабатываем левый канал</span>
&nbsp; &nbsp; stdcall ApplyEffectToChannel<span class="sy1">,</span> <span class="kw4">ebx</span><span class="sy1">,</span> <span class="kw6">dword</span> <span class="br0">&#91;</span><span class="kw4">esi</span><span class="br0">&#93;</span><span class="sy1">,</span> <span class="kw6">dword</span> <span class="br0">&#91;</span><span class="kw4">edi</span><span class="br0">&#93;</span><span class="sy1">,</span> <span class="kw6">dword</span> <span class="br0">&#91;</span>sampleFrames<span class="br0">&#93;</span><span class="sy1">,</span> <span class="kw6">dword</span> <span class="br0">&#91;</span><span class="kw4">ebx</span> <span class="sy1">+</span> ASMCrusher<span class="sy1">.</span>lValue<span class="br0">&#93;</span>
&nbsp; &nbsp; <span class="kw1">mov</span> <span class="br0">&#91;</span><span class="kw4">ebx</span> <span class="sy1">+</span> ASMCrusher<span class="sy1">.</span>lValue<span class="br0">&#93;</span><span class="sy1">,</span> <span class="kw4">eax</span>
&nbsp;
&nbsp; &nbsp; <span class="co1">; // Обрабатываем правый канал</span>
&nbsp; &nbsp; stdcall ApplyEffectToChannel<span class="sy1">,</span> <span class="kw4">ebx</span><span class="sy1">,</span> <span class="kw6">dword</span> <span class="br0">&#91;</span><span class="kw4">esi</span> <span class="sy1">+</span> <span class="nu0">4</span><span class="br0">&#93;</span><span class="sy1">,</span> <span class="kw6">dword</span> <span class="br0">&#91;</span><span class="kw4">edi</span> <span class="sy1">+</span> <span class="nu0">4</span><span class="br0">&#93;</span><span class="sy1">,</span> <span class="kw6">dword</span> <span class="br0">&#91;</span>sampleFrames<span class="br0">&#93;</span><span class="sy1">,</span> <span class="kw6">dword</span> <span class="br0">&#91;</span><span class="kw4">ebx</span> <span class="sy1">+</span> ASMCrusher<span class="sy1">.</span>rValue<span class="br0">&#93;</span>
&nbsp; &nbsp; <span class="kw1">mov</span> <span class="br0">&#91;</span><span class="kw4">ebx</span> <span class="sy1">+</span> ASMCrusher<span class="sy1">.</span>rValue<span class="br0">&#93;</span><span class="sy1">,</span> <span class="kw4">eax</span>
&nbsp;
&nbsp; &nbsp; <span class="kw1">ret</span>
&nbsp;
endp</pre></td></tr></table></div></td></tr></tbody></table></div><b>pInputs</b> в нашем случае содержит указатель на два указателя (правый и левый каналы) на звуковые семплы в формате <b>32 bit float</b>, <b>pOutputs</b> - тоже самое только на выходной буфер. <b>sampleFrames</b> содержит количество семплов в канале. В качестве процедуры обработки служит процедура <b>ApplyEffectToChannel</b>:<br />
<div class="codeblock"><table class="asm"><thead><tr><td colspan="2" id="393421914"  class="head">Assembler</td></tr></thead><tbody><tr class="li1"><td><div id="393421914" style="height: 350px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
</pre></td><td class="de1"><pre class="de1"><span class="co1">; // Применить эффект к буферу</span>
<span class="co1">; // Возвращает значение семпла</span>
proc ApplyEffectToChannel uses <span class="kw4">esi</span> <span class="kw4">edi</span> <span class="kw4">ebx</span><span class="sy1">,</span> pObject<span class="sy1">,</span> pInput<span class="sy1">,</span> pOutput<span class="sy1">,</span> nCount<span class="sy1">,</span> fValue
&nbsp;
&nbsp; &nbsp; <span class="kw1">mov</span> <span class="kw4">ebx</span><span class="sy1">,</span> <span class="br0">&#91;</span>pObject<span class="br0">&#93;</span>
&nbsp; &nbsp; <span class="kw1">mov</span> <span class="kw4">esi</span><span class="sy1">,</span> <span class="br0">&#91;</span>pInput<span class="br0">&#93;</span>
&nbsp; &nbsp; <span class="kw1">mov</span> <span class="kw4">edi</span><span class="sy1">,</span> <span class="br0">&#91;</span>pOutput<span class="br0">&#93;</span>
&nbsp;
&nbsp; &nbsp; <span class="co1">; // Вычисляем количество уровней</span>
&nbsp; &nbsp; stdcall CalcLevels<span class="sy1">,</span> <span class="br0">&#91;</span><span class="kw4">ebx</span> <span class="sy1">+</span> ASMCrusher<span class="sy1">.</span>quantize<span class="br0">&#93;</span>
&nbsp; &nbsp; <span class="kw3">cvtsi2ss</span> <span class="kw4">xmm1</span><span class="sy1">,</span> <span class="kw4">eax</span>
&nbsp;
&nbsp; &nbsp; <span class="co1">; // Вычисляем количество семплов для частоты среза</span>
&nbsp; &nbsp; <span class="kw1">mov</span> <span class="kw4">eax</span><span class="sy1">,</span> <span class="nu0">2.0</span>
&nbsp; &nbsp; <span class="kw3">movd</span> <span class="kw4">xmm0</span><span class="sy1">,</span> <span class="kw4">eax</span>
&nbsp; &nbsp; <span class="kw3">divss</span> <span class="kw4">xmm0</span><span class="sy1">,</span> <span class="br0">&#91;</span><span class="kw4">ebx</span> <span class="sy1">+</span> ASMCrusher<span class="sy1">.</span>downsampling<span class="br0">&#93;</span>
&nbsp;
&nbsp; &nbsp; <span class="co1">; // Восстанавливаем регистр счетчика фильтра</span>
&nbsp; &nbsp; <span class="kw3">movss</span> <span class="kw4">xmm2</span><span class="sy1">,</span> <span class="br0">&#91;</span><span class="kw4">ebx</span> <span class="sy1">+</span> ASMCrusher<span class="sy1">.</span>sampleCounter<span class="br0">&#93;</span>
&nbsp;
&nbsp; &nbsp; <span class="co1">; // Загружаем граничные значения</span>
&nbsp; &nbsp; <span class="kw1">mov</span> <span class="kw4">eax</span><span class="sy1">,</span> <span class="nu0">1.0</span>
&nbsp; &nbsp; <span class="kw3">movd</span> <span class="kw4">xmm3</span><span class="sy1">,</span> <span class="kw4">eax</span>
&nbsp; &nbsp; <span class="kw1">mov</span> <span class="kw4">eax</span><span class="sy1">,</span> <span class="sy1">-</span><span class="nu0">1.0</span>
&nbsp; &nbsp; <span class="kw3">movd</span> <span class="kw4">xmm4</span><span class="sy1">,</span> <span class="kw4">eax</span>
&nbsp;
&nbsp; &nbsp; <span class="co1">; // Загружаем значение громкости в регистр</span>
&nbsp; &nbsp; <span class="kw3">movss</span> <span class="kw4">xmm6</span><span class="sy1">,</span> <span class="br0">&#91;</span><span class="kw4">ebx</span> <span class="sy1">+</span> ASMCrusher<span class="sy1">.</span>volume<span class="br0">&#93;</span>
&nbsp;
&nbsp; &nbsp; <span class="co1">; // Загружаем сохраненное значение семпла и применяем уровень громкости</span>
&nbsp; &nbsp; <span class="kw3">movd</span> <span class="kw4">xmm5</span><span class="sy1">,</span> <span class="br0">&#91;</span>fValue<span class="br0">&#93;</span>
&nbsp; &nbsp; <span class="kw3">mulss</span> <span class="kw4">xmm5</span><span class="sy1">,</span> <span class="kw4">xmm6</span>
&nbsp;
&nbsp; &nbsp; <span class="co1">; // Задаем количество семплов</span>
&nbsp; &nbsp; <span class="kw1">mov</span> <span class="kw4">ecx</span><span class="sy1">,</span> <span class="br0">&#91;</span>nCount<span class="br0">&#93;</span>
&nbsp;
&nbsp; &nbsp; <span class="co1">; // Проход по семплам</span>
&nbsp; &nbsp; <span class="sy1">.</span>PROCESS_SAMLE<span class="sy1">:</span>
&nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">; // Увеличиваем счетчик регистра фильтра</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">addss</span> <span class="kw4">xmm2</span><span class="sy1">,</span> <span class="kw4">xmm3</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">comiss</span> <span class="kw4">xmm2</span><span class="sy1">,</span> <span class="kw4">xmm0</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">; // Если количество семлов превышает порог, загружаем новый</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">jb</span> <span class="sy1">.</span>STORE_SAMPLE
&nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">; // Сравниваем с 1</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">comiss</span> <span class="kw4">xmm3</span><span class="sy1">,</span> <span class="kw6">dword</span> <span class="br0">&#91;</span><span class="kw4">esi</span><span class="br0">&#93;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">jb</span> <span class="sy1">.</span>SET_MAX
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">; // Сравниваем с -1</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">comiss</span> <span class="kw4">xmm4</span><span class="sy1">,</span> <span class="kw6">dword</span> <span class="br0">&#91;</span><span class="kw4">esi</span><span class="br0">&#93;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">ja</span> <span class="sy1">.</span>SET_MIN
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">movss</span> <span class="kw4">xmm5</span><span class="sy1">,</span> <span class="kw6">dword</span> <span class="br0">&#91;</span><span class="kw4">esi</span><span class="br0">&#93;</span>
&nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; <span class="sy1">.</span>CALC_SAMPLE<span class="sy1">:</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">; // Сохраняем семп в регистр edx</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">movd</span> <span class="kw4">edx</span><span class="sy1">,</span> <span class="kw4">xmm5</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">; // Вычисляем семпл по формуле int(sample * levels) / levels</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">mulss</span> <span class="kw4">xmm5</span><span class="sy1">,</span> <span class="kw4">xmm1</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">cvtss2si</span> <span class="kw4">eax</span><span class="sy1">,</span> <span class="kw4">xmm5</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">cvtsi2ss</span> <span class="kw4">xmm5</span><span class="sy1">,</span> <span class="kw4">eax</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">divss</span> <span class="kw4">xmm5</span><span class="sy1">,</span> <span class="kw4">xmm1</span>
&nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">; // Изменяем громкость</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">mulss</span> <span class="kw4">xmm5</span><span class="sy1">,</span> <span class="kw4">xmm6</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">; // Обновляем downsampling регистр</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">subss</span> <span class="kw4">xmm2</span><span class="sy1">,</span> <span class="kw4">xmm0</span>
&nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">jmp</span> <span class="sy1">.</span>STORE_SAMPLE
&nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; <span class="sy1">.</span>SET_MAX<span class="sy1">:</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">movss</span> <span class="kw4">xmm5</span><span class="sy1">,</span> <span class="kw4">xmm3</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">jmp</span> <span class="sy1">.</span>CALC_SAMPLE
&nbsp; &nbsp; &nbsp; &nbsp; <span class="sy1">.</span>SET_MIN<span class="sy1">:</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">movss</span> <span class="kw4">xmm5</span><span class="sy1">,</span> <span class="kw4">xmm4</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">jmp</span> <span class="sy1">.</span>CALC_SAMPLE
&nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; <span class="sy1">.</span>STORE_SAMPLE<span class="sy1">:</span>
&nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">; // Сохраняем текущий семпл</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">movss</span> <span class="kw6">dword</span> <span class="br0">&#91;</span><span class="kw4">edi</span><span class="br0">&#93;</span><span class="sy1">,</span> <span class="kw4">xmm5</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">add</span> <span class="kw4">edi</span><span class="sy1">,</span> <span class="nu0">4</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">add</span> <span class="kw4">esi</span><span class="sy1">,</span> <span class="nu0">4</span>
&nbsp;
&nbsp; &nbsp; <span class="kw1">loop</span> <span class="sy1">.</span>PROCESS_SAMLE
&nbsp;
&nbsp; &nbsp; <span class="co1">; // Сохраняем значения</span>
&nbsp; &nbsp; <span class="kw3">movd</span> <span class="br0">&#91;</span><span class="kw4">ebx</span> <span class="sy1">+</span> ASMCrusher<span class="sy1">.</span>sampleCounter<span class="br0">&#93;</span><span class="sy1">,</span> <span class="kw4">xmm2</span>
&nbsp;
&nbsp; &nbsp; <span class="co1">; // Возвращаем значение семпла</span>
&nbsp; &nbsp; <span class="kw1">mov</span> <span class="kw4">eax</span><span class="sy1">,</span> <span class="kw4">edx</span>
&nbsp;
&nbsp; &nbsp; <span class="kw1">ret</span>
&nbsp;
endp</pre></td></tr></table></div></td></tr></tbody></table></div>Эта процедура работает по алгоритмам описаным выше. Стоит отметить что для ускорения большинство действий выполняются в регистрах, на выходе тоолько значения сохраняются в объект для последующего восстановления состояния. Регистр <b>xmm0</b> содержит количество семплов которые необходимо повторять (удержать) чтобы получить необходимую частоту среза. <b>xmm1</b> содержит количество уровней квантования. <b>xmm3</b> и <b>xmm4</b> содержат константы 1 и -1 которые нужны для проверки выхода за диапазон допустимых значений. <b>xmm6</b> содержит текуще значение громкости, <b>xmm5</b> содержит текущее значение семпла умноженное на громкость. <b>xmm2</b> - счетчик семплов. <b>edx</b> содержит текущее значение семпла без применения умножения громкости. Остальное все понятно из кода и пиведенного в начале описания алгоритма.  <br />
Все, пробуем компилировать, и если все выполнено без ошибок в папке с исходником появится DLL. Эту DLL можно теперь подключать к любому хосту. Здесь я приведу несколько примеров GUI хостов:<br />
<a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=4031&amp;d=1480204851" rel="Lightbox" id="attachment4031" ><img src="https://www.cyberforum.ru/blog_attachment.php?attachmentid=4031&amp;thumb=1&amp;d=1480204851" class="thumbnail" border="0" alt="Нажмите на изображение для увеличения
Название: test.png
Просмотров: 1613
Размер:	39.3 Кб
ID:	4031" style="margin: 5px" /></a><br />
Исходник прикреплен к сообщению. Всем спасибо за внимание!<br />
С уважением,<br />
Кривоус Анатолий (The trick).</div>


<!-- attachments -->
	<div style="margin-top:10px">

		
		
		
		
			<fieldset class="fieldset">
				<legend>Вложения</legend>
				<table cellpadding="0" cellspacing="3" border="0">
				<tr>
	<td><img loading="lazy" decoding="async" class="inlineimg" src="http://www.cyberforum.ru//cyberstatic.net/images/attach/zip.gif" alt="Тип файла: zip" width="16" height="16" border="0" style="vertical-align:baseline" /></td>
	<td><a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=4032&amp;d=1480205267">ASMCrusher.zip</a> (5.1 Кб, 555 просмотров)</td>
</tr>
				</table>
			</fieldset>
		

	</div>
<!-- / attachments -->
]]></content:encoded>
			<dc:creator>The trick</dc:creator>
			<guid isPermaLink="true">https://www.cyberforum.ru/blogs/354370/4494.html</guid>
		</item>
		<item>
			<title>Trick Advanced Tools.</title>
			<link>https://www.cyberforum.ru/blogs/354370/4409.html</link>
			<pubDate>Wed, 28 Sep 2016 08:03:30 GMT</pubDate>
			<description>Всем привет! 
Представляю вашему вниманию небольшую разработку - Add-in который позволяет в...</description>
			<content:encoded><![CDATA[<div>Всем привет!<br />
Представляю вашему вниманию небольшую разработку - Add-in который позволяет в некоторой степени облегчить отладку некоторых программ, а также расширяет возможности компиляции. Все исходные коды прилагаются.<br />
Данный Add-in имеет следующие возможности:<ul><li>Исправляет баг с <u>Not Not Array</u> после которого часто выскакивала ошибка <b>&quot;Expression too complex&quot;</b> если начать работать с вещественными числами;</li>
<li>Позволяет использовать константы условной компиляции автоматически в зависимости от режима работы (IDE/EXE), как например в C++ (<b>NDEBUG</b>);</li>
<li>Позволяет отключать проверку переполнения целочисленных операций в IDE;</li>
<li>Позволяет отключать проверку операций с плавающей точкой в IDE;</li>
<li>Позволяет отключать проверку границ массивов в IDE;</li>
<li>Предоставляет события компиляции/линковки (для компиляции также в режиме работы в IDE/EXE), т.е. можно выполнять команды до и после этих событий. Используя  эти события можно делать много чего полезного (шифрование, подмену OBJ файлов, статическую линковку и т.п)</li>
</ul><b><br />
<font size="6">Как это работает?</font></b><br />
<br />
<a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=3981&amp;d=1475050943" rel="Lightbox" id="attachment3981" ><img src="https://www.cyberforum.ru/blog_attachment.php?attachmentid=3981&amp;thumb=1&amp;d=1475050943" class="thumbnail" border="0" alt="Нажмите на изображение для увеличения
Название: TAT_screen.png
Просмотров: 1081
Размер:	27.4 Кб
ID:	3981" style="margin: 5px" /></a><br />
<br />
Для исправления бага <u>Not Not</u>, а также отключения проверок используется модуль замены обработчиков опкодов (P-code) на наши с отключенными проверками. Сначала ищется в таблица опкодов по сигнатуре в секции <b>ENGINE</b> модуля <b>vba6.dll</b>. Опкоды бывают одно и двух байтовые. Однобайтовые опкоды имеют номер меньший <b>0xFB</b>, остальные двухбайтовые. Для поиска таблиц я использовал дизассемблер длин от <b>Ms-Rem</b>'а который я портировал на VB6. Также ищется процедура обработки перехода на следующий опкод, а также адрес процедуры генерации ошибки. Поскольку очень легко теперь стало уронить VB, я все-таки оставил некоторые проверки. К примеру обращение к неинициализированному массиву неизбежно вызовет креш - такие ситуации обрабатываются обработчиками.  Поскольку не существует (по крайней мере я не нашел) никакой официальной документации по опкодам VB6, все исследования я делал сам, поэтому какие-либо опкоды могут вызвать ошибки. В этом случае пишите примеры сюда - я добавлю обработчики.<br />
Для остальных фичей используется обычный сплайсинг функций: <ul><li><b>TipCompileProject</b>;</li>
<li><b>TipCompileProjectFull</b>;</li>
<li><b>TipMakeExe2</b>;</li>
<li><b>TipFinishExe2</b>.</li>
</ul>Для установки/получения констант условной компиляции есть функции <b>TipSetConstantValues</b>/<b>TipGetConstantValues</b> из VBA6.dll. В качестве событий используется обычный вызов <b>ShellExecuteEx</b>. Существуют события до начала компиляции проекта, после (в IDE); тоже самое для (EXE) + до начала и после линковки.<br />
Проект очень слабо тестировался, поэтому могут быть многочисленные баги - пишите сюда. <br />
Всем спасибо за внимание!</div>


<!-- attachments -->
	<div style="margin-top:10px">

		
		
		
		
			<fieldset class="fieldset">
				<legend>Вложения</legend>
				<table cellpadding="0" cellspacing="3" border="0">
				<tr>
	<td><img loading="lazy" decoding="async" class="inlineimg" src="http://www.cyberforum.ru//cyberstatic.net/images/attach/zip.gif" alt="Тип файла: zip" width="16" height="16" border="0" style="vertical-align:baseline" /></td>
	<td><a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=3982&amp;d=1475154412">TrickAdvancedTools.zip</a> (72.5 Кб, 444 просмотров)</td>
</tr>
				</table>
			</fieldset>
		

	</div>
<!-- / attachments -->
]]></content:encoded>
			<dc:creator>The trick</dc:creator>
			<guid isPermaLink="true">https://www.cyberforum.ru/blogs/354370/4409.html</guid>
		</item>
		<item>
			<title>Загрузчик, шеллкод, без рантайма... (часть 2)</title>
			<link>https://www.cyberforum.ru/blogs/354370/4392.html</link>
			<pubDate>Wed, 14 Sep 2016 16:28:40 GMT</pubDate>
			<description><![CDATA[Первая часть ->. (https://www.cyberforum.ru/blogs/354370/blog4391.html) 
После извлечения файлов...]]></description>
			<content:encoded><![CDATA[<div><a href="https://www.cyberforum.ru/blogs/354370/blog4391.html">Первая часть -&gt;.</a><br />
После извлечения файлов вызывается функция <b>ExecuteProcess</b> которая запускает выполнение команд используя функцию <b>ShellExecuteEx</b>:<br />
<div class="codeblock"><table class="vb"><thead><tr><td colspan="2" id="788388193"  class="head">Visual Basic</td></tr></thead><tbody><tr class="li1"><td><div id="788388193" style="height: 350px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
</pre></td><td class="de1"><pre class="de1"><span class="co1">' // Execution command process</span>
<span class="kw2">Function</span> ExecuteProcess() <span class="kw4">As</span> <span class="kw1">Boolean</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> index &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>: &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">Dim</span> bItem &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> BinExecListItem
&nbsp; &nbsp; <span class="kw4">Dim</span> pPath &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>: &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">Dim</span> pErrMsg &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> shInfo &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> SHELLEXECUTEINFO: &nbsp; &nbsp;<span class="kw4">Dim</span> pTempString <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> pItem &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>: &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">Dim</span> status &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp;
&nbsp; &nbsp; <span class="co1">' // Set pointer and size</span>
 &nbsp; &nbsp;shInfo.cbSize = Len(shInfo)
&nbsp; &nbsp; pItem = pExecutesTable
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="co1">' // Go thru all items</span>
 &nbsp; &nbsp;<span class="kw3">For</span> index = 0 <span class="kw3">To</span> ProjectDesc.execListDescriptor.dwNumberOfItems - 1
&nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' // Copy item</span>
 &nbsp; &nbsp; &nbsp; &nbsp;CopyMemory bItem, <span class="kw4">ByVal</span> pItem, ProjectDesc.execListDescriptor.dwSizeOfItem
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' // Set pointer to next item</span>
 &nbsp; &nbsp; &nbsp; &nbsp;pItem = pItem + ProjectDesc.execListDescriptor.dwSizeOfItem
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' // Normalize path</span>
 &nbsp; &nbsp; &nbsp; &nbsp;pPath = NormalizePath(pStringsTable + bItem.ofstFileName, 0)
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' // Fill SHELLEXECUTEINFO</span>
 &nbsp; &nbsp; &nbsp; &nbsp;shInfo.lpFile = pPath
&nbsp; &nbsp; &nbsp; &nbsp; shInfo.lpParameters = pStringsTable + bItem.ofstParameters
&nbsp; &nbsp; &nbsp; &nbsp; shInfo.fMask = SEE_MASK_NOCLOSEPROCESS <span class="kw3">Or</span> SEE_MASK_FLAG_NO_UI
&nbsp; &nbsp; &nbsp; &nbsp; shInfo.nShow = SW_SHOWDEFAULT
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' // Performing...</span>
 &nbsp; &nbsp; &nbsp; &nbsp;status = ShellExecuteEx(shInfo)
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' // If error occurs show notification (retry, abort, ignore)</span>
 &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw3">Do</span> <span class="kw3">Until</span> status
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> pErrMsg <span class="kw3">Then</span> SysFreeString pErrMsg: pErrMsg = 0
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' // Ignore error</span>
 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw3">If</span> bItem.dwFlags <span class="kw3">And</span> EF_IGNOREERROR <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">Exit</span> <span class="kw3">Do</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; pTempString = GetString(MID_ERROREXECUTELINE)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; pErrMsg = StrCat(pTempString, pPath)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; SysFreeString pTempString: pTempString = 0
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">Select</span> <span class="kw3">Case</span> MessageBox(0, pErrMsg, 0, MB_ICONERROR <span class="kw3">Or</span> MB_SYSTEMMODAL <span class="kw3">Or</span> MB_CANCELTRYCONTINUE)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">Case</span> MESSAGEBOXRETURN.IDCONTINUE: <span class="kw2">Exit</span> <span class="kw3">Do</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">Case</span> MESSAGEBOXRETURN.IDTRYAGAIN
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">Case</span> <span class="kw3">Else</span>: <span class="kw3">GoTo</span> CleanUp
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">Select</span>
&nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; status = ShellExecuteEx(shInfo)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">Loop</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' // Wait for process terminaton</span>
 &nbsp; &nbsp; &nbsp; &nbsp;WaitForSingleObject shInfo.hProcess, INFINITE
&nbsp; &nbsp; &nbsp; &nbsp; CloseHandle shInfo.hProcess
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">Next</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="co1">' // Success</span>
 &nbsp; &nbsp;ExecuteProcess = <span class="kw5">True</span>
&nbsp; &nbsp; 
CleanUp:
&nbsp;
&nbsp; &nbsp; <span class="kw3">If</span> pTempString <span class="kw3">Then</span> SysFreeString pTempString
&nbsp; &nbsp; <span class="kw3">If</span> pErrMsg <span class="kw3">Then</span> SysFreeString pErrMsg
&nbsp; &nbsp; <span class="kw3">If</span> pPath <span class="kw3">Then</span> SysFreeString pPath
&nbsp; &nbsp; 
<span class="kw3">End</span> <span class="kw2">Function</span></pre></td></tr></table></div></td></tr></tbody></table></div>Эта функция похожа на предыдущую за исключением того что здесь используется функция <b>ShellExecuteEx</b> вместо извлечения. Обратите внимание что каждая операция выполняется синхронно, т.е. каждый вызов процедуры <b>ShellExecuteEx</b> ждет окончания выполнения команды.<br />
Если предыдущая функция выполнилась успешно тогда вызывается функция <b>RunProcess</b> которая подготовливает данные для исполнения главного исполняемого файла из памяти:<br />
<div class="codeblock"><table class="vb"><thead><tr><td colspan="2" id="7552776"  class="head">Visual Basic</td></tr></thead><tbody><tr class="li1"><td><div id="7552776" style="height: 350px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
</pre></td><td class="de1"><pre class="de1"><span class="co1">' // Run exe from project in memory</span>
<span class="kw2">Function</span> RunProcess() <span class="kw4">As</span> <span class="kw1">Boolean</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> bItem &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> BinStorageListItem: &nbsp;<span class="kw4">Dim</span> Length &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> pFileData &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="co1">' // Get descriptor of executable file</span>
 &nbsp; &nbsp;CopyMemory bItem, <span class="kw4">ByVal</span> pStoragesTable + ProjectDesc.storageDescriptor.dwSizeOfItem * _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ProjectDesc.storageDescriptor.iExecutableIndex, Len(bItem)
&nbsp; &nbsp; 
&nbsp;
&nbsp; &nbsp; <span class="co1">' // Alloc memory within top memory addresses</span>
 &nbsp; &nbsp;pFileData = VirtualAlloc(<span class="kw4">ByVal</span> 0&amp;, bItem.dwSizeOfFile, MEM_TOP_DOWN <span class="kw3">Or</span> MEM_COMMIT, PAGE_READWRITE)
&nbsp; &nbsp; <span class="kw3">If</span> pFileData = 0 <span class="kw3">Then</span> <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="co1">' // Copy raw exe file to this memory</span>
 &nbsp; &nbsp;CopyMemory <span class="kw4">ByVal</span> pFileData, <span class="kw4">ByVal</span> pFilesTable + bItem.ofstBeginOfData, bItem.dwSizeOfFile
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="co1">' // Free decompressed project data</span>
 &nbsp; &nbsp;HeapFree GetProcessHeap(), HEAP_NO_SERIALIZE, pProjectData
&nbsp; &nbsp; pProjectData = 0
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="co1">' // Run exe from memory</span>
 &nbsp; &nbsp;RunExeFromMemory pFileData, bItem.dwFlags <span class="kw3">And</span> FF_IGNOREERROR
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="co1">' ----------------------------------------------------</span>
 &nbsp; &nbsp;<span class="co1">' // An error occurs</span>
 &nbsp; &nbsp;<span class="co1">' // Clean memory</span>
 &nbsp; &nbsp;
&nbsp; &nbsp; VirtualFree <span class="kw4">ByVal</span> pFileData, 0, MEM_RELEASE
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="co1">' // If ignore error then success</span>
 &nbsp; &nbsp;<span class="kw3">If</span> bItem.dwFlags <span class="kw3">And</span> FF_IGNOREERROR <span class="kw3">Then</span> RunProcess = <span class="kw5">True</span>
&nbsp; &nbsp; 
<span class="kw3">End</span> <span class="kw2">Function</span></pre></td></tr></table></div></td></tr></tbody></table></div>Эта процедура выделяет память в верхних областях виртуального адресного пространства (поскольку большинство EXE файлов грузятся по довольно низким адресам (обычно 0x00400000). После этого очишается память данных проекта поскольку если EXE файл запустится, то эта память не будет освобождена, затем вызывается функция <b>RunExeFromMemory</b> которая делает следующий шаг в загрузке EXE из памяти. Если по какой-либо причине загрузка EXE файла не состоялась то освобождается выделенная память и управление передается функции <b>Main</b>. Итак, для того чтобы загрузить EXE файл нам нужно освободить память загрузчика, т.е. выгрузить загрузчик. Нам нужно только оставить маленькуий кусочек кода который будет загружать EXE файл и запускать его. Для этого я решил использовать шеллкод, хотя можно использовать и DLL. Шеллкод - это маленький базонезависимый код (код который не ссылается к внешним данным). Но в любом случае нам придется обеспечить доступ к API функциям из шеллкода. Мы не можем вызывать API функции непосредственно из шеллкода поскольку наш главный исполняемый файл будет выгружен и любое обращение к таблице импорта вызовет креш. Второе ограничение - это то что инструкция <b>call</b> использует относительное смещение (это наиболее частый случай). Из этого следует что нам нужно инициализировать некие &quot;трамплины&quot; которые будут перебрасывать нас на API функции. Я решил делать это посредством <a rel="nofollow noopener noreferrer" href="https://ru.wikipedia.org/wiki/%D0%9F%D0%B5%D1%80%D0%B5%D1%85%D0%B2%D0%B0%D1%82_(%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5)" target="_blank" title="https://ru.wikipedia.org/wiki/%D0%9F%D0%B5%D1%80%D0%B5%D1%85%D0%B2%D0%B0%D1%82_(%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5)">сплайсинга</a>. Я просто заменяю первые 5 байт функции пусттышки на ассемблерную инструкцию <b>jmp</b> которая ссылается на необходимую API функцию:<br />
<div class="codeblock"><table class="vb"><thead><tr><td colspan="2" id="255949149"  class="head">Visual Basic</td></tr></thead><tbody><tr class="li1"><td><div id="255949149" style="height: 350px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
</pre></td><td class="de1"><pre class="de1"><span class="co1">' // Run EXE file by memory address</span>
<span class="kw2">Function</span> RunExeFromMemory( _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">ByVal</span> pExeData <span class="kw4">As</span> <span class="kw1">Long</span>, _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">ByVal</span> IgnoreError <span class="kw4">As</span> <span class="kw1">Boolean</span>) <span class="kw4">As</span> <span class="kw1">Boolean</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> Length &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>: &nbsp; &nbsp;<span class="kw4">Dim</span> pCode &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> pszMsg &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>: &nbsp; &nbsp;<span class="kw4">Dim</span> pMsgTable &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> index &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>: &nbsp; &nbsp;<span class="kw4">Dim</span> pCurMsg &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="co1">' // Get size of shellcode</span>
 &nbsp; &nbsp;Length = GetAddr(AddressOf ENDSHELLLOADER) - GetAddr(AddressOf BEGINSHELLLOADER)
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="co1">' // Alloc memory within top addresses</span>
 &nbsp; &nbsp;pCode = VirtualAlloc(<span class="kw4">ByVal</span> 0&amp;, Length, MEM_TOP_DOWN <span class="kw3">Or</span> MEM_COMMIT, PAGE_EXECUTE_READWRITE)
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="co1">' // Copy shellcode to allocated memory</span>
 &nbsp; &nbsp;CopyMemory <span class="kw4">ByVal</span> pCode, <span class="kw4">ByVal</span> GetAddr(AddressOf BEGINSHELLLOADER), Length
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="co1">' // Initialization of shellcode</span>
 &nbsp; &nbsp;<span class="kw3">If</span> <span class="kw3">Not</span> InitShellLoader(pCode) <span class="kw3">Then</span> <span class="kw3">GoTo</span> CleanUp
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="co1">' // Splice CallLoader function in order to call shellcode</span>
 &nbsp; &nbsp;Splice AddressOf CallLoader, pCode + GetAddr(AddressOf LoadExeFromMemory) - GetAddr(AddressOf BEGINSHELLLOADER)
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="co1">' // Check ignore errors</span>
 &nbsp; &nbsp;<span class="kw3">If</span> <span class="kw3">Not</span> IgnoreError <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' // Alloc memory for messages table</span>
 &nbsp; &nbsp; &nbsp; &nbsp;pMsgTable = VirtualAlloc(<span class="kw4">ByVal</span> 0&amp;, 1024, MEM_TOP_DOWN <span class="kw3">Or</span> MEM_COMMIT, PAGE_READWRITE)
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> pMsgTable = 0 <span class="kw3">Then</span> <span class="kw3">GoTo</span> CleanUp
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' // Skip pointers</span>
 &nbsp; &nbsp; &nbsp; &nbsp;pCurMsg = pMsgTable + EM_END * 4
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">For</span> index = 0 <span class="kw3">To</span> EM_END - 1
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' // Load message string</span>
 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;pszMsg = GetString(MSG_LOADER_ERROR + index)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> pszMsg = 0 <span class="kw3">Then</span> <span class="kw3">GoTo</span> CleanUp
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Length = SysStringLen(pszMsg)
&nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; lstrcpyn <span class="kw4">ByVal</span> pCurMsg, <span class="kw4">ByVal</span> pszMsg, Length + 1
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' // Store pointer</span>
 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;CopyMemory <span class="kw4">ByVal</span> pMsgTable + index * 4, pCurMsg, Len(pCurMsg)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' // Next message offset</span>
 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;pCurMsg = pCurMsg + (Length + 1) * 2
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; SysFreeString pszMsg
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">Next</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="co1">' // Call shellcode</span>
 &nbsp; &nbsp;CallLoader pExeData, pCode, pMsgTable
&nbsp; &nbsp; 
CleanUp:
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> pMsgTable <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; VirtualFree <span class="kw4">ByVal</span> pMsgTable, 0, MEM_RELEASE
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> pCode <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; VirtualFree <span class="kw4">ByVal</span> pCode, 0, MEM_RELEASE
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; 
<span class="kw3">End</span> <span class="kw2">Function</span></pre></td></tr></table></div></td></tr></tbody></table></div>Как видно из кода он вычисляет размер шеллкода используя разницу между крайними функциями - <b>ENDSHELLLOADER</b> и <b>BEGINSHELLLOADER</b>. Эти функции должны окружать наш шеллкод и иметь разный прототип поскольку VB6 компилятор может объединять идентичные функции. Затем выделяется память для самого шеллкода и он копируется в эту область памяти. После этого вызывается функция <b>InitShellLoader</b> которая сплайсит все функции в шеллкоде:<br />
<div class="codeblock"><table class="vb"><thead><tr><td colspan="2" id="30181818"  class="head">Visual Basic</td></tr></thead><tbody><tr class="li1"><td><div id="30181818" style="height: 350px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
</pre></td><td class="de1"><pre class="de1"><span class="co1">' // Shellcode initialization</span>
<span class="kw2">Function</span> InitShellLoader( _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">ByVal</span> pShellCode <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> <span class="kw1">Boolean</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> hLib &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>: &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">Dim</span> sName &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> sFunc &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>: &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">Dim</span> lpAddr &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> libIdx &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>: &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">Dim</span> fncIdx &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> libName <span class="kw4">As</span> MessagesID: &nbsp;<span class="kw4">Dim</span> fncName <span class="kw4">As</span> MessagesID
&nbsp; &nbsp; <span class="kw4">Dim</span> fncSpc &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>: &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">Dim</span> splAddr <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="co1">' // +----------------------------------------------------------------+</span>
 &nbsp; &nbsp;<span class="co1">' // | &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;Fixing of API addresses &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; |</span>
 &nbsp; &nbsp;<span class="co1">' // +----------------------------------------------------------------+</span>
 &nbsp; &nbsp;<span class="co1">' // | In order to call api function from shellcode i use splicing of |</span>
 &nbsp; &nbsp;<span class="co1">' // | &nbsp; &nbsp;our VB functions and redirect call to corresponding api. &nbsp; &nbsp;|</span>
 &nbsp; &nbsp;<span class="co1">' // | &nbsp; &nbsp; I did same in the code that injects to other process. &nbsp; &nbsp; &nbsp;|</span>
 &nbsp; &nbsp;<span class="co1">' // +----------------------------------------------------------------+</span>
 &nbsp; &nbsp;
&nbsp; &nbsp; splAddr = GetAddr(AddressOf tVirtualAlloc) - GetAddr(AddressOf BEGINSHELLLOADER) + pShellCode
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="co1">' // Get size in bytes between stub functions</span>
 &nbsp; &nbsp;fncSpc = GetAddr(AddressOf tVirtualProtect) - GetAddr(AddressOf tVirtualAlloc)
&nbsp;
&nbsp; &nbsp; <span class="co1">' // Use 3 library: kernel32, ntdll и user32</span>
 &nbsp; &nbsp;<span class="kw3">For</span> libIdx = 0 <span class="kw3">To</span> 2
&nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' // Get number of imported functions depending on library</span>
 &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw3">Select</span> <span class="kw3">Case</span> libIdx
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">Case</span> 0: libName = API_LIB_KERNEL32: fncIdx = 13
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">Case</span> 1: libName = API_LIB_NTDLL: &nbsp; &nbsp;fncIdx = 1
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">Case</span> 2: libName = API_LIB_USER32: &nbsp; fncIdx = 1
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">Select</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' // Get library name from resources</span>
 &nbsp; &nbsp; &nbsp; &nbsp;sName = GetString(libName): <span class="kw3">If</span> sName = 0 <span class="kw3">Then</span> <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' // Get module handle</span>
 &nbsp; &nbsp; &nbsp; &nbsp;hLib = GetModuleHandle(<span class="kw4">ByVal</span> sName): <span class="kw3">If</span> hLib = 0 <span class="kw3">Then</span> <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; &nbsp; &nbsp; SysFreeString sName
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' // Go thru functions</span>
 &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw3">Do</span> <span class="kw3">While</span> fncIdx
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; libName = libName + 1
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' // Get function name</span>
 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;sName = GetString(libName): <span class="kw3">If</span> sName = 0 <span class="kw3">Then</span> <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' // Because of GetProcAddress works with ANSI string translate it to ANSI</span>
 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;sFunc = ToAnsi(sName): <span class="kw3">If</span> sFunc = 0 <span class="kw3">Then</span> <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' // Get function address</span>
 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;lpAddr = GetProcAddress(hLib, sFunc)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; SysFreeString sName: SysFreeString sFunc
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' // Error</span>
 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw3">If</span> lpAddr = 0 <span class="kw3">Then</span> <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' // Splice stub</span>
 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;Splice splAddr, lpAddr
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' // Next stub</span>
 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;splAddr = splAddr + fncSpc
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; fncIdx = fncIdx - 1
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">Loop</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">Next</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="co1">' // Modify CallByPointer</span>
 &nbsp; &nbsp;lpAddr = GetAddr(AddressOf CallByPointer) - GetAddr(AddressOf BEGINSHELLLOADER) + pShellCode
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="co1">' // pop eax &nbsp; &nbsp;- 0x58</span>
 &nbsp; &nbsp;<span class="co1">' // pop ecx &nbsp; &nbsp;- 0x59</span>
 &nbsp; &nbsp;<span class="co1">' // push eax &nbsp; - 0x50</span>
 &nbsp; &nbsp;<span class="co1">' // jmp ecx &nbsp; &nbsp;- 0xFFE1</span>
 &nbsp; &nbsp;
&nbsp; &nbsp; CopyMemory <span class="kw4">ByVal</span> lpAddr, &amp;HFF505958, 4
&nbsp; &nbsp; CopyMemory <span class="kw4">ByVal</span> lpAddr + 4, &amp;HE1, 1
&nbsp;
&nbsp; &nbsp; <span class="co1">' // Success</span>
 &nbsp; &nbsp;InitShellLoader = <span class="kw5">True</span>
&nbsp; &nbsp; 
<span class="kw3">End</span> <span class="kw2">Function</span>
&nbsp;
<span class="co1">' // Splice function</span>
<span class="kw2">Sub</span> Splice( _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">ByVal</span> Func <span class="kw4">As</span> <span class="kw1">Long</span>, _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">ByVal</span> NewAddr <span class="kw4">As</span> <span class="kw1">Long</span>)
&nbsp; &nbsp; <span class="co1">' // Set memory permissions</span>
 &nbsp; &nbsp;VirtualProtect <span class="kw4">ByVal</span> Func, 5, PAGE_EXECUTE_READWRITE, 0
&nbsp; &nbsp; CopyMemory <span class="kw4">ByVal</span> Func, &amp;HE9, 1 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">' // JMP</span>
 &nbsp; &nbsp;CopyMemory <span class="kw4">ByVal</span> Func + 1, NewAddr - Func - 5, 4 &nbsp; &nbsp;<span class="co1">' // Relative address</span>
<span class="kw3">End</span> <span class="kw2">Sub</span></pre></td></tr></table></div></td></tr></tbody></table></div>Вначале код вычисляет смещение первого &quot;трамплина&quot; (в нашем случае это функция <b>tVirtualAlloc</b>) относительно начала шеллкода, и вычисляет расстояние (в байтах) между функциями &quot;трамплинами&quot;. Когда компилятор VB6 компилирует стандартный модуль он размещает функции в том же порядке в котором они определены в модуле. Необходимое условие - обеспечить уникальное возвращаемое значение для каждой функции. Затем код проходит по всем необходимым библиотекам (kernel32, ntdll, user32 - в этом порядке) и их функциям. Первая запись в ресурсах строк соответствует имени библиотеки за котором идут имена функций в этой библиотеке. Когда строка имени функции из ресурсов получена она транслируется в ANSI формат и вызывается функция <b>GetProcAddress</b>. Затем вызывается функция <b>Splice</b> которая собирает &quot;трамплин&quot; к необходимой функции из шеллкода. В конце модифицируется функция <b>CallByPointer</b> для того чтобы обеспечить прыжок из шеллкода на точку входа EXE файла. Далее функция <b>RunExeFromMemory</b> патчит функцию <b>CallLoader</b> для того чтобы обеспечить вызов шеллкода из загрузчика. После этой операции функция формирует таблицу сообщений об ошибках (если нужно) которая представляет из себя просто набор указателей на стоки сообщений. И наконец вызывается пропатченная <b>CallLoader</b> которая прыгает на функцию шеллкода <b>LoadExeFromMemory</b> которая больше не расположена внутри загрузчика, а находится в верхних адресах АП процесса.<br />
<br />
<div align="center"><font size="6"><b><font color="#BF4000">Внутри шеллкода.</font></b></font></div><br />
Итак, я сделал несколько функций внутри шеллкода:<ul><li><b>LoadExeFromMemory</b> - стартовая функция шеллкода;</li>
<li><b>GetImageNtHeaders</b> - возвращает структуру <b>IMAGE_NT_HEADERS</b> и ее адрес по базовому адресу;</li>
<li><b>GetDataDirectory</b> - возвращает структуру <b>IMAGE_DATA_DIRECTORY</b> и ее адрес по базовому адресу и каталоговому индексу;</li>
<li><b>EndProcess</b> - показать сообщение об ошибке (если есть такое) и завершить процесс;</li>
<li><b>ProcessSectionsAndHeaders</b> - выделить память под все заголовки (DOS, NT, секции) и все секции. Скопировать данные в секции;</li>
<li><b>ReserveMemory</b> - зарезервировать необходимую память под EXE;</li>
<li><b>ProcessRelocations</b> - настроить адреса иесли EXE был загружен не по базовому адресу;</li>
<li><b>ProcessImportTable</b> - сканировать таблицу импорта EXE файла, загрузить необходимые библиотеки и заполнить таблицу адресов импорта (IAT);</li>
<li><b>SetMemoryPermissions</b> - настроить разрешения памяти для каждой секции;</li>
<li><b>UpdateNewBaseAddress</b> - обновить новый базовый адрес в системных структурах PEB и LDR.</li>
</ul>Из-за того что нельзя использовать функцию <b>VarPtr</b>, я сделалпохожую функцию используя функцию <b>lstrcpyn</b> - <b>IntPtr</b>. Итак, функция <b>LoadExeFromMemory</b> извлекает вначале заголовок NT и проверяет архитектуру процессора, является ли PE файл исполняемым и является ли он 32-битным приложением. Если проверка прошла успешно тогда шеллкод выгружает загрузчик из памяти используя функцию <b>ZwUnmapViewOfSection</b>. Если функция выполняется успешно EXE образ загрузчика больше не находится в памяти и занимаемая им память освобождается. Отныне мы не можем напрямую вызывать API функции, теперь мы должны использовать наши &quot;трамплины&quot;:<br />
<div class="codeblock"><table class="vb"><thead><tr><td colspan="2" id="453475976"  class="head">Visual Basic</td></tr></thead><tbody><tr class="li1"><td><div id="453475976" style="height: 350px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
</pre></td><td class="de1"><pre class="de1"><span class="co1">' // Parse exe in memory</span>
<span class="kw2">Function</span> LoadExeFromMemory( _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">ByVal</span> pRawData <span class="kw4">As</span> <span class="kw1">Long</span>, _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">ByVal</span> pMyBaseAddress <span class="kw4">As</span> <span class="kw1">Long</span>, _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">ByVal</span> pErrMsgTable <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> <span class="kw1">Boolean</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> NtHdr &nbsp; <span class="kw4">As</span> IMAGE_NT_HEADERS
&nbsp; &nbsp; <span class="kw4">Dim</span> pBase &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> index &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> iError &nbsp;<span class="kw4">As</span> ERROR_MESSAGES
&nbsp; &nbsp; <span class="kw4">Dim</span> pszMsg &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="co1">' // Get IMAGE_NT_HEADERS</span>
 &nbsp; &nbsp;<span class="kw3">If</span> GetImageNtHeaders(pRawData, NtHdr) = 0 <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; iError = EM_UNABLE_TO_GET_NT_HEADERS
&nbsp; &nbsp; &nbsp; &nbsp; EndProcess pErrMsgTable, iError
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="co1">' // Check flags</span>
 &nbsp; &nbsp;<span class="kw3">If</span> NtHdr.FileHeader.Machine &lt;&gt; IMAGE_FILE_MACHINE_I386 <span class="kw3">Or</span> _
&nbsp; &nbsp; &nbsp; &nbsp;(NtHdr.FileHeader.Characteristics <span class="kw3">And</span> IMAGE_FILE_EXECUTABLE_IMAGE) = 0 <span class="kw3">Or</span> _
&nbsp; &nbsp; &nbsp; &nbsp;(NtHdr.FileHeader.Characteristics <span class="kw3">And</span> IMAGE_FILE_32BIT_MACHINE) = 0 <span class="kw3">Then</span> <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp;
&nbsp; &nbsp; <span class="co1">' // Release main EXE memory. After that main exe is unloaded from memory.</span>
 &nbsp; &nbsp;ZwUnmapViewOfSection GetCurrentProcess(), GetModuleHandle(<span class="kw4">ByVal</span> 0&amp;)
&nbsp;
&nbsp; &nbsp; <span class="co1">' // Reserve memory for EXE</span>
 &nbsp; &nbsp;iError = ReserveMemory(pRawData, pBase)
&nbsp; &nbsp; <span class="kw3">If</span> iError <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; EndProcess pErrMsgTable, iError
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="co1">' // Place data</span>
 &nbsp; &nbsp;iError = ProcessSectionsAndHeaders(pRawData, pBase)
&nbsp; &nbsp; <span class="kw3">If</span> iError <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; EndProcess pErrMsgTable, iError
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="co1">' // Update new base address</span>
 &nbsp; &nbsp;iError = UpdateNewBaseAddress(pBase)
&nbsp; &nbsp; <span class="kw3">If</span> iError <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; EndProcess pErrMsgTable, iError
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="co1">' // Import table processing</span>
 &nbsp; &nbsp;iError = ProcessImportTable(pBase)
&nbsp; &nbsp; <span class="kw3">If</span> iError <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; EndProcess pErrMsgTable, iError
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="co1">' // Relocations processing</span>
 &nbsp; &nbsp;iError = ProcessRelocations(pBase)
&nbsp; &nbsp; <span class="kw3">If</span> iError <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; EndProcess pErrMsgTable, iError
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="co1">' // Set the memory attributes</span>
 &nbsp; &nbsp;iError = SetMemoryPermissions(pBase)
&nbsp; &nbsp; <span class="kw3">If</span> iError <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; EndProcess pErrMsgTable, iError
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="co1">' // Release error message table</span>
 &nbsp; &nbsp;<span class="kw3">If</span> pErrMsgTable <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; tVirtualFree pErrMsgTable, 0, MEM_RELEASE
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="co1">' // Call entry point</span>
 &nbsp; &nbsp;CallByPointer NtHdr.OptionalHeader.AddressOfEntryPoint + pBase
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="co1">' // End process</span>
 &nbsp; &nbsp;EndProcess
&nbsp; &nbsp; 
<span class="kw3">End</span> <span class="kw2">Function</span></pre></td></tr></table></div></td></tr></tbody></table></div>Затем шеллкод вызывает функцию <b>ReserveMemory</b> показанную ниже. Эта функция извлекает заголовок NT из загружаемого EXE и пытается зарезервировать регион памяти по адресу указанному в поле <b>ImageBase</b> размера <b>SizeOfmage</b>. Если регион по какой-то причине не был выделен функция проверяет имеет ли EXE файл таблицу релокаций. Если так, тогда функция пытается выделять память по любому адресу. Информация о релокациях позволяет загрузить EXE по любому адресу отличному от <b>ImageBase</b>. Она содержит все места в EXE файле где он использует абсолютную адресацию. Мы можем потом подкорректировать эти адреса используя разницу между реальным базовым адресом и адресом указанным в поле <b>ImageBase</b>:<br />
<div class="codeblock"><table class="vb"><thead><tr><td colspan="2" id="87736672"  class="head">Visual Basic</td></tr></thead><tbody><tr class="li1"><td><div id="87736672" style="height: 350px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
</pre></td><td class="de1"><pre class="de1"><span class="co1">' // Reserve memory for EXE</span>
<span class="kw2">Function</span> ReserveMemory( _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">ByVal</span> pRawExeData <span class="kw4">As</span> <span class="kw1">Long</span>, _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">ByRef</span> pBase <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> ERROR_MESSAGES
&nbsp; &nbsp; <span class="kw4">Dim</span> NtHdr &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> IMAGE_NT_HEADERS
&nbsp; &nbsp; <span class="kw4">Dim</span> pLocBase &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> GetImageNtHeaders(pRawExeData, NtHdr) = 0 <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; ReserveMemory = EM_UNABLE_TO_GET_NT_HEADERS
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="co1">' // Reserve memory for EXE</span>
 &nbsp; &nbsp;pLocBase = tVirtualAlloc(<span class="kw4">ByVal</span> NtHdr.OptionalHeader.ImageBase, _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; NtHdr.OptionalHeader.SizeOfImage, _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; MEM_RESERVE, PAGE_EXECUTE_READWRITE)
&nbsp; &nbsp; <span class="kw3">If</span> pLocBase = 0 <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' // If relocation information not found error</span>
 &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw3">If</span> NtHdr.FileHeader.Characteristics <span class="kw3">And</span> IMAGE_FILE_RELOCS_STRIPPED <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ReserveMemory = EM_UNABLE_TO_ALLOCATE_MEMORY
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">Else</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' // Reserve memory in other region</span>
 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;pLocBase = tVirtualAlloc(<span class="kw4">ByVal</span> 0&amp;, NtHdr.OptionalHeader.SizeOfImage, _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;MEM_RESERVE, PAGE_EXECUTE_READWRITE)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> pLocBase = 0 <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ReserveMemory = EM_UNABLE_TO_ALLOCATE_MEMORY
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; pBase = pLocBase
&nbsp; &nbsp; 
<span class="kw3">End</span> <span class="kw2">Function</span></pre></td></tr></table></div></td></tr></tbody></table></div>Если при вызове функции произошла ошибка то показывается сообщение о ней и приложение завершается. В противном случае вызывается функция <b>ProcessSectionsAndHeaders</b>. Эта функция размещает все заголовки в выделенную память, извлекает информацию о всех секциях и копирует все данные в выделенную для них память. Если какая-либо секция имеет неинициализированные данные то этот регион заполняется нулями:<br />
<div class="codeblock"><table class="vb"><thead><tr><td colspan="2" id="903614870"  class="head">Visual Basic</td></tr></thead><tbody><tr class="li1"><td><div id="903614870" style="height: 350px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
</pre></td><td class="de1"><pre class="de1"><span class="co1">' // Allocate memory for sections and copy them data to there</span>
<span class="kw2">Function</span> ProcessSectionsAndHeaders( _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">ByVal</span> pRawExeData <span class="kw4">As</span> <span class="kw1">Long</span>, _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">ByVal</span> pBase <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> ERROR_MESSAGES
&nbsp;
&nbsp; &nbsp; <span class="kw4">Dim</span> iSec &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> pNtHdr &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> NtHdr &nbsp; <span class="kw4">As</span> IMAGE_NT_HEADERS
&nbsp; &nbsp; <span class="kw4">Dim</span> sec &nbsp; &nbsp; <span class="kw4">As</span> IMAGE_SECTION_HEADER
&nbsp; &nbsp; <span class="kw4">Dim</span> lpSec &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> pData &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; pNtHdr = GetImageNtHeaders(pRawExeData, NtHdr)
&nbsp; &nbsp; <span class="kw3">If</span> pNtHdr = 0 <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; ProcessSectionsAndHeaders = EM_UNABLE_TO_GET_NT_HEADERS
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="co1">' // Alloc memory for headers</span>
 &nbsp; &nbsp;pData = tVirtualAlloc(<span class="kw4">ByVal</span> pBase, NtHdr.OptionalHeader.SizeOfHeaders, MEM_COMMIT, PAGE_READWRITE)
&nbsp; &nbsp; <span class="kw3">If</span> pData = 0 <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; ProcessSectionsAndHeaders = EM_UNABLE_TO_ALLOCATE_MEMORY
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="co1">' // Copy headers</span>
 &nbsp; &nbsp;tCopyMemory pData, pRawExeData, NtHdr.OptionalHeader.SizeOfHeaders
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="co1">' // Get address of beginnig of sections headers</span>
 &nbsp; &nbsp;pData = pNtHdr + Len(NtHdr.Signature) + Len(NtHdr.FileHeader) + NtHdr.FileHeader.SizeOfOptionalHeader
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="co1">' // Go thru sections</span>
 &nbsp; &nbsp;<span class="kw3">For</span> iSec = 0 <span class="kw3">To</span> NtHdr.FileHeader.NumberOfSections - 1
&nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' // Copy section descriptor</span>
 &nbsp; &nbsp; &nbsp; &nbsp;tCopyMemory IntPtr(sec.SectionName(0)), pData, Len(sec)
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' // Alloc memory for section</span>
 &nbsp; &nbsp; &nbsp; &nbsp;lpSec = tVirtualAlloc(sec.VirtualAddress + pBase, sec.VirtualSize, MEM_COMMIT, PAGE_READWRITE)
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> lpSec = 0 <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ProcessSectionsAndHeaders = EM_UNABLE_TO_ALLOCATE_MEMORY
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' If there is initialized data</span>
 &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw3">If</span> sec.SizeOfRawData <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' // Take into account &nbsp;file alignment</span>
 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw3">If</span> sec.SizeOfRawData &gt; sec.VirtualSize <span class="kw3">Then</span> sec.SizeOfRawData = sec.VirtualSize
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' // Copy initialized data to section</span>
 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;tCopyMemory lpSec, pRawExeData + sec.PointerToRawData, sec.SizeOfRawData
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; lpSec = lpSec + sec.SizeOfRawData
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; sec.VirtualSize = sec.VirtualSize - sec.SizeOfRawData
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' // Fill remain part with zero</span>
 &nbsp; &nbsp; &nbsp; &nbsp;tFillMemory lpSec, sec.VirtualSize, 0
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' // Next section</span>
 &nbsp; &nbsp; &nbsp; &nbsp;pData = pData + Len(sec)
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">Next</span>
&nbsp; &nbsp; 
<span class="kw3">End</span> <span class="kw2">Function</span></pre></td></tr></table></div></td></tr></tbody></table></div>Затем функция <b>LoadExeFromMemory</b> вызывает функцию <b>UpdateNewBaseAddress</b> которая обновляет новый базовый адрес в user-mode системных структурах. Windows создает специальную структуру называемую <b>PEB</b> (Process Environment Block) для каждого процесса. Это очень полезная структура которая позволяет получить очень много информации о процессе. Множество API функций берут информацию из этой структуры. Для примера <b>GetModuleHandle(NULL)</b> берет возвращаемое значение из <b>PEB.ImageBaseAddress</b> или <b>GetModuleHandle(&quot;MyExeName&quot;)</b> извлекает информацию из списка загруженных модулей - <b>PEB.Ldr</b>. Нам нужно обновить эту информацию согласно новому базовому адресу для того чтобы API функции возвращали корректное значение. Вот небольшая часть структуры <b>PEB</b>:<br />
<div class="codeblock"><table class="vb"><thead><tr><td colspan="2" id="420226250"  class="head">Visual Basic</td></tr></thead><tbody><tr class="li1"><td><div id="420226250" style="height: 158px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
5
6
7
8
</pre></td><td class="de1"><pre class="de1"><span class="kw4">Type</span> PEB
&nbsp; &nbsp; NotUsed &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; Mutant &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; ImageBaseAddress &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; LoaderData &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span> <span class="co1">' // Pointer to PEB_LDR_DATA</span>
 &nbsp; &nbsp;ProcessParameters &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="co1">' // ....</span>
<span class="kw3">End</span> <span class="kw4">Type</span></pre></td></tr></table></div></td></tr></tbody></table></div>Нам интересно только поле <b>ImageBaseAddress</b> и <b>LoaderData</b>. Первое поле содержит базовый адрес EXE файла. Второе поле содержит указатель на структуру <b>PEB_LDR_DATA</b> которая описывает все загруженные модули в процессе:<br />
<div class="codeblock"><table class="vb"><thead><tr><td colspan="2" id="763980020"  class="head">Visual Basic</td></tr></thead><tbody><tr class="li1"><td><div id="763980020" style="height: 158px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
5
6
7
8
</pre></td><td class="de1"><pre class="de1"><span class="kw4">Type</span> PEB_LDR_DATA
&nbsp; &nbsp; Length &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; Initialized &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; SsHandle &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; InLoadOrderModuleList &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> LIST_ENTRY
&nbsp; &nbsp; InMemoryOrderModuleList &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> LIST_ENTRY
&nbsp; &nbsp; InInitializationOrderModuleList <span class="kw4">As</span> LIST_ENTRY
<span class="kw3">End</span> <span class="kw4">Type</span></pre></td></tr></table></div></td></tr></tbody></table></div>Эта структура содержит три двухсвязных списка что описывают каждый модуль. Список <b>InLoadOrderModuleList</b> содержит ссылки на элементы в порядке загрузки, т.е. ссылки в этом списке расположены в порядке загрузки (первый модуль в начале). Список <b>InMemoryOrderModuleList</b> тоже самое только в порядке расположения в памяти, а <b>InInitializationOrderModuleList</b> в порядке инициализации. Нам нужно получить первый элемент списка <b>InLoadOrderModuleList</b> который является указателем на структуру <b>LDR_MODULE</b>:<br />
<div class="codeblock"><table class="vb"><thead><tr><td colspan="2" id="329828742"  class="head">Visual Basic</td></tr></thead><tbody><tr class="li1"><td><div id="329828742" style="height: 270px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
</pre></td><td class="de1"><pre class="de1"><span class="kw4">Type</span> LDR_MODULE
&nbsp; &nbsp; InLoadOrderModuleList &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> LIST_ENTRY
&nbsp; &nbsp; InMemoryOrderModuleList &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> LIST_ENTRY
&nbsp; &nbsp; InInitOrderModuleList &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> LIST_ENTRY
&nbsp; &nbsp; BaseAddress &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; EntryPoint &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; SizeOfImage &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; FullDllName &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> UNICODE_STRING
&nbsp; &nbsp; BaseDllName &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> UNICODE_STRING
&nbsp; &nbsp; Flags &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; LoadCount &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Integer</span>
&nbsp; &nbsp; TlsIndex &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Integer</span>
&nbsp; &nbsp; HashTableEntry &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> LIST_ENTRY
&nbsp; &nbsp; TimeDateStamp &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw3">End</span> <span class="kw4">Type</span></pre></td></tr></table></div></td></tr></tbody></table></div>Эта структура описывает один модуль. Первый элемент списка <b>InLoadOrderModuleList</b> является описателем главного исполняемого файла. Нам нужно изменить поле <b>BaseAddress</b> на новый базовый адрес и сохранить изменения. Итак, для того чтобы получить адрес структуры <b>PEB</b> мы можем использовать функцию <b>NtQueryInformationProcess</b> которая извлекает множество полезной информации о процессе (узнать подробнее можно в книге 'Windows NT/2000 Native API Reference' by Gary Nebbett). Структура <b>PEB</b> может быть получена из структуры <b>PROCESS_BASIC_INFORMATION</b> которая описывает базовую информацию о процессе:<br />
<div class="codeblock"><table class="vb"><thead><tr><td colspan="2" id="666371986"  class="head">Visual Basic</td></tr></thead><tbody><tr class="li1"><td><div id="666371986" style="height: 158px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
5
6
7
8
</pre></td><td class="de1"><pre class="de1"><span class="kw4">Type</span> PROCESS_BASIC_INFORMATION
&nbsp; &nbsp; ExitStatus &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; PebBaseAddress &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; AffinityMask &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; BasePriority &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; UniqueProcessId &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; InheritedFromUniqueProcessId &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw3">End</span> <span class="kw4">Type</span></pre></td></tr></table></div></td></tr></tbody></table></div>Поле <b>PebBaseAddress</b> содержит адрес структуры <b>PEB</b>.<br />
Для того чтобы извлечь структуру <b>PROCESS_BASIC_INFORMATION</b> нам нужно передать в качестве параметра класса информации значение <b>ProcessBasicInformation</b>. Поскольку размер структуры может меняться в различных версиях Windows я использую кучу для извлечения структуры <b>PROCESS_BASIC_INFORMATION</b>. Если размер не подходит код увеличивает размер памяти для структуры <b>PROCESS_BASIC_INFORMATION</b> и повторяет заново пока структура не будет извлечена:<br />
<div class="codeblock"><table class="vb"><thead><tr><td colspan="2" id="137565459"  class="head">Visual Basic</td></tr></thead><tbody><tr class="li1"><td><div id="137565459" style="height: 350px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
</pre></td><td class="de1"><pre class="de1"><span class="kw2">Function</span> UpdateNewBaseAddress( _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">ByVal</span> pBase <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> ERROR_MESSAGES
&nbsp; &nbsp; <span class="kw4">Dim</span> pPBI &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>: &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">Dim</span> PBIlen &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> PBI &nbsp; &nbsp; <span class="kw4">As</span> PROCESS_BASIC_INFORMATION: &nbsp; <span class="kw4">Dim</span> cPEB &nbsp; &nbsp;<span class="kw4">As</span> PEB
&nbsp; &nbsp; <span class="kw4">Dim</span> ntstat &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> ldrData <span class="kw4">As</span> PEB_LDR_DATA
&nbsp; &nbsp; <span class="kw4">Dim</span> ldrMod &nbsp;<span class="kw4">As</span> LDR_MODULE
&nbsp; &nbsp; 
&nbsp; &nbsp; ntstat = tNtQueryInformationProcess(tGetCurrentProcess(), ProcessBasicInformation, IntPtr(PBI.ExitStatus), Len(PBI), PBIlen)
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">Do</span> <span class="kw3">While</span> ntstat = STATUS_INFO_LENGTH_MISMATCH
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; PBIlen = PBIlen * 2
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> pPBI <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; tHeapFree tGetProcessHeap(), HEAP_NO_SERIALIZE, pPBI
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; pPBI = tHeapAlloc(tGetProcessHeap(), HEAP_NO_SERIALIZE, PBIlen)
&nbsp; &nbsp; &nbsp; &nbsp; ntstat = tNtQueryInformationProcess(tGetCurrentProcess(), ProcessBasicInformation, pPBI, PBIlen, PBIlen)
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">Loop</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> ntstat &lt;&gt; STATUS_SUCCESS <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; UpdateNewBaseAddress = EM_PROCESS_INFORMATION_NOT_FOUND
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">GoTo</span> CleanUp
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> pPBI <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' // Copy to PROCESS_BASIC_INFORMATION</span>
 &nbsp; &nbsp; &nbsp; &nbsp;tCopyMemory IntPtr(PBI.ExitStatus), pPBI, Len(PBI)
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp;
&nbsp; &nbsp; <span class="co1">' // Get PEB</span>
 &nbsp; &nbsp;tCopyMemory IntPtr(cPEB.NotUsed), PBI.PebBaseAddress, Len(cPEB)
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="co1">' // Modify image base</span>
 &nbsp; &nbsp;cPEB.ImageBaseAddress = pBase
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="co1">' // Restore PEB</span>
 &nbsp; &nbsp;tCopyMemory PBI.PebBaseAddress, IntPtr(cPEB.NotUsed), Len(cPEB)
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="co1">' // Fix base address in PEB_LDR_DATA list</span>
 &nbsp; &nbsp;tCopyMemory IntPtr(ldrData.Length), cPEB.LoaderData, Len(ldrData)
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="co1">' // Get first element</span>
 &nbsp; &nbsp;tCopyMemory IntPtr(ldrMod.InLoadOrderModuleList.Flink), ldrData.InLoadOrderModuleList.Flink, Len(ldrMod)
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="co1">' // Fix base</span>
 &nbsp; &nbsp;ldrMod.BaseAddress = pBase
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="co1">' // Restore</span>
 &nbsp; &nbsp;tCopyMemory ldrData.InLoadOrderModuleList.Flink, IntPtr(ldrMod.InLoadOrderModuleList.Flink), Len(ldrMod)
&nbsp; &nbsp; 
CleanUp:
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="co1">' // Free memory</span>
 &nbsp; &nbsp;<span class="kw3">If</span> pPBI <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; tHeapFree tGetProcessHeap(), HEAP_NO_SERIALIZE, pPBI
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; 
<span class="kw3">End</span> <span class="kw2">Function</span></pre></td></tr></table></div></td></tr></tbody></table></div>После обновления базового адреса в системных структурах шеллкод вызывает функцию <b>ProcessImportTable</b> которая загружает необходимые библиотеки для работы EXE файла. Вначале извлекается директория <b>IMAGE_DIRECTORY_ENTRY_IMPORT</b> которая содержит RVA массива структур <b>IMAGE_IMPORT_DESCRIPTOR</b>:<br />
<div class="codeblock"><table class="vb"><thead><tr><td colspan="2" id="852977449"  class="head">Visual Basic</td></tr></thead><tbody><tr class="li1"><td><div id="852977449" style="height: 142px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
5
6
7
</pre></td><td class="de1"><pre class="de1"><span class="kw4">Type</span> IMAGE_IMPORT_DESCRIPTOR
&nbsp; &nbsp; Characteristics &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; TimeDateStamp &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; ForwarderChain &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; pName &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; FirstThunk &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw3">End</span> <span class="kw4">Type</span></pre></td></tr></table></div></td></tr></tbody></table></div>Каждая такая структура описывает одну DLL. Поле <b>pName</b> содержит RVA ASCIIZ строки с именем библиотеки. Поле <b>Characteristics</b> содержит RVA таблицы импортируемых функций, а поле <b>FirstThunk</b> содержит RVA таблицы адресов импорта (IAT). Таблица имен представляет из себя массив структур <b>IMAGE_THUNK_DATA</b>. Эта структура представляет из себя 32 битное значение в котором если установлен старший бит остальные биты представляют из себя ординал функции (импорт по ординалу), иначе остальные биты содержат RVA имени функции предваренной значением <b>Hint</b>. Если же структура <b>IMAGE_THUNK_DATA</b> содержит 0 то значит список имен закончен. Если все поля структуры <b>IMAGE_IMPORT_DESCRIPTOR</b> равны 0 это означает что список структур также окончен.<br />
<div class="codeblock"><table class="vb"><thead><tr><td colspan="2" id="797477925"  class="head">Visual Basic</td></tr></thead><tbody><tr class="li1"><td><div id="797477925" style="height: 350px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
</pre></td><td class="de1"><pre class="de1"><span class="co1">' // Process import table</span>
<span class="kw2">Function</span> ProcessImportTable( _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">ByVal</span> pBase <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> ERROR_MESSAGES
&nbsp; &nbsp; <span class="kw4">Dim</span> NtHdr &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> IMAGE_NT_HEADERS: &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">Dim</span> datDirectory &nbsp; &nbsp;<span class="kw4">As</span> IMAGE_DATA_DIRECTORY
&nbsp; &nbsp; <span class="kw4">Dim</span> dsc &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> IMAGE_IMPORT_DESCRIPTOR: <span class="kw4">Dim</span> hLib &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> thnk &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>: &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">Dim</span> Addr &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> fnc &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>: &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">Dim</span> pData &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> GetImageNtHeaders(pBase, NtHdr) = 0 <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; ProcessImportTable = EM_UNABLE_TO_GET_NT_HEADERS
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="co1">' // Import table processing</span>
 &nbsp; &nbsp;<span class="kw3">If</span> NtHdr.OptionalHeader.NumberOfRvaAndSizes &gt; 1 <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> GetDataDirectory(pBase, IMAGE_DIRECTORY_ENTRY_IMPORT, datDirectory) = 0 <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ProcessImportTable = EM_INVALID_DATA_DIRECTORY
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' // If import table exists</span>
 &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw3">If</span> datDirectory.Size &gt; 0 <span class="kw3">And</span> datDirectory.VirtualAddress &gt; 0 <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' // Copy import descriptor</span>
 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;pData = datDirectory.VirtualAddress + pBase
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; tCopyMemory IntPtr(dsc.Characteristics), pData, Len(dsc)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' // Go thru all descriptors</span>
 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw3">Do</span> <span class="kw3">Until</span> dsc.Characteristics = 0 <span class="kw3">And</span> _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;dsc.FirstThunk = 0 <span class="kw3">And</span> _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;dsc.ForwarderChain = 0 <span class="kw3">And</span> _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;dsc.pName = 0 <span class="kw3">And</span> _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;dsc.TimeDateStamp = 0
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> dsc.pName &gt; 0 <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' // Load needed library</span>
 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;hLib = tLoadLibrary(dsc.pName + pBase)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> hLib = 0 <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ProcessImportTable = EM_LOADLIBRARY_FAILED
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> dsc.Characteristics <span class="kw3">Then</span> fnc = dsc.Characteristics + pBase <span class="kw3">Else</span> fnc = dsc.FirstThunk + pBase
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' // Go to names table</span>
 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;tCopyMemory IntPtr(thnk), fnc, 4
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' // Go thru names table</span>
 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw3">Do</span> <span class="kw3">While</span> thnk
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' // Check import type</span>
 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw3">If</span> thnk &lt; 0 <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' // By ordinal</span>
 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;Addr = tGetProcAddress(hLib, thnk <span class="kw3">And</span> &amp;HFFFF&amp;)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">Else</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' // By name</span>
 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;Addr = tGetProcAddress(hLib, thnk + 2 + pBase)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' // Next function</span>
 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;fnc = fnc + 4
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; tCopyMemory IntPtr(thnk), fnc, 4
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; tCopyMemory dsc.FirstThunk + pBase, IntPtr(Addr), 4
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; dsc.FirstThunk = dsc.FirstThunk + 4
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">Loop</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' // Next descriptor</span>
 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;pData = pData + Len(dsc)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; tCopyMemory IntPtr(dsc.Characteristics), pData, Len(dsc)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">Loop</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
<span class="kw3">End</span> <span class="kw2">Function</span></pre></td></tr></table></div></td></tr></tbody></table></div>Функция <b>ProcessRelocation</b> вызывается после обработки импорта. Эта функция настраивает все абсолютные ссылки (если таковые имеются). Извлекается каталог <b>IMAGE_DIRECTORY_ENTRY_BASERELOC</b> который содержит RVA массива структур <b>IMAGE_BASE_RELOCATION</b>. Каждый элемент этого масива содержит настройки в пределах 4Кб относительно адреса <b>VirtualAddress</b>:<br />
<div class="codeblock"><table class="vb"><thead><tr><td colspan="2" id="56655948"  class="head">Visual Basic</td></tr></thead><tbody><tr class="li1"><td><div id="56655948" style="height: 94px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
</pre></td><td class="de1"><pre class="de1"><span class="kw4">Type</span> IMAGE_BASE_RELOCATION
&nbsp; &nbsp; VirtualAddress &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; SizeOfBlock &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw3">End</span> <span class="kw4">Type</span></pre></td></tr></table></div></td></tr></tbody></table></div>Поле <b>SizeOfBlock</b> содержит размер элемента в байтах. Массив 16-битных значений дескрипторов расположен после каждой структуры <b>IMAGE_BASE_RELOCATION</b>. Мы можем вычислить количество этих значений по формуле: <i>(SizeOfBlock - Len(IMAGE_BASE_RELOCATION)) \ Len(Integer)</i>. Каждый элемент массива дескрипторов имеет следующуюю структуру:<br />
<br />
<div align="center"><a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=3966&amp;d=1473870389" rel="Lightbox" id="attachment3966" ><img src="https://www.cyberforum.ru/blog_attachment.php?attachmentid=3966&amp;thumb=1&amp;d=1473870389" class="thumbnail" border="0" alt="Нажмите на изображение для увеличения
Название: REloc_rus.png
Просмотров: 1247
Размер:	8.7 Кб
ID:	3966" style="margin: 5px" /></a></div><br />
Верхние 4 бита содержат тип настройки. Нам интересна только настройка <b>IMAGE_REL_BASED_HIGHLOW</b> которая означает что нам нужно добавить разницу <i>(RealBaseAddress - ImageBaseAddress)</i> к значению Long которое расположено по адресу <b>VirtualAddress</b> + 12 младших бит дескриптора. Массив струкутр <b>IMAGE_BASE_RELOCATION</b> заканчивается структурой где все поля заполнены нулями:<br />
<div class="codeblock"><table class="vb"><thead><tr><td colspan="2" id="965598639"  class="head">Visual Basic</td></tr></thead><tbody><tr class="li1"><td><div id="965598639" style="height: 350px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
</pre></td><td class="de1"><pre class="de1"><span class="co1">' // Process relocations</span>
<span class="kw2">Function</span> ProcessRelocations( _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">ByVal</span> pBase <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> ERROR_MESSAGES
&nbsp; &nbsp; <span class="kw4">Dim</span> NtHdr &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> IMAGE_NT_HEADERS: &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">Dim</span> datDirectory &nbsp; &nbsp;<span class="kw4">As</span> IMAGE_DATA_DIRECTORY
&nbsp; &nbsp; <span class="kw4">Dim</span> relBase &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> IMAGE_BASE_RELOCATION: &nbsp; <span class="kw4">Dim</span> entriesCount &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> relType &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>: &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">Dim</span> dwAddress &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> dwOrig &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>: &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">Dim</span> pRelBase &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> delta &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>: &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">Dim</span> pData &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="co1">' // Check if module has not been loaded to image base value</span>
 &nbsp; &nbsp;<span class="kw3">If</span> GetImageNtHeaders(pBase, NtHdr) = 0 <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; ProcessRelocations = EM_UNABLE_TO_GET_NT_HEADERS
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; delta = pBase - NtHdr.OptionalHeader.ImageBase
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="co1">' // Process relocations</span>
 &nbsp; &nbsp;<span class="kw3">If</span> delta <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' // Get address of relocation table</span>
 &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw3">If</span> GetDataDirectory(pBase, IMAGE_DIRECTORY_ENTRY_BASERELOC, datDirectory) = 0 <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ProcessRelocations = EM_INVALID_DATA_DIRECTORY
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> datDirectory.Size &gt; 0 <span class="kw3">And</span> datDirectory.VirtualAddress &gt; 0 <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' // Copy relocation base</span>
 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;pRelBase = datDirectory.VirtualAddress + pBase
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; tCopyMemory IntPtr(relBase.VirtualAddress), pRelBase, Len(relBase)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">Do</span> <span class="kw3">While</span> relBase.VirtualAddress
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' // To first reloc chunk</span>
 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;pData = pRelBase + Len(relBase)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; entriesCount = (relBase.SizeOfBlock - Len(relBase)) \ 2
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">Do</span> <span class="kw3">While</span> entriesCount &gt; 0
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; tCopyMemory IntPtr(relType), pData, 2
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">Select</span> <span class="kw3">Case</span> (relType \ 4096) <span class="kw3">And</span> &amp;HF
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">Case</span> IMAGE_REL_BASED_HIGHLOW
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' // Calculate address</span>
 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;dwAddress = relBase.VirtualAddress + (relType <span class="kw3">And</span> &amp;HFFF&amp;) + pBase
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' // Get original address</span>
 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;tCopyMemory IntPtr(dwOrig), dwAddress, Len(dwOrig)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' // Add delta</span>
 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;dwOrig = dwOrig + delta
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' // Save</span>
 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;tCopyMemory dwAddress, IntPtr(dwOrig), Len(dwOrig)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">Select</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; pData = pData + 2
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; entriesCount = entriesCount - 1
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">Loop</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' // Next relocation base</span>
 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;pRelBase = pRelBase + relBase.SizeOfBlock
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; tCopyMemory IntPtr(relBase.VirtualAddress), pRelBase, Len(relBase)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">Loop</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp;
<span class="kw3">End</span> <span class="kw2">Function</span></pre></td></tr></table></div></td></tr></tbody></table></div>После настройки релокаций шеллкод вызывает функцию <b>SetMemoryPermissions</b> которая настраивает разрешения памяти согласно полю <b>Characteristics</b> структуры <b>IMAGE_SECTION_HEADER</b>. Для этого просто вызывается функция <b>VirtualProtect</b> с определенными атрибутами памяти:<br />
<div class="codeblock"><table class="vb"><thead><tr><td colspan="2" id="851871370"  class="head">Visual Basic</td></tr></thead><tbody><tr class="li1"><td><div id="851871370" style="height: 350px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
</pre></td><td class="de1"><pre class="de1"><span class="co1">' // Set memory permissions</span>
<span class="kw2">Private</span> <span class="kw2">Function</span> SetMemoryPermissions( _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">ByVal</span> pBase <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> ERROR_MESSAGES
&nbsp; &nbsp; <span class="kw4">Dim</span> iSec &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>: &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">Dim</span> pNtHdr &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> NtHdr &nbsp; <span class="kw4">As</span> IMAGE_NT_HEADERS: &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">Dim</span> sec &nbsp; &nbsp; <span class="kw4">As</span> IMAGE_SECTION_HEADER
&nbsp; &nbsp; <span class="kw4">Dim</span> Attr &nbsp; &nbsp;<span class="kw4">As</span> MEMPROTECT: &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">Dim</span> pSec &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> ret &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; pNtHdr = GetImageNtHeaders(pBase, NtHdr)
&nbsp; &nbsp; <span class="kw3">If</span> pNtHdr = 0 <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; SetMemoryPermissions = EM_UNABLE_TO_GET_NT_HEADERS
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp;
&nbsp; &nbsp; <span class="co1">' // Get address of first section header</span>
 &nbsp; &nbsp;pSec = pNtHdr + 4 + Len(NtHdr.FileHeader) + NtHdr.FileHeader.SizeOfOptionalHeader
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="co1">' // Go thru section headers</span>
 &nbsp; &nbsp;<span class="kw3">For</span> iSec = 0 <span class="kw3">To</span> NtHdr.FileHeader.NumberOfSections - 1
&nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' // Copy section descriptor</span>
 &nbsp; &nbsp; &nbsp; &nbsp;tCopyMemory IntPtr(sec.SectionName(0)), pSec, Len(sec)
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' // Get type</span>
 &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw3">If</span> sec.Characteristics <span class="kw3">And</span> IMAGE_SCN_MEM_EXECUTE <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> sec.Characteristics <span class="kw3">And</span> IMAGE_SCN_MEM_READ <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> sec.Characteristics <span class="kw3">And</span> IMAGE_SCN_MEM_WRITE <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Attr = PAGE_EXECUTE_READWRITE
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">Else</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Attr = PAGE_EXECUTE_READ
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">Else</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> sec.Characteristics <span class="kw3">And</span> IMAGE_SCN_MEM_WRITE <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Attr = PAGE_EXECUTE_WRITECOPY
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">Else</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Attr = PAGE_EXECUTE
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">Else</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> sec.Characteristics <span class="kw3">And</span> IMAGE_SCN_MEM_READ <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> sec.Characteristics <span class="kw3">And</span> IMAGE_SCN_MEM_WRITE <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Attr = PAGE_READWRITE
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">Else</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Attr = PAGE_READONLY
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">Else</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> sec.Characteristics <span class="kw3">And</span> IMAGE_SCN_MEM_WRITE <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Attr = PAGE_WRITECOPY
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">Else</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Attr = PAGE_NOACCESS
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' // Set memory permissions</span>
 &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw3">If</span> tVirtualProtect(sec.VirtualAddress + pBase, sec.VirtualSize, Attr, IntPtr(ret)) = 0 <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; SetMemoryPermissions = EM_UNABLE_TO_PROTECT_MEMORY
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' // Next section</span>
 &nbsp; &nbsp; &nbsp; &nbsp;pSec = pSec + Len(sec)
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">Next</span>
&nbsp; &nbsp; 
<span class="kw3">End</span> <span class="kw2">Function</span></pre></td></tr></table></div></td></tr></tbody></table></div>В конце концов очищается таблица сообщений об ошибках (если нужно) и вызывается точка входа загруженного EXE. В предыдущей версии загрузчика я выгружал шеллкод тоже, но некоторые EXE не вызывают <b>ExitProcess</b> следовательно это могло вызывать креши. Загрузчик готов.<br />
Хотя мы написал загрузчик без использвания рантайма, компилятор VB6 добавляет его все-равно поскольку все OBJ файлы имеют ссылки на MSVBVM60 во время компиляции. Нам придется удалить рантайм из таблицы импорта загрузчика вручную. Для этого я сделал специальную утилиту - <b>Patcher</b> которая ищет рантайм в таблице импорта и таблице связанного импорта и удаляет его оттуда. Эта утилита также была полезна для <a href="https://www.cyberforum.ru/blogs/354370/blog2930.html">драйвера режима ядра</a>. Я не буду описывать ее работу поскольку она использует те же концепции PE-формата что я уже описал здесь. В общем и целом мы сделали рабочий EXE который не использует MSVBVM60 на целевой машине.<br />
Для того чтобы использовать загрузчик нужно скомпилировать его затем с помощью патчера пропатчить его. После этог можно использовать его в компиляторе.<br />
 <br />
Я надеюсь вам понравилось. Спасибо за внимание!<br />
С уважением,<br />
Кривоус Анатолий (The trick).</div>


<!-- attachments -->
	<div style="margin-top:10px">

		
		
		
		
			<fieldset class="fieldset">
				<legend>Вложения</legend>
				<table cellpadding="0" cellspacing="3" border="0">
				<tr>
	<td><img loading="lazy" decoding="async" class="inlineimg" src="http://www.cyberforum.ru//cyberstatic.net/images/attach/zip.gif" alt="Тип файла: zip" width="16" height="16" border="0" style="vertical-align:baseline" /></td>
	<td><a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=3967&amp;d=1473870491">VBLoader.zip</a> (176.4 Кб, 408 просмотров)</td>
</tr>
				</table>
			</fieldset>
		

	</div>
<!-- / attachments -->
]]></content:encoded>
			<dc:creator>The trick</dc:creator>
			<guid isPermaLink="true">https://www.cyberforum.ru/blogs/354370/4392.html</guid>
		</item>
		<item>
			<title>Загрузчик, шеллкод, без рантайма... (часть 1)</title>
			<link>https://www.cyberforum.ru/blogs/354370/4391.html</link>
			<pubDate>Wed, 14 Sep 2016 16:20:15 GMT</pubDate>
			<description>ANe_8nckVbc 
Всем привет! Когда-то давно я исследовал PE-формат, в особенности EXE. Я решил создать...</description>
			<content:encoded><![CDATA[<div><div align="center"><iframe width="640" height="360" src="https://www.youtube.com/embed/ANe_8nckVbc" frameborder="0" allowfullscreen></iframe></div>Всем привет! Когда-то давно я исследовал PE-формат, в особенности EXE. Я решил создать простой загрузчик исполняемых файлов специально для VB6-скомпилированных приложений. Этот загрузчик, по моим задумкам, должен загружать любое VB6-скомпилированное приложение из памяти, миную запись в файл. ВСЕ ЭТО БЫЛО СДЕЛАНО ДЛЯ ЭКСПЕРИМЕНТАЛЬНЫХ ЦЕЛЕЙ ДЛЯ ТОГО ЧТОБЫ ПРОВЕРИТЬ ТАКУЮ ВОЗМОЖНОСТЬ НА VB6. Из-за того что VB6-скомпилированные приложения не используют большинство PE-фичей это было довольно легкой задачей. Также большинство программистов говорят что любая VB6-скомпилированная программа неукоснительно связана с VB6-рантаймом (msvbvm60) и что такая программа не будет работать без рантайма и рантайм является довольно медленным. Сегодня я докажу что можно написать приложение абсолютно не использующее  рантайм (хотя я такое уже делал в драйвере). Я думаю что это могло бы быть интересным для тех кто хочет изучить базовые принципы работы с PE файлами.<br />
Прежде чем мы начнем я бы хотел сказать пару слов о проектах. Эти проекты не тестировались достаточно хорошо, поэтому они могут содержать различные проблемы. Также загрузчик не поддерживает множество возможностей PE-файлов следовательно некоторые приложения могут не работать.<br />
Итак...<br />
Этот обзор включает три проекта:<ul><li>Compiler - самый большой проект из всех. Он позволяет создавать лаунчер базируемый на загрузчике, пользовательских файлах, командах и манифесте;</li>
<li>Loader - простейший загрузчик который выполняет команды, распаковывает файлы и запускает EXE из памяти;</li>
<li>Patcher - маленькая утилита которая удаляет рантайм из VB6-скомпилированного приложения.</li>
</ul>Я буду называть EXE что содержит команды, файлы и исполнительный файл - инсталляцией. Главная идея этой задумки - это положить информацию об инсталляции в ресурсы загрузчика. Когда загрузчик загружается он считывает эту информацию и выполняет команды из ресурсов. Я решил использовать специальное хранилище для хранения файлов и EXE и отдельное хранилище для команд.<br />
Перое хранилище хранит все файлы которые будут распакованы и главный EXE который будет запускаться из памяти. Второе хранилище хранит команды которые будут переданы в функцию <b>ShellExecuteEx</b> после процесса того как процесс распаковки будет окончен. <br />
Загрузчик поддерживает следующие подставляемые символы (для путей):<ul><li>&lt;app&gt; - путь, откуда запущен EXE;</li>
<li>&lt;win&gt; - системная директория;</li>
<li>&lt;sys&gt; - System32;</li>
<li>&lt;drv&gt; - системный диск;</li>
<li>&lt;tmp&gt; - временная директория;</li>
<li>&lt;dtp&gt; - рабочий стол.</li>
</ul><br />
<div align="center"><font size="6"><b><font color="#BF4000">Компилятор.</font></b></font><br />
<br />
<a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=3963&amp;d=1473869325" rel="Lightbox" id="attachment3963" ><img src="https://www.cyberforum.ru/blog_attachment.php?attachmentid=3963&amp;thumb=1&amp;d=1473869325" class="thumbnail" border="0" alt="Нажмите на изображение для увеличения
Название: Compiler.png
Просмотров: 817
Размер:	20.5 Кб
ID:	3963" style="margin: 5px" /></a></div><br />
Это приложение формирующее информацию для инсталляции и размещающее ее в ресурсах загрузчика. Вся информация хранится в файлах проекта. Вы можете сохранять и загружать проекты из файлов. Класс <b>clsProject</b> описывает такой проект. Компилятор содержит 3 секции: storage, execute, mainfest.<br />
Секция 'storage' позволяет добавлять файлы которые будут скопированы в момент запуска приложения. Каждая запись в списке имеет флаги: 'replace if exists', 'main executable', 'ignore error'. Если выбрана 'replace if exists' то файл будет скопирован из ресурсов даже если он есть на диске. Флаг 'main executable' может быть установлен только единственного исполняемого файла который будет запущен когда все операции будут исполнены. И наконец 'ignore error' просто заставляет игнорировать все ошибки и не выводить сообщения. Порядок расположения записей в списке соответствует порядку распаковки файлов, исключая главный исполняемый файл. Главный исполняемый файл не извлекается и запускается после всех операций. Класс <b>clsStorage</b> описывает данную секцию. Этот класс содержит коллекцию объектов класса <br />
<b>clsStorageItem</b> и дополнительные методы. Свойство <b>MainExecutable</b> определяет индекс главного исполняемого файла в хранилище. Когда этот параметр равен -1 значит главный исполняемый файл не задан. Класс <b>clsStoragaItem</b> описывает одну запись из списка хранилища, который содержит свойства определяющие поведение итема. Секция 'storage' полезна если вы хотите скопировать файлы на диск перед выполнением главного приложения (различные ресурсы/OCX/DLL и т.п.).<br />
Следующая секция называется 'execute'. Она содержит список выполняемых команд. Эти команды просто передаются в функцию <b>ShellExecuteEx</b>. Таким образом можно к примеру зарегистрировать библиотеки или сделать что-то еще. Каждый элемент этого списка имеет два свойства: путь и параметры. Стоит отметить что все команды выполняються синхронно в порядке заданным в списке. Также каждый элемент списка может иметь флаг 'ignore error' который предотвращает вывод каких-либо сообщений об ошибках. Секция 'execute' представлена двумя классами <b>clsExecute</b> and <b>clsExecuteItem</b> которые очень похожи на классы хранилища.<br />
Последняя секция - 'manifest'. Это просто текстовый файл который добавляеться в финальный файл в качестве манифеста. Для того чтобы включить манифест в EXE нужно просто выбрать флажок 'include manifest' во вкладке 'mainfest'. Это может быть полезно для использования библиотек без регистрации, визуальных стилей и т.п.<br />
Все классы ссылаються на объект проекта (<b>clsProject</b>) который управляет ими. Каждый класс который ссылается на проект может быть сохранен или заружен используя <b>PropertyBag</b> в качестве контейнера. Все ссылки сохраняються с относительными путями (как в .vbp файле) поэтому можно перемещать папку с проектом без проблем с путями. Для того чтобы транслировать из/то относительного/абсолютного пути я использовал функции <b>PathRelativePathTo</b> и <b>PathCanonicalize</b>.<br />
Итак, это была базовая информация о проекте Compiler. Сейчас я расскажу о процедуре компиляции. Как я уже сказал вся информация об инсталляции сохраняется в ресурсы загрузчика. Вначале на нужно определить формат данных:<br />
<div class="codeblock"><table class="vb"><thead><tr><td colspan="2" id="793875063"  class="head">Visual Basic</td></tr></thead><tbody><tr class="li1"><td><div id="793875063" style="height: 350px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
</pre></td><td class="de1"><pre class="de1"><span class="co1">' // Storage list item</span>
<span class="kw2">Private</span> <span class="kw4">Type</span> BinStorageListItem
&nbsp; &nbsp; ofstFileName &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">' // Offset of file name</span>
 &nbsp; &nbsp;ofstDestPath &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">' // Offset of file path</span>
 &nbsp; &nbsp;dwSizeOfFile &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">' // Size of file</span>
 &nbsp; &nbsp;ofstBeginOfData &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">' // Offset of beginning data</span>
 &nbsp; &nbsp;dwFlags &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> FileFlags &nbsp; &nbsp; &nbsp; <span class="co1">' // Flags</span>
<span class="kw3">End</span> <span class="kw4">Type</span>
&nbsp;
<span class="co1">' // Execute list item</span>
<span class="kw2">Private</span> <span class="kw4">Type</span> BinExecListItem
&nbsp; &nbsp; ofstFileName &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">' // Offset of file name</span>
 &nbsp; &nbsp;ofstParameters &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">' // Offset of parameters</span>
 &nbsp; &nbsp;dwFlags &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> ExeFlags &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">' // Flags</span>
<span class="kw3">End</span> <span class="kw4">Type</span>
&nbsp;
<span class="co1">' // Storage descriptor</span>
<span class="kw2">Private</span> <span class="kw4">Type</span> BinStorageList
&nbsp; &nbsp; dwSizeOfStructure &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">' // Size of structure</span>
 &nbsp; &nbsp;iExecutableIndex &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">' // Index of main executable</span>
 &nbsp; &nbsp;dwSizeOfItem &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">' // Size of BinaryStorageItem structure</span>
 &nbsp; &nbsp;dwNumberOfItems &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">' // Number of files in storage</span>
<span class="kw3">End</span> <span class="kw4">Type</span>
&nbsp;
<span class="co1">' // Execute list descriptor</span>
<span class="kw2">Private</span> <span class="kw4">Type</span> BinExecList
&nbsp; &nbsp; dwSizeOfStructure &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">' // Size of structure</span>
 &nbsp; &nbsp;dwSizeOfItem &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">' // Size of BinaryExecuteItem structure</span>
 &nbsp; &nbsp;dwNumberOfItems &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">' // Number of items</span>
<span class="kw3">End</span> <span class="kw4">Type</span>
&nbsp;
<span class="co1">' // Base information about project</span>
<span class="kw2">Private</span> <span class="kw4">Type</span> BinProject
&nbsp; &nbsp; dwSizeOfStructure &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">' // Size of structure</span>
 &nbsp; &nbsp;storageDescriptor &nbsp; <span class="kw4">As</span> BinStorageList &nbsp;<span class="co1">' // Storage descriptor</span>
 &nbsp; &nbsp;execListDescriptor &nbsp;<span class="kw4">As</span> BinExecList &nbsp; &nbsp; <span class="co1">' // Command descriptor</span>
 &nbsp; &nbsp;dwStringsTableLen &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">' // Size of strings table</span>
 &nbsp; &nbsp;dwFileTableLen &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">' // Size of data table</span>
<span class="kw3">End</span> <span class="kw4">Type</span></pre></td></tr></table></div></td></tr></tbody></table></div>Структура <b>BinProject</b> размещается в начале ресурсов. Заметьте что проект сохраняется как <b>RT_RCDATA</b> с именем <b>PROJECT</b>. Поле <b>dwSizeOfStructure</b> определяет размер структуры <b>BinProject</b>. <b>storageDescriptor</b> и <b>execListDescriptor</b> определяют описатели хранилища и команд соответственно. Поле <b>dwStringsTableLen</b> показывает размер строковой таблицы. Строковая таблица содержит все имена и команды в формате UNICODE. Поле <b>dwFileTableLen</b> определяет размер всех данных в хранилище. И хранилище <b>BinStorageList</b> и списки команд <b>BinExecList</b> также имеют поля <b>dwSizeOfItem</b> и <b>dwSizeOfStructure</b> которые определяют размер структуры описателя и размер одного элемента в списке. Эти структуры также содержат поле <b>dwNumberOfItems</b> которое показывает количество элементов в списке. Поле <b>iExecutableIndex</b> содержит индекс исполняемого файла в хранилище. Общая структура показана на рисунке:<br />
<br />
<div align="center"><a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=3964&amp;d=1473869325" rel="Lightbox" id="attachment3964" ><img src="https://www.cyberforum.ru/blog_attachment.php?attachmentid=3964&amp;thumb=1&amp;d=1473869325" class="thumbnail" border="0" alt="Нажмите на изображение для увеличения
Название: BinProject_rus.png
Просмотров: 475
Размер:	65.1 Кб
ID:	3964" style="margin: 5px" /></a></div><br />
Любой элемент может ссылаться на таблицу строк и таблицу файлов. Для этой цели используется смещение относительно начала таблицы. Все итемы расположены одна за другой. Теперь мы знаем внутренний формат проекта и можем поговорить о том как постороить загрузчик который будет содержать эти данные. Как я уже сказал мы сохраняем данные в ресурсы загрузчика. О самом загрузчике я расскажу позднее, а сейчас я хотел бы заметить одну важную особенность. Когда мы ложим данные проекта в EXE файл загрузчика то это не затрагивает другие данные в ресурсах. Для примера, если запустить такой EXE то информация хранящаяся в ресурсах внутреннего EXE не будет загружена. Тоже самое относится к иконкам и версии приложения. Для избежания данных проблем нужно скопировать все ресурсы из внутреннего EXE в загрузчик. WinAPI предоставляет набор функций для замены ресурсов. Для того чтобы получить список ресурсов нам нужно распарсить EXE файл и извлечь данные. Я написал функцию <b>LoadResources</b> которая извлекает все ресурсы EXE файла в массив.<br />
<br />
<div align="center"><font size="6"><b><font color="#BF4000">PE формат.</font></b></font></div><br />
Для того чтобы получить ресурсы из EXE файла, запустить EXE из памяти и хорошо разбираться в структуре EXE фала мы должны изучить PE (portable executable) формат. PE формат имеет довольно сложную структуру. Когда загрузчик запускает PE file (exe или dll) он делает довольно много работы. Каждый PE файл начинается со специальной структуры <b>IMAGE_DOS_HEADER</b> aka. DOS-заглушка. Поскольку и DOS и Windows приложения имеют расширение exe существует возможность запуска exe файла в DOS, но если попытаться сделать это в DOS то он выполнит это заглушку. Обычно в этом случае показываетсясообщение: &quot;This program cannot be run in DOS mode&quot;, но мы можем написать там любую программу:<br />
<br />
<div align="center"><a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=3965&amp;d=1473869325" rel="Lightbox" id="attachment3965" ><img src="https://www.cyberforum.ru/blog_attachment.php?attachmentid=3965&amp;thumb=1&amp;d=1473869325" class="thumbnail" border="0" alt="Нажмите на изображение для увеличения
Название: DOs.png
Просмотров: 664
Размер:	1.4 Кб
ID:	3965" style="margin: 5px" /></a></div><br />
<div class="codeblock"><table class="vb"><thead><tr><td colspan="2" id="29372413"  class="head">Visual Basic</td></tr></thead><tbody><tr class="li1"><td><div id="29372413" style="height: 350px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
</pre></td><td class="de1"><pre class="de1"><span class="kw4">Type</span> IMAGE_DOS_HEADER
&nbsp; &nbsp; e_magic &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Integer</span>
&nbsp; &nbsp; e_cblp &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Integer</span>
&nbsp; &nbsp; e_cp &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Integer</span>
&nbsp; &nbsp; e_crlc &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Integer</span>
&nbsp; &nbsp; e_cparhdr &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Integer</span>
&nbsp; &nbsp; e_minalloc &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Integer</span>
&nbsp; &nbsp; e_maxalloc &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Integer</span>
&nbsp; &nbsp; e_ss &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Integer</span>
&nbsp; &nbsp; e_sp &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Integer</span>
&nbsp; &nbsp; e_csum &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Integer</span>
&nbsp; &nbsp; e_ip &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Integer</span>
&nbsp; &nbsp; e_cs &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Integer</span>
&nbsp; &nbsp; e_lfarlc &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Integer</span>
&nbsp; &nbsp; e_ovno &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Integer</span>
&nbsp; &nbsp; e_res(0 <span class="kw3">To</span> 3) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Integer</span>
&nbsp; &nbsp; e_oemid &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Integer</span>
&nbsp; &nbsp; e_oeminfo &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Integer</span>
&nbsp; &nbsp; e_res2(0 <span class="kw3">To</span> 9) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Integer</span>
&nbsp; &nbsp; e_lfanew &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw3">End</span> <span class="kw4">Type</span></pre></td></tr></table></div></td></tr></tbody></table></div>Но поскольку мы не пишем DOS программы для нас эта структура не важна. Нам интересно только поля <b>e_magic</b> и <b>e_lfanew</b>. Первое поле должно содержать сигнатуру 'MZ' aka. <b>IMAGE_DOS_SIGNATURE</b> а второе смещение до очень важной структуры <b>IMAGE_NT_HEADERS</b>:<br />
<div class="codeblock"><table class="vb"><thead><tr><td colspan="2" id="329236709"  class="head">Visual Basic</td></tr></thead><tbody><tr class="li1"><td><div id="329236709" style="height: 110px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
5
</pre></td><td class="de1"><pre class="de1"><span class="kw4">Type</span> IMAGE_NT_HEADERS
&nbsp; &nbsp; Signature &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; FileHeader &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> IMAGE_FILE_HEADER
&nbsp; &nbsp; OptionalHeader &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> IMAGE_OPTIONAL_HEADER
<span class="kw3">End</span> <span class="kw4">Type</span></pre></td></tr></table></div></td></tr></tbody></table></div>Первое поле этой структуры содержит сигнатуру 'PE\0\0' (aka. <b>IMAGE_NT_SIGNATURE</b>). Следующее поле описывает исполняемый файл и имеет следующий формат:<br />
<div class="codeblock"><table class="vb"><thead><tr><td colspan="2" id="430652573"  class="head">Visual Basic</td></tr></thead><tbody><tr class="li1"><td><div id="430652573" style="height: 174px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
5
6
7
8
9
</pre></td><td class="de1"><pre class="de1"><span class="kw4">Type</span> IMAGE_FILE_HEADER
&nbsp; &nbsp; Machine &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Integer</span>
&nbsp; &nbsp; NumberOfSections &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Integer</span>
&nbsp; &nbsp; TimeDateStamp &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; PointerToSymbolTable &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; NumberOfSymbols &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; SizeOfOptionalHeader &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Integer</span>
&nbsp; &nbsp; Characteristics &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Integer</span>
<span class="kw3">End</span> <span class="kw4">Type</span></pre></td></tr></table></div></td></tr></tbody></table></div>Поле <b>Machine</b> определяет архитектуру процессора и должно иметь значение <b>IMAGE_FILE_MACHINE_I386</b> в нашем случае. Поле <b>NumberOfSections</b> определяет количество секций в PE файле.<ul><li><font size="1">Любой EXE файл содержит секции. Каждая секция занимает место в адресном пространстве процесса и опционально в файле. Секция может содержать как код так и данные (инизиализированные или не), а также имеет имя. Наиболее распространенные имена: <b>.text</b>, <b>.data</b>, <b>.rsrc</b>. Обычно секция <b>.text</b> содержит код, <b>.data</b> инициализированные данные, а <b>.rsrc</b> - ресурсы. Можно изменять это поведение используя дериктивы линкера. Каждая секция имеет адрес называемый виртуальным адресом. В общем в PE формате существует несколько типов адресации. Первый - относительный виртуальный адрес (RVA). Из-за того что PE фал может быть загружен по любому адресу все ссылки внутри PE файла имеют относительную адресацию. RVA - это смещение относительно базового адреса (адреса первого байта PE-образа в памяти). Сумма RVA и базового адреса называется виртуальным адресом (VA). Также существует RAW-смещение которое показывает смещение относительно начала файла относительно RVA. Заметьте что RVA &lt;&gt; RAW. Когда модуль загружается каждая секция размещается по виртуальному адресу. Для примера модуль может иметь секцию что не имеет инициализированных данных. Такая секция не будет занимать место в PE-файле, но будет в памяти. Это очень важный момент поскольку мы будем работать с сырым EXE файлом.</font></li>
</ul>Поле <b>TimeDateStamp</b> содержит дату создания PE модуля в формате UTC. Поля <b>PointerToSymbolTable</b> and <b>NumberOfSymbols</b> содержат информацию о символах в PE файлах. В общем эти поля содержат нули, но эти поля всегда используються в объектных файлах (*.OBJ, *.LIB) для разрешения ссылок во время линковки а также содержат отладочную информацию для PE модуля. Следующее поле <b>SizeOfOptionalHeader</b> содержит размер структуры расположенной после <b>IMAGE_FILE_HEADER</b> так называемой <b>IMAGE_OPTIONAL_HEADER</b> которая всегда присутствует в PE файлах (хотя может отсутствовать в OBJ файлах). Эта структура являеться очень важной для загрузки PE модуля в память. Заметьте что эта структура различается в 32 битных и 64 битных PE-модулях. И наконец поле <b>Characteristics</b> содержит PE-аттрибуты.<br />
Структура <b>IMAGE_OPTIONAL_HEADER</b> имеет следующий формат:<br />
<div class="codeblock"><table class="vb"><thead><tr><td colspan="2" id="356349502"  class="head">Visual Basic</td></tr></thead><tbody><tr class="li1"><td><div id="356349502" style="height: 350px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
</pre></td><td class="de1"><pre class="de1"><span class="kw4">Type</span> IMAGE_OPTIONAL_HEADER
&nbsp; &nbsp; Magic &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Integer</span>
&nbsp; &nbsp; MajorLinkerVersion &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Byte</span>
&nbsp; &nbsp; MinorLinkerVersion &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Byte</span>
&nbsp; &nbsp; SizeOfCode &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; SizeOfInitializedData &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; SizeOfUnitializedData &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; AddressOfEntryPoint &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; BaseOfCode &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; BaseOfData &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; ImageBase &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; SectionAlignment &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; FileAlignment &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; MajorOperatingSystemVersion &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Integer</span>
&nbsp; &nbsp; MinorOperatingSystemVersion &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Integer</span>
&nbsp; &nbsp; MajorImageVersion &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Integer</span>
&nbsp; &nbsp; MinorImageVersion &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Integer</span>
&nbsp; &nbsp; MajorSubsystemVersion &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Integer</span>
&nbsp; &nbsp; MinorSubsystemVersion &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Integer</span>
&nbsp; &nbsp; W32VersionValue &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; SizeOfImage &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; SizeOfHeaders &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; CheckSum &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; SubSystem &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Integer</span>
&nbsp; &nbsp; DllCharacteristics &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Integer</span>
&nbsp; &nbsp; SizeOfStackReserve &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; SizeOfStackCommit &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; SizeOfHeapReserve &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; SizeOfHeapCommit &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; LoaderFlags &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; NumberOfRvaAndSizes &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; DataDirectory(15) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> IMAGE_DATA_DIRECTORY
<span class="kw3">End</span> <span class="kw4">Type</span></pre></td></tr></table></div></td></tr></tbody></table></div>Первое поле содержит тип образа (x86, x64 или ROM образ). Нас интересует только <b>IMAGE_NT_OPTIONAL_HDR32_MAGIC</b> который представляет собой 32 битное приложение. Следующие 2 поля не являются важными (они использовались на старых системах) и содержат 4. Следующая группа полей содержит размер всех секций с кодом, инициализированными данными и неинициализированными данными. Эти значения должны быть кратными значению <b>SectionAlignment</b> этой структуры (см. далее). Поле <b>AddressOfEntryPoint</b> является очень важным RVA значением которое определяет точку входа в программу. Мы будем использовать это поле когда загрузим PE образ в память для запуска кода. Следующим важным полем является <b>ImageBase</b> которое задает предпочитаемый виртуальный адрес загрузки модуля. Когда загрузчик начинает загружать модуль, то он старается сделать это по предпочитаемому виртуальному адресу (находящимся в <b>ImageBase</b>). Если этот адрес занят, то загрузчик проверяет поле <b>Characteristics</b> структуры <b>IMAGE_FILE_HEADER</b>. Если это поле содержит флаг <b>IMAGE_FILE_RELOCS_STRIPPED</b> то модуль не сможет быть загружен. Для того чтобы загрузить такие модули нам нужно добавить информацию о релокации которая позволит загрузчику настроить адреса внутри PE-образа если модуль не может загрузится по предпочитаемому базовому адресу. Мы будем использоват это поле вместе с <b>SizeOfImage</b> для того чтобы зарезервировать память под распакованный EXE. Поля <b>SectionAlignment</b> and <b>FileAlignment</b> содержат выравнивание секций в памяти и в файле соответственно. Изменяя файловое выравнивание можно уменьшить размер PE файла, но система может не загрузить данный PE файл. Выравнивание секций обычно равно размеру страницы в памяти. Поле <b>SizeOfHeaders</b> задает размер всех заголовков (DOS Заголовок, NT заголовок, заголовки секций) выровненное на <b>FileAlignment</b>. Значения <b>SizeOfStackReserve</b> и <b>SizeOfStackCommit</b> определяют общий размер стека и начальный размер стека. Тоже самое и для полей <b>SizeOfHeapReserve</b> и <b>SizeOfHeapCommit</b>, но для кучи. Поле <b>NumberOfRvaAndSizes</b> содержит количество элементов в массиве <b>DataDirectory</b>. Это поле всегда равно 16. Массив <b>DataDirectory</b> является также очень важным поскольку в нем содержатся каталоги данных которые содержат нужную информацию об импорте, экспорте, ресурсах, релокациях и т.д. Мы будем использовать только несколько элементов из этого каталога которые используются VB6 компилятором. Я расскажу о каталогах немного позже, давайте посмотрим что находится за каталогами. За каталогами содержаться описатели секций. Количество этих описателей, если вспомнить, мы получили из структуры <b>IMAGE_FILE_HEADER</b>. Рассмотрим формат заголовка секции:<br />
<div class="codeblock"><table class="vb"><thead><tr><td colspan="2" id="263737210"  class="head">Visual Basic</td></tr></thead><tbody><tr class="li1"><td><div id="263737210" style="height: 222px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
5
6
7
8
9
10
11
12
</pre></td><td class="de1"><pre class="de1"><span class="kw4">Type</span> IMAGE_SECTION_HEADER
&nbsp; &nbsp; SectionName(7) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Byte</span>
&nbsp; &nbsp; VirtualSize &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; VirtualAddress &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; SizeOfRawData &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; PointerToRawData &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; PointerToRelocations &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; PointerToLinenumbers &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; NumberOfRelocations &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Integer</span>
&nbsp; &nbsp; NumberOfLinenumbers &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Integer</span>
&nbsp; &nbsp; Characteristics &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw3">End</span> <span class="kw4">Type</span></pre></td></tr></table></div></td></tr></tbody></table></div>Первое поле содержит имя секции в формате UTF-8 c завершающим нуль-терминалом. Это имя ограничено 8-ю символами (если имя секции имеет размер 8 символов то нуль-терминатор игнорируется). COFF файл может иметь имя больше чем 8 символов в этом случае имя начинается с символа '/' за которым следует ASCII строка с десятичным значением смещения в строковой таблице (поле <b>IMAGE_FILE_HEADER</b>). PE файл не поддерживает длинные имена секций. Поля <b>VirtualSize</b> и <b>VirtualAddress</b> содержат размер секции в памяти и адрес (RVA). Поля <b>SizeOfRawData</b> и <b>PointerToRawData</b> содержат RAW адрес данных в файле (если секция содержит инициализированные данные). Это ключевой момент потому что мы можем вычислить RAW адрес с помощью относительного виртуального адреса используя информацию из заголовка секций. Я написал функцию для перевода RVA адресации в RAW смещение в файле:<br />
<div class="codeblock"><table class="vb"><thead><tr><td colspan="2" id="126012146"  class="head">Visual Basic</td></tr></thead><tbody><tr class="li1"><td><div id="126012146" style="height: 334px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
</pre></td><td class="de1"><pre class="de1"><span class="co1">' // RVA to RAW</span>
<span class="kw2">Function</span> RVA2RAW( _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">ByVal</span> rva <span class="kw4">As</span> <span class="kw1">Long</span>, _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">ByRef</span> sec() <span class="kw4">As</span> IMAGE_SECTION_HEADER) <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> index <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">For</span> index = 0 <span class="kw3">To</span> <span class="kw4">UBound</span>(sec)
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> rva &gt;= sec(index).VirtualAddress <span class="kw3">And</span> _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;rva &lt; sec(index).VirtualAddress + sec(index).VirtualSize <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; RVA2RAW = sec(index).PointerToRawData + (rva - sec(index).VirtualAddress)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">Next</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; RVA2RAW = rva
&nbsp; &nbsp; 
<span class="kw3">End</span> <span class="kw2">Function</span></pre></td></tr></table></div></td></tr></tbody></table></div>Эта функция перечисляет все секции и проверяет если переданный адрес находится в пределах секции. Следующие 5 полей используються только в COFF файлах и не важны в PE файлах. Поле <b>Characteristics</b> содержит атрибуты секции такие как права доступа к памяти и управление. Мы будем использовать это поле для защиты памяти exe файла в загрузчике. <br />
Давайте теперь вернемся к каталогам данных. Как мы видели существует 16 элементов в данном каталоге. Обычно PE файл не использует их все. Давайте рассмотрим структуру элемента каталога:<br />
<div class="codeblock"><table class="vb"><thead><tr><td colspan="2" id="335236511"  class="head">Visual Basic</td></tr></thead><tbody><tr class="li1"><td><div id="335236511" style="height: 94px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
</pre></td><td class="de1"><pre class="de1"><span class="kw2">Private</span> <span class="kw4">Type</span> IMAGE_DATA_DIRECTORY
&nbsp; &nbsp; VirtualAddress &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; Size &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw3">End</span> <span class="kw4">Type</span></pre></td></tr></table></div></td></tr></tbody></table></div>Эта структура содержит два поля. Первое поле содержит RVA адрес данных каталога, воторое - размер. Когда элемент каталога не представлен в PE файле то оба поля содержат нули. Вообще большинство VB6-компилируемых приложений имеют  только 4 каталога: таблица импорта, таблица ресурсов, таблица связанного импорта и таблица адресов импорта (IAT). Сейчас мы рассмотрим таблицу ресурсов которая имеет индекс <b>IMAGE_DIRECTORY_ENTRY_RESOURCE</b> потому что мы работаем с этой информацией в проекте Compiler.<br />
Все ресурсы в EXE файле представлены в виде трехуровнего дерева. Первый уровень определяет тип ресурса (RT_BITMAP, RT_MANIFEST, RT_RCDATA, и т.д.), следующий - идентификатор ресурса и наконец третий - язык. В стандартном редакторе ресурсов VB Resource Editor можно изменять только первые 2 уровня. Все ресурсы размещаются таблице ресурсов расположенной в секции <b>.rsrc</b> EXE файла. Благодаря такой структуре мы можем изменять ресурсы даже в готовом EXE файле. Для того чтобы добраться до самих данных в секции ресурсов нам сначала нужно прочитать <b>IMAGE_DIRECTORY_ENTRY_RESOURCE</b> из опционального хидера. Поле <b>VirtualAddress</b> содержит RVA таблицы ресурсов которая имеет следующий формат:<br />
<div class="codeblock"><table class="vb"><thead><tr><td colspan="2" id="375130422"  class="head">Visual Basic</td></tr></thead><tbody><tr class="li1"><td><div id="375130422" style="height: 158px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
5
6
7
8
</pre></td><td class="de1"><pre class="de1"><span class="kw4">Type</span> IMAGE_RESOURCE_DIRECTORY
&nbsp; &nbsp; Characteristics &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; TimeDateStamp &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; MajorVersion &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Integer</span>
&nbsp; &nbsp; MinorVersion &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Integer</span>
&nbsp; &nbsp; NumberOfNamedEntries &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Integer</span>
&nbsp; &nbsp; NumberOfIdEntries &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Integer</span>
<span class="kw3">End</span> <span class="kw4">Type</span></pre></td></tr></table></div></td></tr></tbody></table></div>Эта структура описывает все ресурсы в PE файле. Первые 4 поля не важны для нас; поле <b>NumberOfNamedEntries</b> и <b>NumberOfIdEntries</b> содержат количество именованных записей и записей с числовыми идентификаторами соответственно. Для примера, когда мы добавляем картинку в стандартном редакторе это добавит запись с числовым идентификатором равным 2 (<b>RT_BITMAP</b>). Сами записи расположены сразу после <b>IMAGE_RESOURCE_DIRECTORY</b> и имеют следующую структуру:<br />
<div class="codeblock"><table class="vb"><thead><tr><td colspan="2" id="19712756"  class="head">Visual Basic</td></tr></thead><tbody><tr class="li1"><td><div id="19712756" style="height: 94px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
</pre></td><td class="de1"><pre class="de1"><span class="kw4">Type</span> IMAGE_RESOURCE_DIRECTORY_ENTRY
&nbsp; &nbsp; NameId &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; OffsetToData &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw3">End</span> <span class="kw4">Type</span></pre></td></tr></table></div></td></tr></tbody></table></div>Первое поле этой структуры определяет является ли это именованной запись либо это запись с числовым идентификатором в зависимости от старшего бита. Если этот бит установлен то остальные биты определяют смещение от начала ресурсов к структуре <b>IMAGE_RESOURCE_DIR_STRING_U</b> которая имет следующий формат:<br />
<div class="codeblock"><table class="vb"><thead><tr><td colspan="2" id="800321311"  class="head">Visual Basic</td></tr></thead><tbody><tr class="li1"><td><div id="800321311" style="height: 94px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
</pre></td><td class="de1"><pre class="de1"><span class="kw4">Type</span> IMAGE_RESOURCE_DIR_STRING_U
&nbsp; &nbsp; Length &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Integer</span>
&nbsp; &nbsp; NameString &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">String</span>
<span class="kw3">End</span> <span class="kw4">Type</span></pre></td></tr></table></div></td></tr></tbody></table></div>Заметьте что это не правильная VB-структура и показана для наглядности. Первые два байта являются беззнаковым целым которые показывают длину строки в формате UNICODE (в символах) которая следует за ними. Таким образом для того чтобы получить строку нам нужно прочитать первые два байта с размером, выделить память для строки согласно этого размера и прочитать данные в строковую переменную. Напротив, если старший бит поля <b>NameId</b> сброшен то оно содержит числовой идентификатор ресурса (<b>RT_BITMAP</b> в примере). Поле <b>OffsetToData</b> имеет также двойную интерпретацию. Если старший бит установлен то это смещение (от начала ресурсов) до следующего уровня дерева ресурсов, т.е. до структуры <b>IMAGE_RESOURCE_DIRECTORY</b>. Иначе - это смещение до структуры <b>IMAGE_RESOURCE_DATA_ENTRY</b>:<br />
<div class="codeblock"><table class="vb"><thead><tr><td colspan="2" id="136445936"  class="head">Visual Basic</td></tr></thead><tbody><tr class="li1"><td><div id="136445936" style="height: 126px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
5
6
</pre></td><td class="de1"><pre class="de1"><span class="kw4">Type</span> IMAGE_RESOURCE_DATA_ENTRY
&nbsp; &nbsp; OffsetToData &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; Size &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; CodePage &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; Reserved &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw3">End</span> <span class="kw4">Type</span></pre></td></tr></table></div></td></tr></tbody></table></div>Наиболее важными для нас являются поля <b>OffsetToData</b> and <b>Size</b> которые содержат RVA и размер сырых данных ресурса. Теперь мы можем извлечь все данные из ресурсов любого PE файла.<br />
<br />
<div align="center"><font size="6"><b><font color="#BF4000">Компиляция.</font></b></font></div><br />
Итак, когда мы начинаем компиляцию проекта то вызывается метод <b>Compile</b> объекта класса <b>clsProject</b>. Вначале упаковываются все элементы хранилища и команд в бинарный формат (<b>BinProject</b>, <b>BinStorageListItem</b>, и т.д.) и формируются таблица строк и файловая таблица. Строковая таблица сохраняется как набор строк разделенных нуль-терминалом. Я использую специальный класс <b>clsStream</b> для безопасной работы с бинарными данными. Этот класс позволяет читать и писать любые данные или потоки в двоичный буфер, сжимать буфер. Я использую функцию <b>RtlCompressBuffer</b> для сжатия потока которая использует LZ-сжатие. После упаковки и сжатия проверяется выходной формат файла. Поддерживаются 2 типа файлов: бинарный (сырые данные проекта) и исполняемый (загрузчик). Двоичный формат не интересен поэтому мы будем рассматривать исполняемый формат. Вначале извлекаются все ресурсы из главного исполняемого файла в трехуровневый каталог. Эта операция выполняется с помощью функции <b>ExtractResorces</b>. Имена-идентификаторы сохраняются в строковом виде с префиксом '#'. Потом клонируется шаблон загрузчика в результирующий файл, начинается процесс модификации ресурсов в EXE файле используя функцию <b>BeginUpdateResource</b>. После этого последовательно копируются все извлеченные ресурсы (<b>UpdateResource</b>), двоичный проект и манифест (если нужно) в результирующий файл и применяются изменения функцией <b>EndUpdateResource</b>. Опять повторюсь, бинарный проект сохраняется с именем <b>PROJECT</b> и имеет тип <b>RT_DATA</b>. В общем все.<br />
<br />
<div align="center"><font size="6"><b><font color="#BF4000">Загрузчик.</font></b></font></div><br />
Итак. я думаю это наиболее интересная часть. Итак, нам нужно избегать использование рантайма. Как этого добится? Я дам некоторые правила:<ul><li>Установить в качестве стартовой функции пользовательскую функцию;</li>
<li>Избегать любых объектов и классов в проекте;</li>
<li>Избегать непосредственных массивов. Массивы фиксированного размера в пользовательских типах не запрещены;</li>
<li>Избегать строковых переменных а также Variant/Object переменных. В некоторых случаях Currency/Date;</li>
<li>Избегать API функции задекларированые с помощью ключевого слова Declare;</li>
<li>Избегать VarPtr/StrPtr/ObjPtr и некоторые стандартные функции;</li>
<li>...</li>
<li>...</li>
</ul>Это неполный список ограничений, а во время выполнения шеллкода добавляются дополнительные ограничения.<br />
Итак, начнем. Для того чтобы избежать использования строковых переменных я храню все строковые переменные как Long указатели на строки. Существует проблема с загрузкой строк поскольку мы не можем обращаться к любой строке чтобы загрузить ее. Я решил использовать ресурсы в качестве хранилища строк и загружать их по числовому идентификатору. Таким образом мы можем хранить указатель в переменной Long без обращения к рантайму. Я использовал TLB (библиотеку типов) для всех API функций без атрибута <b>usesgetlasterror</b> чтобы избежать объявление через Declare. Для установки стартовой функции я использую опции линкера. Стартовая функция в загрузчике - <b>Main</b>. Обратите внимание, если в IDE выбрать стартовую функцию <b>Main</b> на самом деле это не будет стартовой функцией приложения потому что VB6-скомпилированное приложение начинается с функции <b>__vbaS</b> которая вызывает функцию <b>ThunRTMain</b> из рантайма, которая инициализирует рантайм и поток. <br />
Загрузчик содержит три модуля:<ul><li><b>modMain</b> - стартовая функция и работа с хранилищем;</li>
<li><b>modConstants</b> - работа со строковыми константами;</li>
<li><b>modLoader</b> - загрузчик EXE файла.</li>
</ul>Когда загрузчик запустился выполняется функция <b>Main</b>:<br />
<div class="codeblock"><table class="vb"><thead><tr><td colspan="2" id="852373819"  class="head">Visual Basic</td></tr></thead><tbody><tr class="li1"><td><div id="852373819" style="height: 350px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
</pre></td><td class="de1"><pre class="de1"><span class="co1">' // Startup subroutine</span>
<span class="kw2">Sub</span> Main()
&nbsp;
&nbsp; &nbsp; <span class="co1">' // Load constants</span>
 &nbsp; &nbsp;<span class="kw3">If</span> <span class="kw3">Not</span> LoadConstants <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; MessageBox 0, GetString(MID_ERRORLOADINGCONST), 0, MB_ICONERROR <span class="kw3">Or</span> MB_SYSTEMMODAL
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">GoTo</span> EndOfProcess
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="co1">' // Load project</span>
 &nbsp; &nbsp;<span class="kw3">If</span> <span class="kw3">Not</span> ReadProject <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; MessageBox 0, GetString(MID_ERRORREADINGPROJECT), 0, MB_ICONERROR <span class="kw3">Or</span> MB_SYSTEMMODAL
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">GoTo</span> EndOfProcess
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="co1">' // Copying from storage</span>
 &nbsp; &nbsp;<span class="kw3">If</span> <span class="kw3">Not</span> CopyProcess <span class="kw3">Then</span> <span class="kw3">GoTo</span> EndOfProcess
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="co1">' // Execution process</span>
 &nbsp; &nbsp;<span class="kw3">If</span> <span class="kw3">Not</span> ExecuteProcess <span class="kw3">Then</span> <span class="kw3">GoTo</span> EndOfProcess
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="co1">' // If main executable is not presented exit</span>
 &nbsp; &nbsp;<span class="kw3">If</span> ProjectDesc.storageDescriptor.iExecutableIndex = -1 <span class="kw3">Then</span> <span class="kw3">GoTo</span> EndOfProcess
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="co1">' // Run exe from memory</span>
 &nbsp; &nbsp;<span class="kw3">If</span> <span class="kw3">Not</span> RunProcess <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' // Error occrurs</span>
 &nbsp; &nbsp; &nbsp; &nbsp;MessageBox 0, GetString(MID_ERRORSTARTUPEXE), 0, MB_ICONERROR <span class="kw3">Or</span> MB_SYSTEMMODAL
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; 
EndOfProcess:
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> pProjectData <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; HeapFree GetProcessHeap(), HEAP_NO_SERIALIZE, pProjectData
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; ExitProcess 0
&nbsp; &nbsp; 
<span class="kw3">End</span> <span class="kw2">Sub</span></pre></td></tr></table></div></td></tr></tbody></table></div>Вначале вызывается функция <b>LoadConstants</b> для того чтобы загрузить все необходимые константы из ресурсов:<br />
<div class="codeblock"><table class="vb"><thead><tr><td colspan="2" id="992468666"  class="head">Visual Basic</td></tr></thead><tbody><tr class="li1"><td><div id="992468666" style="height: 350px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
</pre></td><td class="de1"><pre class="de1"><span class="co1">' // modConstants.bas - main module for loading constants</span>
<span class="co1">' // © Krivous Anatoly Anatolevich (The trick), 2016</span>
&nbsp;
<span class="kw2">Option</span> <span class="kw2">Explicit</span>
&nbsp;
<span class="kw2">Public</span> <span class="kw1">Enum</span> MessagesID
&nbsp; &nbsp; MID_ERRORLOADINGCONST = 100 &nbsp; &nbsp; <span class="co1">' // Errors</span>
 &nbsp; &nbsp;MID_ERRORREADINGPROJECT = 101 &nbsp; <span class="co1">'</span>
 &nbsp; &nbsp;MID_ERRORCOPYINGFILE = 102 &nbsp; &nbsp; &nbsp;<span class="co1">'</span>
 &nbsp; &nbsp;MID_ERRORWIN32 = 103 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">'</span>
 &nbsp; &nbsp;MID_ERROREXECUTELINE = 104 &nbsp; &nbsp; &nbsp;<span class="co1">'</span>
 &nbsp; &nbsp;MID_ERRORSTARTUPEXE = 105 &nbsp; &nbsp; &nbsp; <span class="co1">'</span>
 &nbsp; &nbsp;PROJECT = 200 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' // Project resource ID</span>
 &nbsp; &nbsp;API_LIB_KERNEL32 = 300 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">' // Library names</span>
 &nbsp; &nbsp;API_LIB_NTDLL = 350 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">'</span>
 &nbsp; &nbsp;API_LIB_USER32 = 400 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">'</span>
 &nbsp; &nbsp;MSG_LOADER_ERROR = 500
<span class="kw3">End</span> <span class="kw1">Enum</span>
&nbsp;
<span class="co1">' // Paths</span>
&nbsp;
<span class="kw2">Public</span> pAppPath &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">' // Path to application</span>
<span class="kw2">Public</span> pSysPath &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">' // Path to System32</span>
<span class="kw2">Public</span> pTmpPath &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">' // Path to Temp</span>
<span class="kw2">Public</span> pWinPath &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">' // Path to Windows</span>
<span class="kw2">Public</span> pDrvPath &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">' // Path to system drive</span>
<span class="kw2">Public</span> pDtpPath &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">' // Path to desktop</span>
&nbsp;
<span class="co1">' // Substitution constants</span>
&nbsp;
<span class="kw2">Public</span> pAppRepl &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw2">Public</span> pSysRepl &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw2">Public</span> pTmpRepl &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw2">Public</span> pWinRepl &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw2">Public</span> pDrvRepl &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw2">Public</span> pDtpRepl &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw2">Public</span> pStrNull &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">' // \0</span>
&nbsp;
<span class="kw2">Public</span> hInstance &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span> &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' // Base address</span>
<span class="kw2">Public</span> lpCmdLine &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span> &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' // Command line</span>
<span class="kw2">Public</span> SI &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> STARTUPINFO &nbsp;<span class="co1">' // Startup parameters</span>
<span class="kw2">Public</span> LCID &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span> &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' // LCID</span>
&nbsp;
<span class="co1">' // Load constants</span>
<span class="kw2">Function</span> LoadConstants() <span class="kw4">As</span> <span class="kw1">Boolean</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> lSize &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> pBuf &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> index &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> ctl &nbsp; &nbsp; <span class="kw4">As</span> tagINITCOMMONCONTROLSEX
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="co1">' // Load windows classes</span>
 &nbsp; &nbsp;ctl.dwSize = Len(ctl)
&nbsp; &nbsp; ctl.dwICC = &amp;H3FFF&amp;
&nbsp; &nbsp; InitCommonControlsEx ctl
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="co1">' // Get startup parameters</span>
 &nbsp; &nbsp;GetStartupInfo SI
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="co1">' // Get command line</span>
 &nbsp; &nbsp;lpCmdLine = GetCommandLine()
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="co1">' // Get base address</span>
 &nbsp; &nbsp;hInstance = GetModuleHandle(<span class="kw4">ByVal</span> 0&amp;)
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="co1">' // Get LCID</span>
 &nbsp; &nbsp;LCID = GetUserDefaultLCID()
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="co1">' // Alloc memory for strings</span>
 &nbsp; &nbsp;pBuf = SysAllocStringLen(0, MAX_PATH)
&nbsp; &nbsp; <span class="kw3">If</span> pBuf = 0 <span class="kw3">Then</span> <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="co1">' // Get path to process file name</span>
 &nbsp; &nbsp;<span class="kw3">If</span> GetModuleFileName(hInstance, pBuf, MAX_PATH) = 0 <span class="kw3">Then</span> <span class="kw3">GoTo</span> CleanUp
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="co1">' // Leave only directory</span>
 &nbsp; &nbsp;PathRemoveFileSpec pBuf
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="co1">' // Save path</span>
 &nbsp; &nbsp;pAppPath = SysAllocString(pBuf)
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="co1">' // Get Windows folder</span>
 &nbsp; &nbsp;<span class="kw3">If</span> GetWindowsDirectory(pBuf, MAX_PATH) = 0 <span class="kw3">Then</span> <span class="kw3">GoTo</span> CleanUp
&nbsp; &nbsp; pWinPath = SysAllocString(pBuf)
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="co1">' // Get System32 folder</span>
 &nbsp; &nbsp;<span class="kw3">If</span> GetSystemDirectory(pBuf, MAX_PATH) = 0 <span class="kw3">Then</span> <span class="kw3">GoTo</span> CleanUp
&nbsp; &nbsp; pSysPath = SysAllocString(pBuf)
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="co1">' // Get Temp directory</span>
 &nbsp; &nbsp;<span class="kw3">If</span> GetTempPath(MAX_PATH, pBuf) = 0 <span class="kw3">Then</span> <span class="kw3">GoTo</span> CleanUp
&nbsp; &nbsp; pTmpPath = SysAllocString(pBuf)
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="co1">' // Get system drive</span>
 &nbsp; &nbsp;PathStripToRoot pBuf
&nbsp; &nbsp; pDrvPath = SysAllocString(pBuf)
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="co1">' // Get desktop path</span>
 &nbsp; &nbsp;<span class="kw3">If</span> SHGetFolderPath(0, CSIDL_DESKTOPDIRECTORY, 0, SHGFP_TYPE_CURRENT, pBuf) <span class="kw3">Then</span> <span class="kw3">GoTo</span> CleanUp
&nbsp; &nbsp; pDtpPath = SysAllocString(pBuf)
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="co1">' // Load wildcards</span>
 &nbsp; &nbsp;<span class="kw3">For</span> index = 1 <span class="kw3">To</span> 6
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> LoadString(hInstance, index, pBuf, MAX_PATH) = 0 <span class="kw3">Then</span> <span class="kw3">GoTo</span> CleanUp
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">Select</span> <span class="kw3">Case</span> index
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">Case</span> 1: pAppRepl = SysAllocString(pBuf)
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">Case</span> 2: pSysRepl = SysAllocString(pBuf)
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">Case</span> 3: pTmpRepl = SysAllocString(pBuf)
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">Case</span> 4: pWinRepl = SysAllocString(pBuf)
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">Case</span> 5: pDrvRepl = SysAllocString(pBuf)
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">Case</span> 6: pDtpRepl = SysAllocString(pBuf)
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">Select</span>
&nbsp; &nbsp; <span class="kw3">Next</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="co1">' // vbNullChar</span>
 &nbsp; &nbsp;pStrNull = SysAllocStringLen(0, 0)
&nbsp;
&nbsp; &nbsp; <span class="co1">' // Success</span>
 &nbsp; &nbsp;LoadConstants = <span class="kw5">True</span>
&nbsp; &nbsp; 
CleanUp:
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> pBuf <span class="kw3">Then</span> SysFreeString pBuf
&nbsp; &nbsp; 
<span class="kw3">End</span> <span class="kw2">Function</span>
&nbsp;
<span class="co1">' // Obtain string from resource (it should be less or equal MAX_PATH)</span>
<span class="kw2">Public</span> <span class="kw2">Function</span> GetString( _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">ByVal</span> ID <span class="kw4">As</span> MessagesID) <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; GetString = SysAllocStringLen(0, MAX_PATH)
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> GetString <span class="kw3">Then</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> LoadString(hInstance, ID, GetString, MAX_PATH) = 0 <span class="kw3">Then</span> SysFreeString GetString: GetString = 0: <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> SysReAllocString(GetString, GetString) = 0 <span class="kw3">Then</span> SysFreeString GetString: GetString = 0: <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; 
<span class="kw3">End</span> <span class="kw2">Function</span></pre></td></tr></table></div></td></tr></tbody></table></div>Функция <b>LoadConstants</b> загружает все необходимые переменные и строки (hInstance, LCID, командная строка, подстановочные символы, пути по умолчанию, и т.д.). Все строки сохраняются в формате UNICODE-BSTR. Функция <b>GetString</b> загружает строку из ресурсов по ее идентификатору. Перечисление <b>MessagesID</b> содержит некоторые строковые идентификаторы  нужные для работы программы (сообщения об ошибках, имена библиотек, и.т.д.). Когда все константы загрузятся вызывается функция <b>ReadProject</b> которая загружает проект:<br />
<div class="codeblock"><table class="vb"><thead><tr><td colspan="2" id="122116332"  class="head">Visual Basic</td></tr></thead><tbody><tr class="li1"><td><div id="122116332" style="height: 350px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
</pre></td><td class="de1"><pre class="de1"><span class="co1">' // Load project</span>
<span class="kw2">Function</span> ReadProject() <span class="kw4">As</span> <span class="kw1">Boolean</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> hResource &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>: &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">Dim</span> hMememory &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> lResSize &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>: &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">Dim</span> pRawData &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> status &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>: &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">Dim</span> pUncompressed &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> lUncompressSize <span class="kw4">As</span> <span class="kw1">Long</span>: &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">Dim</span> lResultSize &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> tmpStorageItem &nbsp;<span class="kw4">As</span> BinStorageListItem: &nbsp;<span class="kw4">Dim</span> tmpExecuteItem &nbsp;<span class="kw4">As</span> BinExecListItem
&nbsp; &nbsp; <span class="kw4">Dim</span> pLocalBuffer &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="co1">' // Load resource</span>
 &nbsp; &nbsp;hResource = FindResource(hInstance, GetString(PROJECT), RT_RCDATA)
&nbsp; &nbsp; <span class="kw3">If</span> hResource = 0 <span class="kw3">Then</span> <span class="kw3">GoTo</span> CleanUp
&nbsp; &nbsp; 
&nbsp; &nbsp; hMememory = LoadResource(hInstance, hResource)
&nbsp; &nbsp; <span class="kw3">If</span> hMememory = 0 <span class="kw3">Then</span> <span class="kw3">GoTo</span> CleanUp
&nbsp; &nbsp; 
&nbsp; &nbsp; lResSize = SizeofResource(hInstance, hResource)
&nbsp; &nbsp; <span class="kw3">If</span> lResSize = 0 <span class="kw3">Then</span> <span class="kw3">GoTo</span> CleanUp
&nbsp; &nbsp; 
&nbsp; &nbsp; pRawData = LockResource(hMememory)
&nbsp; &nbsp; <span class="kw3">If</span> pRawData = 0 <span class="kw3">Then</span> <span class="kw3">GoTo</span> CleanUp
&nbsp; &nbsp; 
&nbsp; &nbsp; pLocalBuffer = HeapAlloc(GetProcessHeap(), HEAP_NO_SERIALIZE, lResSize)
&nbsp; &nbsp; <span class="kw3">If</span> pLocalBuffer = 0 <span class="kw3">Then</span> <span class="kw3">GoTo</span> CleanUp
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="co1">' // Copy to local buffer</span>
 &nbsp; &nbsp;CopyMemory <span class="kw4">ByVal</span> pLocalBuffer, <span class="kw4">ByVal</span> pRawData, lResSize
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="co1">' // Set default size</span>
 &nbsp; &nbsp;lUncompressSize = lResSize * 2
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="co1">' // Do decompress...</span>
 &nbsp; &nbsp;<span class="kw3">Do</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> pUncompressed <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; pUncompressed = HeapReAlloc(GetProcessHeap(), HEAP_NO_SERIALIZE, <span class="kw4">ByVal</span> pUncompressed, lUncompressSize)
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">Else</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; pUncompressed = HeapAlloc(GetProcessHeap(), HEAP_NO_SERIALIZE, lUncompressSize)
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; status = RtlDecompressBuffer(COMPRESSION_FORMAT_LZNT1, _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">ByVal</span> pUncompressed, lUncompressSize, _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">ByVal</span> pLocalBuffer, lResSize, lResultSize)
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; lUncompressSize = lUncompressSize * 2
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">Loop</span> <span class="kw3">While</span> status = STATUS_BAD_COMPRESSION_BUFFER
&nbsp; &nbsp; 
&nbsp; &nbsp; pProjectData = pUncompressed
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> status <span class="kw3">Then</span> <span class="kw3">GoTo</span> CleanUp
&nbsp;
&nbsp; &nbsp; <span class="co1">' // Validation check</span>
 &nbsp; &nbsp;<span class="kw3">If</span> lResultSize &lt; LenB(ProjectDesc) <span class="kw3">Then</span> <span class="kw3">GoTo</span> CleanUp
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="co1">' // Copy descriptor</span>
 &nbsp; &nbsp;CopyMemory ProjectDesc, <span class="kw4">ByVal</span> pProjectData, LenB(ProjectDesc)
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="co1">' // Check all members</span>
 &nbsp; &nbsp;<span class="kw3">If</span> ProjectDesc.dwSizeOfStructure &lt;&gt; Len(ProjectDesc) <span class="kw3">Then</span> <span class="kw3">GoTo</span> CleanUp
&nbsp; &nbsp; <span class="kw3">If</span> ProjectDesc.storageDescriptor.dwSizeOfStructure &lt;&gt; Len(ProjectDesc.storageDescriptor) <span class="kw3">Then</span> <span class="kw3">GoTo</span> CleanUp
&nbsp; &nbsp; <span class="kw3">If</span> ProjectDesc.storageDescriptor.dwSizeOfItem &lt;&gt; Len(tmpStorageItem) <span class="kw3">Then</span> <span class="kw3">GoTo</span> CleanUp
&nbsp; &nbsp; <span class="kw3">If</span> ProjectDesc.execListDescriptor.dwSizeOfStructure &lt;&gt; Len(ProjectDesc.execListDescriptor) <span class="kw3">Then</span> <span class="kw3">GoTo</span> CleanUp
&nbsp; &nbsp; <span class="kw3">If</span> ProjectDesc.execListDescriptor.dwSizeOfItem &lt;&gt; Len(tmpExecuteItem) <span class="kw3">Then</span> <span class="kw3">GoTo</span> CleanUp
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="co1">' // Initialize pointers</span>
 &nbsp; &nbsp;pStoragesTable = pProjectData + ProjectDesc.dwSizeOfStructure
&nbsp; &nbsp; pExecutesTable = pStoragesTable + ProjectDesc.storageDescriptor.dwSizeOfItem * ProjectDesc.storageDescriptor.dwNumberOfItems
&nbsp; &nbsp; pFilesTable = pExecutesTable + ProjectDesc.execListDescriptor.dwSizeOfItem * ProjectDesc.execListDescriptor.dwNumberOfItems
&nbsp; &nbsp; pStringsTable = pFilesTable + ProjectDesc.dwFileTableLen
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="co1">' // Check size</span>
 &nbsp; &nbsp;<span class="kw3">If</span> (pStringsTable + ProjectDesc.dwStringsTableLen - pProjectData) &lt;&gt; lResultSize <span class="kw3">Then</span> <span class="kw3">GoTo</span> CleanUp
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="co1">' // Success</span>
 &nbsp; &nbsp;ReadProject = <span class="kw5">True</span>
&nbsp; &nbsp; 
CleanUp:
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> pLocalBuffer <span class="kw3">Then</span> HeapFree GetProcessHeap(), HEAP_NO_SERIALIZE, pLocalBuffer
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> <span class="kw3">Not</span> ReadProject <span class="kw3">And</span> pProjectData <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; HeapFree GetProcessHeap(), HEAP_NO_SERIALIZE, pProjectData
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; 
<span class="kw3">End</span> <span class="kw2">Function</span></pre></td></tr></table></div></td></tr></tbody></table></div>Как можно увидеть я использую кучу процесса вместо массивов. Вначале загружается ресурс с проектом - <b>PROJECT</b> и копируется в кучу, затем производится декомпрессия используя функцию <b>RtlDecompressBuffer</b>. Эта функция не возвращает необходимый размер буфера поэтому мы пытаемся распаковать буфер увеличивая выходной размер буфера пока декомпрессия не будет успешно выполнена. После декомпрессии проверяются все параметры и инициализируются глобальные указатели проекта.<br />
Если проект успешно загружен то вызывается функция <b>CopyProcess</b> которая распаковывает все файлы из хранилища, согласно данным проекта:<br />
<div class="codeblock"><table class="vb"><thead><tr><td colspan="2" id="163681146"  class="head">Visual Basic</td></tr></thead><tbody><tr class="li1"><td><div id="163681146" style="height: 350px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
</pre></td><td class="de1"><pre class="de1"><span class="co1">' // Copying process</span>
<span class="kw2">Function</span> CopyProcess() <span class="kw4">As</span> <span class="kw1">Boolean</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> bItem &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> BinStorageListItem: &nbsp;<span class="kw4">Dim</span> index &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> pPath &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>: &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">Dim</span> dwWritten &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> msg &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>: &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">Dim</span> lStep &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> isError &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Boolean</span>: &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">Dim</span> pItem &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> pErrMsg &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>: &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">Dim</span> pTempString <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="co1">' // Set pointer</span>
 &nbsp; &nbsp;pItem = pStoragesTable
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="co1">' // Go thru file list</span>
 &nbsp; &nbsp;<span class="kw3">For</span> index = 0 <span class="kw3">To</span> ProjectDesc.storageDescriptor.dwNumberOfItems - 1
&nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' // Copy file descriptor</span>
 &nbsp; &nbsp; &nbsp; &nbsp;CopyMemory bItem, <span class="kw4">ByVal</span> pItem, Len(bItem)
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' // Next item</span>
 &nbsp; &nbsp; &nbsp; &nbsp;pItem = pItem + ProjectDesc.storageDescriptor.dwSizeOfItem
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' // If it is not main executable</span>
 &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw3">If</span> index &lt;&gt; ProjectDesc.storageDescriptor.iExecutableIndex <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' // Normalize path</span>
 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;pPath = NormalizePath(pStringsTable + bItem.ofstDestPath, pStringsTable + bItem.ofstFileName)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' // Error occurs</span>
 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw3">If</span> pPath = 0 <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; pErrMsg = GetString(MID_ERRORWIN32)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; MessageBox 0, pErrMsg, 0, MB_ICONERROR <span class="kw3">Or</span> MB_SYSTEMMODAL
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">GoTo</span> CleanUp
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">Else</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">Dim</span> hFile &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">Dim</span> disp &nbsp; &nbsp;<span class="kw4">As</span> CREATIONDISPOSITION
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' // Set overwrite flags</span>
 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw3">If</span> bItem.dwFlags <span class="kw3">And</span> FF_REPLACEONEXIST <span class="kw3">Then</span> disp = CREATE_ALWAYS <span class="kw3">Else</span> disp = CREATE_NEW
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' // Set number of subroutine</span>
 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;lStep = 0
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' // Run subroutines</span>
 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw3">Do</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' // Disable error flag</span>
 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;isError = <span class="kw5">False</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' // Free string</span>
 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw3">If</span> pErrMsg <span class="kw3">Then</span> SysFreeString pErrMsg: pErrMsg = 0
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' // Choose subroutine</span>
 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw3">Select</span> <span class="kw3">Case</span> lStep
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">Case</span> 0 &nbsp;<span class="co1">' // 0. Create folder</span>
 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> <span class="kw3">Not</span> CreateSubdirectories(pPath) <span class="kw3">Then</span> isError = <span class="kw5">True</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">Case</span> 1 &nbsp;<span class="co1">' // 1. Create file</span>
 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; hFile = CreateFile(pPath, FILE_GENERIC_WRITE, 0, <span class="kw4">ByVal</span> 0&amp;, disp, FILE_ATTRIBUTE_NORMAL, 0)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> hFile = INVALID_HANDLE_VALUE <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> GetLastError = ERROR_FILE_EXISTS <span class="kw3">Then</span> <span class="kw2">Exit</span> <span class="kw3">Do</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; isError = <span class="kw5">True</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">Case</span> 2 &nbsp;<span class="co1">' // 2. Copy data to file</span>
 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> WriteFile(hFile, <span class="kw4">ByVal</span> pFilesTable + bItem.ofstBeginOfData, _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;bItem.dwSizeOfFile, dwWritten, <span class="kw4">ByVal</span> 0&amp;) = 0 <span class="kw3">Then</span> isError = <span class="kw5">True</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> dwWritten &lt;&gt; bItem.dwSizeOfFile <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; isError = <span class="kw5">True</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">Else</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; CloseHandle hFile: hFile = INVALID_HANDLE_VALUE
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">Select</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' // If error occurs show notification (retry, abort, ignore)</span>
 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw3">If</span> isError <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' // Ignore error</span>
 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw3">If</span> bItem.dwFlags <span class="kw3">And</span> FF_IGNOREERROR <span class="kw3">Then</span> <span class="kw2">Exit</span> <span class="kw3">Do</span>
&nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; pTempString = GetString(MID_ERRORCOPYINGFILE)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; pErrMsg = StrCat(pTempString, pPath)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' // Cleaning</span>
 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;SysFreeString pTempString: pTempString = 0
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">Select</span> <span class="kw3">Case</span> MessageBox(0, pErrMsg, 0, MB_ICONERROR <span class="kw3">Or</span> MB_SYSTEMMODAL <span class="kw3">Or</span> MB_CANCELTRYCONTINUE)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">Case</span> MESSAGEBOXRETURN.IDCONTINUE: <span class="kw2">Exit</span> <span class="kw3">Do</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">Case</span> MESSAGEBOXRETURN.IDTRYAGAIN
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">Case</span> <span class="kw3">Else</span>: &nbsp;<span class="kw3">GoTo</span> CleanUp
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">Select</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">Else</span>: lStep = lStep + 1
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">Loop</span> <span class="kw3">While</span> lStep &lt;= 2
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> hFile &lt;&gt; INVALID_HANDLE_VALUE <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; CloseHandle hFile: hFile = INVALID_HANDLE_VALUE
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' // Cleaning</span>
 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;SysFreeString pPath: pPath = 0
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">Next</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="co1">' // Success</span>
 &nbsp; &nbsp;CopyProcess = <span class="kw5">True</span>
&nbsp; &nbsp; 
CleanUp:
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> pTempString <span class="kw3">Then</span> SysFreeString pTempString
&nbsp; &nbsp; <span class="kw3">If</span> pErrMsg <span class="kw3">Then</span> SysFreeString pErrMsg
&nbsp; &nbsp; <span class="kw3">If</span> pPath <span class="kw3">Then</span> SysFreeString pPath
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> hFile &lt;&gt; INVALID_HANDLE_VALUE <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; CloseHandle hFile
&nbsp; &nbsp; &nbsp; &nbsp; hFile = INVALID_HANDLE_VALUE
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; 
<span class="kw3">End</span> <span class="kw2">Function</span></pre></td></tr></table></div></td></tr></tbody></table></div>Эта процедура проходит по всем элементам хранилища и распаковывает их одна за одной исключая главный исполняемый файл. Функция <b>NormalizePath</b> заменяет подстановочные знаки на реальные пути. Также существует функция <b>CreateSubdirectories</b> которая создает промежуточные директории (если необходимо) по переданному в качестве параметра пути. Затем вызывается функция <b>CreateFile</b> для создания файла затем через <b>WriteFile</b> данные пишутся в файл. Если происходит ошибка то выводится стандартное сообщение с предложением повторить, отменить или игнорировать.<br />
<div class="codeblock"><table class="vb"><thead><tr><td colspan="2" id="648004464"  class="head">Visual Basic</td></tr></thead><tbody><tr class="li1"><td><div id="648004464" style="height: 350px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
</pre></td><td class="de1"><pre class="de1"><span class="co1">' // Create all subdirectories by path</span>
<span class="kw2">Function</span> CreateSubdirectories( _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">ByVal</span> pPath <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> <span class="kw1">Boolean</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> pComponent <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> tChar &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Integer</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="co1">' // Pointer to first char</span>
 &nbsp; &nbsp;pComponent = pPath
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="co1">' // Go thru path components</span>
 &nbsp; &nbsp;<span class="kw3">Do</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' // Get next component</span>
 &nbsp; &nbsp; &nbsp; &nbsp;pComponent = PathFindNextComponent(pComponent)
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' // Check if end of line</span>
 &nbsp; &nbsp; &nbsp; &nbsp;CopyMemory tChar, <span class="kw4">ByVal</span> pComponent, 2
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> tChar = 0 <span class="kw3">Then</span> <span class="kw2">Exit</span> <span class="kw3">Do</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' // Write null-terminator</span>
 &nbsp; &nbsp; &nbsp; &nbsp;CopyMemory <span class="kw4">ByVal</span> pComponent - 2, 0, 2
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' // Check if path exists</span>
 &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw3">If</span> PathIsDirectory(pPath) = 0 <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' // Create folder</span>
 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw3">If</span> CreateDirectory(pPath, <span class="kw4">ByVal</span> 0&amp;) = 0 <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' // Error</span>
 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;CopyMemory <span class="kw4">ByVal</span> pComponent - 2, &amp;H5C, 2
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' // Restore path delimiter</span>
 &nbsp; &nbsp; &nbsp; &nbsp;CopyMemory <span class="kw4">ByVal</span> pComponent - 2, &amp;H5C, 2
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">Loop</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="co1">' // Success</span>
 &nbsp; &nbsp;CreateSubdirectories = <span class="kw5">True</span>
&nbsp; &nbsp; 
<span class="kw3">End</span> <span class="kw2">Function</span>
&nbsp;
<span class="co1">' // Get normalize path (replace wildcards, append file name)</span>
<span class="kw2">Function</span> NormalizePath( _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">ByVal</span> pPath <span class="kw4">As</span> <span class="kw1">Long</span>, _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">ByVal</span> pTitle <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> lPathLen &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>: &nbsp; &nbsp;<span class="kw4">Dim</span> lRelacerLen <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> lTitleLen &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>: &nbsp; &nbsp;<span class="kw4">Dim</span> pRelacer &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> lTotalLen &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>: &nbsp; &nbsp;<span class="kw4">Dim</span> lPtr &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> pTempString <span class="kw4">As</span> <span class="kw1">Long</span>: &nbsp; &nbsp;<span class="kw4">Dim</span> pRetString &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="co1">' // Determine wildcard</span>
 &nbsp; &nbsp;<span class="kw3">Select</span> <span class="kw3">Case</span> <span class="kw5">True</span>
&nbsp; &nbsp; <span class="kw3">Case</span> IntlStrEqWorker(0, pPath, pAppRepl, 5): pRelacer = pAppPath
&nbsp; &nbsp; <span class="kw3">Case</span> IntlStrEqWorker(0, pPath, pSysRepl, 5): pRelacer = pSysPath
&nbsp; &nbsp; <span class="kw3">Case</span> IntlStrEqWorker(0, pPath, pTmpRepl, 5): pRelacer = pTmpPath
&nbsp; &nbsp; <span class="kw3">Case</span> IntlStrEqWorker(0, pPath, pWinRepl, 5): pRelacer = pWinPath
&nbsp; &nbsp; <span class="kw3">Case</span> IntlStrEqWorker(0, pPath, pDrvRepl, 5): pRelacer = pDrvPath
&nbsp; &nbsp; <span class="kw3">Case</span> IntlStrEqWorker(0, pPath, pDtpRepl, 5): pRelacer = pDtpPath
&nbsp; &nbsp; <span class="kw3">Case</span> <span class="kw3">Else</span>: pRelacer = pStrNull
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">Select</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="co1">' // Get string size</span>
 &nbsp; &nbsp;lPathLen = lstrlen(<span class="kw4">ByVal</span> pPath)
&nbsp; &nbsp; lRelacerLen = lstrlen(<span class="kw4">ByVal</span> pRelacer)
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="co1">' // Skip wildcard</span>
 &nbsp; &nbsp;<span class="kw3">If</span> lRelacerLen <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; pPath = pPath + 5 * 2
&nbsp; &nbsp; &nbsp; &nbsp; lPathLen = lPathLen - 5
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> pTitle <span class="kw3">Then</span> lTitleLen = lstrlen(<span class="kw4">ByVal</span> pTitle)
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="co1">' // Get length all strings</span>
 &nbsp; &nbsp;lTotalLen = lPathLen + lRelacerLen + lTitleLen
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="co1">' // Check overflow (it should be les or equal MAX_PATH)</span>
 &nbsp; &nbsp;<span class="kw3">If</span> lTotalLen &gt; MAX_PATH <span class="kw3">Then</span> <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="co1">' // Create string</span>
 &nbsp; &nbsp;pTempString = SysAllocStringLen(0, MAX_PATH)
&nbsp; &nbsp; <span class="kw3">If</span> pTempString = 0 <span class="kw3">Then</span> <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="co1">' // Copy</span>
 &nbsp; &nbsp;lstrcpyn <span class="kw4">ByVal</span> pTempString, <span class="kw4">ByVal</span> pRelacer, lRelacerLen + 1
&nbsp; &nbsp; lstrcat <span class="kw4">ByVal</span> pTempString, <span class="kw4">ByVal</span> pPath
&nbsp;
&nbsp; &nbsp; <span class="co1">' // If title is presented append</span>
 &nbsp; &nbsp;<span class="kw3">If</span> pTitle <span class="kw3">Then</span>
&nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' // Error</span>
 &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw3">If</span> PathAddBackslash(pTempString) = 0 <span class="kw3">Then</span> <span class="kw3">GoTo</span> CleanUp
&nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' // Copy file name</span>
 &nbsp; &nbsp; &nbsp; &nbsp;lstrcat <span class="kw4">ByVal</span> pTempString, <span class="kw4">ByVal</span> pTitle
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="co1">' // Alloc memory for translation relative path to absolute</span>
 &nbsp; &nbsp;pRetString = SysAllocStringLen(0, MAX_PATH)
&nbsp; &nbsp; <span class="kw3">If</span> pRetString = 0 <span class="kw3">Then</span> <span class="kw3">GoTo</span> CleanUp
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="co1">' // Normalize</span>
 &nbsp; &nbsp;<span class="kw3">If</span> PathCanonicalize(pRetString, pTempString) = 0 <span class="kw3">Then</span> <span class="kw3">GoTo</span> CleanUp
&nbsp; &nbsp; 
&nbsp; &nbsp; NormalizePath = pRetString
&nbsp; &nbsp; 
CleanUp:
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> pTempString <span class="kw3">Then</span> SysFreeString pTempString
&nbsp; &nbsp; <span class="kw3">If</span> pRetString &lt;&gt; 0 <span class="kw3">And</span> NormalizePath = 0 <span class="kw3">Then</span> SysFreeString pRetString
&nbsp; &nbsp; 
<span class="kw3">End</span> <span class="kw2">Function</span>
&nbsp;
<span class="co1">' // Concatenation strings</span>
<span class="kw2">Function</span> StrCat( _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">ByVal</span> pStringDest <span class="kw4">As</span> <span class="kw1">Long</span>, _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">ByVal</span> pStringAppended <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> l1 <span class="kw4">As</span> <span class="kw1">Long</span>, l2 <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; l1 = lstrlen(<span class="kw4">ByVal</span> pStringDest): l2 = lstrlen(<span class="kw4">ByVal</span> pStringAppended)
&nbsp; &nbsp; StrCat = SysAllocStringLen(0, l1 + l2)
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> StrCat = 0 <span class="kw3">Then</span> <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; lstrcpyn <span class="kw4">ByVal</span> StrCat, <span class="kw4">ByVal</span> pStringDest, l1 + 1
&nbsp; &nbsp; lstrcat <span class="kw4">ByVal</span> StrCat, <span class="kw4">ByVal</span> pStringAppended
&nbsp; &nbsp; 
<span class="kw3">End</span> <span class="kw2">Function</span></pre></td></tr></table></div></td></tr></tbody></table></div><a href="https://www.cyberforum.ru/blogs/354370/blog4392.html">Вторая часть -&gt;.</a></div>

]]></content:encoded>
			<dc:creator>The trick</dc:creator>
			<guid isPermaLink="true">https://www.cyberforum.ru/blogs/354370/4391.html</guid>
		</item>
		<item>
			<title>Компьютер сочиняет музыку</title>
			<link>https://www.cyberforum.ru/blogs/354370/4099.html</link>
			<pubDate>Wed, 27 Jan 2016 10:10:33 GMT</pubDate>
			<description>AB0FaDtHeRE 
s_aPUuhPKxc 
scBioOW0_rk 
Всем привет. Как-то давно я делал виртуального композитора...</description>
			<content:encoded><![CDATA[<div><iframe width="640" height="360" src="https://www.youtube.com/embed/AB0FaDtHeRE" frameborder="0" allowfullscreen></iframe><br />

			   <div class="spoiler-wrap">
				   <div class="spoiler-head folded clickable">Кликните здесь для просмотра всего текста</div>
				   <div class="spoiler-body">
					   <iframe width="640" height="360" src="https://www.youtube.com/embed/s_aPUuhPKxc" frameborder="0" allowfullscreen></iframe>
				   </div>
			   </div><br />

			   <div class="spoiler-wrap">
				   <div class="spoiler-head folded clickable">Кликните здесь для просмотра всего текста</div>
				   <div class="spoiler-body">
					   <iframe width="640" height="360" src="https://www.youtube.com/embed/scBioOW0_rk" frameborder="0" allowfullscreen></iframe>
				   </div>
			   </div><br />
Всем привет. Как-то давно я делал виртуального композитора для создания 8-бит музыки, но в итоге забросил это дело. Я даже писал об этом и выкладывал демки (<a rel="nofollow noopener noreferrer" href="http://promodj.com/Thetrik/tracks/2757679/Demo8Bit" target="_blank" title="http://promodj.com/Thetrik/tracks/2757679/Demo8Bit">вот</a> и <a rel="nofollow noopener noreferrer" href="http://promodj.com/Thetrik/tracks/2757644/Demo8BitCreater" target="_blank" title="http://promodj.com/Thetrik/tracks/2757644/Demo8BitCreater">вот</a>). На днях я немного доработал его + добавил свои синтезаторы (<a rel="nofollow noopener noreferrer" href="https://www.youtube.com/watch?v=tVxFpqVWdwk" target="_blank" title="https://www.youtube.com/watch?v=tVxFpqVWdwk">вот</a> и <a rel="nofollow noopener noreferrer" href="https://www.youtube.com/watch?v=Dt6ac7kAgEo" target="_blank" title="https://www.youtube.com/watch?v=Dt6ac7kAgEo">вот</a>)  в него + банки пресетов к ним.<br />
Кстати там же можете добавлять новые пресеты и сохранять их.<br />
Конечно музыкой это назвать нельзя (из-за кривого алгоритма, который я разработал давно, когда еще и музыку толком сам не умел делать и программировать не умел), но поиграться можно.<br />
Для создания нового трека нужно придумать имя трека и ввести его в поле <b>Name</b>, если поле ничего не содержит оно заполнится произвольным числом. На основе имени создается трек (вычисляется хэш и это значение используется в качестве зерна псевдослучайного генератора), т.е. имя будет однозначно идентифицировать трек. Поле <b>Tempo </b>задает темп. Правой кнопкой мыши по треку - открывает окно синтезатора трека, можно менять параметры тем самым изменяя звучание. Левой кнопкой по паттерну - просмотреть паттерн. Никаких поверок нет, так что не нужно туда вводить неправильные значения иначе программа &quot;рухнет&quot;.<br />
Также программа может зависнуть (опять-таки из-за кривого алгоритма) при создании трека. <br />

			   <div class="spoiler-wrap">
				   <div class="spoiler-head folded clickable">Кликните здесь для просмотра всего текста</div>
				   <div class="spoiler-body">
					   Некоторые интересные найденные треки:<ol style="list-style-type: decimal"><li>45645</li>
<li>Хакер</li>
<li>VBStreets.ru</li>
<li>Телепат</li>
<li>681</li>
<li>60000</li>
<li>43964</li>
<li>412</li>
<li>44170</li>
<li>30691</li>
<li>28259</li>
<li>56865</li>
<li>38399</li>
<li>basic</li>
<li>48029</li>
<li>space</li>
<li>73180</li>
<li>41990</li>
<li>52528</li>
<li>7902</li>
<li>95280</li>
</ol>
				   </div>
			   </div><br />
Удачи!<br />
<a rel="nofollow noopener noreferrer" href="https://yadi.sk/d/RzJY8Ts03Hbhpv" target="_blank" title="https://yadi.sk/d/RzJY8Ts03Hbhpv">Скачать.</a></div>

]]></content:encoded>
			<dc:creator>The trick</dc:creator>
			<guid isPermaLink="true">https://www.cyberforum.ru/blogs/354370/4099.html</guid>
		</item>
		<item>
			<title>Inline assembler</title>
			<link>https://www.cyberforum.ru/blogs/354370/4075.html</link>
			<pubDate>Mon, 11 Jan 2016 13:38:16 GMT</pubDate>
			<description>Всем привет. 
Бывают ситуации когда в VB нужно использовать ассемблер. Обычно для этого используют...</description>
			<content:encoded><![CDATA[<div>Всем привет.<br />
Бывают ситуации когда в VB нужно использовать ассемблер. Обычно для этого используют предварительно скомпилированный код размещенный в памяти и запускают его одним из миллиона способов. Очевидным недостатком этого метода является то, что любая модификация ассемблерного кода требует изменения в процедурах размещения кода в памяти. К тому же это является довольно медленной процедурой. Я написал Add-in, который делает вышеописанные процедуры автоматически, а также после компиляции никакие дополнительные действия по размещению кода не выполняются - ассемблерный код влинковывается в EXE. Ассемблерный код работает и в IDE и в скомпилированном (только Native!) коде.<br />
Как этим пользоваться?<br />
Для начала нужно установить Add-in (установщик доступен по ссылке ниже). После установки нужно запустить его из VB (Add-Ins -&gt; Add-in Manager -&gt; Inline assembler). После этого в меню добавится одноименный пункт. Если проект еще не использовал функционал Add-in'а то при первой активации добавится стандартный модуль, в который нужно будет добавить прототипы ассемблерных функций для VB6. Сам модуль можно переименовывать, добавлять процедуры/функции, но размещать в них какой-либо функционал запрещено. После создания модуля можно уже открыть сам редактор ассемблерного кода. В нем доступен раскрывающийся список с именами функций, определенных в добавленном модуле. Для каждой функции можно переопределить код используя синтаксис ассемблера <a rel="nofollow noopener noreferrer" href="http://www.nasm.us/" target="_blank" title="http://www.nasm.us/">NASM</a>, однако если кода нет, то функция не модифицируется (т.е. получается обычный вызов пустой функции). С каждым проектом (если использовался функционал Add-in'а) связывается еще один файл с расширением *ia в папке с файлом проекта, в котором хранятся ассемблерные процедуры для проекта. Add-in работает &quot;прозрачно&quot;, т.е. если Add-in отключен, то проект также будет работать и компилироваться, просто &quot;функции-пустышки&quot; будут работать как обычные. Файл *ia не является жизненно-необходимым для работы проекта, если его не будет, то соответственно &quot;функции-пустышки&quot; останутся нетронутыми. <br />
Давайте посмотрим работу Add-in'а на простом примере. К примеру нам необходимо сложить два Integer-массива без переполнения, т.е. если результат сложения больше 32767 он и останется 32767, а если меньше -32768 он останется -32768. К тому же нам нужно сделать это как можно быстрее.  Для этого очень хорошо подходит расширение MMX, в нем есть инструкции для работы с векторными данными с насыщением. Перейдем к реализации. Создадим новый проект, откроем Add-in. Это добавит новый модуль, переименуем его в <b>modInlineAsm</b>. Теперь определим прототип функции:<br />
<div class="codeblock"><table class="vb"><thead><tr><td colspan="2" id="499748655"  class="head">Visual Basic</td></tr></thead><tbody><tr class="li1"><td><div id="499748655" style="height: 110px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
5
</pre></td><td class="de1"><pre class="de1"><span class="kw2">Public</span> <span class="kw2">Function</span> MMXAdd( _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">ByRef</span> dest <span class="kw4">As</span> <span class="kw1">Integer</span>, _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">ByRef</span> src <span class="kw4">As</span> <span class="kw1">Integer</span>, _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">ByVal</span> count <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw3">End</span> <span class="kw2">Function</span></pre></td></tr></table></div></td></tr></tbody></table></div>Первым параметром передаем первый элемент массива, он же результирующий; вторым параметром первый элемент второго массива; наконец третьим параметром передаем количество элементов. Кстати размер массивов должен быть кратен 8 байтам, т.к. мы будем использовать векторные инструкции которые одновременно работают с 8-ю байтами. Теперь определим процедуру в форме, которая будет вызывать эту функцию:<br />
<div class="codeblock"><table class="vb"><thead><tr><td colspan="2" id="681469206"  class="head">Visual Basic</td></tr></thead><tbody><tr class="li1"><td><div id="681469206" style="height: 350px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
</pre></td><td class="de1"><pre class="de1"><span class="kw2">Private</span> <span class="kw2">Sub</span> Form_Load()
&nbsp; &nbsp; <span class="kw4">Dim</span> src() &nbsp; <span class="kw4">As</span> <span class="kw1">Integer</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> dst() &nbsp; <span class="kw4">As</span> <span class="kw1">Integer</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> size &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> index &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; size = 1024
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw4">ReDim</span> src(size - 1)
&nbsp; &nbsp; <span class="kw4">ReDim</span> dst(size - 1)
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">For</span> index = 0 <span class="kw3">To</span> size - 1
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' // Fill arrays with sine</span>
 &nbsp; &nbsp; &nbsp; &nbsp;src(index) = Sin(index / 40) * 20000
&nbsp; &nbsp; &nbsp; &nbsp; dst(index) = Sin(index / 23) * 20000
&nbsp; &nbsp; <span class="kw3">Next</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="co1">' // Add with saturation</span>
 &nbsp; &nbsp;MMXAdd dst(0), src(0), size
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="co1">'// Draw result</span>
 &nbsp; &nbsp;AutoRedraw = <span class="kw5">True</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; Scale (0, 32767)-(index, -32768)
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">For</span> index = 0 <span class="kw3">To</span> size - 1
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> index <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Me.<span class="kw4">Line</span> -(index, dst(index))
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">Else</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Me.PSet (index, dst(index))
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; <span class="kw3">Next</span>
&nbsp; &nbsp; 
<span class="kw3">End</span> <span class="kw2">Sub</span></pre></td></tr></table></div></td></tr></tbody></table></div>Как видно здесь просто заполняются 2 массива синусоидой, а потом мы их складываем с помощью нашей функции, и выводим результат на форму в виде графика. Теперь нам нужно переопределить процедуру, для этого активируем Add-in. Откроется окно с редактором кода. В нем мы выбираем нашу функцию и добавляем следующий ассемблерный код:<br />
<div class="codeblock"><table class="asm"><thead><tr><td colspan="2" id="727918660"  class="head">Assembler</td></tr></thead><tbody><tr class="li1"><td><div id="727918660" style="height: 350px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
</pre></td><td class="de1"><pre class="de1"><span class="kw5">BITS</span> <span class="nu0">32</span>
&nbsp;
<span class="co1">; Addition of two arrays using saturation</span>
<span class="co1">; Size of arrays should be a multiple of 8</span>
&nbsp;
<span class="kw1">push</span> &nbsp; &nbsp;<span class="kw4">EBP</span>
<span class="kw1">mov</span> <span class="kw4">EBP</span><span class="sy1">,</span> <span class="kw4">ESP</span>
<span class="kw1">push</span> &nbsp; &nbsp;<span class="kw4">EBX</span>
<span class="kw1">push</span> &nbsp; &nbsp;<span class="kw4">ESI</span>
<span class="kw1">push</span> &nbsp; &nbsp;<span class="kw4">EDI</span>
<span class="kw1">mov</span> <span class="kw4">ESI</span><span class="sy1">,</span><span class="kw6">DWORD</span> <span class="br0">&#91;</span><span class="kw4">EBP</span><span class="sy1">+</span><span class="nu0">0x8</span><span class="br0">&#93;</span>
<span class="kw1">mov</span> <span class="kw4">EDI</span><span class="sy1">,</span><span class="kw6">DWORD</span> <span class="br0">&#91;</span><span class="kw4">EBP</span><span class="sy1">+</span><span class="nu0">0x0C</span><span class="br0">&#93;</span>
<span class="kw1">mov</span> <span class="kw4">ECX</span><span class="sy1">,</span><span class="kw6">DWORD</span> <span class="br0">&#91;</span><span class="kw4">EBP</span><span class="sy1">+</span><span class="nu0">0x10</span><span class="br0">&#93;</span>
<span class="kw1">shr</span> <span class="kw4">ECX</span><span class="sy1">,</span><span class="nu0">2</span>
&nbsp;
<span class="kw1">test</span>&nbsp; &nbsp; <span class="kw4">ECX</span><span class="sy1">,</span><span class="kw4">ECX</span>
<span class="kw1">je</span>&nbsp; EXIT_PROC
<span class="kw3">emms</span>&nbsp; &nbsp; <span class="co1">; Initialize MMX</span>
&nbsp;
CYCLE<span class="sy1">:</span>
&nbsp; &nbsp; <span class="kw3">movq</span>&nbsp; &nbsp; <span class="kw4">MM0</span><span class="sy1">,</span><span class="kw6">QWORD</span> <span class="br0">&#91;</span><span class="kw4">EDI</span><span class="br0">&#93;</span>
&nbsp; &nbsp; <span class="kw3">movq</span>&nbsp; &nbsp; <span class="kw4">MM1</span><span class="sy1">,</span><span class="kw6">QWORD</span> <span class="br0">&#91;</span><span class="kw4">ESI</span><span class="br0">&#93;</span>
&nbsp; &nbsp; <span class="kw3">paddsw</span>&nbsp; <span class="kw4">MM1</span><span class="sy1">,</span><span class="kw4">MM0</span>
&nbsp; &nbsp; <span class="kw3">movq</span>&nbsp; &nbsp; <span class="kw6">QWORD</span> <span class="br0">&#91;</span><span class="kw4">ESI</span><span class="br0">&#93;</span><span class="sy1">,</span><span class="kw4">MM1</span>
&nbsp; &nbsp; <span class="kw1">add</span> <span class="kw4">ESI</span><span class="sy1">,</span><span class="nu0">0x8</span>
&nbsp; &nbsp; <span class="kw1">add</span> <span class="kw4">EDI</span><span class="sy1">,</span><span class="nu0">0x8</span>
<span class="kw1">loop</span>&nbsp; &nbsp; CYCLE
&nbsp;
<span class="kw3">emms</span>
&nbsp;
EXIT_PROC<span class="sy1">:</span>
<span class="kw1">pop</span> &nbsp; &nbsp; <span class="kw4">EDI</span>
<span class="kw1">pop</span> <span class="kw4">ESI</span>
<span class="kw1">pop</span> <span class="kw4">EBX</span>
<span class="kw1">mov</span> <span class="kw4">esp</span><span class="sy1">,</span> <span class="kw4">ebp</span>
<span class="kw1">pop</span> <span class="kw4">ebp</span>
&nbsp;
<span class="kw1">ret</span> <span class="nu0">0x0c</span></pre></td></tr></table></div></td></tr></tbody></table></div>Здесь все просто, если знать инструкции. Самая главная <b>paddsw</b> складывает два четырехмерных вектора 16-битных значений со знаком за одну операцию. Теперь сохраняем проект и запускаем:<br />
<a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=3525&amp;d=1452519409" rel="Lightbox" id="attachment3525" ><img src="https://www.cyberforum.ru/blog_attachment.php?attachmentid=3525&amp;thumb=1&amp;d=1452519409" class="thumbnail" border="0" alt="Нажмите на изображение для увеличения
Название: MMX_test.PNG
Просмотров: 752
Размер:	20.7 Кб
ID:	3525" style="margin: 5px" /></a><br />
Как видно из скриншота, две синусоиды сложились причем с насыщением, это можно заметить по пикам. Теперь давайте попробуем скомпилировать EXE файл и посмотреть что у нас вызывается и во что компилируется:<br />
<a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=3526&amp;d=1452519409" rel="Lightbox" id="attachment3526" ><img src="https://www.cyberforum.ru/blog_attachment.php?attachmentid=3526&amp;thumb=1&amp;d=1452519409" class="thumbnail" border="0" alt="Нажмите на изображение для увеличения
Название: Disasm_Listing.PNG
Просмотров: 806
Размер:	29.6 Кб
ID:	3526" style="margin: 5px" /></a><br />
Как видим код уже находится внутри EXE, без всяких выделений буферов и т.п. ненужных вещей.<br />
Как это работает?<br />
На самом деле все довольно просто. В самом начале ставятся обработчики на ключевые события вроде компиляции, запуска кода, закрытия/сохранения проекта и т.п. При запуске из IDE компилируются все коды заданные пользователем, а также вычисляются адреса переопределяемых функций. Затем код переписывается на код скомпилированный через NASM. И при вызове кода, вызывается наш код. При остановке, код восстанавливается. При компиляции (а точнее перед линковкой) ищется OBJ файл переопределяемого модуля и код функций пустышек переписывается на ассемблерный код, и файл пересохраняется. Для этого я специально писал COFF парсер. Вообще это открывает кучу разных возможностей от замены функций рантайма на свои до шифрования кода. Вообщем много чего можно придумать.<br />
<br />
Проект, можно сказать, совсем не тестировался, т.к. у меня в ближайшее время не будет такой возможности, поэтому скорее-всего будет много багов, учитывая то, что половина функционала Add-in'а использует недокументированные трюки, которые возможно работают не так как я думаю. В проекте нет даже подсветки синтаксиса, у меня есть мой самодельный текстбокс с подсветкой написанный с нуля, но его нужно дебажить еще и дописывать, поэтому пока я использовать стандартный текстбокс. Если кого-нибудь заинтересует, и будут баги, пишите сюда. <br />
Кстати, я редко стал писать сюда, да и вообще бывать на форуме, но это не значит что я перестал заниматься VB, у меня еще много проектов которые наполовину реализованы. К примеру готовится проект для использования оптимизаций компиляции в IDE, т.е. отключить переполнения при арифметических операциях для использования циклической арифметики в IDE, а также проверку границ массивов в IDE. <br />
Также к примеру вот:<br />
<iframe width="640" height="360" src="https://www.youtube.com/embed/ZTWrZC3CO70" frameborder="0" allowfullscreen></iframe><br />
-нативная загрузка PNG а альфаканалом в IDE в любые контролы. Так что ждите).<br />
Всем спасибо за внимание.<br />
<a rel="nofollow noopener noreferrer" href="https://yadi.sk/d/pWTzbQ4Hmogyy" target="_blank" title="https://yadi.sk/d/pWTzbQ4Hmogyy">Скачать Add-in.</a></div>

]]></content:encoded>
			<dc:creator>The trick</dc:creator>
			<guid isPermaLink="true">https://www.cyberforum.ru/blogs/354370/4075.html</guid>
		</item>
		<item>
			<title>Стеганография в WAVE файлах.</title>
			<link>https://www.cyberforum.ru/blogs/354370/4018.html</link>
			<pubDate>Sat, 26 Dec 2015 19:43:39 GMT</pubDate>
			<description>djlRv0U4vwo 
Всем привет! 
Сегодня я хотел бы поговорить о криптографии. Я сделал пример...</description>
			<content:encoded><![CDATA[<div><div align="center"><iframe width="640" height="360" src="https://www.youtube.com/embed/djlRv0U4vwo" frameborder="0" allowfullscreen></iframe></div>Всем привет!<br />
Сегодня я хотел бы поговорить о криптографии. Я сделал пример использования специальной криптографии - стеганографии. Этот метод скрывает сам факт шифрования данных. Существует множество видов стеганографии. Я бы хотел поговорить о LBS-методе, в котором данные скрываются в младших битах аудио данных. Это выглядит так, как-будто пользователи обмениваются аудио файлами, но на самом деле они обмениваются секретными данными. Тот кто не знает об этом методе не будет даже подозревать о обмене секретными данными. В некоторых случаях это может быть очень полезно.<br />
Как это работает?<br />
WAVE-PCM файл (без компрессии) содержит аудио данные. На самом деле звук является аналоговым событием, т.е. непрерывным. Для того чтобы перевести его в цифровую форму нужно проквантовать его с некоторыми потерями. Этот процесс характеризуется двумя параметрами: разрядностью и частотой дискретизации. Разрядность влияет на то как много уровней сигнала может содержаться в каждом семпле. Частота дискретизации влияет на то какая максимальная частота может содержатся в аудиоданных:<br />
<div align="center"><a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=3483&amp;d=1451157166" rel="Lightbox" id="attachment3483" ><img src="https://www.cyberforum.ru/blog_attachment.php?attachmentid=3483&amp;thumb=1&amp;d=1451157166" class="thumbnail" border="0" alt="Нажмите на изображение для увеличения
Название: Pic1_rus.png
Просмотров: 566
Размер:	36.7 Кб
ID:	3483" style="margin: 5px" /></a></div>В нашем случае нас интересует только разрядность аудиоданных. Она может быть 32, 24, 16, ... бит на семпл. Главная идея стеганографии (в нашем случае) - переписать младшие биты аудиоданных. Чем больше бит мы будем использовать, тем сильнее будут искажения.<br />
Наглядно:<br />
<div align="center"><a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=3485&amp;d=1451157631" rel="Lightbox" id="attachment3485" ><img src="https://www.cyberforum.ru/blog_attachment.php?attachmentid=3485&amp;thumb=1&amp;d=1451157631" class="thumbnail" border="0" alt="Нажмите на изображение для увеличения
Название: pic2_rus.png
Просмотров: 538
Размер:	30.7 Кб
ID:	3485" style="margin: 5px" /></a></div>Как видно из рисунка метод сохраняет все шифруемые данные в определенных битах аудиоданных (в данном случае  4 бита на каждый семпл). Также заметьте что для сохранения данных нужно использовать аудио файл бо&#769;льший по размеру чем шифруемый файл. Для примера, если мы будем использовать 3 бита для кодирования, аудиофайл должен будет иметь размер как минимум в 16/3 раза больше чем кодируемый. Я говорю 16 потому что я использую 16 бит на семпл в моем примере.<br />
В прикрепленном примере я также сохраняю оригинальное имя файла. Формат данных наглядно:<br />
<div align="center"><a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=3486&amp;d=1451158452" rel="Lightbox" id="attachment3486" ><img src="https://www.cyberforum.ru/blog_attachment.php?attachmentid=3486&amp;thumb=1&amp;d=1451158452" class="thumbnail" border="0" alt="Нажмите на изображение для увеличения
Название: pic3_rus.png
Просмотров: 759
Размер:	18.8 Кб
ID:	3486" style="margin: 5px" /></a></div>Когда происходит упаковка берется каждый байт из шифруемого файла. Затем извлекается необходимые биты из очередного байта, а также очищаются соответственные биты в аудиоданных. Далее эти биты устанавливаются посредством операции побитовое-ИЛИ. Для извлечения необходимых бит используются маски и сдвиги. Маски оставляют необходимые биты, а сдвиги размещают их в начале байта. <br />
Распаковка происходит с точностью наоборот. Извлекаются биты из аудио файла и собирается исходный файл.<br />
Надеюсь что этот небольшой обзор будет полезным.<br />
Спасибо за внимание.<br />
С уважением,<br />
Кривоус Анатолий.</div>


<!-- attachments -->
	<div style="margin-top:10px">

		
		
		
		
			<fieldset class="fieldset">
				<legend>Вложения</legend>
				<table cellpadding="0" cellspacing="3" border="0">
				<tr>
	<td><img loading="lazy" decoding="async" class="inlineimg" src="http://www.cyberforum.ru//cyberstatic.net/images/attach/zip.gif" alt="Тип файла: zip" width="16" height="16" border="0" style="vertical-align:baseline" /></td>
	<td><a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=3487&amp;d=1451158876">WaveSteganography.zip</a> (8.5 Кб, 393 просмотров)</td>
</tr>
				</table>
			</fieldset>
		

	</div>
<!-- / attachments -->
]]></content:encoded>
			<dc:creator>The trick</dc:creator>
			<guid isPermaLink="true">https://www.cyberforum.ru/blogs/354370/4018.html</guid>
		</item>
		<item>
			<title>DirectSound в VB6.</title>
			<link>https://www.cyberforum.ru/blogs/354370/3664.html</link>
			<pubDate>Mon, 27 Jul 2015 10:46:50 GMT</pubDate>
			<description>Параллельно с *Direct3D9* я делал библиотеку типов и модуль с вспомогательными функциями для...</description>
			<content:encoded><![CDATA[<div>Параллельно с <b>Direct3D9</b> я делал библиотеку типов и модуль с вспомогательными функциями для <b>DirectSound</b>. В архиве библиотека типов dsvb.tlb и модуль <b>DS_Functions.bas</b>. В дальнейшем добавлю модуль класса для поддержки асинхронных уведомлений, пока можно пользоваться <a href="https://www.cyberforum.ru/blogs/354370/blog2686.html">этим</a>.<br />
В модуле DS_Functions содержатся следующие функции:<ul><li><b>DSCreateSoundBufferFromFile</b> - создает объект с интерфейсом <b>IDirectSoundBuffer8</b> из файла. Поддерживаются только WAVE и MP3 файлы. MP3 файлы могут содержать только ID3v1 и ID3v2 теги, какие-либо другие возможно не распознаются/не будут работать. Слишком длинные (по времени) файлы не поддерживаются. Для потокового воспроизведения нужно писать потоковое декодирование на основе кода функции <b>DSCreateSoundBufferFromMemory</b>.</li>
<li><b>DSCreateSoundBufferFromMemory</b> - тоже самое, но вместо файла передается указатель на данные файла в памяти и их размер.</li>
</ul>Также в архиве содержится пример плеера который реализует некоторые методы <b>IDirectSoundBuffer8</b> интерфейса (громкость, панорама, частота, эффекты).<br />
TLB также особо сильно не тестировалась, поэтому что-то может не работать. Если что-то не работает пишите сюда.<br />
<a rel="nofollow noopener noreferrer" href="https://yadi.sk/d/za2eXaCWi7PZG" target="_blank" title="https://yadi.sk/d/za2eXaCWi7PZG">Скачать.</a></div>

]]></content:encoded>
			<dc:creator>The trick</dc:creator>
			<guid isPermaLink="true">https://www.cyberforum.ru/blogs/354370/3664.html</guid>
		</item>
		<item>
			<title>Direct3D9 в VB6</title>
			<link>https://www.cyberforum.ru/blogs/354370/3663.html</link>
			<pubDate>Mon, 27 Jul 2015 10:42:43 GMT</pubDate>
			<description>Всем привет. 
Уже довольно давно занимаюсь созданием библиотеки типов *Direct3D9* и вспомогательных...</description>
			<content:encoded><![CDATA[<div>Всем привет.<br />
Уже довольно давно занимаюсь созданием библиотеки типов <b>Direct3D9</b> и вспомогательных функций <b>D3DX</b> для VB6.<br />
Итак в архиве содержится библиотека типов &quot;<b>DirectX 9 for Visual Basic 6.0 type library by The trick</b>&quot; (<b><b>dx9vb.tlb</b></b>) содержащая описание следующих интерфейсов:<ul><li>IDirect3D9;</li>
<li>IDirect3DDevice9;</li>
<li>IDirect3DSurface9;</li>
<li>IDirect3DResource9;</li>
<li>IDirect3DSwapChain9;</li>
<li>IDirect3DTexture9;</li>
<li>IDirect3DBaseTexture9;</li>
<li>IDirect3DVolumeTexture9;</li>
<li>IDirect3DVolume9;</li>
<li>IDirect3DCubeTexture9;</li>
<li>IDirect3DVertexBuffer9;</li>
<li>IDirect3DIndexBuffer9;</li>
<li>IDirect3DStateBlock9;</li>
<li>IDirect3DVertexDeclaration9;</li>
<li>IDirect3DVertexShader9;</li>
<li>IDirect3DPixelShader9;</li>
<li>IDirect3DQuery9;</li>
</ul>Также в этой библиотеке задеклалриованы множество типов, констант и энумов. Работа этой библиотеки слабо тестировалась, поэтому что-то может не работать.<br />
Также в архиве содержится несколько модулей написанных на VB6:<ul><li>D3DX_COLOR.bas - для работы с цветами</li>
<li>D3DX_MATRICES.bas - для работы с матрицами</li>
<li>D3DX_QUATERNION.bas - для работы с кватернионами</li>
<li>D3DX_VECTOR2.bas, D3DX_VECTOR3.bas, D3DX_VECTOR4.bas - для работы с векторами</li>
<li>D3DX_MISC.bas - различные функции которые не вошли не в одну из категорий</li>
</ul>Эти модули содержат аналоги соответствующих функций <b>D3DX</b>.<br />
Также в архиве содержится несколько тестовых примеров работы.<br />
<br />
<a rel="nofollow noopener noreferrer" href="https://yadi.sk/d/UAlif2763VRxLQ" target="_blank" title="https://yadi.sk/d/UAlif2763VRxLQ">Скачать.</a><br />
<br />
<i><font color="#FF0000">08.24.2015. добавлен пример вывода текста</font></i><br />
<br />
<i><font color="#FF0000">09.17.2015. добавлен пример 3D форма</font></i><br />
<br />
<i><font color="Red">02.27.2016. Исправлен баг в функции <b>D3DXMatrixTranspose</b>. Изменены типы указателей на <b>Any</b> в методах <b>IDirect3DDevice9::CreateVertexShader</b>, <b>IDirect3DDevice9::CreatePixelShader</b>, <b>IDirect3DDevice9::SetPixelShaderConstant B</b>, <b>IDirect3DDevice9::SetPixelShaderConstant I</b>, <b>IDirect3DDevice9::SetPixelShaderConstant F</b>, <b>IDirect3DDevice9::SetVertexShaderConstan tB</b>, <b>IDirect3DDevice9::SetVertexShaderConstan tI</b>, <b>IDirect3DDevice9::SetVertexShaderConstan tF</b></font></i><br />
<br />
<i><font color="#FF0000">06.05.2018. Добавлены примеры: Сцена (камера, процедурная генерация, выделение мышью, трансформации), &quot;Лазерные&quot; линии, Рендеринг в отдельном потоке<br />
Исправлены баги в функциях: <b>D3DXMatrixTranspose</b>, <b>D3DXMatrixRotationQuaternion</b>, <b>D3DXVec2Length</b>;<br />
Изменен прототип для функции <b>D3DXPlaneIntersectLine</b>, вместо генерации ошибки функция возвращает значение;<br />
Изменено поведение функции <b>D3DXQuaternionToAxisAngle</b>, теперь она может принимать ненормализованные кватернионы.</font></i></div>


<!-- attachments -->
	<div style="margin-top:10px">

		
			<fieldset class="fieldset">
				<legend>Миниатюры</legend>
				<div style="padding:3px">
				
	<a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=3235&amp;d=1437993130" target="attachment" rel="Lightbox" id="attachment3235"><img loading="lazy" decoding="async" class="thumbnail" src="https://www.cyberforum.ru/blog_attachment.php?attachmentid=3235&amp;stc=1&amp;thumb=1&amp;d=1437993130" border="0" alt="Нажмите на изображение для увеличения
Название: Landscape.JPG
Просмотров: 1128
Размер:	44.2 Кб
ID:	3235" /></a>
	&nbsp;
	

	<a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=3236&amp;d=1437993130" target="attachment" rel="Lightbox" id="attachment3236"><img loading="lazy" decoding="async" class="thumbnail" src="https://www.cyberforum.ru/blog_attachment.php?attachmentid=3236&amp;stc=1&amp;thumb=1&amp;d=1437993130" border="0" alt="Нажмите на изображение для увеличения
Название: Fire.JPG
Просмотров: 732
Размер:	13.2 Кб
ID:	3236" /></a>
	&nbsp;
	

				</div>
			</fieldset>
		
		
		
		
			<fieldset class="fieldset">
				<legend>Вложения</legend>
				<table cellpadding="0" cellspacing="3" border="0">
				
				</table>
			</fieldset>
		

	</div>
<!-- / attachments -->
]]></content:encoded>
			<dc:creator>The trick</dc:creator>
			<guid isPermaLink="true">https://www.cyberforum.ru/blogs/354370/3663.html</guid>
		</item>
		<item>
			<title>Использование COM/ActiveX библиотек без регистрации в реестре.</title>
			<link>https://www.cyberforum.ru/blogs/354370/3550.html</link>
			<pubDate>Mon, 25 May 2015 19:15:07 GMT</pubDate>
			<description>Привет всем. Выкладываю модуль для работы с COM-Dll без регистрации в реестре. 
Модуль имеет...</description>
			<content:encoded><![CDATA[<div>Привет всем. Выкладываю модуль для работы с COM-Dll без регистрации в реестре.<br />
Модуль имеет несколько функций:<ol style="list-style-type: decimal"><li><b>GetAllCoclasses</b> - возвращает список имен классов вместе с идентификаторами извлеченными из библиотеки типов.</li>
<li><b>CreateIDispatch</b> - создает реализацию IDispatch на основе объекта и имени интерфейса.</li>
<li><b>CreateObjectEx2</b> - создает объект по имени.</li>
<li><b>CreateObjectEx</b> - создает объект по CLSID.</li>
<li><b>UnloadLibrary</b> - Выгружает библиотеку, если та не используется.</li>
</ol><br />
<div class="codeblock"><table class="vb"><thead><tr><td colspan="2" id="302533465"  class="head">Visual Basic</td></tr></thead><tbody><tr class="li1"><td><div id="302533465" style="height: 350px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
</pre></td><td class="de1"><pre class="de1"><span class="co1">' The module modTrickUnregCOM.bas - for working with COM libraries without registration.</span>
<span class="co1">' © Krivous Anatolii Anatolevich (The trick), 2015</span>
&nbsp;
<span class="kw2">Option</span> <span class="kw2">Explicit</span>
&nbsp;
<span class="kw2">Public</span> <span class="kw4">Type</span> GUID
&nbsp; &nbsp; data1 &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; data2 &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Integer</span>
&nbsp; &nbsp; data3 &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Integer</span>
&nbsp; &nbsp; data4(7) &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Byte</span>
<span class="kw3">End</span> <span class="kw4">Type</span>
&nbsp;
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Function</span> CLSIDFromString Lib <span class="st0">&quot;ole32.dll&quot;</span> ( _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">ByVal</span> lpszCLSID <span class="kw4">As</span> <span class="kw1">Long</span>, _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">ByRef</span> clsid <span class="kw4">As</span> GUID) <span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Function</span> GetMem4 Lib <span class="st0">&quot;msvbvm60&quot;</span> ( _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">ByRef</span> src <span class="kw4">As</span> Any, _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">ByRef</span> dst <span class="kw4">As</span> Any) <span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Function</span> SysFreeString Lib <span class="st0">&quot;oleaut32&quot;</span> ( _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">ByVal</span> lpbstr <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Function</span> LoadLibrary Lib <span class="st0">&quot;kernel32&quot;</span> _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;Alias <span class="st0">&quot;LoadLibraryW&quot;</span> ( _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">ByVal</span> lpLibFileName <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Function</span> GetModuleHandle Lib <span class="st0">&quot;kernel32&quot;</span> _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;Alias <span class="st0">&quot;GetModuleHandleW&quot;</span> ( _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">ByVal</span> lpModuleName <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Function</span> FreeLibrary Lib <span class="st0">&quot;kernel32&quot;</span> ( _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">ByVal</span> hLibModule <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Function</span> GetProcAddress Lib <span class="st0">&quot;kernel32&quot;</span> ( _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">ByVal</span> hModule <span class="kw4">As</span> <span class="kw1">Long</span>, _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">ByVal</span> lpProcName <span class="kw4">As</span> <span class="kw1">String</span>) <span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Function</span> DispCallFunc Lib <span class="st0">&quot;oleaut32&quot;</span> ( _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">ByVal</span> pvInstance <span class="kw4">As</span> Any, _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">ByVal</span> oVft <span class="kw4">As</span> <span class="kw1">Long</span>, _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">ByVal</span> cc <span class="kw4">As</span> <span class="kw1">Integer</span>, _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">ByVal</span> vtReturn <span class="kw4">As</span> <span class="kw1">Integer</span>, _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">ByVal</span> cActuals <span class="kw4">As</span> <span class="kw1">Long</span>, _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">ByRef</span> prgvt <span class="kw4">As</span> Any, _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">ByRef</span> prgpvarg <span class="kw4">As</span> Any, _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">ByRef</span> pvargResult <span class="kw4">As</span> <span class="kw1">Variant</span>) <span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Function</span> LoadTypeLibEx Lib <span class="st0">&quot;oleaut32&quot;</span> ( _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">ByVal</span> szFile <span class="kw4">As</span> <span class="kw1">Long</span>, _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">ByVal</span> regkind <span class="kw4">As</span> <span class="kw1">Long</span>, _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">ByRef</span> pptlib <span class="kw4">As</span> IUnknown) <span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Function</span> memcpy Lib <span class="st0">&quot;kernel32&quot;</span> _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;Alias <span class="st0">&quot;RtlMoveMemory&quot;</span> ( _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">ByRef</span> Destination <span class="kw4">As</span> Any, _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">ByRef</span> Source <span class="kw4">As</span> Any, _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">ByVal</span> Length <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Function</span> CreateStdDispatch Lib <span class="st0">&quot;oleaut32&quot;</span> ( _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">ByVal</span> punkOuter <span class="kw4">As</span> IUnknown, _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">ByVal</span> pvThis <span class="kw4">As</span> IUnknown, _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">ByVal</span> ptinfo <span class="kw4">As</span> IUnknown, _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">ByRef</span> ppunkStdDisp <span class="kw4">As</span> IUnknown) <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
<span class="kw2">Private</span> Const IID_IClassFactory &nbsp; <span class="kw4">As</span> <span class="kw1">String</span> = <span class="st0">&quot;{00000001-0000-0000-C000-000000000046}&quot;</span>
<span class="kw2">Private</span> Const IID_IUnknown &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">String</span> = <span class="st0">&quot;{00000000-0000-0000-C000-000000000046}&quot;</span>
<span class="kw2">Private</span> Const CC_STDCALL &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span> = 4
<span class="kw2">Private</span> Const REGKIND_NONE &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span> = 2
<span class="kw2">Private</span> Const TKIND_COCLASS &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span> = 5
<span class="kw2">Private</span> Const TKIND_DISPATCH &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span> = 4
<span class="kw2">Private</span> Const TKIND_INTERFACE &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span> = 3
&nbsp;
<span class="kw4">Dim</span> iidClsFctr &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> GUID
<span class="kw4">Dim</span> iidUnk &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> GUID
<span class="kw4">Dim</span> isInit &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Boolean</span>
&nbsp;
<span class="co1">' // Get all co-classes described in type library.</span>
<span class="kw2">Public</span> <span class="kw2">Function</span> GetAllCoclasses( _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">ByRef</span> path <span class="kw4">As</span> <span class="kw1">String</span>, _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">ByRef</span> listOfClsid() <span class="kw4">As</span> GUID, _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">ByRef</span> listOfNames() <span class="kw4">As</span> <span class="kw1">String</span>, _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">ByRef</span> countCoClass <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> <span class="kw1">Boolean</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw4">Dim</span> typeLib <span class="kw4">As</span> IUnknown
&nbsp; &nbsp; <span class="kw4">Dim</span> typeInf <span class="kw4">As</span> IUnknown
&nbsp; &nbsp; <span class="kw4">Dim</span> ret &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> count &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> index &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> pAttr &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> tKind &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; ret = LoadTypeLibEx(StrPtr(path), REGKIND_NONE, typeLib)
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> ret <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; Err.Raise ret
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; count = ITypeLib_GetTypeInfoCount(typeLib)
&nbsp; &nbsp; countCoClass = 0
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> count &gt; 0 <span class="kw3">Then</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">ReDim</span> listOfClsid(count - 1)
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">ReDim</span> listOfNames(count - 1)
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">For</span> index = 0 <span class="kw3">To</span> count - 1
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ret = ITypeLib_GetTypeInfo(typeLib, index, typeInf)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> ret <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Err.Raise ret
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ITypeInfo_GetTypeAttr typeInf, pAttr
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; GetMem4 <span class="kw4">ByVal</span> pAttr + &amp;H28, tKind
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> tKind = TKIND_COCLASS <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; memcpy listOfClsid(countCoClass), <span class="kw4">ByVal</span> pAttr, Len(listOfClsid(countCoClass))
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ret = ITypeInfo_GetDocumentation(typeInf, -1, listOfNames(countCoClass), vbNullString, 0, vbNullString)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> ret <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ITypeInfo_ReleaseTypeAttr typeInf, pAttr
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Err.Raise ret
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; countCoClass = countCoClass + 1
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ITypeInfo_ReleaseTypeAttr typeInf, pAttr
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">Set</span> typeInf = <span class="kw5">Nothing</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">Next</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> countCoClass <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">ReDim</span> <span class="kw4">Preserve</span> listOfClsid(countCoClass - 1)
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">ReDim</span> <span class="kw4">Preserve</span> listOfNames(countCoClass - 1)
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">Else</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; Erase listOfClsid()
&nbsp; &nbsp; &nbsp; &nbsp; Erase listOfNames()
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; GetAllCoclasses = <span class="kw5">True</span>
&nbsp; &nbsp; 
<span class="kw3">End</span> <span class="kw2">Function</span>
&nbsp;
<span class="co1">' // Create IDispach implementation described in type library.</span>
<span class="kw2">Public</span> <span class="kw2">Function</span> CreateIDispatch( _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">ByRef</span> obj <span class="kw4">As</span> IUnknown, _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">ByRef</span> typeLibPath <span class="kw4">As</span> <span class="kw1">String</span>, _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">ByRef</span> interfaceName <span class="kw4">As</span> <span class="kw1">String</span>) <span class="kw4">As</span> <span class="kw1">Object</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw4">Dim</span> typeLib <span class="kw4">As</span> IUnknown
&nbsp; &nbsp; <span class="kw4">Dim</span> typeInf <span class="kw4">As</span> IUnknown
&nbsp; &nbsp; <span class="kw4">Dim</span> ret &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> retObj &nbsp;<span class="kw4">As</span> IUnknown
&nbsp; &nbsp; <span class="kw4">Dim</span> pAttr &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> tKind &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; ret = LoadTypeLibEx(StrPtr(typeLibPath), REGKIND_NONE, typeLib)
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> ret <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; Err.Raise ret
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; ret = ITypeLib_FindName(typeLib, interfaceName, 0, typeInf, 0, 1)
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> typeInf <span class="kw3">Is</span> <span class="kw5">Nothing</span> <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; Err.Raise &amp;H80004002, , <span class="st0">&quot;Interface not found&quot;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; ITypeInfo_GetTypeAttr typeInf, pAttr
&nbsp; &nbsp; GetMem4 <span class="kw4">ByVal</span> pAttr + &amp;H28, tKind
&nbsp; &nbsp; ITypeInfo_ReleaseTypeAttr typeInf, pAttr
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> tKind = TKIND_DISPATCH <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">Set</span> CreateIDispatch = obj
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; <span class="kw3">ElseIf</span> tKind &lt;&gt; TKIND_INTERFACE <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; Err.Raise &amp;H80004002, , <span class="st0">&quot;Interface not found&quot;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; 
&nbsp; &nbsp; ret = CreateStdDispatch(<span class="kw5">Nothing</span>, obj, typeInf, retObj)
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> ret <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; Err.Raise ret
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw4">Set</span> CreateIDispatch = retObj
&nbsp;
<span class="kw3">End</span> <span class="kw2">Function</span>
&nbsp;
<span class="co1">' // Create object by Name.</span>
<span class="kw2">Public</span> <span class="kw2">Function</span> CreateObjectEx2( _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">ByRef</span> pathToDll <span class="kw4">As</span> <span class="kw1">String</span>, _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">ByRef</span> pathToTLB <span class="kw4">As</span> <span class="kw1">String</span>, _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">ByRef</span> className <span class="kw4">As</span> <span class="kw1">String</span>) <span class="kw4">As</span> IUnknown
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw4">Dim</span> typeLib <span class="kw4">As</span> IUnknown
&nbsp; &nbsp; <span class="kw4">Dim</span> typeInf <span class="kw4">As</span> IUnknown
&nbsp; &nbsp; <span class="kw4">Dim</span> ret &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> pAttr &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> tKind &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> clsid &nbsp; <span class="kw4">As</span> GUID
&nbsp; &nbsp; 
&nbsp; &nbsp; ret = LoadTypeLibEx(StrPtr(pathToTLB), REGKIND_NONE, typeLib)
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> ret <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; Err.Raise ret
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; ret = ITypeLib_FindName(typeLib, className, 0, typeInf, 0, 1)
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> typeInf <span class="kw3">Is</span> <span class="kw5">Nothing</span> <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; Err.Raise &amp;H80040111, , <span class="st0">&quot;Class not found in type library&quot;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp;
&nbsp; &nbsp; ITypeInfo_GetTypeAttr typeInf, pAttr
&nbsp; &nbsp; 
&nbsp; &nbsp; GetMem4 <span class="kw4">ByVal</span> pAttr + &amp;H28, tKind
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> tKind = TKIND_COCLASS <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; memcpy clsid, <span class="kw4">ByVal</span> pAttr, Len(clsid)
&nbsp; &nbsp; <span class="kw3">Else</span>
&nbsp; &nbsp; &nbsp; &nbsp; Err.Raise &amp;H80040111, , <span class="st0">&quot;Class not found in type library&quot;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; ITypeInfo_ReleaseTypeAttr typeInf, pAttr
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw4">Set</span> CreateObjectEx2 = CreateObjectEx(pathToDll, clsid)
&nbsp; &nbsp; 
<span class="kw3">End</span> <span class="kw2">Function</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
<span class="co1">' // Create object by CLSID and path.</span>
<span class="kw2">Public</span> <span class="kw2">Function</span> CreateObjectEx( _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">ByRef</span> path <span class="kw4">As</span> <span class="kw1">String</span>, _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">ByRef</span> clsid <span class="kw4">As</span> GUID) <span class="kw4">As</span> IUnknown
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw4">Dim</span> hLib &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> lpAddr &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> isLoad &nbsp;<span class="kw4">As</span> <span class="kw1">Boolean</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; hLib = GetModuleHandle(StrPtr(path))
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> hLib = 0 <span class="kw3">Then</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; hLib = LoadLibrary(StrPtr(path))
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> hLib = 0 <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Err.Raise 53, , <span class="kw4">Error</span>(53) &amp; <span class="st0">&quot; &quot;</span> &amp; Chr$(34) &amp; path &amp; Chr$(34)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; isLoad = <span class="kw5">True</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; lpAddr = GetProcAddress(hLib, <span class="st0">&quot;DllGetClassObject&quot;</span>)
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> lpAddr = 0 <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> isLoad <span class="kw3">Then</span> FreeLibrary hLib
&nbsp; &nbsp; &nbsp; &nbsp; Err.Raise 453, , <span class="st0">&quot;Can't find dll entry point DllGetClasesObject in &quot;</span> &amp; Chr$(34) &amp; path &amp; Chr$(34)
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp;
&nbsp; &nbsp; <span class="kw3">If</span> <span class="kw3">Not</span> isInit <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; CLSIDFromString StrPtr(IID_IClassFactory), iidClsFctr
&nbsp; &nbsp; &nbsp; &nbsp; CLSIDFromString StrPtr(IID_IUnknown), iidUnk
&nbsp; &nbsp; &nbsp; &nbsp; isInit = <span class="kw5">True</span>
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw4">Dim</span> ret &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> out &nbsp; &nbsp; <span class="kw4">As</span> IUnknown
&nbsp; &nbsp; 
&nbsp; &nbsp; ret = DllGetClassObject(lpAddr, clsid, iidClsFctr, out)
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> ret = 0 <span class="kw3">Then</span>
&nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; ret = IClassFactory_CreateInstance(out, 0, iidUnk, CreateObjectEx)
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">Else</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> isLoad <span class="kw3">Then</span> FreeLibrary hLib
&nbsp; &nbsp; &nbsp; &nbsp; Err.Raise ret
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw4">Set</span> out = <span class="kw5">Nothing</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> ret <span class="kw3">Then</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> isLoad <span class="kw3">Then</span> FreeLibrary hLib
&nbsp; &nbsp; &nbsp; &nbsp; Err.Raise ret
&nbsp;
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; 
<span class="kw3">End</span> <span class="kw2">Function</span>
&nbsp;
<span class="co1">' // Unload DLL if not used.</span>
<span class="kw2">Public</span> <span class="kw2">Function</span> UnloadLibrary( _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">ByRef</span> path <span class="kw4">As</span> <span class="kw1">String</span>) <span class="kw4">As</span> <span class="kw1">Boolean</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw4">Dim</span> hLib &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> lpAddr &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> ret &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> <span class="kw3">Not</span> isInit <span class="kw3">Then</span> <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; hLib = GetModuleHandle(StrPtr(path))
&nbsp; &nbsp; <span class="kw3">If</span> hLib = 0 <span class="kw3">Then</span> <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; lpAddr = GetProcAddress(hLib, <span class="st0">&quot;DllCanUnloadNow&quot;</span>)
&nbsp; &nbsp; <span class="kw3">If</span> lpAddr = 0 <span class="kw3">Then</span> <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; ret = DllCanUnloadNow(lpAddr)
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> ret = 0 <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; FreeLibrary hLib
&nbsp; &nbsp; &nbsp; &nbsp; UnloadLibrary = <span class="kw5">True</span>
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; 
<span class="kw3">End</span> <span class="kw2">Function</span>
&nbsp;
<span class="co1">' // Call &quot;DllGetClassObject&quot; function using a pointer.</span>
<span class="kw2">Private</span> <span class="kw2">Function</span> DllGetClassObject( _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">ByVal</span> funcAddr <span class="kw4">As</span> <span class="kw1">Long</span>, _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">ByRef</span> clsid <span class="kw4">As</span> GUID, _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">ByRef</span> iid <span class="kw4">As</span> GUID, _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">ByRef</span> out <span class="kw4">As</span> IUnknown) <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; <span class="kw4">Dim</span> params(2) &nbsp; <span class="kw4">As</span> <span class="kw1">Variant</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> types(2) &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Integer</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> list(2) &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> resultCall &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> pIndex &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> pReturn &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Variant</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; params(0) = VarPtr(clsid)
&nbsp; &nbsp; params(1) = VarPtr(iid)
&nbsp; &nbsp; params(2) = VarPtr(out)
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">For</span> pIndex = 0 <span class="kw3">To</span> <span class="kw4">UBound</span>(params)
&nbsp; &nbsp; &nbsp; &nbsp; list(pIndex) = VarPtr(params(pIndex)): &nbsp; types(pIndex) = VarType(params(pIndex))
&nbsp; &nbsp; <span class="kw3">Next</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; resultCall = DispCallFunc(0&amp;, funcAddr, CC_STDCALL, vbLong, 3, types(0), list(0), pReturn)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; <span class="kw3">If</span> resultCall <span class="kw3">Then</span> Err.Raise 5: <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; DllGetClassObject = pReturn
&nbsp; &nbsp; 
<span class="kw3">End</span> <span class="kw2">Function</span>
&nbsp;
<span class="co1">' // Call &quot;DllCanUnloadNow&quot; function using a pointer.</span>
<span class="kw2">Private</span> <span class="kw2">Function</span> DllCanUnloadNow( _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">ByVal</span> funcAddr <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; <span class="kw4">Dim</span> resultCall &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> pReturn &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Variant</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; resultCall = DispCallFunc(0&amp;, funcAddr, CC_STDCALL, vbLong, 0, <span class="kw4">ByVal</span> 0&amp;, <span class="kw4">ByVal</span> 0&amp;, pReturn)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; <span class="kw3">If</span> resultCall <span class="kw3">Then</span> Err.Raise 5: <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; DllCanUnloadNow = pReturn
&nbsp; &nbsp; 
<span class="kw3">End</span> <span class="kw2">Function</span>
&nbsp;
<span class="co1">' // Call &quot;IClassFactory:CreateInstance&quot; method.</span>
<span class="kw2">Private</span> <span class="kw2">Function</span> IClassFactory_CreateInstance( _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">ByVal</span> obj <span class="kw4">As</span> IUnknown, _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">ByVal</span> punkOuter <span class="kw4">As</span> <span class="kw1">Long</span>, _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">ByRef</span> riid <span class="kw4">As</span> GUID, _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">ByRef</span> out <span class="kw4">As</span> IUnknown) <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw4">Dim</span> params(2) &nbsp; <span class="kw4">As</span> <span class="kw1">Variant</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> types(2) &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Integer</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> list(2) &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> resultCall &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> pIndex &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> pReturn &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Variant</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; params(0) = punkOuter
&nbsp; &nbsp; params(1) = VarPtr(riid)
&nbsp; &nbsp; params(2) = VarPtr(out)
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">For</span> pIndex = 0 <span class="kw3">To</span> <span class="kw4">UBound</span>(params)
&nbsp; &nbsp; &nbsp; &nbsp; list(pIndex) = VarPtr(params(pIndex)): &nbsp; types(pIndex) = VarType(params(pIndex))
&nbsp; &nbsp; <span class="kw3">Next</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; resultCall = DispCallFunc(obj, &amp;HC, CC_STDCALL, vbLong, 3, types(0), list(0), pReturn)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> resultCall <span class="kw3">Then</span> Err.Raise resultCall: <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; IClassFactory_CreateInstance = pReturn
&nbsp; &nbsp; 
<span class="kw3">End</span> <span class="kw2">Function</span>
&nbsp;
<span class="co1">' // Call &quot;ITypeLib:GetTypeInfoCount&quot; method.</span>
<span class="kw2">Private</span> <span class="kw2">Function</span> ITypeLib_GetTypeInfoCount( _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">ByVal</span> obj <span class="kw4">As</span> IUnknown) <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw4">Dim</span> resultCall &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> pReturn &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Variant</span>
&nbsp;
&nbsp; &nbsp; resultCall = DispCallFunc(obj, &amp;HC, CC_STDCALL, vbLong, 0, <span class="kw4">ByVal</span> 0&amp;, <span class="kw4">ByVal</span> 0&amp;, pReturn)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> resultCall <span class="kw3">Then</span> Err.Raise resultCall: <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; ITypeLib_GetTypeInfoCount = pReturn
&nbsp; &nbsp; 
<span class="kw3">End</span> <span class="kw2">Function</span>
&nbsp;
<span class="co1">' // Call &quot;ITypeLib:GetTypeInfo&quot; method.</span>
<span class="kw2">Private</span> <span class="kw2">Function</span> ITypeLib_GetTypeInfo( _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">ByVal</span> obj <span class="kw4">As</span> IUnknown, _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">ByVal</span> index <span class="kw4">As</span> <span class="kw1">Long</span>, _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">ByRef</span> ppTInfo <span class="kw4">As</span> IUnknown) <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw4">Dim</span> params(1) &nbsp; <span class="kw4">As</span> <span class="kw1">Variant</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> types(1) &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Integer</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> list(1) &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> resultCall &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> pIndex &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> pReturn &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Variant</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; params(0) = index
&nbsp; &nbsp; params(1) = VarPtr(ppTInfo)
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">For</span> pIndex = 0 <span class="kw3">To</span> <span class="kw4">UBound</span>(params)
&nbsp; &nbsp; &nbsp; &nbsp; list(pIndex) = VarPtr(params(pIndex)): &nbsp; types(pIndex) = VarType(params(pIndex))
&nbsp; &nbsp; <span class="kw3">Next</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; resultCall = DispCallFunc(obj, &amp;H10, CC_STDCALL, vbLong, 2, types(0), list(0), pReturn)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> resultCall <span class="kw3">Then</span> Err.Raise resultCall: <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; ITypeLib_GetTypeInfo = pReturn
&nbsp; &nbsp; 
<span class="kw3">End</span> <span class="kw2">Function</span>
&nbsp;
<span class="co1">' // Call &quot;ITypeLib:FindName&quot; method.</span>
<span class="kw2">Private</span> <span class="kw2">Function</span> ITypeLib_FindName( _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">ByVal</span> obj <span class="kw4">As</span> IUnknown, _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">ByRef</span> szNameBuf <span class="kw4">As</span> <span class="kw1">String</span>, _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">ByVal</span> lHashVal <span class="kw4">As</span> <span class="kw1">Long</span>, _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">ByRef</span> ppTInfo <span class="kw4">As</span> IUnknown, _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">ByRef</span> rgMemId <span class="kw4">As</span> <span class="kw1">Long</span>, _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">ByRef</span> pcFound <span class="kw4">As</span> <span class="kw1">Integer</span>) <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw4">Dim</span> params(4) &nbsp; <span class="kw4">As</span> <span class="kw1">Variant</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> types(4) &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Integer</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> list(4) &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> resultCall &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> pIndex &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> pReturn &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Variant</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; params(0) = StrPtr(szNameBuf)
&nbsp; &nbsp; params(1) = lHashVal
&nbsp; &nbsp; params(2) = VarPtr(ppTInfo)
&nbsp; &nbsp; params(3) = VarPtr(rgMemId)
&nbsp; &nbsp; params(4) = VarPtr(pcFound)
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">For</span> pIndex = 0 <span class="kw3">To</span> <span class="kw4">UBound</span>(params)
&nbsp; &nbsp; &nbsp; &nbsp; list(pIndex) = VarPtr(params(pIndex)): &nbsp; types(pIndex) = VarType(params(pIndex))
&nbsp; &nbsp; <span class="kw3">Next</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; resultCall = DispCallFunc(obj, &amp;H2C, CC_STDCALL, vbLong, 5, types(0), list(0), pReturn)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> resultCall <span class="kw3">Then</span> Err.Raise resultCall: <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; ITypeLib_FindName = pReturn
&nbsp; &nbsp; 
<span class="kw3">End</span> <span class="kw2">Function</span>
&nbsp;
<span class="co1">' // Call &quot;ITypeInfo:GetTypeAttr&quot; method.</span>
<span class="kw2">Private</span> <span class="kw2">Sub</span> ITypeInfo_GetTypeAttr( _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">ByVal</span> obj <span class="kw4">As</span> IUnknown, _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">ByRef</span> ppTypeAttr <span class="kw4">As</span> <span class="kw1">Long</span>)
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw4">Dim</span> resultCall &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> pReturn &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Variant</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; pReturn = VarPtr(ppTypeAttr)
&nbsp; &nbsp; 
&nbsp; &nbsp; resultCall = DispCallFunc(obj, &amp;HC, CC_STDCALL, vbEmpty, 1, vbLong, VarPtr(pReturn), 0)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> resultCall <span class="kw3">Then</span> Err.Raise resultCall: <span class="kw2">Exit</span> <span class="kw2">Sub</span>
&nbsp;
<span class="kw3">End</span> <span class="kw2">Sub</span>
&nbsp;
<span class="co1">' // Call &quot;ITypeInfo:GetDocumentation&quot; method.</span>
<span class="kw2">Private</span> <span class="kw2">Function</span> ITypeInfo_GetDocumentation( _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">ByVal</span> obj <span class="kw4">As</span> IUnknown, _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">ByVal</span> memid <span class="kw4">As</span> <span class="kw1">Long</span>, _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">ByRef</span> pBstrName <span class="kw4">As</span> <span class="kw1">String</span>, _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">ByRef</span> pBstrDocString <span class="kw4">As</span> <span class="kw1">String</span>, _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">ByRef</span> pdwHelpContext <span class="kw4">As</span> <span class="kw1">Long</span>, _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">ByRef</span> pBstrHelpFile <span class="kw4">As</span> <span class="kw1">String</span>) <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw4">Dim</span> params(4) &nbsp; <span class="kw4">As</span> <span class="kw1">Variant</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> types(4) &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Integer</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> list(4) &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> resultCall &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> pIndex &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> pReturn &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Variant</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; params(0) = memid
&nbsp; &nbsp; params(1) = VarPtr(pBstrName)
&nbsp; &nbsp; params(2) = VarPtr(pBstrDocString)
&nbsp; &nbsp; params(3) = VarPtr(pdwHelpContext)
&nbsp; &nbsp; params(4) = VarPtr(pBstrHelpFile)
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">For</span> pIndex = 0 <span class="kw3">To</span> <span class="kw4">UBound</span>(params)
&nbsp; &nbsp; &nbsp; &nbsp; list(pIndex) = VarPtr(params(pIndex)): &nbsp; types(pIndex) = VarType(params(pIndex))
&nbsp; &nbsp; <span class="kw3">Next</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; resultCall = DispCallFunc(obj, &amp;H30, CC_STDCALL, vbLong, 5, types(0), list(0), pReturn)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> resultCall <span class="kw3">Then</span> Err.Raise resultCall: <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; ITypeInfo_GetDocumentation = pReturn
&nbsp; &nbsp; 
<span class="kw3">End</span> <span class="kw2">Function</span>
&nbsp;
<span class="co1">' // Call &quot;ITypeInfo:ReleaseTypeAttr&quot; method.</span>
<span class="kw2">Private</span> <span class="kw2">Sub</span> ITypeInfo_ReleaseTypeAttr( _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">ByVal</span> obj <span class="kw4">As</span> IUnknown, _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">ByVal</span> ppTypeAttr <span class="kw4">As</span> <span class="kw1">Long</span>)
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw4">Dim</span> resultCall &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; resultCall = DispCallFunc(obj, &amp;H4C, CC_STDCALL, vbEmpty, 1, vbLong, VarPtr(CVar(ppTypeAttr)), 0)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> resultCall <span class="kw3">Then</span> Err.Raise resultCall: <span class="kw2">Exit</span> <span class="kw2">Sub</span>
&nbsp;
<span class="kw3">End</span> <span class="kw2">Sub</span></pre></td></tr></table></div></td></tr></tbody></table></div><a rel="nofollow noopener noreferrer" href="https://yadi.sk/d/ZAqaEO2Mgtoqo" target="_blank" title="https://yadi.sk/d/ZAqaEO2Mgtoqo">Скачать.</a></div>

]]></content:encoded>
			<dc:creator>The trick</dc:creator>
			<guid isPermaLink="true">https://www.cyberforum.ru/blogs/354370/3550.html</guid>
		</item>
		<item>
			<title>Класс - MP3 проигрыватель из памяти.</title>
			<link>https://www.cyberforum.ru/blogs/354370/3511.html</link>
			<pubDate>Sun, 26 Apr 2015 19:10:30 GMT</pubDate>
			<description>Всем привет. Я разработал класс для асинхронного воспроизведения MP3 файлов в памяти. Например это...</description>
			<content:encoded><![CDATA[<div>Всем привет. Я разработал класс для асинхронного воспроизведения MP3 файлов в памяти. Например это может пригодится для воспроизведения фоновой музыки из ресурсов или из сети минуя запись в файл. Воспроизводить можно несколько файлов одновременно, но некоторые параметры воспроизведения (громкость, панорама) для всех проигрывателей будут общими. Класс разработан так, что корректно обрабатывает ситуации остановки среды кнопками &quot;стоп&quot;, &quot;пауза&quot; и выхода по End. По тегам, корректно обрабатываются только ID3v1 и ID3v2 теги, другие не распознаются и файл скорее всего не будет играться. <br />
Методы:<ul><li><i>Initialize</i> - инициализирует проигрыватель, в качестве первого параметра передается указатель на данные MP3 файла. Второй параметр указывает на размер данных. Третий параметр определяет нужно ли копировать файл во внутренний буфер внутри объекта и воспроизводить файл оттуда;</li>
<li><i>Play</i> - запускает воспроизведение, параметр <b>looped</b> при первом воспроизведении определяет будет ли файл зацикливаться;</li>
<li><i>Pause</i> - приостанавливает воспроизведение, следующее воспроизведение начнется с текущей позиции;</li>
<li><i>StopPlaying</i> - останавливает воспроизведение;</li>
<li><i>SetPositionMs</i> - устанавливает текущую позицию воспроизведения (мс);</li>
<li><i>GetPositionMs</i> - возвращает текущую позицию воспроизведения (мс);</li>
<li><i>GetDurationMs</i> - возвращает длину файла в миллисекундах;</li>
<li><i>GetBitrate</i> - возвращает битрейт на момент воспроизведения (кб/с);</li>
<li><i>IsPlaying</i> - определяет играется ли файл;</li>
</ul>Свойства:<ul><li><i>Volume</i> - задает/возвращает текущую громкость воспроизведения (0...1);</li>
<li><i>Pan</i> -  задает/возвращает текущую панораму воспроизведения ((левый канал)-1...1(правый канал)).</li>
</ul><div class="codeblock"><table class="vb"><thead><tr><td colspan="2" id="906964873"  class="head">Visual Basic</td></tr></thead><tbody><tr class="li1"><td><div id="906964873" style="height: 350px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
</pre></td><td class="de1"><pre class="de1"><span class="co1">' Class clsTrickMP3Player.cls - for asynchronous play mp3-files from memory.</span>
<span class="co1">' © Krivous Anatolii Anatolevich (The trick), 2015</span>
<span class="co1">' Version 1.1</span>
&nbsp;
<span class="kw2">Option</span> <span class="kw2">Explicit</span>
&nbsp;
<span class="kw2">Private</span> <span class="kw4">Type</span> WNDCLASSEX
&nbsp; &nbsp; cbSize &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; style &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; lpfnwndproc &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; cbClsextra &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; cbWndExtra2 &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; hInstance &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; hIcon &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; hCursor &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; hbrBackground &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; lpszMenuName &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; lpszClassName &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; hIconSm &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw3">End</span> <span class="kw4">Type</span>
&nbsp;
<span class="kw2">Private</span> <span class="kw4">Type</span> MPEGLAYER3WAVEFORMAT
&nbsp; &nbsp; wFormatTag &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Integer</span>
&nbsp; &nbsp; nChannels &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Integer</span>
&nbsp; &nbsp; nSamplesPerSec &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; nAvgBytesPerSec &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; nBlockAlign &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Integer</span>
&nbsp; &nbsp; wBitsPerSample &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Integer</span>
&nbsp; &nbsp; cbSize &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Integer</span>
&nbsp; &nbsp; wID &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Integer</span>
&nbsp; &nbsp; fdwFlags &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; nBlockSize &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Integer</span>
&nbsp; &nbsp; nFramesPerBlock &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Integer</span>
&nbsp; &nbsp; nCodecDelay &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Integer</span>
<span class="kw3">End</span> <span class="kw4">Type</span>
&nbsp;
<span class="kw2">Private</span> <span class="kw4">Type</span> FrameInfo
&nbsp; &nbsp; offset &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; bitrate &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw3">End</span> <span class="kw4">Type</span>
&nbsp;
<span class="kw2">Private</span> <span class="kw4">Type</span> Mp3Info
&nbsp; &nbsp; format &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> MPEGLAYER3WAVEFORMAT
&nbsp; &nbsp; lpFrameOffset &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; szDataSize &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; samplesPerFrame &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; framesCount &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; frameOffset() &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> FrameInfo
<span class="kw3">End</span> <span class="kw4">Type</span>
&nbsp;
<span class="kw2">Private</span> <span class="kw4">Type</span> WAVEHDR
&nbsp; &nbsp; lpData &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; dwBufferLength &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; dwBytesRecorded &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; dwUser &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; dwFlags &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; dwLoops &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; lpNext &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; Reserved &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw3">End</span> <span class="kw4">Type</span>
&nbsp;
<span class="kw2">Private</span> <span class="kw4">Type</span> mp3Buffer
&nbsp; &nbsp; header &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> WAVEHDR
&nbsp; &nbsp; status &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Boolean</span>
<span class="kw3">End</span> <span class="kw4">Type</span>
&nbsp;
<span class="kw2">Private</span> <span class="kw4">Type</span> mp3Const
&nbsp; &nbsp; bitrate(1, 15) &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Integer</span>
&nbsp; &nbsp; smprate(2, 3) &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw3">End</span> <span class="kw4">Type</span>
&nbsp;
<span class="kw2">Private</span> <span class="kw4">Type</span> curBuffer
&nbsp; &nbsp; b(15) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Currency</span>
<span class="kw3">End</span> <span class="kw4">Type</span>
&nbsp;
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Function</span> GetMem4 Lib <span class="st0">&quot;msvbvm60&quot;</span> (Src <span class="kw4">As</span> Any, Dst <span class="kw4">As</span> Any) <span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Function</span> GetMem8 Lib <span class="st0">&quot;msvbvm60&quot;</span> (Src <span class="kw4">As</span> Any, Dst <span class="kw4">As</span> Any) <span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Sub</span> memcpy Lib <span class="st0">&quot;kernel32&quot;</span> Alias <span class="st0">&quot;RtlMoveMemory&quot;</span> (Destination <span class="kw4">As</span> Any, Source <span class="kw4">As</span> Any, <span class="kw4">ByVal</span> Length <span class="kw4">As</span> <span class="kw1">Long</span>)
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Function</span> GetModuleHandle Lib <span class="st0">&quot;kernel32&quot;</span> Alias <span class="st0">&quot;GetModuleHandleW&quot;</span> (<span class="kw4">ByVal</span> lpModuleName <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Function</span> GetProcAddress Lib <span class="st0">&quot;kernel32&quot;</span> (<span class="kw4">ByVal</span> hModule <span class="kw4">As</span> <span class="kw1">Long</span>, <span class="kw4">ByVal</span> lpProcName <span class="kw4">As</span> <span class="kw1">String</span>) <span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Function</span> GetProcessHeap Lib <span class="st0">&quot;kernel32&quot;</span> () <span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Function</span> HeapCreate Lib <span class="st0">&quot;kernel32&quot;</span> (<span class="kw4">ByVal</span> flOptions <span class="kw4">As</span> <span class="kw1">Long</span>, <span class="kw4">ByVal</span> dwInitialSize <span class="kw4">As</span> <span class="kw1">Long</span>, <span class="kw4">ByVal</span> dwMaximumSize <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Function</span> HeapDestroy Lib <span class="st0">&quot;kernel32&quot;</span> (<span class="kw4">ByVal</span> hHeap <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Function</span> HeapAlloc Lib <span class="st0">&quot;kernel32&quot;</span> (<span class="kw4">ByVal</span> hHeap <span class="kw4">As</span> <span class="kw1">Long</span>, <span class="kw4">ByVal</span> dwFlags <span class="kw4">As</span> <span class="kw1">Long</span>, <span class="kw4">ByVal</span> dwBytes <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Function</span> HeapFree Lib <span class="st0">&quot;kernel32&quot;</span> (<span class="kw4">ByVal</span> hHeap <span class="kw4">As</span> <span class="kw1">Long</span>, <span class="kw4">ByVal</span> dwFlags <span class="kw4">As</span> <span class="kw1">Long</span>, lpMem <span class="kw4">As</span> Any) <span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Function</span> SetEnvironmentVariable Lib <span class="st0">&quot;kernel32&quot;</span> Alias <span class="st0">&quot;SetEnvironmentVariableW&quot;</span> (<span class="kw4">ByVal</span> lpName <span class="kw4">As</span> <span class="kw1">Long</span>, lpValue <span class="kw4">As</span> Any) <span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Function</span> GetEnvironmentVariable Lib <span class="st0">&quot;kernel32&quot;</span> Alias <span class="st0">&quot;GetEnvironmentVariableW&quot;</span> (<span class="kw4">ByVal</span> lpName <span class="kw4">As</span> <span class="kw1">Long</span>, lpBuffer <span class="kw4">As</span> Any, <span class="kw4">ByVal</span> nSize <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Function</span> UnregisterClass Lib <span class="st0">&quot;user32&quot;</span> Alias <span class="st0">&quot;UnregisterClassW&quot;</span> (<span class="kw4">ByVal</span> lpClassName <span class="kw4">As</span> <span class="kw1">Long</span>, <span class="kw4">ByVal</span> hInstance <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Function</span> RegisterClassEx Lib <span class="st0">&quot;user32&quot;</span> Alias <span class="st0">&quot;RegisterClassExW&quot;</span> (pcWndClassEx <span class="kw4">As</span> WNDCLASSEX) <span class="kw4">As</span> <span class="kw1">Integer</span>
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Function</span> CreateWindowEx Lib <span class="st0">&quot;user32&quot;</span> Alias <span class="st0">&quot;CreateWindowExW&quot;</span> (<span class="kw4">ByVal</span> dwExStyle <span class="kw4">As</span> <span class="kw1">Long</span>, <span class="kw4">ByVal</span> lpClassName <span class="kw4">As</span> <span class="kw1">Long</span>, <span class="kw4">ByVal</span> lpWindowName <span class="kw4">As</span> <span class="kw1">Long</span>, <span class="kw4">ByVal</span> dwStyle <span class="kw4">As</span> <span class="kw1">Long</span>, <span class="kw4">ByVal</span> X <span class="kw4">As</span> <span class="kw1">Long</span>, <span class="kw4">ByVal</span> Y <span class="kw4">As</span> <span class="kw1">Long</span>, <span class="kw4">ByVal</span> nWidth <span class="kw4">As</span> <span class="kw1">Long</span>, <span class="kw4">ByVal</span> nHeight <span class="kw4">As</span> <span class="kw1">Long</span>, <span class="kw4">ByVal</span> hWndParent <span class="kw4">As</span> <span class="kw1">Long</span>, <span class="kw4">ByVal</span> hMenu <span class="kw4">As</span> <span class="kw1">Long</span>, <span class="kw4">ByVal</span> hInstance <span class="kw4">As</span> <span class="kw1">Long</span>, lpParam <span class="kw4">As</span> Any) <span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Function</span> DestroyWindow Lib <span class="st0">&quot;user32&quot;</span> (<span class="kw4">ByVal</span> hWnd <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Function</span> SetWindowLong Lib <span class="st0">&quot;user32&quot;</span> Alias <span class="st0">&quot;SetWindowLongW&quot;</span> (<span class="kw4">ByVal</span> hWnd <span class="kw4">As</span> <span class="kw1">Long</span>, <span class="kw4">ByVal</span> nIndex <span class="kw4">As</span> <span class="kw1">Long</span>, <span class="kw4">ByVal</span> dwNewLong <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Function</span> GetWindowLong Lib <span class="st0">&quot;user32&quot;</span> Alias <span class="st0">&quot;GetWindowLongW&quot;</span> (<span class="kw4">ByVal</span> hWnd <span class="kw4">As</span> <span class="kw1">Long</span>, <span class="kw4">ByVal</span> nIndex <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Function</span> SetWindowSubclass Lib <span class="st0">&quot;Comctl32&quot;</span> (<span class="kw4">ByVal</span> hWnd <span class="kw4">As</span> <span class="kw1">Long</span>, <span class="kw4">ByVal</span> pfnSubclass <span class="kw4">As</span> <span class="kw1">Long</span>, <span class="kw4">ByVal</span> uIdSubclass <span class="kw4">As</span> <span class="kw1">Long</span>, dwRefData <span class="kw4">As</span> Any) <span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Function</span> RemoveWindowSubclass Lib <span class="st0">&quot;Comctl32&quot;</span> (<span class="kw4">ByVal</span> hWnd <span class="kw4">As</span> <span class="kw1">Long</span>, <span class="kw4">ByVal</span> pfnSubclass <span class="kw4">As</span> <span class="kw1">Long</span>, <span class="kw4">ByVal</span> uIdSubclass <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Function</span> DefSubclassProc Lib <span class="st0">&quot;Comctl32&quot;</span> (<span class="kw4">ByVal</span> hWnd <span class="kw4">As</span> <span class="kw1">Long</span>, <span class="kw4">ByVal</span> uMsg <span class="kw4">As</span> <span class="kw1">Long</span>, <span class="kw4">ByVal</span> wParam <span class="kw4">As</span> <span class="kw1">Long</span>, <span class="kw4">ByVal</span> lParam <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Function</span> LoadLibrary Lib <span class="st0">&quot;kernel32&quot;</span> Alias <span class="st0">&quot;LoadLibraryW&quot;</span> (<span class="kw4">ByVal</span> lpLibFileName <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Function</span> KillTimer Lib <span class="st0">&quot;user32&quot;</span> (<span class="kw4">ByVal</span> hWnd <span class="kw4">As</span> <span class="kw1">Long</span>, <span class="kw4">ByVal</span> nIDEvent <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Function</span> waveOutOpen Lib <span class="st0">&quot;winmm&quot;</span> (lphWaveOut <span class="kw4">As</span> <span class="kw1">Long</span>, <span class="kw4">ByVal</span> uDeviceID <span class="kw4">As</span> <span class="kw1">Long</span>, lpFormat <span class="kw4">As</span> Any, <span class="kw4">ByVal</span> dwCallback <span class="kw4">As</span> <span class="kw1">Long</span>, <span class="kw4">ByVal</span> dwInstance <span class="kw4">As</span> <span class="kw1">Long</span>, <span class="kw4">ByVal</span> dwFlags <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Function</span> waveOutPrepareHeader Lib <span class="st0">&quot;winmm&quot;</span> (<span class="kw4">ByVal</span> hWaveOut <span class="kw4">As</span> <span class="kw1">Long</span>, lpWaveOutHdr <span class="kw4">As</span> WAVEHDR, <span class="kw4">ByVal</span> uSize <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Function</span> waveOutWrite Lib <span class="st0">&quot;winmm&quot;</span> (<span class="kw4">ByVal</span> hWaveOut <span class="kw4">As</span> <span class="kw1">Long</span>, lpWaveOutHdr <span class="kw4">As</span> WAVEHDR, <span class="kw4">ByVal</span> uSize <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Function</span> waveOutUnprepareHeader Lib <span class="st0">&quot;winmm&quot;</span> (<span class="kw4">ByVal</span> hWaveOut <span class="kw4">As</span> <span class="kw1">Long</span>, lpWaveOutHdr <span class="kw4">As</span> WAVEHDR, <span class="kw4">ByVal</span> uSize <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Function</span> waveOutClose Lib <span class="st0">&quot;winmm&quot;</span> (<span class="kw4">ByVal</span> hWaveOut <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Function</span> waveOutReset Lib <span class="st0">&quot;winmm&quot;</span> (<span class="kw4">ByVal</span> hWaveOut <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Function</span> waveOutPause Lib <span class="st0">&quot;winmm&quot;</span> (<span class="kw4">ByVal</span> hWaveOut <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Function</span> waveOutRestart Lib <span class="st0">&quot;winmm&quot;</span> (<span class="kw4">ByVal</span> hWaveOut <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Function</span> waveOutSetVolume Lib <span class="st0">&quot;winmm&quot;</span> (<span class="kw4">ByVal</span> wDeviceID <span class="kw4">As</span> <span class="kw1">Long</span>, <span class="kw4">ByVal</span> dwVolume <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Function</span> waveOutGetVolume Lib <span class="st0">&quot;winmm&quot;</span> (<span class="kw4">ByVal</span> wDeviceID <span class="kw4">As</span> <span class="kw1">Long</span>, dwVolume <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp;
<span class="kw2">Private</span> Const Mp3Class &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">String</span> = <span class="st0">&quot;TrickMP3PlayerClass&quot;</span>
<span class="kw2">Private</span> Const HWND_MESSAGE &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span> = -3
<span class="kw2">Private</span> Const WAVE_MAPPER &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span> = -1&amp;
<span class="kw2">Private</span> Const WHDR_DONE &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span> = &amp;H1
<span class="kw2">Private</span> Const CALLBACK_WINDOW &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span> = &amp;H10000
<span class="kw2">Private</span> Const MM_WOM_DONE &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span> = &amp;H3BD
<span class="kw2">Private</span> Const WM_TIMER &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span> = &amp;H113
<span class="kw2">Private</span> Const WNDPROCINDEX &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span> = 13
<span class="kw2">Private</span> Const HEAP_CREATE_ENABLE_EXECUTE &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span> = &amp;H40000
<span class="kw2">Private</span> Const HEAP_NO_SERIALIZE &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span> = &amp;H1
<span class="kw2">Private</span> Const HEAP_ZERO_MEMORY &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span> = &amp;H8
<span class="kw2">Private</span> Const GWL_USERDATA &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span> = (-21)
<span class="kw2">Private</span> Const MPEGLAYER3_FLAG_PADDING_OFF &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span> = 2
<span class="kw2">Private</span> Const WAVE_FORMAT_MPEGLAYER3 &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span> = &amp;H55
<span class="kw2">Private</span> Const MPEGLAYER3_WFX_EXTRA_BYTES &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span> = 12
<span class="kw2">Private</span> Const MPEGLAYER3_ID_MPEG &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span> = 1
<span class="kw2">Private</span> Const BUFFERS_COUNT &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span> = 8
&nbsp;
<span class="kw2">Private</span> init &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Boolean</span>
<span class="kw2">Private</span> loaded &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Boolean</span>
<span class="kw2">Private</span> playing &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Boolean</span>
<span class="kw2">Private</span> paused &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Boolean</span>
<span class="kw2">Private</span> isLoop &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Boolean</span>
<span class="kw2">Private</span> constants &nbsp; <span class="kw4">As</span> mp3Const
<span class="kw2">Private</span> hWnd &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw2">Private</span> hHeap &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw2">Private</span> lpWndProc &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw2">Private</span> hWave &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw2">Private</span> headers() &nbsp; <span class="kw4">As</span> mp3Buffer
<span class="kw2">Private</span> curPosition <span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw2">Private</span> fileInfo &nbsp; &nbsp;<span class="kw4">As</span> Mp3Info
<span class="kw2">Private</span> buffer() &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Byte</span>
<span class="kw2">Private</span> mPan &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Single</span>
<span class="kw2">Private</span> mVolume &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Single</span>
&nbsp;
<span class="co1">' // Initialize playback. The first parameter is a pointer to data of the raw mp3 file.</span>
<span class="co1">' // Second parameter is a size of this file in bytes.</span>
<span class="co1">' // Last parameter indicates that need to copy this file in the internal buffer.</span>
<span class="kw2">Public</span> <span class="kw2">Function</span> Initialize(<span class="kw4">ByVal</span> lpData <span class="kw4">As</span> <span class="kw1">Long</span>, <span class="kw4">ByVal</span> szData <span class="kw4">As</span> <span class="kw1">Long</span>, <span class="kw4">Optional</span> <span class="kw4">ByVal</span> blCopy <span class="kw4">As</span> <span class="kw1">Boolean</span>) <span class="kw4">As</span> <span class="kw1">Boolean</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> status &nbsp;<span class="kw4">As</span> <span class="kw1">Boolean</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> info &nbsp; &nbsp;<span class="kw4">As</span> Mp3Info
&nbsp; &nbsp; <span class="kw4">Dim</span> ret &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> index &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> <span class="kw3">Not</span> init <span class="kw3">Then</span> <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; status = Mp3GetInfo(lpData, szData, info)
&nbsp; &nbsp; <span class="kw3">If</span> <span class="kw3">Not</span> status <span class="kw3">Then</span> <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> hWave <span class="kw3">Then</span> ClearAll
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> blCopy <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">ReDim</span> buffer(info.szDataSize - 1)
&nbsp; &nbsp; &nbsp; &nbsp; memcpy buffer(0), <span class="kw4">ByVal</span> info.lpFrameOffset, info.szDataSize
&nbsp; &nbsp; &nbsp; &nbsp; info.lpFrameOffset = VarPtr(buffer(0))
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; ret = waveOutOpen(hWave, WAVE_MAPPER, info.format, hWnd, 0, CALLBACK_WINDOW)
&nbsp; &nbsp; <span class="kw3">If</span> ret <span class="kw3">Then</span> hWave = 0: &nbsp;<span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp;
&nbsp; &nbsp; fileInfo = info
&nbsp; &nbsp; curPosition = 0
&nbsp; &nbsp; Me.Pan = mPan
&nbsp; &nbsp; Me.Volume = mVolume
&nbsp; &nbsp; 
&nbsp; &nbsp; loaded = <span class="kw5">True</span>
&nbsp; &nbsp; playing = <span class="kw5">False</span>
&nbsp; &nbsp; 
<span class="kw3">End</span> <span class="kw2">Function</span>
&nbsp;
<span class="co1">' // Start playback. If it is the first call after stopping or initialization then parameter &quot;looped&quot; allows to play a data by circularly.</span>
<span class="kw2">Public</span> <span class="kw2">Function</span> Play(<span class="kw4">Optional</span> <span class="kw4">ByVal</span> looped <span class="kw4">As</span> <span class="kw1">Boolean</span>) <span class="kw4">As</span> <span class="kw1">Boolean</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> index &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> ret &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> <span class="kw3">Not</span> (init <span class="kw3">And</span> loaded) <span class="kw3">Then</span> <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; isLoop = looped
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> paused <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> waveOutRestart(hWave) <span class="kw3">Then</span> <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; &nbsp; &nbsp; paused = <span class="kw5">False</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">Else</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; curPosition = 0
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">For</span> index = 0 <span class="kw3">To</span> BUFFERS_COUNT - 1
&nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; headers(index).header.lpData = fileInfo.lpFrameOffset + fileInfo.frameOffset(curPosition).offset
&nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> index &lt; fileInfo.framesCount - 1 <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; headers(index).header.dwBufferLength = fileInfo.frameOffset(curPosition + 1).offset - fileInfo.frameOffset(curPosition).offset
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">Else</span>
&nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; headers(index).header.dwBufferLength = fileInfo.szDataSize - fileInfo.frameOffset(curPosition).offset
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> isLoop <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; curPosition = 0
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">Else</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">Exit</span> <span class="kw3">For</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ret = waveOutPrepareHeader(hWave, headers(index).header, Len(headers(index).header))
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; headers(index).status = ret = 0
&nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> ret <span class="kw3">Then</span> ClearAll: <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ret = waveOutWrite(hWave, headers(index).header, Len(headers(index).header))
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> ret <span class="kw3">Then</span> ClearAll: <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; curPosition = curPosition + 1
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">Next</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; playing = <span class="kw5">True</span>
&nbsp; &nbsp; Play = <span class="kw5">True</span>
&nbsp; &nbsp; 
<span class="kw3">End</span> <span class="kw2">Function</span>
&nbsp;
<span class="co1">' // Pause playback.</span>
<span class="kw2">Public</span> <span class="kw2">Function</span> Pause() <span class="kw4">As</span> <span class="kw1">Boolean</span>
&nbsp;
&nbsp; &nbsp; <span class="kw3">If</span> <span class="kw3">Not</span> (init <span class="kw3">And</span> loaded <span class="kw3">And</span> playing) <span class="kw3">Then</span> <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; waveOutPause hWave
&nbsp; &nbsp; 
&nbsp; &nbsp; paused = <span class="kw5">True</span>
&nbsp; &nbsp; Pause = <span class="kw5">True</span>
&nbsp; &nbsp; 
<span class="kw3">End</span> <span class="kw2">Function</span>
&nbsp;
<span class="co1">' // Stop playback.</span>
<span class="kw2">Public</span> <span class="kw2">Function</span> StopPlaying() <span class="kw4">As</span> <span class="kw1">Boolean</span>
&nbsp;
&nbsp; &nbsp; <span class="kw3">If</span> <span class="kw3">Not</span> (init <span class="kw3">And</span> loaded <span class="kw3">And</span> playing) <span class="kw3">Then</span> <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; paused = <span class="kw5">False</span>
&nbsp; &nbsp; playing = <span class="kw5">False</span>
&nbsp; &nbsp; curPosition = -1
&nbsp; &nbsp; 
&nbsp; &nbsp; waveOutReset hWave
&nbsp;
&nbsp; &nbsp; StopPlaying = <span class="kw5">True</span>
&nbsp; &nbsp; 
<span class="kw3">End</span> <span class="kw2">Function</span>
&nbsp;
<span class="co1">' // Set current playback position (in milliseconds).</span>
<span class="kw2">Public</span> <span class="kw2">Function</span> SetPositionMs(<span class="kw4">ByVal</span> pos <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> <span class="kw1">Boolean</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> frameLength <span class="kw4">As</span> <span class="kw1">Single</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> index &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> <span class="kw3">Not</span> (init <span class="kw3">And</span> loaded) <span class="kw3">Then</span> Err.Raise 5: <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; frameLength = fileInfo.samplesPerFrame / fileInfo.format.nSamplesPerSec
&nbsp; &nbsp; index = pos / 1000 / frameLength
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> index &gt;= fileInfo.framesCount <span class="kw3">Then</span> Err.Raise 5: &nbsp;<span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; curPosition = index
&nbsp; &nbsp; SetPositionMs = <span class="kw5">True</span>
&nbsp; &nbsp; 
<span class="kw3">End</span> <span class="kw2">Function</span>
&nbsp;
<span class="co1">' // Get current playback position (in milliseconds).</span>
<span class="kw2">Public</span> <span class="kw2">Function</span> GetPositionMs() <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> frameLength <span class="kw4">As</span> <span class="kw1">Single</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> <span class="kw3">Not</span> (init <span class="kw3">And</span> loaded) <span class="kw3">Then</span> <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; frameLength = fileInfo.samplesPerFrame / fileInfo.format.nSamplesPerSec
&nbsp; &nbsp; GetPositionMs = curPosition * frameLength * 1000
&nbsp; &nbsp; 
<span class="kw3">End</span> <span class="kw2">Function</span>
&nbsp;
<span class="co1">' // Get duration of the data in milliseconds.</span>
<span class="kw2">Public</span> <span class="kw2">Function</span> GetDurationMs() <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> frameLength <span class="kw4">As</span> <span class="kw1">Single</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> <span class="kw3">Not</span> (init <span class="kw3">And</span> loaded) <span class="kw3">Then</span> <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; frameLength = fileInfo.samplesPerFrame / fileInfo.format.nSamplesPerSec
&nbsp; &nbsp; GetDurationMs = fileInfo.framesCount * frameLength * 1000
&nbsp; &nbsp; 
<span class="kw3">End</span> <span class="kw2">Function</span>
&nbsp;
<span class="co1">' // Get current bitrate.</span>
<span class="kw2">Public</span> <span class="kw2">Function</span> GetBitrate() <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> curPosition &lt; 0 <span class="kw3">Then</span> <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; GetBitrate = fileInfo.frameOffset(curPosition).bitrate
&nbsp; &nbsp; 
<span class="kw3">End</span> <span class="kw2">Function</span>
&nbsp;
<span class="co1">' // If playback is active then true.</span>
<span class="kw2">Public</span> <span class="kw4">Property</span> <span class="kw4">Get</span> IsPlaying() <span class="kw4">As</span> <span class="kw1">Boolean</span>
&nbsp; &nbsp; IsPlaying = init <span class="kw3">And</span> loaded <span class="kw3">And</span> playing <span class="kw3">And</span> <span class="kw3">Not</span> paused
<span class="kw3">End</span> <span class="kw4">Property</span>
&nbsp;
<span class="co1">' // Volume</span>
<span class="kw2">Public</span> <span class="kw4">Property</span> <span class="kw4">Get</span> Volume() <span class="kw4">As</span> <span class="kw1">Single</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> dwVolume &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> volLeft &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> volRight &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; waveOutGetVolume hWave, dwVolume
&nbsp; &nbsp; 
&nbsp; &nbsp; volLeft = dwVolume <span class="kw3">And</span> &amp;HFFFF&amp;
&nbsp; &nbsp; volRight = ((dwVolume <span class="kw3">And</span> &amp;HFFFF0000) \ &amp;H10000) <span class="kw3">And</span> &amp;HFFFF&amp;
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> volLeft &gt; volRight <span class="kw3">Then</span> Volume = volLeft / 65535 <span class="kw3">Else</span> Volume = volRight / 65535
&nbsp; &nbsp; 
<span class="kw3">End</span> <span class="kw4">Property</span>
<span class="kw2">Public</span> <span class="kw4">Property</span> <span class="kw4">Let</span> Volume(<span class="kw4">ByVal</span> value <span class="kw4">As</span> <span class="kw1">Single</span>)
&nbsp; &nbsp; <span class="kw4">Dim</span> dwVolume &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> volRight &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> value &gt; 1 <span class="kw3">Or</span> value &lt;= 0 <span class="kw3">Then</span> Err.Raise 6: <span class="kw2">Exit</span> <span class="kw4">Property</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; mVolume = value
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> mPan &gt; 0 <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; volRight = value * 65535
&nbsp; &nbsp; &nbsp; &nbsp; dwVolume = volRight * (1 - mPan)
&nbsp; &nbsp; <span class="kw3">Else</span>
&nbsp; &nbsp; &nbsp; &nbsp; dwVolume = value * 65535
&nbsp; &nbsp; &nbsp; &nbsp; volRight = dwVolume * (1 + mPan)
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> volRight <span class="kw3">And</span> &amp;H8000&amp; <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; dwVolume = dwVolume <span class="kw3">Or</span> ((volRight <span class="kw3">And</span> &amp;H7FFF&amp;) * &amp;H10000) <span class="kw3">Or</span> &amp;H80000000
&nbsp; &nbsp; <span class="kw3">Else</span>
&nbsp; &nbsp; &nbsp; &nbsp; dwVolume = dwVolume <span class="kw3">Or</span> (volRight * &amp;H10000)
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; waveOutSetVolume hWave, dwVolume
&nbsp; &nbsp; &nbsp;
<span class="kw3">End</span> <span class="kw4">Property</span>
&nbsp;
<span class="co1">' // Pan</span>
<span class="kw2">Public</span> <span class="kw4">Property</span> <span class="kw4">Get</span> Pan() <span class="kw4">As</span> <span class="kw1">Single</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> dwVolume &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> volLeft &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> volRight &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; waveOutGetVolume hWave, dwVolume
&nbsp; &nbsp; 
&nbsp; &nbsp; volLeft = dwVolume <span class="kw3">And</span> &amp;HFFFF&amp;
&nbsp; &nbsp; volRight = ((dwVolume <span class="kw3">And</span> &amp;HFFFF0000) \ &amp;H10000) <span class="kw3">And</span> &amp;HFFFF&amp;
&nbsp; &nbsp; <span class="kw3">If</span> volLeft &gt; volRight <span class="kw3">Then</span> dwVolume = volLeft <span class="kw3">Else</span> dwVolume = volRight
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> dwVolume = 0 <span class="kw3">Then</span> dwVolume = 1
&nbsp; &nbsp; Pan = (volRight - volLeft) / dwVolume
&nbsp; &nbsp; 
<span class="kw3">End</span> <span class="kw4">Property</span>
<span class="kw2">Public</span> <span class="kw4">Property</span> <span class="kw4">Let</span> Pan(<span class="kw4">ByVal</span> value <span class="kw4">As</span> <span class="kw1">Single</span>)
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> value &gt; 1 <span class="kw3">Or</span> value &lt; -1 <span class="kw3">Then</span> Err.Raise 6: <span class="kw2">Exit</span> <span class="kw4">Property</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; mPan = value
&nbsp; &nbsp; Me.Volume = mVolume
&nbsp; &nbsp; 
<span class="kw3">End</span> <span class="kw4">Property</span>
&nbsp;
<span class="co1">' // Local procedures.</span>
<span class="kw2">Private</span> <span class="kw2">Function</span> SUBCLASSPROC(<span class="kw4">ByVal</span> hWnd <span class="kw4">As</span> <span class="kw1">Long</span>, <span class="kw4">ByVal</span> Msg <span class="kw4">As</span> <span class="kw1">Long</span>, <span class="kw4">ByVal</span> wParam <span class="kw4">As</span> <span class="kw1">Long</span>, <span class="kw4">ByVal</span> lParam <span class="kw4">As</span> <span class="kw1">Long</span>, <span class="kw4">ByVal</span> uIdSubclass <span class="kw4">As</span> <span class="kw1">Long</span>, <span class="kw4">ByVal</span> dwRefData <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> index &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> lpData &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> inIDE &nbsp; <span class="kw4">As</span> <span class="kw1">Boolean</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; Debug.Assert MakeTrue(inIDE)
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> inIDE <span class="kw3">Then</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> Msg = WM_TIMER <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; KillTimer hWnd, wParam
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">For</span> index = 0 <span class="kw3">To</span> BUFFERS_COUNT - 1
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> headers(index).header.dwFlags <span class="kw3">And</span> WHDR_DONE <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; WriteNext index
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">Next</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> Msg = MM_WOM_DONE <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> wParam &lt;&gt; hWave <span class="kw3">Then</span> <span class="kw3">GoTo</span> DefCall
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; GetMem4 <span class="kw4">ByVal</span> lParam, lpData
&nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; index = GetBufferIndex(lpData)
&nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> index = -1 <span class="kw3">Then</span> <span class="kw3">GoTo</span> DefCall
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; WriteNext index
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; 
DefCall:
&nbsp; &nbsp; 
&nbsp; &nbsp; SUBCLASSPROC = DefSubclassProc(hWnd, Msg, wParam, lParam)
&nbsp; &nbsp; 
<span class="kw3">End</span> <span class="kw2">Function</span>
&nbsp;
<span class="kw2">Private</span> <span class="kw2">Sub</span> WriteNext(<span class="kw4">ByVal</span> index <span class="kw4">As</span> <span class="kw1">Long</span>)
&nbsp; &nbsp; 
&nbsp; &nbsp; waveOutUnprepareHeader hWave, headers(index).header, Len(headers(index).header)
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> playing = <span class="kw5">False</span> <span class="kw3">And</span> paused = <span class="kw5">False</span> <span class="kw3">Then</span> <span class="kw2">Exit</span> <span class="kw2">Sub</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> curPosition = -1 <span class="kw3">Then</span> <span class="kw2">Exit</span> <span class="kw2">Sub</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; headers(index).header.dwFlags = headers(index).header.dwFlags <span class="kw3">And</span> <span class="kw3">Not</span> WHDR_DONE
&nbsp; &nbsp; headers(index).header.lpData = fileInfo.lpFrameOffset + fileInfo.frameOffset(curPosition).offset
&nbsp;
&nbsp; &nbsp; <span class="kw3">If</span> curPosition &lt; fileInfo.framesCount - 1 <span class="kw3">Then</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; headers(index).header.dwBufferLength = fileInfo.frameOffset(curPosition + 1).offset - fileInfo.frameOffset(curPosition).offset
&nbsp; &nbsp; &nbsp; &nbsp; curPosition = curPosition + 1
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">Else</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; headers(index).header.dwBufferLength = fileInfo.szDataSize - fileInfo.frameOffset(curPosition).offset
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> isLoop <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; curPosition = 0
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">Else</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; curPosition = -1
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; waveOutPrepareHeader hWave, headers(index).header, Len(headers(index).header)
&nbsp; &nbsp; waveOutWrite hWave, headers(index).header, Len(headers(index).header)
&nbsp; &nbsp; 
<span class="kw3">End</span> <span class="kw2">Sub</span>
&nbsp;
<span class="kw2">Private</span> <span class="kw2">Sub</span> ClearAll()
&nbsp; &nbsp; <span class="kw4">Dim</span> index &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp;
&nbsp; &nbsp; <span class="kw3">If</span> hWave = 0 <span class="kw3">Then</span> <span class="kw2">Exit</span> <span class="kw2">Sub</span>
&nbsp;
&nbsp; &nbsp; <span class="kw3">For</span> index = 0 <span class="kw3">To</span> BUFFERS_COUNT - 1
&nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> headers(index).status <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; waveOutUnprepareHeader hWave, headers(index).header, Len(headers(index).header)
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp;
&nbsp; &nbsp; <span class="kw3">Next</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> playing <span class="kw3">Or</span> paused <span class="kw3">Then</span> waveOutReset hWave
&nbsp; &nbsp; 
&nbsp; &nbsp; waveOutClose hWave
&nbsp; &nbsp; 
&nbsp; &nbsp; loaded = <span class="kw5">False</span>
&nbsp; &nbsp; playing = <span class="kw5">False</span>
&nbsp; &nbsp; paused = <span class="kw5">False</span>
&nbsp; &nbsp; hWave = 0
&nbsp; &nbsp; 
<span class="kw3">End</span> <span class="kw2">Sub</span>
&nbsp;
<span class="kw2">Private</span> <span class="kw2">Function</span> GetBufferIndex(<span class="kw4">ByVal</span> ptr <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> index <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp;
&nbsp; &nbsp; <span class="kw3">For</span> index = 0 <span class="kw3">To</span> <span class="kw4">UBound</span>(headers)
&nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> headers(index).header.lpData = ptr <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; GetBufferIndex = index
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp;
&nbsp; &nbsp; <span class="kw3">Next</span>
&nbsp;
&nbsp; &nbsp; GetBufferIndex = -1
<span class="kw3">End</span> <span class="kw2">Function</span>
&nbsp;
<span class="kw2">Private</span> <span class="kw2">Function</span> Mp3GetInfo(<span class="kw4">ByVal</span> lpData <span class="kw4">As</span> <span class="kw1">Long</span>, <span class="kw4">ByVal</span> szData <span class="kw4">As</span> <span class="kw1">Long</span>, info <span class="kw4">As</span> Mp3Info) <span class="kw4">As</span> <span class="kw1">Boolean</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> hdr(9) &nbsp;<span class="kw4">As</span> <span class="kw1">Byte</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> size &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> szData &gt;= 128 <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' Skip ID3V1 tag</span>
 &nbsp; &nbsp; &nbsp; &nbsp;memcpy hdr(0), <span class="kw4">ByVal</span> lpData + szData - 128, 3
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> hdr(0) = &amp;H54 <span class="kw3">And</span> hdr(1) = &amp;H41 <span class="kw3">And</span> hdr(2) = &amp;H47 <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; szData = szData - 128
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="co1">' Skip ID3V2 tags from beginning</span>
 &nbsp; &nbsp;memcpy hdr(0), <span class="kw4">ByVal</span> lpData, 10
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> hdr(0) = &amp;H49 <span class="kw3">And</span> hdr(1) = &amp;H44 <span class="kw3">And</span> hdr(2) = &amp;H33 <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' footer present</span>
 &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw3">If</span> hdr(5) <span class="kw3">And</span> &amp;H10 <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; szData = szData - 10
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; size = hdr(6) * &amp;H200000
&nbsp; &nbsp; &nbsp; &nbsp; size = size <span class="kw3">Or</span> (hdr(7) * &amp;H4000&amp;)
&nbsp; &nbsp; &nbsp; &nbsp; size = size <span class="kw3">Or</span> (hdr(8) * &amp;H80&amp;)
&nbsp; &nbsp; &nbsp; &nbsp; size = size <span class="kw3">Or</span> hdr(9)
&nbsp; &nbsp; &nbsp; &nbsp; size = size + 10
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; lpData = lpData + size
&nbsp; &nbsp; &nbsp; &nbsp; szData = szData - size
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">Else</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' Skip ID3V2 tags from end</span>
 &nbsp; &nbsp; &nbsp; &nbsp;memcpy hdr(0), <span class="kw4">ByVal</span> lpData + szData - 10, 10
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> hdr(2) = &amp;H49 <span class="kw3">And</span> hdr(1) = &amp;H44 <span class="kw3">And</span> hdr(0) = &amp;H33 <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; szData = szData - 10
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; size = hdr(6) * &amp;H200000
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; size = size <span class="kw3">Or</span> (hdr(7) * &amp;H4000&amp;)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; size = size <span class="kw3">Or</span> (hdr(8) * &amp;H80&amp;)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; size = size <span class="kw3">Or</span> hdr(9)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; size = size + 10
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; szData = szData - size
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> szData &lt; 4 <span class="kw3">Then</span> <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; info.framesCount = 0
&nbsp; &nbsp; <span class="co1">'Scan headers</span>
 &nbsp; &nbsp;<span class="kw3">Do</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' Find a frame sync</span>
 &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw3">Do</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; GetMem4 <span class="kw4">ByVal</span> lpData, hdr(0)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> hdr(0) = &amp;HFF <span class="kw3">And</span> (hdr(1) <span class="kw3">And</span> &amp;HE0) = &amp;HE0 <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">Dim</span> vers &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">Dim</span> layer &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">Dim</span> bitrate <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">Dim</span> smprate <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">Dim</span> padding <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">Dim</span> channel <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; vers = (hdr(1) <span class="kw3">And</span> &amp;H18) \ 8
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> vers = 1 <span class="kw3">Then</span> <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; layer = (hdr(1) <span class="kw3">And</span> &amp;H6) \ 2
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> layer &lt;&gt; 1 <span class="kw3">Then</span> <span class="kw2">Exit</span> <span class="kw2">Function</span> <span class="co1">' Only Layer 3</span>
 &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> vers = 3 <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; bitrate = constants.bitrate(0, (hdr(2) <span class="kw3">And</span> &amp;HF0) \ &amp;H10)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">Else</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; bitrate = constants.bitrate(1, (hdr(2) <span class="kw3">And</span> &amp;HF0) \ &amp;H10)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> vers = 3 <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; smprate = constants.smprate(0, (hdr(2) <span class="kw3">And</span> &amp;HC) \ &amp;H4)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">ElseIf</span> vers = 2 <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; smprate = constants.smprate(1, (hdr(2) <span class="kw3">And</span> &amp;HC) \ &amp;H4)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">Else</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; smprate = constants.smprate(2, (hdr(2) <span class="kw3">And</span> &amp;HC) \ &amp;H4)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; padding = (hdr(2) <span class="kw3">And</span> &amp;H2) \ 2
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; channel = -(((hdr(3) <span class="kw3">And</span> &amp;HC0) \ 64) &lt;&gt; 3) + 1
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> vers = 3 <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; size = Int(144000 * bitrate / smprate) + padding
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">Else</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; size = Int(72000 * bitrate / smprate) + padding
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">With</span> info
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> .framesCount = 0 <span class="kw3">Then</span>
&nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">With</span> .format
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .wFormatTag = WAVE_FORMAT_MPEGLAYER3
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .cbSize = MPEGLAYER3_WFX_EXTRA_BYTES
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .nChannels = channel
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .nAvgBytesPerSec = bitrate * 128
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .wBitsPerSample = 0
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .nBlockAlign = 1
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .nSamplesPerSec = smprate
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .nFramesPerBlock = 1
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .nCodecDelay = 0
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .fdwFlags = MPEGLAYER3_FLAG_PADDING_OFF
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .wID = MPEGLAYER3_ID_MPEG
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .nBlockSize = size
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">With</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .lpFrameOffset = lpData
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .szDataSize = szData
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> vers = 3 <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .samplesPerFrame = 1152
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">Else</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .samplesPerFrame = 576
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">ReDim</span> .frameOffset(511)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">Else</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> <span class="kw4">UBound</span>(.frameOffset) = info.framesCount <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">ReDim</span> <span class="kw4">Preserve</span> .frameOffset(<span class="kw4">UBound</span>(.frameOffset) + 512)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .frameOffset(info.framesCount).offset = lpData - .lpFrameOffset
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .frameOffset(info.framesCount).bitrate = bitrate
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">With</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; lpData = lpData + size
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; szData = szData - size
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">Exit</span> <span class="kw3">Do</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; lpData = lpData + 1
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; szData = szData - 1
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">Loop</span> <span class="kw3">While</span> szData &gt;= 4
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; info.framesCount = info.framesCount + 1
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">Loop</span> <span class="kw3">While</span> szData &gt;= 4
&nbsp;
&nbsp; &nbsp; Mp3GetInfo = <span class="kw5">True</span>
&nbsp; &nbsp; 
<span class="kw3">End</span> <span class="kw2">Function</span>
&nbsp;
<span class="kw2">Private</span> <span class="kw2">Function</span> GetWindowAndHeap(l_hwnd <span class="kw4">As</span> <span class="kw1">Long</span>, l_hHeap <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> <span class="kw1">Boolean</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> i1 &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> i2 &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> b &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> arr(16) <span class="kw4">As</span> <span class="kw1">Integer</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> GetEnvironmentVariable(StrPtr(Mp3Class), arr(0), 32) <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; i1 = 0: i2 = 8
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">Do</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> arr(i1) &lt;= &amp;H39 <span class="kw3">Then</span> b = arr(i1) - &amp;H30 <span class="kw3">Else</span> b = arr(i1) - &amp;H37
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> l_hHeap <span class="kw3">And</span> &amp;H8000000 <span class="kw3">Then</span> l_hHeap = ((l_hHeap <span class="kw3">And</span> &amp;H7FFFFF) * &amp;H10 <span class="kw3">Or</span> &amp;H80000000) <span class="kw3">Or</span> b <span class="kw3">Else</span> l_hHeap = (l_hHeap * &amp;H10) <span class="kw3">Or</span> b
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> arr(i2) &lt;= &amp;H39 <span class="kw3">Then</span> b = arr(i2) - &amp;H30 <span class="kw3">Else</span> b = arr(i2) - &amp;H37
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> l_hwnd <span class="kw3">And</span> &amp;H8000000 <span class="kw3">Then</span> l_hwnd = ((l_hwnd <span class="kw3">And</span> &amp;H7FFFFF) * &amp;H10 <span class="kw3">Or</span> &amp;H80000000) <span class="kw3">Or</span> b <span class="kw3">Else</span> l_hwnd = (l_hwnd * &amp;H10) <span class="kw3">Or</span> b
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; i1 = i1 + 1: i2 = i2 + 1
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">Loop</span> <span class="kw3">While</span> i1 &lt; 8
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; GetWindowAndHeap = l_hwnd &lt;&gt; 0 <span class="kw3">And</span> l_hHeap &lt;&gt; 0
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp;
<span class="kw3">End</span> <span class="kw2">Function</span>
&nbsp;
<span class="kw2">Private</span> <span class="kw2">Function</span> SaveWindowAndHeap(<span class="kw4">ByVal</span> l_hwnd <span class="kw4">As</span> <span class="kw1">Long</span>, <span class="kw4">ByVal</span> l_hHeap <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> <span class="kw1">Boolean</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> i1 &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> i2 &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> b &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> arr(16) <span class="kw4">As</span> <span class="kw1">Integer</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; i1 = 7: i2 = 15
&nbsp; &nbsp; <span class="kw3">Do</span>
&nbsp; &nbsp; &nbsp; &nbsp; b = l_hHeap <span class="kw3">And</span> &amp;HF
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> b &lt; 10 <span class="kw3">Then</span> arr(i1) = b + &amp;H30 <span class="kw3">Else</span> arr(i1) = b + &amp;H37
&nbsp; &nbsp; &nbsp; &nbsp; b = l_hwnd <span class="kw3">And</span> &amp;HF
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> b &lt; 10 <span class="kw3">Then</span> arr(i2) = b + &amp;H30 <span class="kw3">Else</span> arr(i2) = b + &amp;H37
&nbsp; &nbsp; &nbsp; &nbsp; l_hHeap = (l_hHeap <span class="kw3">And</span> &amp;HFFFFFFF0) \ &amp;H10
&nbsp; &nbsp; &nbsp; &nbsp; l_hwnd = (l_hwnd <span class="kw3">And</span> &amp;HFFFFFFF0) \ &amp;H10
&nbsp; &nbsp; &nbsp; &nbsp; i1 = i1 - 1: i2 = i2 - 1
&nbsp; &nbsp; <span class="kw3">Loop</span> <span class="kw3">While</span> i1 &gt;= 0
&nbsp;
&nbsp; &nbsp; SaveWindowAndHeap = SetEnvironmentVariable(StrPtr(Mp3Class), arr(0))
&nbsp; &nbsp; 
<span class="kw3">End</span> <span class="kw2">Function</span>
&nbsp;
<span class="kw2">Private</span> <span class="kw2">Sub</span> Class_Initialize()
&nbsp; &nbsp; <span class="kw4">Dim</span> cls &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> WNDCLASSEX
&nbsp; &nbsp; <span class="kw4">Dim</span> b &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> curBuffer
&nbsp; &nbsp; <span class="kw4">Dim</span> isFirst &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Boolean</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> inIDE &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Boolean</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> AsmSize &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> lpAsm &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> lpFlag &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> hInstVB6 &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> lpEbMode &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> hInstUser32 <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> hComctl32 &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> lpDefProc &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> lpSetTimer &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> clearFlag &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; b.b(0) = 450377142658.6656@: &nbsp; &nbsp;b.b(1) = 900743977448.248@: &nbsp; &nbsp; b.b(2) = 1351114248211.6672@
&nbsp; &nbsp; b.b(3) = 1801487954948.9248@: &nbsp; b.b(4) = 2702228496423.3344@: &nbsp; b.b(5) = 3602975909897.8496@
&nbsp; &nbsp; b.b(6) = 4503737067267.712@: &nbsp; &nbsp;b.b(7) = 18941235272.0895@: &nbsp; &nbsp; b.b(8) = 4735201446.045@
&nbsp; &nbsp; b.b(9) = 10307921515.2@: &nbsp; &nbsp; &nbsp; &nbsp;b.b(10) = 13743895348.4@: &nbsp; &nbsp; &nbsp; b.b(11) = 3435973838.4@
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; memcpy constants.bitrate(0, 1), b.b(0), 96
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw4">ReDim</span> headers(BUFFERS_COUNT - 1)
&nbsp; &nbsp; mVolume = 1
&nbsp; &nbsp; 
&nbsp; &nbsp; isFirst = <span class="kw3">Not</span> GetWindowAndHeap(hWnd, hHeap)
&nbsp;
&nbsp; &nbsp; Debug.Assert MakeTrue(inIDE)
&nbsp; &nbsp; 
&nbsp; &nbsp; hInstUser32 = GetModuleHandle(StrPtr(<span class="st0">&quot;user32&quot;</span>))
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> inIDE <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; AsmSize = &amp;H65
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; hInstVB6 = GetModuleHandle(StrPtr(<span class="st0">&quot;vba6&quot;</span>))
&nbsp; &nbsp; &nbsp; &nbsp; hComctl32 = GetModuleHandle(StrPtr(<span class="st0">&quot;hComctl32&quot;</span>))
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> hComctl32 = 0 <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; hComctl32 = LoadLibrary(StrPtr(<span class="st0">&quot;Comctl32&quot;</span>))
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> hComctl32 = 0 <span class="kw3">Then</span> <span class="kw2">Exit</span> <span class="kw2">Sub</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; &nbsp; &nbsp; lpEbMode = GetProcAddress(hInstVB6, <span class="st0">&quot;EbMode&quot;</span>)
&nbsp; &nbsp; &nbsp; &nbsp; lpDefProc = GetProcAddress(hComctl32, <span class="st0">&quot;DefSubclassProc&quot;</span>)
&nbsp; &nbsp; &nbsp; &nbsp; lpSetTimer = GetProcAddress(hInstUser32, <span class="st0">&quot;SetTimer&quot;</span>)
&nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; b.b(0) = 843073850243758.4259@: b.b(1) = -457424984652572.8729@: &nbsp; &nbsp;b.b(2) = 2989182470102.0276@
&nbsp; &nbsp; &nbsp; &nbsp; b.b(3) = -7165957082854.492@: &nbsp; b.b(4) = -16790531.982@: &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;b.b(5) = 10059.9531@
&nbsp; &nbsp; &nbsp; &nbsp; b.b(6) = 116318324260473.7791@: b.b(7) = 116318324260473.7791@: &nbsp; &nbsp; b.b(8) = 696980420845.4632@
&nbsp; &nbsp; &nbsp; &nbsp; b.b(9) = 522808547116743.0705@: b.b(10) = 756460495277739.1878@: &nbsp; &nbsp;b.b(11) = -10565565861.0689@
&nbsp; &nbsp; &nbsp; &nbsp; b.b(12) = 41538.9951@
&nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> isFirst <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; lpFlag = HeapAlloc(GetProcessHeap(), HEAP_NO_SERIALIZE <span class="kw3">Or</span> HEAP_ZERO_MEMORY, 4)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> lpFlag = 0 <span class="kw3">Then</span> <span class="kw2">Exit</span> <span class="kw2">Sub</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">Else</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; lpFlag = GetWindowLong(hWnd, 0)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; GetMem4 <span class="kw4">ByVal</span> lpFlag, clearFlag
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> clearFlag <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; DestroyWindow hWnd
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; HeapDestroy hHeap
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; UnregisterClass StrPtr(Mp3Class), App.hInstance
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; GetMem4 0&amp;, <span class="kw4">ByVal</span> lpFlag
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; isFirst = <span class="kw5">True</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; hWnd = 0
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; hHeap = 0
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; SaveWindowAndHeap 0, 0
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp;
&nbsp; &nbsp; <span class="kw3">Else</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; AsmSize = &amp;H20
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; b.b(0) = 522808547116743.0705@: b.b(1) = 756460495277739.1878@: &nbsp; &nbsp;b.b(2) = -10565565861.0689@
&nbsp; &nbsp; &nbsp; &nbsp; b.b(3) = 41538.9951@
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> isFirst <span class="kw3">Then</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; hHeap = HeapCreate(HEAP_CREATE_ENABLE_EXECUTE <span class="kw3">Or</span> HEAP_NO_SERIALIZE, 0, 0)
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> hHeap = 0 <span class="kw3">Then</span> <span class="kw2">Exit</span> <span class="kw2">Sub</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; lpAsm = HeapAlloc(hHeap, HEAP_NO_SERIALIZE, AsmSize)
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> lpAsm = 0 <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> isFirst <span class="kw3">Then</span> HeapDestroy hHeap
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">Exit</span> <span class="kw2">Sub</span>
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; lpWndProc = lpAsm
&nbsp;
&nbsp; &nbsp; memcpy <span class="kw4">ByVal</span> lpAsm, b.b(0), AsmSize
&nbsp;
&nbsp; &nbsp; <span class="kw3">If</span> inIDE <span class="kw3">Then</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; GetMem4 lpEbMode - (lpAsm + &amp;H9) - 5, <span class="kw4">ByVal</span> lpAsm + &amp;H9 + 1 &nbsp; &nbsp; <span class="co1">' Call EbMode</span>
 &nbsp; &nbsp; &nbsp; &nbsp;GetMem4 lpSetTimer - (lpAsm + &amp;H23) - 5, <span class="kw4">ByVal</span> lpAsm + &amp;H23 + 1 <span class="co1">' Call SetTimer</span>
 &nbsp; &nbsp; &nbsp; &nbsp;GetMem4 lpDefProc - (lpAsm + &amp;H40) - 5, <span class="kw4">ByVal</span> lpAsm + &amp;H40 + 1 &nbsp;<span class="co1">' call DefSubclassProc</span>
 &nbsp; &nbsp; &nbsp; &nbsp;GetMem4 lpFlag, <span class="kw4">ByVal</span> lpAsm + &amp;H2 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' Cmp [flag], 0</span>
 &nbsp; &nbsp; &nbsp; &nbsp;GetMem4 lpFlag, <span class="kw4">ByVal</span> lpAsm + &amp;H2C &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">' Inc [flag]</span>
 &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; lpAsm = lpAsm + &amp;H48
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw4">Dim</span> lpMeth &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> vTable &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; GetMem4 <span class="kw4">ByVal</span> ObjPtr(Me), vTable
&nbsp; &nbsp; GetMem4 <span class="kw4">ByVal</span> vTable + WNDPROCINDEX * 4 + &amp;H1C, lpMeth
&nbsp; &nbsp; GetMem4 ObjPtr(Me), <span class="kw4">ByVal</span> lpAsm + &amp;H10 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' Push Me</span>
 &nbsp; &nbsp;GetMem4 lpMeth - (lpAsm + &amp;H14) - 5, <span class="kw4">ByVal</span> lpAsm + &amp;H14 + 1 &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">' Call WndProc</span>
 &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; <span class="kw3">If</span> isFirst <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; lpDefProc = GetProcAddress(hInstUser32, <span class="st0">&quot;DefWindowProcW&quot;</span>)
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; cls.hInstance = App.hInstance
&nbsp; &nbsp; &nbsp; &nbsp; cls.lpfnwndproc = lpDefProc
&nbsp; &nbsp; &nbsp; &nbsp; cls.lpszClassName = StrPtr(Mp3Class)
&nbsp; &nbsp; &nbsp; &nbsp; cls.cbSize = Len(cls)
&nbsp; &nbsp; &nbsp; &nbsp; cls.cbWndExtra2 = 8
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> RegisterClassEx(cls) = 0 <span class="kw3">Then</span>
&nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; HeapDestroy hHeap
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">Exit</span> <span class="kw2">Sub</span>
&nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; hWnd = CreateWindowEx(0, StrPtr(Mp3Class), 0, 0, 0, 0, 0, 0, HWND_MESSAGE, 0, App.hInstance, <span class="kw4">ByVal</span> 0&amp;)
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> hWnd = 0 <span class="kw3">Then</span> <span class="kw2">Exit</span> <span class="kw2">Sub</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; SaveWindowAndHeap hWnd, hHeap
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> inIDE <span class="kw3">Then</span> <span class="kw2">Call</span> SetWindowLong(hWnd, 0, lpFlag)
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> SetWindowSubclass(hWnd, lpWndProc, ObjPtr(Me), 0) = 0 <span class="kw3">Then</span> <span class="kw2">Exit</span> <span class="kw2">Sub</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; SetWindowLong hWnd, GWL_USERDATA, GetWindowLong(hWnd, GWL_USERDATA) + 1
&nbsp; &nbsp; 
&nbsp; &nbsp; init = <span class="kw5">True</span>
&nbsp; &nbsp; 
<span class="kw3">End</span> <span class="kw2">Sub</span>
&nbsp;
<span class="kw2">Private</span> <span class="kw2">Sub</span> Class_Terminate()
&nbsp; &nbsp; <span class="kw4">Dim</span> refCt &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> <span class="kw3">Not</span> init <span class="kw3">Then</span> <span class="kw2">Exit</span> <span class="kw2">Sub</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; refCt = GetWindowLong(hWnd, GWL_USERDATA)
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> refCt = 0 <span class="kw3">Then</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; DestroyWindow hWnd
&nbsp; &nbsp; &nbsp; &nbsp; HeapDestroy hHeap
&nbsp; &nbsp; &nbsp; &nbsp; UnregisterClass StrPtr(Mp3Class), App.hInstance
&nbsp; &nbsp; &nbsp; &nbsp; SaveWindowAndHeap 0, 0
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">Else</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; RemoveWindowSubclass hWnd, lpWndProc, ObjPtr(Me)
&nbsp; &nbsp; &nbsp; &nbsp; SetWindowLong hWnd, GWL_USERDATA, refCt - 1
&nbsp; &nbsp; &nbsp; &nbsp; HeapFree hHeap, HEAP_NO_SERIALIZE, <span class="kw4">ByVal</span> lpWndProc
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; 
<span class="kw3">End</span> <span class="kw2">Sub</span>
&nbsp;
<span class="kw2">Private</span> <span class="kw2">Function</span> MakeTrue(refBool <span class="kw4">As</span> <span class="kw1">Boolean</span>) <span class="kw4">As</span> <span class="kw1">Boolean</span>
&nbsp; &nbsp; MakeTrue = <span class="kw5">True</span>
&nbsp; &nbsp; refBool = <span class="kw5">True</span>
<span class="kw3">End</span> <span class="kw2">Function</span></pre></td></tr></table></div></td></tr></tbody></table></div></div>


<!-- attachments -->
	<div style="margin-top:10px">

		
		
		
		
			<fieldset class="fieldset">
				<legend>Вложения</legend>
				<table cellpadding="0" cellspacing="3" border="0">
				<tr>
	<td><img loading="lazy" decoding="async" class="inlineimg" src="http://www.cyberforum.ru//cyberstatic.net/images/attach/zip.gif" alt="Тип файла: zip" width="16" height="16" border="0" style="vertical-align:baseline" /></td>
	<td><a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=3200&amp;d=1435074249">Ver.1.1.zip</a> (717.8 Кб, 2556 просмотров)</td>
</tr>
				</table>
			</fieldset>
		

	</div>
<!-- / attachments -->
]]></content:encoded>
			<dc:creator>The trick</dc:creator>
			<guid isPermaLink="true">https://www.cyberforum.ru/blogs/354370/3511.html</guid>
		</item>
		<item>
			<title>Многопоточность в VB6 часть 4 - многопоточность в Standart EXE.</title>
			<link>https://www.cyberforum.ru/blogs/354370/3262.html</link>
			<pubDate>Mon, 16 Feb 2015 22:07:16 GMT</pubDate>
			<description>W83gi9z1mHA 
 
Всем привет. Сейчас у меня мало времени, поэтому я уже не так часто уделяю внимание...</description>
			<content:encoded><![CDATA[<div><div align="center"><iframe width="640" height="360" src="https://www.youtube.com/embed/W83gi9z1mHA" frameborder="0" allowfullscreen></iframe></div><br />
Всем привет. Сейчас у меня мало времени, поэтому я уже не так часто уделяю внимание бейсику и реже появляюсь на форумах. Сегодня я опять буду говорить о многопоточности, на этот раз в <b>Standart EXE</b>. Сразу скажу что все о чем я пишу является моим личным исследованием и может в чем-то не соответствовать действительности; также из-за моего недостатка времени я буду дополнять этот пост по мере дальнейшего прогресса в исследовании данного вопроса. Итак начнем.<br />
Как я говорил до этого для того чтобы многопоточность работала нужно инициализировать рантайм. Без инициализации мы можем работать очень ограниченно, в том смысле что COM не будет работать, т.е. грубо говоря вся мощь бейсика будет недоступна. Можно работать с API, объявленными в tlb, некоторыми функциями, также убирая проверку <b>__vbaSetSystemError</b>, можно использовать <b>Declared</b>-функции. Все предыдущие публикации показывали работу в отдельных DLL, и мы легко могли инициализировать рантайм используя <b>VBDllGetClassObject</b> функцию для этого. Сегодня мы попытаемся инициализировать рантайм в обычном EXE, т.е. не используя внешние зависимости. Не для кого не секрет что любое приложение написанное в VB6 состоит из хидера проекта, в котором содержится очень много информации о проекте которую рантайм использует для работы: <br />
<div class="codeblock"><table class="vb"><thead><tr><td colspan="2" id="735244741"  class="head">Visual Basic</td></tr></thead><tbody><tr class="li1"><td><div id="735244741" style="height: 350px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
</pre></td><td class="de1"><pre class="de1"><span class="kw4">Type</span> VbHeader
&nbsp; &nbsp; szVbMagic &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">String</span> * 4
&nbsp; &nbsp; wRuntimeBuild &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Integer</span>
&nbsp; &nbsp; szLangDll &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">String</span> * 14
&nbsp; &nbsp; szSecLangDll &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">String</span> * 14
&nbsp; &nbsp; wRuntimeRevision &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Integer</span>
&nbsp; &nbsp; dwLCID &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; dwSecLCID &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; lpSubMain &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; lpProjectInfo &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; fMdlIntCtls &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; fMdlIntCtls2 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; dwThreadFlags &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; dwThreadCount &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; wFormCount &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Integer</span>
&nbsp; &nbsp; wExternalCount &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Integer</span>
&nbsp; &nbsp; dwThunkCount &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; lpGuiTable &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; lpExternalCompTable &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; lpComRegisterData &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; bszProjectDescription &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; bszProjectExeName &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; bszProjectHelpFile &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; bszProjectName &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw3">End</span> <span class="kw4">Type</span></pre></td></tr></table></div></td></tr></tbody></table></div>В этой структуре большое количество полей описывать все я не буду, отмечу только что эта структура ссылается на множество других структур. Некоторые из них нам понадобятся в дальнейшем, например поле <b>lpSubMain</b>, в котором содержится адрес процедуры <b>Main</b>, если она определена, иначе там 0.<br />
Подавляющее большинство EXE файлов начинаются со следующего кода:<div class="codeblock"><table class="asm"><thead><tr><td colspan="2" id="480934633"  class="head">Assembler</td></tr></thead><tbody><tr class="li1"><td><div id="480934633" style="height: 62px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
</pre></td><td class="de1"><pre class="de1"><span class="kw1">PUSH</span> xxxxxxxx
<span class="kw1">CALL</span> MSVBVM60<span class="sy1">.</span>ThunRTMain</pre></td></tr></table></div></td></tr></tbody></table></div>Как раз <b>xxxxxxxx</b> указывает на структуру <b>VBHeader</b>. Эта особенность позволит найти эту структуру внутри EXE для инициализации рантайма. В одной из предыдущих частей я описывал как достать из <b>ActiveX DLL</b> эту структуру - для этого нужно было считать данные в одной из экспортируемых функций (к примеру <b>DllGetClassObject</b>). Для получения из EXE - мы также воспользуемся тем-же методом. Для начала нужно найти точку входа (entry point), т.е. адрес с которого начинается выполнение EXE. Этот адрес можно получить из структуры <b>IMAGE_OPTIONAL_HEADER</b> - поле <b>AddressOfEntryPoint</b>. Сама структура <b>IMAGE_OPTIONAL_HEADER</b> расположена в PE заголовке, а PE заголовок находится по смещению заданному в поле <b>e_lfanew</b> структуры <b>IMAGE_DOS_HEADER</b>, ну а структура <b>IMAGE_DOS_HEADER</b> расположена по адресу <b>App.hInstance</b> (или <b>GetModuleHandle</b>). Указатель на <b>VbHeader</b> будет лежать по смещению <b>AddressOfEntryPoint + 1</b>, т.к. опкод команды <b>push</b> в данном случае 0x68h. Итак, собирая все вместе, получим функцию для получения хидера:<br />
<div class="codeblock"><table class="vb"><thead><tr><td colspan="2" id="60310126"  class="head">Visual Basic</td></tr></thead><tbody><tr class="li1"><td><div id="60310126" style="height: 206px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
5
6
7
8
9
10
11
</pre></td><td class="de1"><pre class="de1"><span class="co1">' // Get VBHeader structure</span>
<span class="kw2">Private</span> <span class="kw2">Function</span> GetVBHeader() <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> ptr &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="co1">' Get e_lfanew</span>
 &nbsp; &nbsp;GetMem4 <span class="kw4">ByVal</span> hModule + &amp;H3C, ptr
&nbsp; &nbsp; <span class="co1">' Get AddressOfEntryPoint</span>
 &nbsp; &nbsp;GetMem4 <span class="kw4">ByVal</span> ptr + &amp;H28 + hModule, ptr
&nbsp; &nbsp; <span class="co1">' Get VBHeader</span>
 &nbsp; &nbsp;GetMem4 <span class="kw4">ByVal</span> ptr + hModule + 1, GetVBHeader
&nbsp; &nbsp; 
<span class="kw3">End</span> <span class="kw2">Function</span></pre></td></tr></table></div></td></tr></tbody></table></div>Теперь если передать эту структуру функции <b>VBDllGetClassObject</b> в новом потоке, то, грубо говоря, эта функция запустит наш проект на выполнение согласно переданной структуре. Конечно смысла в этом мало - это тоже самое что начать выполнение приложения заново в новом потоке. Например если была задана функция <b>Main</b>, то и выполнение начнется опять с нее, а если была форма, то с нее. Нужно как-то сделать так, чтобы проект выполнялся с другой, нужной нам, функции. Для этого можно изменить поле <b>lpSubMain</b> структуры <b>vbHeader</b>. Я тоже сначала сделал так, но это ничего не дало. Как выяснилось, внутри рантайма есть один глобальный объект, который хранит ссылки на проекты и связанные с ними объекты и если передать тот же самый хидер в <b>VBDllGetClassObject</b>, то рантайм проверит, не загружался ли такой проект, и если загружался, то просто запустит новую копию без разбора структуры <b>vbHeader</b>, на основании предыдущего разбора. Поэтому я решил поступить так - можно скопировать структуру <b>vbHeader</b> в другое место и использовать ее. Сразу замечу, что в этой структуре последние 4 поля - это смещения относительно начала структуры, поэтому при копировании струкутуры их нужно будет скорректировать. Если теперь попробовать передать эту структуру в <b>VBDllGetClassObject</b>, то все будет отлично если в качестве стартапа установлена <b>Sub Main</b>, если же форма, то будет запущена и форма и после нее <b>Main</b>. Для исключения такого поведения нужно поправить кое-какие данные на которые ссылается хидер. Я пока точно не знаю что это за данные, т.к. не разбирался в этом, но &quot;поковырявшись&quot; внутри рантайма я нашел их место положение. Поле <b>lpGuiTable</b> структуры <b>vbHeader</b> ссылается на список структур <b>tGuiTable</b>, которые описывают формы в проекте. Структуры идут последовательно, число структур соответствует полю <b>wFormCount</b> структуры <b>vbHeader</b>. В сети я так и не нашел нормальное описание структуры <b>tGuiTable</b>, вот что есть:<div class="codeblock"><table class="vb"><thead><tr><td colspan="2" id="196282267"  class="head">Visual Basic</td></tr></thead><tbody><tr class="li1"><td><div id="196282267" style="height: 286px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
</pre></td><td class="de1"><pre class="de1"><span class="kw4">Type</span> tGuiTable
&nbsp; &nbsp; lStructSize &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; uuidObjectGUI &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> uuid
&nbsp; &nbsp; Unknown1 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; Unknown2 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; Unknown3 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; Unknown4 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; lObjectID &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; Unknown5 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; fOLEMisc &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; uuidObject &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> uuid
&nbsp; &nbsp; Unknown6 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; Unknown7 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; aFormPointer &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; Unknown8 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw3">End</span> <span class="kw4">Type</span></pre></td></tr></table></div></td></tr></tbody></table></div>Как выяснилось внутри рантайма есть код, который проверяет поле <b>Unknown5</b> каждой структуры:<br />
<div align="center"><a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=3039&amp;d=1424108330" rel="Lightbox" id="attachment3039" ><img src="https://www.cyberforum.ru/blog_attachment.php?attachmentid=3039&amp;thumb=1&amp;d=1424108330" class="thumbnail" border="0" alt="Нажмите на изображение для увеличения
Название: 1.png
Просмотров: 876
Размер:	10.0 Кб
ID:	3039" style="margin: 5px" /></a></div>Я проставил комментарии; из них видно что <b>Unknown5</b> содержит флаги и если установлен 5-й бит, то происходит запись ссылки на какой-то объект, заданный регистром EAX, в поле со смещением 0x30 объекта заданного регистром EDX. Что за объекты - я не знаю, возможно позже разберусь с этим, нам важен сам факт записи какого-то значения в поле со смещением 0x30. Теперь, если дальше начать исследовать код то можно наткнутся на такой фрагмент:<br />
<div align="center"><a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=3040&amp;d=1424111038" rel="Lightbox" id="attachment3040" ><img src="https://www.cyberforum.ru/blog_attachment.php?attachmentid=3040&amp;thumb=1&amp;d=1424111038" class="thumbnail" border="0" alt="Нажмите на изображение для увеличения
Название: 2.png
Просмотров: 826
Размер:	17.9 Кб
ID:	3040" style="margin: 5px" /></a></div>Скажу что объект на который указывает ESI, тот же самый объект что в предыдущей рассматриваемой процедуре (регистр EDX). Видно что тестируется значение этого поля на -1 и на 0, и если там любое из этих чисел то запускается процедура <b>Main</b> (если она задана); иначе запускается первая форма.<br />
Итак, теперь чтобы гарантированно запускать только <b>Sub Main</b>, мы изменяем флаг <b>lpGuiTable.Unknown5</b>, сбрасывая пятый бит. Для установки новой <b>Sub Main</b> и модификации флага я создал отдельную процедуру:<br />
<div class="codeblock"><table class="vb"><thead><tr><td colspan="2" id="799841841"  class="head">Visual Basic</td></tr></thead><tbody><tr class="li1"><td><div id="799841841" style="height: 350px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
</pre></td><td class="de1"><pre class="de1"><span class="co1">' // Modify VBHeader to replace Sub Main</span>
<span class="kw2">Private</span> <span class="kw2">Sub</span> ModifyVBHeader(<span class="kw4">ByVal</span> newAddress <span class="kw4">As</span> <span class="kw1">Long</span>)
&nbsp; &nbsp; <span class="kw4">Dim</span> ptr &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> old &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> flag &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> count &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> size &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; ptr = lpVBHeader + &amp;H2C
&nbsp; &nbsp; <span class="co1">' Are allowed to write in the page</span>
 &nbsp; &nbsp;VirtualProtect <span class="kw4">ByVal</span> ptr, 4, PAGE_READWRITE, old
&nbsp; &nbsp; <span class="co1">' Set a new address of Sub Main</span>
 &nbsp; &nbsp;GetMem4 newAddress, <span class="kw4">ByVal</span> ptr
&nbsp; &nbsp; VirtualProtect <span class="kw4">ByVal</span> ptr, 4, old, 0
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="co1">' Remove startup form</span>
 &nbsp; &nbsp;GetMem4 <span class="kw4">ByVal</span> lpVBHeader + &amp;H4C, ptr
&nbsp; &nbsp; <span class="co1">' Get forms count</span>
 &nbsp; &nbsp;GetMem4 <span class="kw4">ByVal</span> lpVBHeader + &amp;H44, count
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">Do</span> <span class="kw3">While</span> count &gt; 0
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' Get structure size</span>
 &nbsp; &nbsp; &nbsp; &nbsp;GetMem4 <span class="kw4">ByVal</span> ptr, size
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' Get flag (unknown5) from current form</span>
 &nbsp; &nbsp; &nbsp; &nbsp;GetMem4 <span class="kw4">ByVal</span> ptr + &amp;H28, flag
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' When set, bit 5,</span>
 &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw3">If</span> flag <span class="kw3">And</span> &amp;H10 <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' Unset bit 5</span>
 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;flag = flag <span class="kw3">And</span> &amp;HFFFFFFEF
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' Are allowed to write in the page</span>
 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;VirtualProtect <span class="kw4">ByVal</span> ptr, 4, PAGE_READWRITE, old
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' Write changet flag</span>
 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;GetMem4 flag, <span class="kw4">ByVal</span> ptr + &amp;H28
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' Restoring the memory attributes</span>
 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;VirtualProtect <span class="kw4">ByVal</span> ptr, 4, old, 0
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; &nbsp; &nbsp; count = count - 1
&nbsp; &nbsp; &nbsp; &nbsp; ptr = ptr + size
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">Loop</span>
&nbsp; &nbsp; 
<span class="kw3">End</span> <span class="kw2">Sub</span></pre></td></tr></table></div></td></tr></tbody></table></div>Теперь, если попробовать запустить эту процедуру перед передачей хидера в <b>VBDllGetClassObject</b>, то будет запускаться процедура, определенная нами. Впрочем многопоточность уже будет работать, но это не удобно, т.к. отсутствует механизм передачи параметра в поток как это реализовано в <b>CreateThread</b>. Для того чтобы сделать полный аналог <b>CreateThread</b> я решил создать аналогичную функцию, которая будет проводить все инициализации и после выполнять вызов переданной функции потока вместе с параметром. Для того чтобы была возможность передать параметр в <b>Sub Main</b>, я использовал локальное хранилище потока (<b>TLS</b>). Мы выделяем индекс для TLS. После выделения индекса мы сможем задавать значение этого индекса, специфичное для каждого потока. В общем идея такова, создаем новый поток, где стартовой функцией будет специальная функция <b>ThreadProc</b>, в параметр которой передаем структуру из двух полей - адреса пользовательской функции и адреса параметра. В этой процедуре мы будем инициализировать рантайм для нового потока и сохранять в TLS переданный параметр. В качестве процедуры <b>Main</b> создадим бинарный код, который будет доставать данные из TLS, формировать стек и прыгать на пользовательскую функцию. В итоге получился такой модуль:<br />
<i>modMultiThreading.bas</i><div class="codeblock"><table class="vb"><thead><tr><td colspan="2" id="270464959"  class="head">Visual Basic</td></tr></thead><tbody><tr class="li1"><td><div id="270464959" style="height: 350px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
</pre></td><td class="de1"><pre class="de1"><span class="co1">' modMultiThreading.bas - The module provides support for multi-threading.</span>
<span class="co1">' © Кривоус Анатолий Анатольевич (The trick), 2015</span>
&nbsp;
<span class="kw2">Option</span> <span class="kw2">Explicit</span>
&nbsp;
<span class="kw2">Private</span> <span class="kw4">Type</span> uuid
&nbsp; &nbsp; data1 &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; data2 &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Integer</span>
&nbsp; &nbsp; data3 &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Integer</span>
&nbsp; &nbsp; data4(7) &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Byte</span>
<span class="kw3">End</span> <span class="kw4">Type</span>
&nbsp;
<span class="kw2">Private</span> <span class="kw4">Type</span> threadData
&nbsp; &nbsp; lpParameter <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; lpAddress &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw3">End</span> <span class="kw4">Type</span>
&nbsp;
<span class="kw2">Private</span> tlsIndex &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span> &nbsp;<span class="co1">' Index of the item in the TLS. There will be data specific to the thread.</span>
<span class="kw2">Private</span> lpVBHeader &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span> &nbsp;<span class="co1">' Pointer to VBHeader structure.</span>
<span class="kw2">Private</span> hModule &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span> &nbsp;<span class="co1">' Base address.</span>
<span class="kw2">Private</span> lpAsm &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span> &nbsp;<span class="co1">' Pointer to a binary code.</span>
&nbsp;
<span class="co1">' // Create a new thread</span>
<span class="kw2">Public</span> <span class="kw2">Function</span> vbCreateThread(<span class="kw4">ByVal</span> lpThreadAttributes <span class="kw4">As</span> <span class="kw1">Long</span>, _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">ByVal</span> dwStackSize <span class="kw4">As</span> <span class="kw1">Long</span>, _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">ByVal</span> lpStartAddress <span class="kw4">As</span> <span class="kw1">Long</span>, _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">ByVal</span> lpParameter <span class="kw4">As</span> <span class="kw1">Long</span>, _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">ByVal</span> dwCreationFlags <span class="kw4">As</span> <span class="kw1">Long</span>, _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;lpThreadId <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> InIDE &nbsp; <span class="kw4">As</span> <span class="kw1">Boolean</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; Debug.Assert MakeTrue(InIDE)
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> InIDE <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">Dim</span> ret <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; ret = MsgBox(<span class="st0">&quot;Multithreading not working in IDE.&quot;</span> &amp; vbNewLine &amp; <span class="st0">&quot;Run it in the same thread?&quot;</span>, vbQuestion <span class="kw3">Or</span> vbYesNo)
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> ret = vbYes <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' Run function in main thread</span>
 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;ret = DispCallFunc(<span class="kw4">ByVal</span> 0&amp;, lpStartAddress, CC_STDCALL, vbEmpty, 1, vbLong, VarPtr(CVar(lpParameter)), CVar(0))
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> ret <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Err.Raise ret
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="co1">' Alloc new index from thread local storage</span>
 &nbsp; &nbsp;<span class="kw3">If</span> tlsIndex = 0 <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; tlsIndex = TlsAlloc()
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> tlsIndex = 0 <span class="kw3">Then</span> <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; <span class="co1">' Get module handle</span>
 &nbsp; &nbsp;<span class="kw3">If</span> hModule = 0 <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; hModule = GetModuleHandle(<span class="kw4">ByVal</span> 0&amp;)
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; <span class="co1">' Create assembler code</span>
 &nbsp; &nbsp;<span class="kw3">If</span> lpAsm = 0 <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; lpAsm = CreateAsm()
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> lpAsm = 0 <span class="kw3">Then</span> <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; <span class="co1">' Get pointer to VBHeader and modify</span>
 &nbsp; &nbsp;<span class="kw3">If</span> lpVBHeader = 0 <span class="kw3">Then</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; lpVBHeader = GetVBHeader()
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> lpVBHeader = 0 <span class="kw3">Then</span> <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; ModifyVBHeader lpAsm
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw4">Dim</span> lpThreadData &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> tmpData &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> threadData
&nbsp; &nbsp; <span class="co1">' Alloc thread-specific memory for threadData structure</span>
 &nbsp; &nbsp;lpThreadData = HeapAlloc(GetProcessHeap(), 0, Len(tmpData))
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> lpThreadData = 0 <span class="kw3">Then</span> <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; <span class="co1">' Set parameters</span>
 &nbsp; &nbsp;tmpData.lpAddress = lpStartAddress
&nbsp; &nbsp; tmpData.lpParameter = lpParameter
&nbsp; &nbsp; <span class="co1">' Copy parameters to thread-specific memory</span>
 &nbsp; &nbsp;GetMem8 tmpData, <span class="kw4">ByVal</span> lpThreadData
&nbsp; &nbsp; <span class="co1">' Create thread</span>
 &nbsp; &nbsp;vbCreateThread = CreateThread(<span class="kw4">ByVal</span> lpThreadAttributes, _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; dwStackSize, _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; AddressOf ThreadProc, _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">ByVal</span> lpThreadData, _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; dwCreationFlags, _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; lpThreadId)
&nbsp; &nbsp; 
<span class="kw3">End</span> <span class="kw2">Function</span>
&nbsp;
<span class="co1">' // Initialize runtime for new thread and run procedure</span>
<span class="kw2">Private</span> <span class="kw2">Function</span> ThreadProc(lpParameter <span class="kw4">As</span> threadData) <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> iid &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> uuid
&nbsp; &nbsp; <span class="kw4">Dim</span> clsid &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> uuid
&nbsp; &nbsp; <span class="kw4">Dim</span> lpNewHdr &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> hHeap &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="co1">' Initialize COM</span>
 &nbsp; &nbsp;vbCoInitialize <span class="kw4">ByVal</span> 0&amp;
&nbsp; &nbsp; <span class="co1">' IID_IUnknown</span>
 &nbsp; &nbsp;iid.data4(0) = &amp;HC0: iid.data4(7) = &amp;H46
&nbsp; &nbsp; <span class="co1">' Store parameter to thread local storage</span>
 &nbsp; &nbsp;TlsSetValue tlsIndex, lpParameter
&nbsp; &nbsp; <span class="co1">' Create the copy of VBHeader</span>
 &nbsp; &nbsp;hHeap = GetProcessHeap()
&nbsp; &nbsp; lpNewHdr = HeapAlloc(hHeap, 0, &amp;H6A)
&nbsp; &nbsp; CopyMemory <span class="kw4">ByVal</span> lpNewHdr, <span class="kw4">ByVal</span> lpVBHeader, &amp;H6A
&nbsp; &nbsp; <span class="co1">' Adjust offsets</span>
 &nbsp; &nbsp;<span class="kw4">Dim</span> names() &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> diff &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> Index &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw4">ReDim</span> names(3)
&nbsp; &nbsp; diff = lpNewHdr - lpVBHeader
&nbsp; &nbsp; CopyMemory names(0), <span class="kw4">ByVal</span> lpVBHeader + &amp;H58, &amp;H10
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">For</span> Index = 0 <span class="kw3">To</span> 3
&nbsp; &nbsp; &nbsp; &nbsp; names(Index) = names(Index) - diff
&nbsp; &nbsp; <span class="kw3">Next</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; CopyMemory <span class="kw4">ByVal</span> lpNewHdr + &amp;H58, names(0), &amp;H10
&nbsp; &nbsp; <span class="co1">' This line calls the binary code that runs the asm function.</span>
 &nbsp; &nbsp;VBDllGetClassObject VarPtr(hModule), 0, lpNewHdr, clsid, iid, 0
&nbsp; &nbsp; <span class="co1">' Free memeory</span>
 &nbsp; &nbsp;HeapFree hHeap, 0, <span class="kw4">ByVal</span> lpNewHdr
&nbsp; &nbsp; HeapFree hHeap, 0, lpParameter
&nbsp; &nbsp; 
<span class="kw3">End</span> <span class="kw2">Function</span>
&nbsp;
<span class="co1">' // Get VBHeader structure</span>
<span class="kw2">Private</span> <span class="kw2">Function</span> GetVBHeader() <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> ptr &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp;
&nbsp; &nbsp; <span class="co1">' Get e_lfanew</span>
 &nbsp; &nbsp;GetMem4 <span class="kw4">ByVal</span> hModule + &amp;H3C, ptr
&nbsp; &nbsp; <span class="co1">' Get AddressOfEntryPoint</span>
 &nbsp; &nbsp;GetMem4 <span class="kw4">ByVal</span> ptr + &amp;H28 + hModule, ptr
&nbsp; &nbsp; <span class="co1">' Get VBHeader</span>
 &nbsp; &nbsp;GetMem4 <span class="kw4">ByVal</span> ptr + hModule + 1, GetVBHeader
&nbsp; &nbsp; 
<span class="kw3">End</span> <span class="kw2">Function</span>
&nbsp;
<span class="co1">' // Modify VBHeader to replace Sub Main</span>
<span class="kw2">Private</span> <span class="kw2">Sub</span> ModifyVBHeader(<span class="kw4">ByVal</span> newAddress <span class="kw4">As</span> <span class="kw1">Long</span>)
&nbsp; &nbsp; <span class="kw4">Dim</span> ptr &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> old &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> flag &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> count &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> size &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; ptr = lpVBHeader + &amp;H2C
&nbsp; &nbsp; <span class="co1">' Are allowed to write in the page</span>
 &nbsp; &nbsp;VirtualProtect <span class="kw4">ByVal</span> ptr, 4, PAGE_READWRITE, old
&nbsp; &nbsp; <span class="co1">' Set a new address of Sub Main</span>
 &nbsp; &nbsp;GetMem4 newAddress, <span class="kw4">ByVal</span> ptr
&nbsp; &nbsp; VirtualProtect <span class="kw4">ByVal</span> ptr, 4, old, 0
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="co1">' Remove startup form</span>
 &nbsp; &nbsp;GetMem4 <span class="kw4">ByVal</span> lpVBHeader + &amp;H4C, ptr
&nbsp; &nbsp; <span class="co1">' Get forms count</span>
 &nbsp; &nbsp;GetMem2 <span class="kw4">ByVal</span> lpVBHeader + &amp;H44, count
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">Do</span> <span class="kw3">While</span> count &gt; 0
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' Get structure size</span>
 &nbsp; &nbsp; &nbsp; &nbsp;GetMem4 <span class="kw4">ByVal</span> ptr, size
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' Get flag (unknown5) from current form</span>
 &nbsp; &nbsp; &nbsp; &nbsp;GetMem4 <span class="kw4">ByVal</span> ptr + &amp;H28, flag
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' When set, bit 5,</span>
 &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw3">If</span> flag <span class="kw3">And</span> &amp;H10 <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' Unset bit 5</span>
 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;flag = flag <span class="kw3">And</span> &amp;HFFFFFFEF
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' Are allowed to write in the page</span>
 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;VirtualProtect <span class="kw4">ByVal</span> ptr, 4, PAGE_READWRITE, old
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' Write changet flag</span>
 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;GetMem4 flag, <span class="kw4">ByVal</span> ptr + &amp;H28
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' Restoring the memory attributes</span>
 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;VirtualProtect <span class="kw4">ByVal</span> ptr, 4, old, 0
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; count = count - 1
&nbsp; &nbsp; &nbsp; &nbsp; ptr = ptr + size
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">Loop</span>
&nbsp; &nbsp; 
<span class="kw3">End</span> <span class="kw2">Sub</span>
&nbsp;
<span class="co1">' // Create binary code.</span>
<span class="kw2">Private</span> <span class="kw2">Function</span> CreateAsm() <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> hMod &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> lpProc &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> ptr &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; hMod = GetModuleHandle(<span class="kw4">ByVal</span> StrPtr(<span class="st0">&quot;kernel32&quot;</span>))
&nbsp; &nbsp; lpProc = GetProcAddress(hMod, <span class="st0">&quot;TlsGetValue&quot;</span>)
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> lpProc = 0 <span class="kw3">Then</span> <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; ptr = VirtualAlloc(<span class="kw4">ByVal</span> 0&amp;, &amp;HF, MEM_RESERVE <span class="kw3">Or</span> MEM_COMMIT, PAGE_EXECUTE_READWRITE)
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> ptr = 0 <span class="kw3">Then</span> <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="co1">' push &nbsp;tlsIndex</span>
 &nbsp; &nbsp;<span class="co1">' call &nbsp;TLSGetValue</span>
 &nbsp; &nbsp;<span class="co1">' pop &nbsp; ecx</span>
 &nbsp; &nbsp;<span class="co1">' push &nbsp;DWORD [eax]</span>
 &nbsp; &nbsp;<span class="co1">' push &nbsp;ecx</span>
 &nbsp; &nbsp;<span class="co1">' jmp &nbsp; DWORD [eax + 4]</span>
 &nbsp; &nbsp;
&nbsp; &nbsp; GetMem4 &amp;H68, <span class="kw4">ByVal</span> ptr + &amp;H0: &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;GetMem4 &amp;HE800, <span class="kw4">ByVal</span> ptr + &amp;H4
&nbsp; &nbsp; GetMem4 &amp;HFF590000, <span class="kw4">ByVal</span> ptr + &amp;H8: &nbsp; &nbsp;GetMem4 &amp;H60FF5130, <span class="kw4">ByVal</span> ptr + &amp;HC
&nbsp; &nbsp; GetMem4 &amp;H4, <span class="kw4">ByVal</span> ptr + &amp;H10: &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;GetMem4 tlsIndex, <span class="kw4">ByVal</span> ptr + 1
&nbsp; &nbsp; GetMem4 lpProc - ptr - 10, <span class="kw4">ByVal</span> ptr + 6
&nbsp; &nbsp; 
&nbsp; &nbsp; CreateAsm = ptr
&nbsp; &nbsp; 
<span class="kw3">End</span> <span class="kw2">Function</span>
&nbsp;
<span class="kw2">Private</span> <span class="kw2">Function</span> MakeTrue(value <span class="kw4">As</span> <span class="kw1">Boolean</span>) <span class="kw4">As</span> <span class="kw1">Boolean</span>
&nbsp; &nbsp; MakeTrue = <span class="kw5">True</span>: value = <span class="kw5">True</span>
<span class="kw3">End</span> <span class="kw2">Function</span></pre></td></tr></table></div></td></tr></tbody></table></div>Все API декларации я сделал в отдельной библиотеке типов - <b>EXEInitialize.tlb</b>. Пока найден один недостаток - не работают формы с приватными контролами, если разберусь в чем причина - исправлю. Работает только в скомпилированном варианте.<br />
В архиве содержится несколько тестов.<br />
1-й: создание формы в новом потоке, с возможностью блокировки ввода посредством длинного цикла.<br />
2-й: обработка событий от объекта, метод которого вызван в другом потоке. Сразу скажу так делать нельзя и неправильно, т.к. передавать между потоками ссылку без маршаллинга опасно и может привести к глюкам, к тому же обработка события выполняется в другом потоке. Этот пример я оставил в качестве <u>демонстрации работы многопоточности</u>, а не для использования в повседневных задачах.<br />
3-й: демонстрация изменения значения общей переменной в одном потоке и считывание его из другого.<br />
<br />
<i><font color="Red">Update: 27.05.2015</font></i><br />
В IDE запуск осуществляется в главном потоке по желанию.<br />
Добавлен 4-й тест - получение списка простых чисел в отдельном потоке.<br />
<br />
<font color="Red"><i>Update: 01.06.2015</i></font><br />
Добавлена нативная DLL которая экспортирует vbCreateThread.<br />
Внесены изменения в код чтобы поддерживать эту возможность.<br />
<br />
<a rel="nofollow noopener noreferrer" href="https://yadi.sk/d/F0o3et53guX6Q" target="_blank" title="https://yadi.sk/d/F0o3et53guX6Q">Скачать материалы.</a><br />
Всем удачи!</div>

]]></content:encoded>
			<dc:creator>The trick</dc:creator>
			<guid isPermaLink="true">https://www.cyberforum.ru/blogs/354370/3262.html</guid>
		</item>
		<item>
			<title>3D елка на рабочий стол</title>
			<link>https://www.cyberforum.ru/blogs/354370/3119.html</link>
			<pubDate>Sun, 04 Jan 2015 13:33:54 GMT</pubDate>
			<description>OZVW30NTySI 
Я как-то уже делал (https://www.cyberforum.ru/blogs/354370/blog1867.html) такую, но в...</description>
			<content:encoded><![CDATA[<div><div align="center"><iframe width="640" height="360" src="https://www.youtube.com/embed/OZVW30NTySI" frameborder="0" allowfullscreen></iframe></div>Я как-то уже <a href="https://www.cyberforum.ru/blogs/354370/blog1867.html">делал</a> такую, но в этот раз я добавил возможность регулировки параметров создания.<br />
Для работы нужна dx8vb.dll (внутри архива, положить в папку с программой). Выход по двойному клику. С новым годом!<br />
<br />
<i><font color="Gray">10.02.2015 - исправлена ошибка начального положения окна.</font></i></div>


<!-- attachments -->
	<div style="margin-top:10px">

		
			<fieldset class="fieldset">
				<legend>Миниатюры</legend>
				<div style="padding:3px">
				
	<a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=2951&amp;d=1420378392" target="attachment" rel="Lightbox" id="attachment2951"><img loading="lazy" decoding="async" class="thumbnail" src="https://www.cyberforum.ru/blog_attachment.php?attachmentid=2951&amp;stc=1&amp;thumb=1&amp;d=1420378392" border="0" alt="Нажмите на изображение для увеличения
Название: Безымянный.png
Просмотров: 879
Размер:	452.8 Кб
ID:	2951" /></a>
	&nbsp;
	

				</div>
			</fieldset>
		
		
		
		
			<fieldset class="fieldset">
				<legend>Вложения</legend>
				<table cellpadding="0" cellspacing="3" border="0">
				<tr>
	<td><img loading="lazy" decoding="async" class="inlineimg" src="http://www.cyberforum.ru//cyberstatic.net/images/attach/rar.gif" alt="Тип файла: rar" width="16" height="16" border="0" style="vertical-align:baseline" /></td>
	<td><a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=3028&amp;d=1423588086">3DFirTree.rar</a> (445.4 Кб, 635 просмотров)</td>
</tr>
				</table>
			</fieldset>
		

	</div>
<!-- / attachments -->
]]></content:encoded>
			<dc:creator>The trick</dc:creator>
			<guid isPermaLink="true">https://www.cyberforum.ru/blogs/354370/3119.html</guid>
		</item>
		<item>
			<title>Вокодер на VB6 часть 2</title>
			<link>https://www.cyberforum.ru/blogs/354370/3009.html</link>
			<pubDate>Mon, 01 Dec 2014 16:09:18 GMT</pubDate>
			<description>Первая часть. (https://www.cyberforum.ru/blogs/354370/blog2998.html) 
Итак, исходный сигнал и...</description>
			<content:encoded><![CDATA[<div><a href="https://www.cyberforum.ru/blogs/354370/blog2998.html">Первая часть.</a><br />
Итак, исходный сигнал и модулирующий мы имеем. Теперь следующим этапом является фильтрация. Можно пойти несколькими путями: использовать банк фильтров (БИХ, КИХ), либо использовать БПФ (FFT, быстрое преобразование Фурье) или Вейвлет-преобразование. Для своей задачи возьмем оконное БПФ, т.к. расчет БИХ фильтров довольно сложная задача, а КИХ фильтры по вычислительной сложности не очень эффективны. (Честно говоря, изначально я сделал реализацию на БИХ фильтрах Баттерворта 2-го порядка, но меня не устраивало качество и нагрузка на процессор).  С БПФ получается все довольно просто. Раскладываем речевой сигнал на гармоники где каждый элемент вектора представляет информацию об определенной частоте (получается что-то вроде большого количества полосовых фильтров). Также раскладываем несущий сигнал и выполняем модуляцию. После всего делаем обратное преобразование и получаем нужный сигнал. Получается что БПФ делает сразу 2 задачи - это раскладывает сигнал на полосы частот (см. схему) и выполняет микширование сигнала после ОБПФ. Для нашей задачи сделаем регулировку количества частотных полос, это позволит настроить нужную окраску тембра. Для БПФ и его обвязки напишем класс <b>clsTrickFFT</b>:<br />
<div class="codeblock"><table class="vb"><thead><tr><td colspan="2" id="153648567"  class="head">Visual Basic</td></tr></thead><tbody><tr class="li1"><td><div id="153648567" style="height: 350px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
</pre></td><td class="de1"><pre class="de1"><span class="co1">' clsTrickFFT &nbsp;- класс для быстрого преобразования Фурье</span>
<span class="co1">' © Кривоус Анатолий Анатольевич (The trick), 2014</span>
&nbsp;
<span class="kw2">Option</span> <span class="kw2">Explicit</span>
&nbsp;
<span class="kw2">Public</span> <span class="kw1">Enum</span> WindowType
&nbsp; &nbsp; WT_RECTANGLE
&nbsp; &nbsp; WT_TRIGANULAR
&nbsp; &nbsp; WT_HAMMING
&nbsp; &nbsp; WT_HANN
<span class="kw3">End</span> <span class="kw1">Enum</span>
&nbsp;
<span class="kw2">Private</span> Coef(1, 13) <span class="kw4">As</span> <span class="kw1">Single</span>
<span class="kw2">Private</span> mFFTSize &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw2">Private</span> mLog &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw2">Private</span> mWindow() &nbsp; <span class="kw4">As</span> <span class="kw1">Single</span>
<span class="kw2">Private</span> mType &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> WindowType
&nbsp;
<span class="co1">' // Тип окна</span>
<span class="kw2">Public</span> <span class="kw4">Property</span> <span class="kw4">Get</span> WindowType() <span class="kw4">As</span> WindowType
&nbsp; &nbsp; WindowType = mType
<span class="kw3">End</span> <span class="kw4">Property</span>
<span class="kw2">Public</span> <span class="kw4">Property</span> <span class="kw4">Let</span> WindowType(<span class="kw4">ByVal</span> Value <span class="kw4">As</span> WindowType)
&nbsp;
&nbsp; &nbsp; <span class="kw3">If</span> InitWindow(Value) <span class="kw3">Then</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; mType = Value
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; 
<span class="kw3">End</span> <span class="kw4">Property</span>
&nbsp;
<span class="co1">' // Задает размер FFT</span>
<span class="kw2">Public</span> <span class="kw4">Property</span> <span class="kw4">Let</span> FFTSize(<span class="kw4">ByVal</span> Value <span class="kw4">As</span> <span class="kw1">Long</span>)
&nbsp; &nbsp; <span class="kw4">Dim</span> log2 &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Double</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; log2 = Log(Value) / Log(2)
&nbsp; &nbsp; <span class="co1">' Число должно быть степенью 2-ки</span>
 &nbsp; &nbsp;<span class="kw3">If</span> log2 &lt;&gt; Fix(log2) <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; err.Raise 5
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">Exit</span> <span class="kw4">Property</span>
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; <span class="co1">' Проверяем выход за пределы</span>
 &nbsp; &nbsp;<span class="kw3">If</span> log2 &lt; 2 <span class="kw3">Or</span> log2 &gt; 16384 <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; err.Raise 9
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">Exit</span> <span class="kw4">Property</span>
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; InitWindow mType
&nbsp; &nbsp; 
&nbsp; &nbsp; mLog = log2
&nbsp; &nbsp; mFFTSize = Value
&nbsp; &nbsp; 
<span class="kw3">End</span> <span class="kw4">Property</span>
&nbsp;
<span class="co1">' // Применить оконную функцию</span>
<span class="kw2">Public</span> <span class="kw2">Function</span> ApplyWindow(data() <span class="kw4">As</span> <span class="kw1">Single</span>) <span class="kw4">As</span> <span class="kw1">Boolean</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> index &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> count &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; count = <span class="kw4">UBound</span>(data, 2) + 1
&nbsp;
&nbsp; &nbsp; <span class="kw3">For</span> index = 0 <span class="kw3">To</span> count - 1
&nbsp; &nbsp; &nbsp; &nbsp; data(0, index) = data(0, index) * mWindow(index)
&nbsp; &nbsp; <span class="kw3">Next</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; ApplyWindow = <span class="kw5">True</span>
&nbsp; &nbsp; 
<span class="kw3">End</span> <span class="kw2">Function</span>
&nbsp;
<span class="co1">' // Конвертировать 16-битные отсчеты в нормализованные комплексные значения</span>
<span class="kw2">Public</span> <span class="kw2">Function</span> Convert16BitToComplex(inData() <span class="kw4">As</span> <span class="kw1">Integer</span>, outData() <span class="kw4">As</span> <span class="kw1">Single</span>) <span class="kw4">As</span> <span class="kw1">Boolean</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> index &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp;
&nbsp; &nbsp; <span class="kw3">For</span> index = 0 <span class="kw3">To</span> <span class="kw4">UBound</span>(inData)
&nbsp; &nbsp; &nbsp; &nbsp; outData(0, index) = inData(index) / 32768
&nbsp; &nbsp; &nbsp; &nbsp; outData(1, index) = 0
&nbsp; &nbsp; <span class="kw3">Next</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; Convert16BitToComplex = <span class="kw5">True</span>
&nbsp; &nbsp; 
<span class="kw3">End</span> <span class="kw2">Function</span>
&nbsp;
<span class="co1">' // Конвертировать комплексные отсчеты, представляющие реальный сигнал в 16-битные реальные</span>
<span class="kw2">Public</span> <span class="kw2">Function</span> ConvertComplexTo16Bit(inData() <span class="kw4">As</span> <span class="kw1">Single</span>, outData() <span class="kw4">As</span> <span class="kw1">Integer</span>) <span class="kw4">As</span> <span class="kw1">Boolean</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> index &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> Value &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">For</span> index = 0 <span class="kw3">To</span> <span class="kw4">UBound</span>(inData, 2)
&nbsp; &nbsp; &nbsp; &nbsp; Value = inData(0, index) * 32767
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> Value &gt; 32767 <span class="kw3">Then</span> Value = 32767 <span class="kw3">Else</span> <span class="kw3">If</span> Value &lt; -32768 <span class="kw3">Then</span> Value = -32768
&nbsp; &nbsp; &nbsp; &nbsp; outData(index) = Value
&nbsp; &nbsp; <span class="kw3">Next</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; ConvertComplexTo16Bit = <span class="kw5">True</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
<span class="kw3">End</span> <span class="kw2">Function</span>
&nbsp;
<span class="co1">' // Выполняет зеркалирование</span>
<span class="kw2">Public</span> <span class="kw2">Function</span> MakeMirror(data() <span class="kw4">As</span> <span class="kw1">Single</span>) <span class="kw4">As</span> <span class="kw1">Boolean</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> index &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> pointer <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; pointer = mFFTSize - 1
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">For</span> index = 1 <span class="kw3">To</span> mFFTSize \ 2 - 1
&nbsp; &nbsp; &nbsp; &nbsp; data(0, pointer) = data(0, index)
&nbsp; &nbsp; &nbsp; &nbsp; data(1, pointer) = -data(1, index)
&nbsp; &nbsp; &nbsp; &nbsp; pointer = pointer - 1
&nbsp; &nbsp; <span class="kw3">Next</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; MakeMirror = <span class="kw5">True</span>
&nbsp; &nbsp; 
<span class="kw3">End</span> <span class="kw2">Function</span>
&nbsp;
<span class="co1">' // Быстрое преобразование Фурье</span>
<span class="kw2">Public</span> <span class="kw2">Function</span> FFT(data() <span class="kw4">As</span> <span class="kw1">Single</span>, <span class="kw4">ByVal</span> IsInverse <span class="kw4">As</span> <span class="kw1">Boolean</span>) <span class="kw4">As</span> <span class="kw1">Boolean</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> i <span class="kw4">As</span> <span class="kw1">Long</span>, j <span class="kw4">As</span> <span class="kw1">Long</span>, n <span class="kw4">As</span> <span class="kw1">Long</span>, K <span class="kw4">As</span> <span class="kw1">Long</span>, io <span class="kw4">As</span> <span class="kw1">Long</span>, ie <span class="kw4">As</span> <span class="kw1">Long</span>, in_ <span class="kw4">As</span> <span class="kw1">Long</span>, nn <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> ur <span class="kw4">As</span> <span class="kw1">Single</span>, ui <span class="kw4">As</span> <span class="kw1">Single</span>, tpr <span class="kw4">As</span> <span class="kw1">Single</span>, tpi <span class="kw4">As</span> <span class="kw1">Single</span>, tqr <span class="kw4">As</span> <span class="kw1">Single</span>, tqi <span class="kw4">As</span> <span class="kw1">Single</span>, _
&nbsp; &nbsp; &nbsp; &nbsp; wr <span class="kw4">As</span> <span class="kw1">Single</span>, wi <span class="kw4">As</span> <span class="kw1">Single</span>, sr <span class="kw4">As</span> <span class="kw1">Single</span>, ti <span class="kw4">As</span> <span class="kw1">Long</span>, tr <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; nn = mFFTSize \ 2: ie = mFFTSize
&nbsp; &nbsp; <span class="kw3">For</span> n = 1 <span class="kw3">To</span> mLog
&nbsp; &nbsp; &nbsp; &nbsp; wr = Coef(0, mLog - n): wi = Coef(1, mLog - n)
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> IsInverse <span class="kw3">Then</span> wi = -wi
&nbsp; &nbsp; &nbsp; &nbsp; in_ = ie \ 2: ur = 1: ui = 0
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">For</span> j = 0 <span class="kw3">To</span> in_ - 1
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">For</span> i = j <span class="kw3">To</span> mFFTSize - 1 <span class="kw3">Step</span> ie
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; io = i + in_
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; tpr = data(0, i) + data(0, io): tpi = data(1, i) + data(1, io)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; tqr = data(0, i) - data(0, io): tqi = data(1, i) - data(1, io)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; data(0, io) = tqr * ur - tqi * ui: data(1, io) = tqi * ur + tqr * ui
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; data(0, i) = tpr: data(1, i) = tpi
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">Next</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; sr = ur: ur = ur * wr - ui * wi: ui = ui * wr + sr * wi
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">Next</span>
&nbsp; &nbsp; &nbsp; &nbsp; ie = ie \ 2
&nbsp; &nbsp; <span class="kw3">Next</span>
&nbsp; &nbsp; <span class="co1">' Перестановка</span>
 &nbsp; &nbsp;j = 1
&nbsp; &nbsp; <span class="kw3">For</span> i = 1 <span class="kw3">To</span> mFFTSize - 1
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> i &lt; j <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; io = i - 1: in_ = j - 1: tpr = data(0, in_): tpi = data(1, in_)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; data(0, in_) = data(0, io): data(1, in_) = data(1, io)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; data(0, io) = tpr: data(1, io) = tpi
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; &nbsp; &nbsp; K = nn
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">Do</span> <span class="kw3">While</span> K &lt; j
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; j = j - K: K = K \ 2
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">Loop</span>
&nbsp; &nbsp; &nbsp; &nbsp; j = j + K
&nbsp; &nbsp; <span class="kw3">Next</span>
&nbsp; &nbsp; <span class="kw3">If</span> IsInverse <span class="kw3">Then</span> FFT = <span class="kw5">True</span>: <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; <span class="co1">' Нормализация</span>
 &nbsp; &nbsp;wr = 1 / mFFTSize
&nbsp; &nbsp; <span class="kw3">For</span> i = 0 <span class="kw3">To</span> mFFTSize - 1
&nbsp; &nbsp; &nbsp; &nbsp; data(0, i) = data(0, i) * wr: data(1, i) = data(1, i) * wr
&nbsp; &nbsp; <span class="kw3">Next</span>
&nbsp; &nbsp; FFT = <span class="kw5">True</span>
&nbsp; &nbsp; 
<span class="kw3">End</span> <span class="kw2">Function</span>
&nbsp;
<span class="co1">' // Инициализация окна</span>
<span class="kw2">Public</span> <span class="kw2">Function</span> InitWindow(<span class="kw4">ByVal</span> Window <span class="kw4">As</span> WindowType) <span class="kw4">As</span> <span class="kw1">Boolean</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> index &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">Select</span> <span class="kw3">Case</span> Window
&nbsp; &nbsp; <span class="kw3">Case</span> WT_RECTANGLE
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">ReDim</span> mWindow(mFFTSize - 1)
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">For</span> index = 0 <span class="kw3">To</span> mFFTSize - 1
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; mWindow(index) = 1
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">Next</span>
&nbsp; &nbsp; <span class="kw3">Case</span> WT_TRIGANULAR
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">ReDim</span> mWindow(mFFTSize - 1)
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">For</span> index = 0 <span class="kw3">To</span> mFFTSize - 1
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; mWindow(index) = IIf(index &lt; mFFTSize \ 2, index / mFFTSize * 2, 1 - index / (mFFTSize - 1))
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">Next</span>
&nbsp; &nbsp; <span class="kw3">Case</span> WT_HAMMING
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">ReDim</span> mWindow(mFFTSize - 1)
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">For</span> index = 0 <span class="kw3">To</span> mFFTSize - 1
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; mWindow(index) = 0.53836 - 0.46164 * Cos(6.28318530717959 * index / (mFFTSize - 1))
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">Next</span>
&nbsp; &nbsp; <span class="kw3">Case</span> WT_HANN
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">ReDim</span> mWindow(mFFTSize - 1)
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">For</span> index = 0 <span class="kw3">To</span> mFFTSize - 1
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; mWindow(index) = 0.5 * (1 - Cos(6.28318530717959 * index / (mFFTSize - 1)))
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">Next</span>
&nbsp; &nbsp; <span class="kw3">Case</span> <span class="kw3">Else</span>
&nbsp; &nbsp; &nbsp; &nbsp; err.Raise 5
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">Select</span>
&nbsp;
&nbsp; &nbsp; InitWindow = <span class="kw5">True</span>
&nbsp; &nbsp; 
<span class="kw3">End</span> <span class="kw2">Function</span>
&nbsp;
<span class="co1">' // Инициализация поворотных множителей для FFT и размера по умолчанию</span>
<span class="kw2">Private</span> <span class="kw2">Sub</span> Class_Initialize()
&nbsp; &nbsp; <span class="kw4">Dim</span> n <span class="kw4">As</span> <span class="kw1">Long</span>, vRcoef <span class="kw4">As</span> <span class="kw1">Variant</span>, vIcoef <span class="kw4">As</span> <span class="kw1">Variant</span>
&nbsp; &nbsp; vRcoef = Array(-1#, 0#, 0.707106781186547 _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; , 0.923879532511287, 0.98078528040323, 0.995184726672197 _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; , 0.998795456205172, 0.999698818696204, 0.999924701839145 _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; , 0.999981175282601, 0.999995293809576, 0.999998823451702 _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; , 0.999999705862882, 0.999999926465718)
&nbsp; &nbsp; vIcoef = Array(0#, -1#, -0.707106781186547 _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;, -0.38268343236509, -0.195090322016128, -9.80171403295606E-02 _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;, -0.049067674327418, -2.45412285229122E-02, -1.22715382857199E-02 _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;, -6.1358846491544E-03, -3.0679567629659E-03, -1.5339801862847E-03 _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;, -7.669903187427E-04, -3.834951875714E-04)
&nbsp; &nbsp; <span class="kw3">For</span> n = 0 <span class="kw3">To</span> 13
&nbsp; &nbsp; &nbsp; &nbsp; Coef(0, n) = vRcoef(n): Coef(1, n) = vIcoef(n)
&nbsp; &nbsp; <span class="kw3">Next</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; mFFTSize = 512
&nbsp; &nbsp; mLog = 9
&nbsp; &nbsp; mType = WT_HAMMING
&nbsp; &nbsp; InitWindow mType
&nbsp; &nbsp; 
<span class="kw3">End</span> <span class="kw2">Sub</span></pre></td></tr></table></div></td></tr></tbody></table></div>Само преобразование выполняет метод <b>FFT</b>; для обратного преобразования вторым параметром передается <b>True</b>. В качестве комплексных чисел будем использовать массив вида <b>arr(1, x)</b>, где <b>x</b> - количество комплексных, чисел <b>arr(0, x)</b> - реальная часть, <b><b>arr(1, x)</b></b> - мнимая часть. Подробно останавливаться на ПФ я не буду, т.к. это очень большая тема, и кому интересно в сети есть много статей где доступным языком объясняется его смысл и свойства; рассмотрим только основные моменты. Для преобразования нужно исходный действительный сигнал загнать в массив комплексных чисел, обнуляя мнимую часть (по правде говоря исходя из свойств ПФ можно еще ускорить если записать в реальную часть одну часть а в мнимую другую, но я не стал так усложнять). После преобразования получим набор комплексных коэффициентов где реальной части соответствуют коэффициенты перед косинусом, а в мнимой перед синусом. Если представить это на комплексной плоскости, то каждый коэффициент представляет собой вектор, длина которого характеризует амплитуду сигнала на этой частоте, а угол - фазу:<br />
<div align="center"><a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=2898&amp;d=1417438226" rel="Lightbox" id="attachment2898" ><img src="https://www.cyberforum.ru/blog_attachment.php?attachmentid=2898&amp;thumb=1&amp;d=1417438226" class="thumbnail" border="0" alt="Нажмите на изображение для увеличения
Название: Project024.gif
Просмотров: 650
Размер:	120.3 Кб
ID:	2898" style="margin: 5px" /></a></div>Также имеет место зеркальный эффект (муар)- зеркальное отображение коэффициентов относительно половины частоты дискретизации, который равен по амплитуде и противоположен по фазе. Это происходит из-за дискретизации сигнала, т.к. частоты могут корректно представлены только до половины частоты дискретизации при увеличении частоты происходит алиасинг:<br />
<div align="center"><a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=2900&amp;d=1417438366" rel="Lightbox" id="attachment2900" ><img src="https://www.cyberforum.ru/blog_attachment.php?attachmentid=2900&amp;thumb=1&amp;d=1417438366" class="thumbnail" border="0" alt="Нажмите на изображение для увеличения
Название: Project026.gif
Просмотров: 690
Размер:	950.5 Кб
ID:	2900" style="margin: 5px" /></a></div>Как видно красная синусоида изначально имеет частоту равную 2 периодам дискретизации, и постепенно период дискретизации увеличивается, частота дискретизированного сигнала уменьшается и в итоге при частоте дискретизации равной частоте синусоиды частота сигнала становится равной 0 герц. Из-за этого коэффициенты Фурье зеркально отображены относительно половины частоты дискретизации. Поэтому при работе со спектром можно обрабатывать только половину спектра, перед ОБПФ нужно просто зеркально скопировать вторую половину массива только сделать комплексное сопряжение (дополнительно мнимые коэффициенты умножить на -1). Для этого предусмотрен метод <b>MakeMirror</b>. При модуляции сигнала у нас будут возникать фазовые искажения, т.к. делая преобразование на каком либо участке сигнала, мы принимаем этот участок за 1 период, который повторяется по обе стороны окна бесконечно долго. И если мы вносим какие-либо изменения в спектр, то наши сигналы могут не совпадать на краях окна и будут возникать разрывы (в нашем случае щелчки). Для предотвращения этого мы умножим сигнал на весовое окно, которое плавно к краям уменьшает амплитуду сигнала, а сами блоки возьмем с перекрытием. Т.к. нам не нужно высокое качество звука, то мы не будем использовать весовые окна до преобразования (хотя следовало бы так сделать, т.к. имеет место размазывание частот), а вычислим в &quot;лоб&quot; с сырым сигналом, преобразуем, выполним ОБПФ и только для результата применим оконную функцию. Также это позволит брать блоки с перекрытием в 50% что на слух приемлемо и достаточно быстро. Чтобы было понятно вот наглядно пример:<br />
<div align="center"><a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=2899&amp;d=1417438297" rel="Lightbox" id="attachment2899" ><img src="https://www.cyberforum.ru/blog_attachment.php?attachmentid=2899&amp;thumb=1&amp;d=1417438297" class="thumbnail" border="0" alt="Нажмите на изображение для увеличения
Название: ку.gif
Просмотров: 834
Размер:	882.2 Кб
ID:	2899" style="margin: 5px" /></a></div>Как видно мы берем исходный сигнал 2 раза со сдвигом, захватывая вторую половину во втором проходе. После манипуляций мы микшируем эти два сигнала в месте перекрытия и выдаем на выход первую часть, половина второй части будет позже микшироваться со следующими частями. В качестве окна мы будем использовать окно Ханна. Сам метод называется <b>ApplyWindow</b>. Исходник класса прокомментирован, поэтому я не буду подробно останавливаться на нем.<br />
Как было сказано выше для работы FFT нам нужно брать данные с перекрытием и отправлять данные на выход с перекрытием. Для этого мы напишем специальный класс (<b>clsTrickOverlappedBuffer</b>), который будет выдавать нам данные с учетом перекрытия:<br />
<div class="codeblock"><table class="vb"><thead><tr><td colspan="2" id="845070592"  class="head">Visual Basic</td></tr></thead><tbody><tr class="li1"><td><div id="845070592" style="height: 350px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
</pre></td><td class="de1"><pre class="de1"><span class="co1">' clsTrickOverlappedBuffer &nbsp;- класс перекрывающегося буфера</span>
<span class="co1">' © Кривоус Анатолий Анатольевич (The trick), 2014</span>
&nbsp;
<span class="kw2">Option</span> <span class="kw2">Explicit</span>
&nbsp;
<span class="kw2">Private</span> iBuffer() &nbsp; <span class="kw4">As</span> <span class="kw1">Single</span> &nbsp; &nbsp; &nbsp; <span class="co1">' Буфер входных значений</span>
<span class="kw2">Private</span> oBuffer() &nbsp; <span class="kw4">As</span> <span class="kw1">Single</span> &nbsp; &nbsp; &nbsp; <span class="co1">' Буфер выходных значений</span>
<span class="kw2">Private</span> mInit &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Boolean</span> &nbsp; &nbsp; &nbsp;<span class="co1">' Инициализирован ли объект</span>
<span class="kw2">Private</span> miWritePtr &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span> &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' Индекс текущей позиции записи во входном буфере</span>
<span class="kw2">Private</span> moWritePtr &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span> &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' Индекс текущей позиции записи в выходном буфере</span>
<span class="kw2">Private</span> mWndSize &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span> &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' Размер порции данных для ввода/вывода</span>
<span class="kw2">Private</span> mOverlap &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span> &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' Размер перекрывания в семплах</span>
<span class="kw2">Private</span> iPtr &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span> &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' Текущая позиция чтения во входном буфере</span>
<span class="kw2">Private</span> oPtr &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span> &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' Текущая позиция чтения в выходном буфере</span>
<span class="kw2">Private</span> sampleSize &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span> &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' Размер выборки в байтах</span>
&nbsp;
<span class="co1">' // Инициализация</span>
<span class="kw2">Public</span> <span class="kw2">Function</span> Init(<span class="kw4">ByVal</span> windowSize <span class="kw4">As</span> <span class="kw1">Long</span>, <span class="kw4">ByVal</span> overlapSizeSamples <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> <span class="kw1">Boolean</span>
&nbsp;
&nbsp; &nbsp; <span class="kw3">If</span> overlapSizeSamples &gt; windowSize <span class="kw3">Or</span> overlapSizeSamples &lt;= 0 <span class="kw3">Then</span> <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; <span class="kw3">If</span> windowSize &lt;= 0 <span class="kw3">Then</span> <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="co1">' Выделяем буфер в 2 раза большего размера для минимального перекрытия windowSize</span>
 &nbsp; &nbsp;<span class="kw4">ReDim</span> iBuffer(1, windowSize * 2 - 1)
&nbsp; &nbsp; <span class="kw4">ReDim</span> oBuffer(1, windowSize * 2 - 1)
&nbsp; &nbsp; 
&nbsp; &nbsp; mInit = <span class="kw5">True</span>
&nbsp; &nbsp; mWndSize = windowSize
&nbsp; &nbsp; mOverlap = overlapSizeSamples
&nbsp; &nbsp; miWritePtr = mWndSize
&nbsp; &nbsp; 
&nbsp; &nbsp; Init = <span class="kw5">True</span>
&nbsp;
<span class="kw3">End</span> <span class="kw2">Function</span>
&nbsp;
<span class="co1">' // Записать фрейм во входной буфер</span>
<span class="kw2">Public</span> <span class="kw2">Function</span> WriteInputData(data() <span class="kw4">As</span> <span class="kw1">Single</span>) <span class="kw4">As</span> <span class="kw1">Boolean</span>
&nbsp;
&nbsp; &nbsp; memcpy iBuffer(0, miWritePtr), data(0, 0), (<span class="kw4">UBound</span>(data, 2) + 1) * sampleSize
&nbsp; &nbsp; miWritePtr = IIf(miWritePtr, 0, mWndSize)
&nbsp; &nbsp; WriteInputData = <span class="kw5">True</span>
&nbsp; &nbsp; 
<span class="kw3">End</span> <span class="kw2">Function</span>
&nbsp;
<span class="co1">' // Записать фрейм в выходной буфер</span>
<span class="kw2">Public</span> <span class="kw2">Function</span> WriteOutputData(data() <span class="kw4">As</span> <span class="kw1">Single</span>) <span class="kw4">As</span> <span class="kw1">Boolean</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> sampleCount <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> inSample &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> pointer &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> rest &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; pointer = moWritePtr
&nbsp; &nbsp; <span class="co1">' Сначала микшируем перекрывающиеся данные</span>
 &nbsp; &nbsp;<span class="co1">' Проверяем количество семплов до конца буфера</span>
 &nbsp; &nbsp;sampleCount = mWndSize * 2 - pointer
&nbsp; &nbsp; <span class="co1">' Если недостаточно семплов до конца буфера, то копируем до конца</span>
 &nbsp; &nbsp;<span class="kw3">If</span> sampleCount &gt; mOverlap <span class="kw3">Then</span> sampleCount = mOverlap
&nbsp; &nbsp; <span class="co1">' Микшируем</span>
 &nbsp; &nbsp;<span class="kw3">For</span> inSample = 0 <span class="kw3">To</span> sampleCount - 1
&nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; oBuffer(0, pointer) = oBuffer(0, pointer) + data(0, inSample)
&nbsp; &nbsp; &nbsp; &nbsp; pointer = pointer + 1
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">Next</span>
&nbsp; &nbsp; <span class="co1">' Если не все скопировали, то продолжаем сначала</span>
 &nbsp; &nbsp;<span class="kw3">If</span> sampleCount &lt; mOverlap <span class="kw3">Then</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; pointer = 0
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">Do</span> <span class="kw3">While</span> pointer &lt; mOverlap - sampleCount
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; oBuffer(0, pointer) = oBuffer(0, pointer) + data(0, inSample)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; pointer = pointer + 1
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; inSample = inSample + 1
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">Loop</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; moWritePtr = pointer
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="co1">' Теперь копируем неперекрывающуюся часть</span>
 &nbsp; &nbsp;sampleCount = mWndSize * 2 - pointer
&nbsp; &nbsp; rest = mWndSize - mOverlap
&nbsp; &nbsp; <span class="co1">' Корректируем с учетом выхода за пределы</span>
 &nbsp; &nbsp;<span class="kw3">If</span> sampleCount &gt; rest <span class="kw3">Then</span> sampleCount = rest
&nbsp; &nbsp; <span class="co1">' Копируем</span>
 &nbsp; &nbsp;<span class="kw3">If</span> sampleCount <span class="kw3">Then</span> memcpy oBuffer(0, pointer), data(0, inSample), sampleCount * sampleSize
&nbsp; &nbsp; <span class="co1">' Если был перенос, то копируем в начало</span>
 &nbsp; &nbsp;<span class="kw3">If</span> sampleCount &lt; rest <span class="kw3">Then</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; pointer = 0
&nbsp; &nbsp; &nbsp; &nbsp; memcpy oBuffer(0, pointer), data(0, inSample), (rest - sampleCount) * sampleSize
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; WriteOutputData = <span class="kw5">True</span>
&nbsp; &nbsp; 
<span class="kw3">End</span> <span class="kw2">Function</span>
&nbsp;
<span class="co1">' // Получить данные входного буфера</span>
<span class="kw2">Public</span> <span class="kw2">Function</span> GetInputBuffer(data() <span class="kw4">As</span> <span class="kw1">Single</span>) <span class="kw4">As</span> <span class="kw1">Boolean</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> sampleCount <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="co1">' Получаем доступное количество семплов до конца буфера</span>
 &nbsp; &nbsp;sampleCount = mWndSize * 2 - iPtr
&nbsp; &nbsp; <span class="co1">' Корректируем</span>
 &nbsp; &nbsp;<span class="kw3">If</span> sampleCount &gt; mWndSize <span class="kw3">Then</span> sampleCount = mWndSize
&nbsp; &nbsp; <span class="co1">' Копируем</span>
 &nbsp; &nbsp;<span class="kw3">If</span> sampleCount &gt; 0 <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; memcpy data(0, 0), iBuffer(0, iPtr), sampleCount * sampleSize
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; <span class="co1">' При необходимости копируем с начала буфера</span>
 &nbsp; &nbsp;<span class="kw3">If</span> sampleCount &lt; mWndSize <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; memcpy data(0, sampleCount), iBuffer(0, 0), (mWndSize - sampleCount) * sampleSize
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; <span class="co1">' Обновляем позицию</span>
 &nbsp; &nbsp;iPtr = (iPtr + mOverlap) <span class="kw4">Mod</span> mWndSize * 2
&nbsp;
&nbsp; &nbsp; GetInputBuffer = <span class="kw5">True</span>
&nbsp;
<span class="kw3">End</span> <span class="kw2">Function</span>
&nbsp;
<span class="co1">' // Получить данные выходного буфера</span>
<span class="kw2">Public</span> <span class="kw2">Function</span> GetOutputBuffer(data() <span class="kw4">As</span> <span class="kw1">Single</span>) <span class="kw4">As</span> <span class="kw1">Boolean</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> sampleCount <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="co1">' Получаем доступное количество семплов до конца буфера</span>
 &nbsp; &nbsp;sampleCount = mWndSize * 2 - oPtr
&nbsp; &nbsp; <span class="co1">' Корректируем</span>
 &nbsp; &nbsp;<span class="kw3">If</span> sampleCount &gt; mWndSize <span class="kw3">Then</span> sampleCount = mWndSize
&nbsp; &nbsp; <span class="co1">' Копируем</span>
 &nbsp; &nbsp;<span class="kw3">If</span> sampleCount &gt; 0 <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; memcpy data(0, 0), oBuffer(0, oPtr), sampleCount * sampleSize
&nbsp; &nbsp; &nbsp; &nbsp; oPtr = oPtr + sampleCount
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; <span class="co1">' При необходимости копируем с начала буфера</span>
 &nbsp; &nbsp;<span class="kw3">If</span> sampleCount &lt; mWndSize <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; memcpy data(0, sampleCount), oBuffer(0, 0), (mWndSize - sampleCount) * sampleSize
&nbsp; &nbsp; &nbsp; &nbsp; oPtr = mWndSize - sampleCount
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp;
&nbsp; &nbsp; GetOutputBuffer = <span class="kw5">True</span>
&nbsp;
<span class="kw3">End</span> <span class="kw2">Function</span>
&nbsp;
<span class="kw2">Private</span> <span class="kw2">Sub</span> Class_Initialize()
&nbsp; &nbsp; sampleSize = 8
<span class="kw3">End</span> <span class="kw2">Sub</span></pre></td></tr></table></div></td></tr></tbody></table></div>Метод <b>Init</b> инициализирует внутренние буферы хранения данных. Метод <b>WriteInputData</b> записывает во внутренний буфер данные входного сигнала. С помощью этого метода мы будем записывать захваченный сигнал и несущий сигнал. Метод <b>WriteOutputData</b> микширует переданные данные во внутреннем буфере с прошлыми данными добавленными в предыдущем вызове этого метода. Этот метод мы будем использовать для обработанных данных и писать уже промодулированный сигнал с помощью этого метода. <b>GetInputBuffer</b> и <b>GetOutputBuffer</b> заполняют входной буфер данными с учетом перекрытия. <b>GetInputBuffer</b> получает данные записанные методом <b>WriteInputData</b>, соответственно метод <b>GetOutputBuffer</b> получает данные записанные методом <b>WriteOutputData</b>.<br />
Теперь рассмотрим сам модулятор представленный классом <b>clsTrickModulator</b>, который занимается непосредственно преобразованием спектра:<br />
<div class="codeblock"><table class="vb"><thead><tr><td colspan="2" id="25334041"  class="head">Visual Basic</td></tr></thead><tbody><tr class="li1"><td><div id="25334041" style="height: 350px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
</pre></td><td class="de1"><pre class="de1"><span class="co1">' clsTrickModulator &nbsp;- класс модулятора</span>
<span class="co1">' © Кривоус Анатолий Анатольевич (The trick), 2014</span>
&nbsp;
<span class="kw2">Option</span> <span class="kw2">Explicit</span>
&nbsp;
<span class="kw2">Private</span> mBands &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span> &nbsp; &nbsp; <span class="co1">' Количество полос</span>
<span class="kw2">Private</span> mDryWet &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Single</span> &nbsp; <span class="co1">' Баланс исходного и обработанного звука</span>
<span class="kw2">Private</span> mVolume &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Single</span> &nbsp; <span class="co1">' Громкость</span>
<span class="kw2">Private</span> mLevels() &nbsp; <span class="kw4">As</span> <span class="kw1">Single</span> &nbsp; <span class="co1">' АЧХ</span>
&nbsp;
<span class="co1">' // Громкость</span>
<span class="kw2">Public</span> <span class="kw4">Property</span> <span class="kw4">Let</span> Volume(<span class="kw4">ByVal</span> Value <span class="kw4">As</span> <span class="kw1">Single</span>)
&nbsp; &nbsp; mVolume = Value
<span class="kw3">End</span> <span class="kw4">Property</span>
<span class="kw2">Public</span> <span class="kw4">Property</span> <span class="kw4">Get</span> Volume() <span class="kw4">As</span> <span class="kw1">Single</span>
&nbsp; &nbsp; Volume = mVolume
<span class="kw3">End</span> <span class="kw4">Property</span>
&nbsp;
<span class="co1">' // АЧХ</span>
<span class="kw2">Public</span> <span class="kw2">Function</span> SetLevels(Value() <span class="kw4">As</span> <span class="kw1">Single</span>) <span class="kw4">As</span> <span class="kw1">Boolean</span>
&nbsp; &nbsp; mLevels = Value
<span class="kw3">End</span> <span class="kw2">Function</span>
<span class="kw2">Public</span> <span class="kw4">Property</span> <span class="kw4">Get</span> Levels(<span class="kw4">ByVal</span> index <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> <span class="kw1">Single</span>
&nbsp; &nbsp; Levels = mLevels(index)
<span class="kw3">End</span> <span class="kw4">Property</span>
&nbsp;
<span class="co1">' // Баланс</span>
<span class="kw2">Public</span> <span class="kw4">Property</span> <span class="kw4">Let</span> DryWet(<span class="kw4">ByVal</span> Value <span class="kw4">As</span> <span class="kw1">Single</span>)
&nbsp; &nbsp; <span class="kw3">If</span> Abs(Value) &gt; 1 <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; err.Raise 9
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">Exit</span> <span class="kw4">Property</span>
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; mDryWet = Value
<span class="kw3">End</span> <span class="kw4">Property</span>
<span class="kw2">Public</span> <span class="kw4">Property</span> <span class="kw4">Get</span> DryWet() <span class="kw4">As</span> <span class="kw1">Single</span>
&nbsp; &nbsp; DryWet = mDryWet
<span class="kw3">End</span> <span class="kw4">Property</span>
&nbsp;
<span class="co1">' // Количество полос</span>
<span class="kw2">Public</span> <span class="kw4">Property</span> <span class="kw4">Let</span> Bands(<span class="kw4">ByVal</span> Value <span class="kw4">As</span> <span class="kw1">Long</span>)
&nbsp; &nbsp; <span class="kw3">If</span> Value &gt; 128 <span class="kw3">Or</span> Value &lt;= 0 <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; err.Raise 9
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">Exit</span> <span class="kw4">Property</span>
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; mBands = Value
<span class="kw3">End</span> <span class="kw4">Property</span>
<span class="kw2">Public</span> <span class="kw4">Property</span> <span class="kw4">Get</span> Bands() <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; Bands = mBands
<span class="kw3">End</span> <span class="kw4">Property</span>
&nbsp;
<span class="co1">' // Функция выполняет обработку</span>
<span class="kw2">Public</span> <span class="kw2">Function</span> Process(carrier() <span class="kw4">As</span> <span class="kw1">Single</span>, modulation() <span class="kw4">As</span> <span class="kw1">Single</span>) <span class="kw4">As</span> <span class="kw1">Boolean</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> nCount &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> band &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> endBand &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> sample &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> samplePerBand &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> offsetSample &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> modValue &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Single</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> ampValue &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Single</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> invDryWet &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Single</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> FFTSize &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; invDryWet = 1 - mDryWet
&nbsp; &nbsp; FFTSize = (<span class="kw4">UBound</span>(carrier, 2) + 1)
&nbsp; &nbsp; <span class="co1">' Зеркальную сторону не вычисляем</span>
 &nbsp; &nbsp;nCount = FFTSize \ 2
&nbsp; &nbsp; <span class="co1">' Получаем число отсчетов на полосу</span>
 &nbsp; &nbsp;samplePerBand = nCount \ mBands
&nbsp; &nbsp; <span class="co1">' Вычисляем величину усиления</span>
 &nbsp; &nbsp;ampValue = (Sqr(mBands) * invDryWet) / 2.5 + mDryWet
&nbsp; &nbsp; <span class="co1">' Проходим по полосам</span>
 &nbsp; &nbsp;<span class="kw3">For</span> band = 0 <span class="kw3">To</span> mBands - 1
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' Проверяем выход за пределы</span>
 &nbsp; &nbsp; &nbsp; &nbsp;endBand = band * samplePerBand + samplePerBand
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> endBand &gt;= nCount <span class="kw3">Then</span> endBand = nCount - 1
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' Обнуляем величину спектральной составляющей для текущей полосы</span>
 &nbsp; &nbsp; &nbsp; &nbsp;modValue = 0
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' Проходим по отсчетам спектра текущей полосы</span>
 &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw3">For</span> sample = band * samplePerBand <span class="kw3">To</span> endBand
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' Вычисляем величину спекта для всех отсчетов полосы</span>
 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;modValue = modValue + Sqr(modulation(0, sample) * modulation(0, sample) + _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; modulation(1, sample) * modulation(1, sample))
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">Next</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' Модулируем в текущей полосе</span>
 &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw3">For</span> sample = band * samplePerBand <span class="kw3">To</span> endBand
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; carrier(0, sample) = ((carrier(0, sample) * modValue * invDryWet) + _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;(modulation(0, sample) * mDryWet)) * ampValue * mLevels(sample) * mVolume
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; carrier(1, sample) = ((carrier(1, sample) * modValue * invDryWet) + _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;(modulation(1, sample) * mDryWet)) * ampValue * mLevels(sample) * mVolume
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">Next</span>
&nbsp; &nbsp; <span class="kw3">Next</span>
&nbsp; &nbsp; 
<span class="kw3">End</span> <span class="kw2">Function</span>
&nbsp;
<span class="kw2">Private</span> <span class="kw2">Sub</span> Class_Initialize()
&nbsp; &nbsp; mDryWet = 0
&nbsp; &nbsp; mVolume = 1
<span class="kw3">End</span> <span class="kw2">Sub</span></pre></td></tr></table></div></td></tr></tbody></table></div>Класс имеет свойство <b>Volume</b>, которое определяет уровень выходной громкости. Свойство <b>Bands</b> определяет количество полос на которые будет делится спектр при модуляции. К примеру при частоте дискретизации 44100 Гц. и размере БПФ равным 2048, получим разрешение по частоте равное 44100 / 2048 &#8776; 21.53 Гц. При количестве частотных полос равной 64 будем брать по 2048 / 2 / 64 = 16 отсчетов (344.48 Гц) частоты, для каждой модуляции. Свойство <b>DryWet</b> определяет баланс между оригинальным сигналом и преобразованным на выходе модулятора. Метод <b>SetLevels</b> задает массив с коэффициентами амплитудно-частотной характеристики (АЧХ) на которую умножается сигнал. Это позволит производить эквализацию сигнала и улучшить качество звука после обработки. Самый главный метод - <b>Process</b>, который собственно и производит обработку; разберем его подробней. Сначала мы вычисляем количество отсчетов на одну полосу исходя из свойства <b>Bands</b>, потом вычисляем коэффициент усиления выходного сигнала в зависимости от количества частотных полос - эта формула получена экспериментально. Дальше мы проходим по частотным полосам речевого (<b>modulation</b>) сигнала и в коэффициентах соответствующих каждой полосе вычисляем энергию данных частот. Ранее я писал что амплитуда спектральной составляющей - это длина вектора, поэтому мы просто суммируем длины векторов соответствующих частот, это и будет энергия в данном диапазоне частот. Далее мы проходим уже по несущему сигналу в тех же спектральных отсчетах изменяем уровень сигнала в соответствии с вычисленной энергией, также сразу вычисляем выходной уровень, применяем эквализацию. При умножении двух компонент вектора (комплексного числа) на величину энергии происходит его масштабирование. Всеми этими манипуляциями мы модулируем несущий сигнал, речевым, что нам и требовалось.<br />
Итак, все компоненты готовы. Теперь нужно все собрать и проверять работу. Для пользовательского интерфейса я разработал несколько контролов специально для вокодера. Описывать принцип работы и разработку каждого я не буду, т.к. это займет много времени, а расскажу вкратце о каждом из них. <b>ctlTrickKnob</b> - контрол регулятор, что-то вроде обычного потенциометра. С ним все понятно это обычный регулятор, подобие того же виндового <b>Slider</b>'а, только с круговой регулировкой. <b>ctlTrickCommand</b> - это обычная кнопка с поддержкой иконки и добавлена только для внешнего вида. <b>ctlTrickEqualizer</b> - самый интересный контрол. Он позволяет корректировать АЧХ сигнала. Его панель имеет логарифмическую шкалу, как по частотам, так и по уровням, что позволяет более естественно для слуха изменять параметры. Для добавления точки на АЧХ нужно нажать левой кнопкой в пустом месте, для удаления - правой. При изменении АЧХ контрол генерирует событие <b>Change</b>. Все контролы предназначены только для вокодера, поэтому их функционал минимален.<br />
Теперь все &quot;закидываем&quot; на форму, и пишем код:<br />
<div class="codeblock"><table class="vb"><thead><tr><td colspan="2" id="783997313"  class="head">Visual Basic</td></tr></thead><tbody><tr class="li1"><td><div id="783997313" style="height: 350px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
</pre></td><td class="de1"><pre class="de1"><span class="co1">' frmTrickVocoder &nbsp;- главная форма TrickVocoder</span>
<span class="co1">' © Кривоус Анатолий Анатольевич (The trick), 2014</span>
&nbsp;
<span class="kw2">Option</span> <span class="kw2">Explicit</span>
&nbsp;
<span class="kw4">Dim</span> <span class="kw3">WithEvents</span> AudioCapture &nbsp; &nbsp; <span class="kw4">As</span> clsTrickSound &nbsp; &nbsp;<span class="co1">' Объект захвата звука</span>
<span class="kw4">Dim</span> <span class="kw3">WithEvents</span> AudioPlayback &nbsp; &nbsp;<span class="kw4">As</span> clsTrickSound &nbsp; &nbsp;<span class="co1">' Объект записи звука</span>
&nbsp;
<span class="kw2">Private</span> inpBuffer() <span class="kw4">As</span> <span class="kw1">Integer</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">' Буфер захвата звука</span>
<span class="kw2">Private</span> outBuffer() <span class="kw4">As</span> <span class="kw1">Integer</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">' Буфер воспроизведения звука</span>
<span class="kw2">Private</span> rawBuffer() <span class="kw4">As</span> <span class="kw1">Integer</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">' Буфер сырых данных исходного Wave-файла</span>
<span class="kw2">Private</span> plyBuffer &nbsp; <span class="kw4">As</span> clsTrickOverlappedBuffer &nbsp; &nbsp; <span class="co1">' Буфер перекрывающихся данных несущей</span>
<span class="kw2">Private</span> capBuffer &nbsp; <span class="kw4">As</span> clsTrickOverlappedBuffer &nbsp; &nbsp; <span class="co1">' Буфер перекрывающихся данных модулятора</span>
<span class="kw2">Private</span> FFT &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> clsTrickFFT &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">' Объект для работы с FFT и преобразованием буфера звука</span>
<span class="kw2">Private</span> Modulator &nbsp; <span class="kw4">As</span> clsTrickModulator &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">' Модулятор</span>
<span class="kw2">Private</span> mFFTSize &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' Размер FFT</span>
<span class="kw2">Private</span> mOverlap &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' Количество перекрытий</span>
<span class="kw2">Private</span> mRawSize &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' Размер сырых данных буфера в семплах</span>
<span class="kw2">Private</span> mInpFile &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">String</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' Имя файла, если захват из файла</span>
<span class="kw2">Private</span> tmpCapBuf() <span class="kw4">As</span> <span class="kw1">Single</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' Временный буфер захвата</span>
<span class="kw2">Private</span> tmpPlyBuf() <span class="kw4">As</span> <span class="kw1">Single</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' Временный буфер воспроизведения</span>
<span class="kw2">Private</span> wavConv &nbsp; &nbsp; <span class="kw4">As</span> clsTrickWavConverter &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' Объект-конвертер сигнала носителя</span>
<span class="kw2">Private</span> inpConv &nbsp; &nbsp; <span class="kw4">As</span> clsTrickWavConverter &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' Объект-конвертер модулирующего сигнала</span>
&nbsp;
<span class="co1">' // Получить объект захвата</span>
<span class="kw2">Public</span> <span class="kw4">Property</span> <span class="kw4">Get</span> AudioCaptureDevice() <span class="kw4">As</span> clsTrickSound
&nbsp; &nbsp; <span class="kw4">Set</span> AudioCaptureDevice = AudioCapture
<span class="kw3">End</span> <span class="kw4">Property</span>
&nbsp;
<span class="co1">' // Получить имя файла захвата</span>
<span class="kw2">Public</span> <span class="kw4">Property</span> <span class="kw4">Get</span> InputFileName() <span class="kw4">As</span> <span class="kw1">String</span>
&nbsp; &nbsp; InputFileName = mInpFile
<span class="kw3">End</span> <span class="kw4">Property</span>
&nbsp;
<span class="co1">' // Закрыть окно</span>
<span class="kw2">Private</span> <span class="kw2">Sub</span> btnClose_Click()
&nbsp; &nbsp; Unload Me
<span class="kw3">End</span> <span class="kw2">Sub</span>
&nbsp;
<span class="co1">' // Открыть файл несущего сигнала</span>
<span class="kw2">Private</span> <span class="kw2">Sub</span> btnOpenCarrier_Click()
&nbsp; &nbsp; <span class="kw4">Dim</span> FileName &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">String</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> conv &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> clsTrickWavConverter
&nbsp; &nbsp; <span class="co1">' Получаем имя файла</span>
 &nbsp; &nbsp;FileName = GetFile(Me.hwnd)
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> Len(FileName) <span class="kw3">Then</span>
&nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">Set</span> conv = <span class="kw2">New</span> clsTrickWavConverter
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' При успешном чтении устанавливаем его в качестве текущего</span>
 &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw3">If</span> conv.ReadWaveFile(FileName) <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">Set</span> wavConv = conv
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; 
<span class="kw3">End</span> <span class="kw2">Sub</span>
&nbsp;
<span class="co1">' // Настройки</span>
<span class="kw2">Private</span> <span class="kw2">Sub</span> btnSettings_Click()
&nbsp; &nbsp; <span class="kw4">Dim</span> frm <span class="kw4">As</span> frmSettings
&nbsp; &nbsp; <span class="kw4">Dim</span> cur <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw4">Set</span> frm = <span class="kw2">New</span> frmSettings
&nbsp; &nbsp; 
&nbsp; &nbsp; frm.Show vbModal
&nbsp; &nbsp; <span class="co1">' При нажатии ОК</span>
 &nbsp; &nbsp;<span class="kw3">If</span> frm.Result = vbOK <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' Получаем текущее устройство захвата</span>
 &nbsp; &nbsp; &nbsp; &nbsp;cur = AudioCapture.CurrentCaptureDeviceID()
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' Очищаем буфер, т.к. если дальше будет неудача то мы будем слышать зацикленный текущий сигнал</span>
 &nbsp; &nbsp; &nbsp; &nbsp;memset inpBuffer(0), mFFTSize * 2, 0
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> frm.SelectedDevice &gt;= AudioCapture.CaptureDevices.count <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' Захват из файла</span>
 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">Set</span> inpConv = <span class="kw5">Nothing</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">Set</span> inpConv = <span class="kw2">New</span> clsTrickWavConverter
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' Читаем файл</span>
 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw3">If</span> <span class="kw3">Not</span> inpConv.ReadWaveFile(frm.FileName) <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' Восстанавливаем назад</span>
 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;InitCapture cur
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">Else</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; mInpFile = frm.FileName
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; AudioCapture.StopProcess
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">Else</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' Захват с устройства</span>
 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;AudioPlayback.StopProcess
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> <span class="kw3">Not</span> InitCapture(frm.SelectedDevice) <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; InitCapture cur
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">Else</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; mInpFile = vbNullString
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">On</span> <span class="kw4">Error</span> <span class="kw4">Resume</span> <span class="kw3">Next</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; AudioCapture.StartProcess
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; AudioPlayback.StartProcess
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">On</span> <span class="kw4">Error</span> <span class="kw3">GoTo</span> 0
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> err.Number <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; MsgBox <span class="st0">&quot;Ошибка&quot;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; 
<span class="kw3">End</span> <span class="kw2">Sub</span>
&nbsp;
<span class="co1">' // Изменение АЧХ</span>
<span class="kw2">Private</span> <span class="kw2">Sub</span> equResponse_Change()
&nbsp; &nbsp; <span class="kw4">Dim</span> data() <span class="kw4">As</span> <span class="kw1">Single</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw4">ReDim</span> data(mFFTSize \ 2 - 1)
&nbsp; &nbsp; <span class="co1">' Получаем из контрола</span>
 &nbsp; &nbsp;equResponse.GetCurve data()
&nbsp; &nbsp; <span class="co1">' Задаем модулятору</span>
 &nbsp; &nbsp;Modulator.SetLevels data()
&nbsp; &nbsp; 
<span class="kw3">End</span> <span class="kw2">Sub</span>
&nbsp;
<span class="co1">' // Загрузка формы</span>
<span class="kw2">Private</span> <span class="kw2">Sub</span> Form_Load()
&nbsp; &nbsp; <span class="co1">' Размер FFT</span>
 &nbsp; &nbsp;mFFTSize = 2048
&nbsp; &nbsp; <span class="co1">' Перекрытие</span>
 &nbsp; &nbsp;mOverlap = 2
&nbsp; &nbsp; <span class="co1">' Инициализация воспроизведения</span>
 &nbsp; &nbsp;<span class="kw3">If</span> <span class="kw3">Not</span> InitPlayback() <span class="kw3">Then</span> Unload Me
&nbsp; &nbsp; <span class="co1">' Инициализация захвата</span>
 &nbsp; &nbsp;<span class="kw3">If</span> <span class="kw3">Not</span> InitCapture() <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">Call</span> btnSettings_Click
&nbsp; &nbsp; <span class="kw3">Else</span>
&nbsp; &nbsp; &nbsp; &nbsp; AudioCapture.StartProcess
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw4">Set</span> plyBuffer = <span class="kw2">New</span> clsTrickOverlappedBuffer
&nbsp; &nbsp; <span class="kw4">Set</span> capBuffer = <span class="kw2">New</span> clsTrickOverlappedBuffer
&nbsp; &nbsp; <span class="co1">' Установка перекрывающихся буферов</span>
 &nbsp; &nbsp;plyBuffer.Init mFFTSize, mFFTSize \ mOverlap
&nbsp; &nbsp; capBuffer.Init mFFTSize, mFFTSize \ mOverlap
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw4">Set</span> FFT = <span class="kw2">New</span> clsTrickFFT
&nbsp; &nbsp; <span class="co1">' Установка размера БПФ и окна</span>
 &nbsp; &nbsp;FFT.FFTSize = mFFTSize
&nbsp; &nbsp; FFT.WindowType = WT_HANN
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw4">Set</span> Modulator = <span class="kw2">New</span> clsTrickModulator
&nbsp; &nbsp; <span class="co1">' Создание буферов</span>
 &nbsp; &nbsp;<span class="kw4">ReDim</span> tmpCapBuf(1, mFFTSize - 1)
&nbsp; &nbsp; <span class="kw4">ReDim</span> tmpPlyBuf(1, mFFTSize - 1)
&nbsp; &nbsp; <span class="kw4">ReDim</span> inpBuffer(mFFTSize - 1)
&nbsp; &nbsp; <span class="kw4">ReDim</span> outBuffer(mFFTSize - 1)
&nbsp; &nbsp; <span class="co1">' Обновление информации</span>
 &nbsp; &nbsp;<span class="kw2">Call</span> equResponse_Change
&nbsp; &nbsp; <span class="kw2">Call</span> knbBands_Change
&nbsp; &nbsp; <span class="kw2">Call</span> knbMix_Change
&nbsp; &nbsp; <span class="kw2">Call</span> knbVolume_Change
&nbsp; &nbsp; <span class="kw2">Call</span> knbPitch_Change
&nbsp; &nbsp; <span class="co1">' Запуск воспроизведения</span>
 &nbsp; &nbsp;AudioPlayback.StartProcess
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw4">Dim</span> hRgn &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="co1">' Задаем регион окну</span>
 &nbsp; &nbsp;hRgn = CreateRoundRectRgn(0, 0, Me.ScaleWidth, Me.ScaleHeight, 2, 2)
&nbsp; &nbsp; SetWindowRgn Me.hwnd, hRgn, <span class="kw5">False</span>
&nbsp; &nbsp; <span class="co1">' Задаем иконку</span>
 &nbsp; &nbsp;SetIcon Me.hwnd
<span class="kw3">End</span> <span class="kw2">Sub</span>
&nbsp;
<span class="co1">' // Получены новые данные с устройства захвата</span>
<span class="kw2">Private</span> <span class="kw2">Sub</span> AudioCapture_NewData(<span class="kw4">ByVal</span> DataPtr <span class="kw4">As</span> <span class="kw1">Long</span>, <span class="kw4">ByVal</span> CountBytes <span class="kw4">As</span> <span class="kw1">Long</span>)
&nbsp; &nbsp; <span class="co1">' Копируем во временный буфер</span>
 &nbsp; &nbsp;memcpy inpBuffer(0), <span class="kw4">ByVal</span> DataPtr, CountBytes
<span class="kw3">End</span> <span class="kw2">Sub</span>
&nbsp;
<span class="co1">' // Нужны новые данные для воспроизведения</span>
<span class="kw2">Private</span> <span class="kw2">Sub</span> AudioPlayback_NewData(<span class="kw4">ByVal</span> DataPtr <span class="kw4">As</span> <span class="kw1">Long</span>, <span class="kw4">ByVal</span> CountBytes <span class="kw4">As</span> <span class="kw1">Long</span>)
&nbsp; &nbsp; <span class="co1">' Обработка прошлых данных</span>
 &nbsp; &nbsp;<span class="kw2">Call</span> Process
&nbsp; &nbsp; <span class="co1">' Копируем</span>
 &nbsp; &nbsp;memcpy <span class="kw4">ByVal</span> DataPtr, outBuffer(0), CountBytes
<span class="kw3">End</span> <span class="kw2">Sub</span>
&nbsp;
<span class="co1">' // Процесс</span>
<span class="kw2">Private</span> <span class="kw2">Sub</span> Process()
&nbsp; &nbsp; <span class="kw4">Dim</span> ovrLap &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> ret &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> idx &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> delta &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Single</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> datSize &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> Len(mInpFile) <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' Захват из файла</span>
 &nbsp; &nbsp; &nbsp; &nbsp;inpConv.Convert VarPtr(inpBuffer(0)), mFFTSize * 2, ret
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' Если данные закончились, то начинаем сначала</span>
 &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw3">If</span> ret &lt; mFFTSize * 2 <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; inpConv.InputCurrentPosition = 0
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; inpConv.Convert VarPtr(inpBuffer(ret \ 2)), mFFTSize * 2 - ret, ret
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; <span class="co1">' Если не задан несущий сигнал</span>
 &nbsp; &nbsp;<span class="kw3">If</span> wavConv <span class="kw3">Is</span> <span class="kw5">Nothing</span> <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' Копируем даные захвата в выходной буфер и выходим</span>
 &nbsp; &nbsp; &nbsp; &nbsp;outBuffer = inpBuffer
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">Exit</span> <span class="kw2">Sub</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; <span class="co1">' Преобразовываем данные в комплексный формат</span>
 &nbsp; &nbsp;FFT.Convert16BitToComplex inpBuffer(), tmpCapBuf()
&nbsp; &nbsp; <span class="co1">' Пишем данные в перекрывающийся буфер</span>
 &nbsp; &nbsp;capBuffer.WriteInputData tmpCapBuf()
&nbsp; &nbsp; <span class="co1">' Получаем размер (в семплах) несущего сигнала</span>
 &nbsp; &nbsp;datSize = wavConv.Rate * wavConv.InputDataSize \ 2
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> datSize &lt; mRawSize <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' Семпл слишком короткий</span>
 &nbsp; &nbsp; &nbsp; &nbsp;wavConv.Convert VarPtr(rawBuffer(0)), mRawSize * 2, ret
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' Семпл целиком не поместился, начинаем сначала</span>
 &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw3">If</span> ret * 2 &lt;&gt; datSize <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; wavConv.InputCurrentPosition = 0
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; wavConv.Convert VarPtr(rawBuffer(ret \ 2)), datSize * 2 - ret, ret
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' Зацикливаем его на всю длину буфера</span>
 &nbsp; &nbsp; &nbsp; &nbsp;ret = datSize
&nbsp; &nbsp; &nbsp; &nbsp; idx = 0
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">Do</span> <span class="kw3">While</span> ret &lt; mRawSize
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; rawBuffer(ret) = rawBuffer(idx)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ret = ret + 1
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; idx = idx + 1
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">Loop</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' Обновляем позицию</span>
 &nbsp; &nbsp; &nbsp; &nbsp;wavConv.InputCurrentPosition = ((wavConv.InputCurrentPosition + idx) <span class="kw4">Mod</span> datSize)
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">Else</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' Семпл достаточно длиный</span>
 &nbsp; &nbsp; &nbsp; &nbsp;wavConv.Convert VarPtr(rawBuffer(0)), mRawSize * 2, ret
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' Если данные закончились, то начинаем сначала</span>
 &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw3">If</span> ret &lt; mRawSize * 2 <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; wavConv.InputCurrentPosition = 0
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; wavConv.Convert VarPtr(rawBuffer(ret \ 2)), mRawSize * 2 - ret, ret
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="co1">' Сжимаем/растягиваем массив с учетом сдвига тона</span>
 &nbsp; &nbsp;delta = 2 ^ (knbPitch.Value / 12)
&nbsp; &nbsp; <span class="kw3">For</span> idx = 0 <span class="kw3">To</span> mFFTSize - 1
&nbsp; &nbsp; &nbsp; &nbsp; outBuffer(idx) = rawBuffer(Fix(idx * delta))
&nbsp; &nbsp; <span class="kw3">Next</span>
&nbsp; &nbsp; <span class="co1">' Конвертируем данные несущего сигнала в комплексную форму</span>
 &nbsp; &nbsp;FFT.Convert16BitToComplex outBuffer(), tmpPlyBuf()
&nbsp; &nbsp; <span class="co1">' Пишем данные в перекрывающийся буфер</span>
 &nbsp; &nbsp;plyBuffer.WriteInputData tmpPlyBuf()
&nbsp;
&nbsp; &nbsp; <span class="co1">' Проходы по перекрытиям</span>
 &nbsp; &nbsp;<span class="kw3">For</span> ovrLap = 0 <span class="kw3">To</span> mOverlap - 1
&nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' Получаем очередные буфера</span>
 &nbsp; &nbsp; &nbsp; &nbsp;capBuffer.GetInputBuffer tmpCapBuf()
&nbsp; &nbsp; &nbsp; &nbsp; plyBuffer.GetInputBuffer tmpPlyBuf()
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' Быстрое преобразование Фурье</span>
 &nbsp; &nbsp; &nbsp; &nbsp;FFT.FFT tmpCapBuf(), <span class="kw5">False</span>
&nbsp; &nbsp; &nbsp; &nbsp; FFT.FFT tmpPlyBuf(), <span class="kw5">False</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' Модуляция</span>
 &nbsp; &nbsp; &nbsp; &nbsp;Modulator.Process tmpPlyBuf(), tmpCapBuf()
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' Зеркалирование</span>
 &nbsp; &nbsp; &nbsp; &nbsp;FFT.MakeMirror tmpPlyBuf()
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' Обратное преобразование Фурье</span>
 &nbsp; &nbsp; &nbsp; &nbsp;FFT.FFT tmpPlyBuf(), <span class="kw5">True</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' Окно</span>
 &nbsp; &nbsp; &nbsp; &nbsp;FFT.ApplyWindow tmpPlyBuf()
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' Запись в выход</span>
 &nbsp; &nbsp; &nbsp; &nbsp;plyBuffer.WriteOutputData tmpPlyBuf()
&nbsp;
&nbsp; &nbsp; <span class="kw3">Next</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="co1">' Получаем данные</span>
 &nbsp; &nbsp;plyBuffer.GetOutputBuffer tmpPlyBuf()
&nbsp; &nbsp; <span class="co1">' Преобразуем</span>
 &nbsp; &nbsp;FFT.ConvertComplexTo16Bit tmpPlyBuf(), outBuffer()
&nbsp; &nbsp; 
<span class="kw3">End</span> <span class="kw2">Sub</span>
&nbsp;
<span class="co1">' // Инициализация захвата звука</span>
<span class="kw2">Private</span> <span class="kw2">Function</span> InitCapture(<span class="kw4">Optional</span> DeviceID <span class="kw4">As</span> <span class="kw1">Long</span> = -1) <span class="kw4">As</span> <span class="kw1">Boolean</span>
&nbsp; &nbsp; <span class="kw4">On</span> <span class="kw4">Error</span> <span class="kw3">GoTo</span> ERROR_LABEL
&nbsp; &nbsp; <span class="kw4">Set</span> AudioCapture = <span class="kw5">Nothing</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw4">Set</span> AudioCapture = <span class="kw2">New</span> clsTrickSound
&nbsp; &nbsp; AudioCapture.InitCapture 1, SampleRate, 16, mFFTSize, DeviceID
&nbsp; &nbsp; 
&nbsp; &nbsp; InitCapture = <span class="kw5">True</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw2">Exit</span> <span class="kw2">Function</span>
ERROR_LABEL:
&nbsp; &nbsp; 
&nbsp; &nbsp; MsgBox <span class="st0">&quot;Error initialize capture&quot;</span>, vbCritical
&nbsp; &nbsp; 
<span class="kw3">End</span> <span class="kw2">Function</span>
&nbsp;
<span class="co1">' // Инициализация проигрывания звука</span>
<span class="kw2">Private</span> <span class="kw2">Function</span> InitPlayback(<span class="kw4">Optional</span> DeviceID <span class="kw4">As</span> <span class="kw1">Long</span> = -1) <span class="kw4">As</span> <span class="kw1">Boolean</span>
&nbsp; &nbsp; <span class="kw4">On</span> <span class="kw4">Error</span> <span class="kw3">GoTo</span> ERROR_LABEL
&nbsp; &nbsp; <span class="kw4">Set</span> AudioPlayback = <span class="kw5">Nothing</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw4">Set</span> AudioPlayback = <span class="kw2">New</span> clsTrickSound
&nbsp; &nbsp; AudioPlayback.InitPlayback 1, SampleRate, 16, mFFTSize, DeviceID
&nbsp; &nbsp; 
&nbsp; &nbsp; InitPlayback = <span class="kw5">True</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw2">Exit</span> <span class="kw2">Function</span>
ERROR_LABEL:
&nbsp; &nbsp; 
&nbsp; &nbsp; MsgBox <span class="st0">&quot;Error initialize playback&quot;</span>, vbCritical
&nbsp; &nbsp; 
<span class="kw3">End</span> <span class="kw2">Function</span>
&nbsp;
<span class="co1">' // Нажатие мыши в окне</span>
<span class="kw2">Private</span> <span class="kw2">Sub</span> Form_MouseDown(Button <span class="kw4">As</span> <span class="kw1">Integer</span>, Shift <span class="kw4">As</span> <span class="kw1">Integer</span>, x <span class="kw4">As</span> <span class="kw1">Single</span>, y <span class="kw4">As</span> <span class="kw1">Single</span>)
&nbsp; &nbsp; <span class="kw4">Dim</span> pos <span class="kw4">As</span> <span class="kw1">Currency</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> y &lt;= 26 <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' Если мышь нажата в пределах заголовка, то включаем перетаскивание</span>
 &nbsp; &nbsp; &nbsp; &nbsp;ReleaseCapture
&nbsp; &nbsp; &nbsp; &nbsp; GetCursorPos pos
&nbsp; &nbsp; &nbsp; &nbsp; SendMessage Me.hwnd, WM_NCLBUTTONDOWN, HTCAPTION, pos
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; 
<span class="kw3">End</span> <span class="kw2">Sub</span>
&nbsp;
<span class="co1">' // Изменение количества полос</span>
<span class="kw2">Private</span> <span class="kw2">Sub</span> knbBands_Change()
&nbsp; &nbsp; 
&nbsp; &nbsp; Modulator.Bands = knbBands.Value
&nbsp; &nbsp; knbBands.Caption = knbBands.Value
&nbsp; &nbsp; 
<span class="kw3">End</span> <span class="kw2">Sub</span>
&nbsp;
<span class="co1">' // Изменение смешивания</span>
<span class="kw2">Private</span> <span class="kw2">Sub</span> knbMix_Change()
&nbsp; &nbsp; <span class="kw4">Dim</span> lg <span class="kw4">As</span> <span class="kw1">Single</span>
&nbsp; &nbsp; <span class="co1">' Логарифмический масштаб</span>
 &nbsp; &nbsp;lg = ((10 ^ (knbMix.Value / 50)) - 1) / 99
&nbsp; &nbsp; Modulator.DryWet = lg
&nbsp; &nbsp; knbMix.Caption = Format(lg, <span class="st0">&quot;#0.00%&quot;</span>)
&nbsp; &nbsp; &nbsp; &nbsp; 
<span class="kw3">End</span> <span class="kw2">Sub</span>
&nbsp;
<span class="co1">' // Изменение тона несущей</span>
<span class="kw2">Private</span> <span class="kw2">Sub</span> knbPitch_Change()
&nbsp; &nbsp; 
&nbsp; &nbsp; mRawSize = -Int(-mFFTSize * (2 ^ (knbPitch.Value / 12)))
&nbsp; &nbsp; <span class="kw4">ReDim</span> rawBuffer(mRawSize - 1)
&nbsp; &nbsp; 
&nbsp; &nbsp; knbPitch.Caption = Format(knbPitch.Value, <span class="st0">&quot;0 sem;-0 sem;non\e&quot;</span>)
&nbsp; &nbsp; 
<span class="kw3">End</span> <span class="kw2">Sub</span>
&nbsp;
<span class="co1">' // Изменение громкости</span>
<span class="kw2">Private</span> <span class="kw2">Sub</span> knbVolume_Change()
&nbsp; &nbsp; <span class="kw4">Dim</span> lg <span class="kw4">As</span> <span class="kw1">Single</span>
&nbsp; &nbsp; <span class="co1">' Логарифмический масштаб</span>
 &nbsp; &nbsp;lg = ((10 ^ (knbVolume.Value / 50)) - 1) / 99
&nbsp; &nbsp; Modulator.Volume = lg
&nbsp; &nbsp; knbVolume.Caption = Format(lg, <span class="st0">&quot;#0.00%&quot;</span>)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
<span class="kw3">End</span> <span class="kw2">Sub</span></pre></td></tr></table></div></td></tr></tbody></table></div>При загрузке формы мы выполняем инициализацию всех компонентов. Захват, воспроизведение звука, размер FFT, величину перекрытия, перекрывающиеся буферы, создание буферов для целочисленных и комплексных данных. Далее я сделал форму окна со скругленными углами, т.к. использую окно без рамки (рисовать в неклиентской области не было желания). Теперь вся задача сводится к обработке событий <b>AudioPlayback_NewData</b> и <b>AudioCapture_NewData</b>. Первое событие возникает когда устройство воспроизведения нуждается в очередной порции звуковых данных, второе при заполнении буфера захвата, в котором мы просто копируем данные во временный буфер откуда потом возьмем их при обработке <b>AudioPlayback_NewData</b>. Самый главный метод - <b>Process</b>, в нем мы непосредственно делаем преобразование. Сначала мы проверяем идет ли у нас захват из файла или устройства. Для этого мы проверяем переменную <b>mInpFile</b>, которая определяет имя входного файла для захвата. Если захват производится из файла, то мы с помощью объекта <b>inpConv</b>, который является экземпляром класса <b>clsTrickWavConverter</b>, конвертируем данные в нужный нам формат. Если данные закончились (число прочитанных байт не соответствует переданному), то значит мы находимся на границе файла и для продолжения нужно начать сначала. Также проверяем несущий сигнал и если он не задан  то просто копируем входные данные на выход и выходим, в этом случае мы будем слышать необработанный звук. В противном случае мы переводим данные в комплексный вид (заносим в реальную часть сигнал, а мнимую обнуляем) и заносим полученный массив в перекрывающийся буфер. Далее начинаем обработку несущего сигнала. Т.к. несущий сигнал у нас может быть очень маленькой длины (можно использовать один период волны), то в целях оптимизации я сделаем сами повторение сигнала если это потребуется. Поясню. Например если у нас несущий сигнал длительностью 10 мс, а буфер 100 мс (к примеру), то можно было бы просто каждый раз вызывать конвертацию с помощью ACM переписывая указатель в массиве назначения, но это будет неоптимально. Для оптимизации можно конвертировать только один раз, а потом просто продублировать данные до конца массива, что мы и сделаем. Только потом не забыть изменить позицию в исходном файле, иначе при следующем чтении фазы не будут совпадать и будут щелчки. Писать мы будем в другой буфер (<b>rawBuffer</b>). Этот буфер имеет длину исходя из сдвига тона. Например если мы хотим сдвинуть тон на величину <b>semitones</b> (полутонов), то размер буфера <b>rawBuffer</b> должен быть в 2<sup>semitones/12</sup> раза больше. Далее мы просто сожмем/растянем буфер до величины <b>mFFTSize</b>, что даст нам ускорение/замедление и как следствие повышение/понижение тона. После всех манипуляций мы пишем данные в перекрывающийся буфер и начинаем обработку. Для этого проходим по количеству перекрытий и обрабатываем данные. Объекты класса <b>clsTrickOverlappedBuffer</b> вернут нам правильные данные. Обработка понятна из кода, т.к. мы подробно разбирали работу каждого класса. После обработки всех перекрытий мы получаем выходные данные и конвертируем их в целочисленные, пригодные для воспроизведения.<br />
В качестве настройки используется форма <b>frmSettings</b>. В качестве списка устройств используется стандартный листбокс, только отрисовка идет через <a href="https://www.cyberforum.ru/blogs/354370/blog2329.html">мой класс</a>. В список устройства добавляются в следующем порядке:<ul><li>Устройство по умолчанию для заданного формата</li>
<li>Устройство 1</li>
<li>Устройство 2</li>
<li>...</li>
<li>Устройство n</li>
<li>Захват из файла</li>
</ul>Для отработки клика по последнему пункту используется сообщение <b>LB_GETITEMRECT</b>, которое получает координаты и размер пункта в списке. Если этого не сделать то клик за пределами листа, если внизу есть пустое пространство будет равносилен клику на последнем пункте. В обработчике кнопки настроек в главной формы <b>frmTrickVocoder</b> мы проверяем устройство захвата и либо открываем файл для конвертации либо инициализируем захват. Для регулировки громкости и подмешивания используем логарифмическую шкалу, т.к. чувствительность человеческого слуха нелинейна. Вот в принципе и все. Спасибо за внимание.<br />
<div align="center"><img src="https://www.cyberforum.ru/blog_attachment.php?attachmentid=2901&amp;d=1417438631" border="0" alt="Название: Безымянный.png
Просмотров: 3846

Размер: 24.5 Кб" style="margin: 5px" /></div></div>


<!-- attachments -->
	<div style="margin-top:10px">

		
		
		
		
			<fieldset class="fieldset">
				<legend>Вложения</legend>
				<table cellpadding="0" cellspacing="3" border="0">
				<tr>
	<td><img loading="lazy" decoding="async" class="inlineimg" src="http://www.cyberforum.ru//cyberstatic.net/images/attach/rar.gif" alt="Тип файла: rar" width="16" height="16" border="0" style="vertical-align:baseline" /></td>
	<td><a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=3157&amp;d=1429796503">TrickVocoder.rar</a> (1.21 Мб, 525 просмотров)</td>
</tr>
				</table>
			</fieldset>
		

	</div>
<!-- / attachments -->
]]></content:encoded>
			<dc:creator>The trick</dc:creator>
			<guid isPermaLink="true">https://www.cyberforum.ru/blogs/354370/3009.html</guid>
		</item>
		<item>
			<title>Вокодер на VB6 часть 1</title>
			<link>https://www.cyberforum.ru/blogs/354370/2998.html</link>
			<pubDate>Mon, 01 Dec 2014 16:08:10 GMT</pubDate>
			<description>KCXZpd-pEkg 
Всем привет. Создавая музыку, я видел много разных виртуальных инструментов и...</description>
			<content:encoded><![CDATA[<div><div align="center"><iframe width="640" height="360" src="https://www.youtube.com/embed/KCXZpd-pEkg" frameborder="0" allowfullscreen></iframe></div>Всем привет. Создавая музыку, я видел много разных виртуальных инструментов и эффектов. Одним из интереснейших эффектов является вокодер, который позволяет промодулировать голос и сделать его например похожим на голос робота или что-то в этом духе. Вокодер изначально использовался для сжатия речевой информации, а после его начали применять в музыкальной сфере. Т.к. у меня появилось свободное время, я решил написать что-то подобное ради эксперимента и подробно описать этапы разработки на VB6.<br />
Итак, взглянем на простейшую схему вокодера:<br />
<div align="center"><a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=2882&amp;d=1417305997" rel="Lightbox" id="attachment2882" ><img src="https://www.cyberforum.ru/blog_attachment.php?attachmentid=2882&amp;thumb=1&amp;d=1417305997" class="thumbnail" border="0" alt="Нажмите на изображение для увеличения
Название: Схема.png
Просмотров: 912
Размер:	37.3 Кб
ID:	2882" style="margin: 5px" /></a></div>Сигнал с микрофона (речь), подается на банк полосовых фильтров, каждый из которых пропускает только небольшую часть диапазона частот речевого сигнала. Чем больше количество фильтров - тем лучше разборчивость речи. В тоже время несущий сигнал (например пилообразный) также пропускается через аналогичный банк фильтров. С выходов фильтров речевого сигнала сигнал поступает на детекторы огибающей которые управляют модуляторами, а с выходов фильтров несущей сигнал поступает на другие входы модуляторов. В итоге каждая полоса речевого сигнала регулирует уровень соответствующей полосы несущей (модулирует ее). После сигнал выходной сигнал со всех модуляторов смешивается и попадает на выход. Для повышения разборчивости речи также применяют дополнительные блоки, вроде детектора &quot;шипящих&quot; звуков. Итак, чтобы начать разработку нужно определиться с исходными сигналами, откуда их будем брать. Можно к примеру захватить данные из файла или напрямую обрабатывать в реальном времени с микрофонного или линейного входа. Для тестирования очень удобно пользоваться файлом, поэтому мы сделаем и так и так. В качестве несущей будем использовать внешний файл зацикленный по кругу, для регулировки тональности просто добавим возможность изменения скорости воспроизведения, что позволит менять тональность. Для захвата звука из файла будем использовать <b>Audio Compression Manager</b> (<b>ACM</b>), с ним очень удобно производить конвертирование между форматами (т.к. файл может быть любого формата, то пришлось бы писать несколько функций для разных форматов). Может так оказаться что для конвертирования в нужный формат не окажется нужного <b>ACM</b> драйвера, тогда воспроизведение этого файла будет недоступным (хотя можно это попробовать сделать в 2 этапа). В качестве входных файлов будем использовать wav - файлы, т.к. для работы с ними в системе есть специальные функции облегчающие получение данных из них. Вот сам исходный код класса <b>clsTrickWavConverter</b>:<br />
<div class="codeblock"><table class="vb"><thead><tr><td colspan="2" id="478490057"  class="head">Visual Basic</td></tr></thead><tbody><tr class="li1"><td><div id="478490057" style="height: 350px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
</pre></td><td class="de1"><pre class="de1"><span class="co1">' clsTrickWavConverter - класс для конвертации Wav файлов используя ACM</span>
<span class="co1">' © Кривоус Анатолий Анатольевич (The trick), 2014</span>
&nbsp;
<span class="kw2">Option</span> <span class="kw2">Explicit</span>
&nbsp;
<span class="kw2">Private</span> <span class="kw4">Type</span> WAVEFORMATEX
&nbsp; &nbsp; wFormatTag &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Integer</span>
&nbsp; &nbsp; nChannels &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Integer</span>
&nbsp; &nbsp; nSamplesPerSec &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; nAvgBytesPerSec <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; nBlockAlign &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Integer</span>
&nbsp; &nbsp; wBitsPerSample &nbsp;<span class="kw4">As</span> <span class="kw1">Integer</span>
&nbsp; &nbsp; cbSize &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Integer</span>
<span class="kw3">End</span> <span class="kw4">Type</span>
&nbsp;
<span class="kw2">Private</span> <span class="kw4">Type</span> ACMSTREAMHEADER
&nbsp; &nbsp; cbStruct &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; fdwStatus &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; lpdwUser &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; lppbSrc &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; cbSrcLength &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; cbSrcLengthUsed <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; lpdwSrcUser &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; lppbDst &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; cbDstLength &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; cbDstLengthUsed <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; lpdwDstUser &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; dwDriver(9) &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw3">End</span> <span class="kw4">Type</span>
&nbsp;
<span class="kw2">Private</span> <span class="kw4">Type</span> MMCKINFO
&nbsp; &nbsp; ckid &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; ckSize &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; fccType &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; dwDataOffset &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; dwFlags &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw3">End</span> <span class="kw4">Type</span>
&nbsp;
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Function</span> acmStreamClose Lib <span class="st0">&quot;msacm32&quot;</span> (<span class="kw4">ByVal</span> has <span class="kw4">As</span> <span class="kw1">Long</span>, <span class="kw4">ByVal</span> fdwClose <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Function</span> acmStreamConvert Lib <span class="st0">&quot;msacm32&quot;</span> (<span class="kw4">ByVal</span> has <span class="kw4">As</span> <span class="kw1">Long</span>, <span class="kw4">ByRef</span> pash <span class="kw4">As</span> ACMSTREAMHEADER, <span class="kw4">ByVal</span> fdwConvert <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Function</span> acmStreamMessage Lib <span class="st0">&quot;msacm32&quot;</span> (<span class="kw4">ByVal</span> has <span class="kw4">As</span> <span class="kw1">Long</span>, <span class="kw4">ByVal</span> uMsg <span class="kw4">As</span> <span class="kw1">Long</span>, <span class="kw4">ByVal</span> lParam1 <span class="kw4">As</span> <span class="kw1">Long</span>, <span class="kw4">ByVal</span> lParam2 <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Function</span> acmStreamOpen Lib <span class="st0">&quot;msacm32&quot;</span> (phas <span class="kw4">As</span> Any, <span class="kw4">ByVal</span> had <span class="kw4">As</span> <span class="kw1">Long</span>, pwfxSrc <span class="kw4">As</span> WAVEFORMATEX, pwfxDst <span class="kw4">As</span> WAVEFORMATEX, pwfltr <span class="kw4">As</span> Any, dwCallback <span class="kw4">As</span> Any, dwInstance <span class="kw4">As</span> Any, <span class="kw4">ByVal</span> fdwOpen <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Function</span> acmStreamPrepareHeader Lib <span class="st0">&quot;msacm32&quot;</span> (<span class="kw4">ByVal</span> has <span class="kw4">As</span> <span class="kw1">Long</span>, <span class="kw4">ByRef</span> pash <span class="kw4">As</span> ACMSTREAMHEADER, <span class="kw4">ByVal</span> fdwPrepare <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Function</span> acmStreamReset Lib <span class="st0">&quot;msacm32&quot;</span> (<span class="kw4">ByVal</span> has <span class="kw4">As</span> <span class="kw1">Long</span>, <span class="kw4">ByVal</span> fdwReset <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Function</span> acmStreamSize Lib <span class="st0">&quot;msacm32&quot;</span> (<span class="kw4">ByVal</span> has <span class="kw4">As</span> <span class="kw1">Long</span>, <span class="kw4">ByVal</span> cbInput <span class="kw4">As</span> <span class="kw1">Long</span>, <span class="kw4">ByRef</span> pdwOutputBytes <span class="kw4">As</span> <span class="kw1">Long</span>, <span class="kw4">ByVal</span> fdwSize <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Function</span> acmStreamUnprepareHeader Lib <span class="st0">&quot;msacm32&quot;</span> (<span class="kw4">ByVal</span> has <span class="kw4">As</span> <span class="kw1">Long</span>, <span class="kw4">ByRef</span> pash <span class="kw4">As</span> ACMSTREAMHEADER, <span class="kw4">ByVal</span> fdwUnprepare <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp;
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Function</span> mmioClose Lib <span class="st0">&quot;winmm.dll&quot;</span> (<span class="kw4">ByVal</span> hmmio <span class="kw4">As</span> <span class="kw1">Long</span>, <span class="kw4">ByVal</span> uFlags <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Function</span> mmioDescend Lib <span class="st0">&quot;winmm.dll&quot;</span> (<span class="kw4">ByVal</span> hmmio <span class="kw4">As</span> <span class="kw1">Long</span>, lpck <span class="kw4">As</span> MMCKINFO, lpckParent <span class="kw4">As</span> Any, <span class="kw4">ByVal</span> uFlags <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Function</span> mmioOpen Lib <span class="st0">&quot;winmm.dll&quot;</span> Alias <span class="st0">&quot;mmioOpenA&quot;</span> (<span class="kw4">ByVal</span> szFileName <span class="kw4">As</span> <span class="kw1">String</span>, lpmmioinfo <span class="kw4">As</span> Any, <span class="kw4">ByVal</span> dwOpenFlags <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Function</span> mmioRead Lib <span class="st0">&quot;winmm.dll&quot;</span> (<span class="kw4">ByVal</span> hmmio <span class="kw4">As</span> <span class="kw1">Long</span>, pch <span class="kw4">As</span> Any, <span class="kw4">ByVal</span> cch <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Function</span> mmioStringToFOURCC Lib <span class="st0">&quot;winmm.dll&quot;</span> Alias <span class="st0">&quot;mmioStringToFOURCCA&quot;</span> (<span class="kw4">ByVal</span> sz <span class="kw4">As</span> <span class="kw1">String</span>, <span class="kw4">ByVal</span> uFlags <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Function</span> mmioAscend Lib <span class="st0">&quot;winmm.dll&quot;</span> (<span class="kw4">ByVal</span> hmmio <span class="kw4">As</span> <span class="kw1">Long</span>, lpck <span class="kw4">As</span> MMCKINFO, <span class="kw4">ByVal</span> uFlags <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp;
<span class="kw2">Private</span> Const MMIO_READ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span> = &amp;H0
<span class="kw2">Private</span> Const MMIO_FINDCHUNK &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span> = &amp;H10
<span class="kw2">Private</span> Const MMIO_FINDRIFF &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span> = &amp;H20
<span class="kw2">Private</span> Const ACM_STREAMOPENF_QUERY &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span> = &amp;H1
<span class="kw2">Private</span> Const ACM_STREAMSIZEF_DESTINATION &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span> = &amp;H1&amp;
<span class="kw2">Private</span> Const ACM_STREAMSIZEF_SOURCE &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span> = &amp;H0&amp;
<span class="kw2">Private</span> Const ACM_STREAMCONVERTF_BLOCKALIGN <span class="kw4">As</span> <span class="kw1">Long</span> = &amp;H4
<span class="kw2">Private</span> Const ACM_STREAMCONVERTF_START &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span> = &amp;H10
&nbsp;
<span class="kw2">Private</span> mInpFmt &nbsp; &nbsp; <span class="kw4">As</span> WAVEFORMATEX &nbsp; &nbsp; <span class="co1">' Входной формат, определяется файлом</span>
<span class="kw2">Private</span> mOutFmt &nbsp; &nbsp; <span class="kw4">As</span> WAVEFORMATEX &nbsp; &nbsp; <span class="co1">' Выходной формат, определяется пользователем</span>
<span class="kw2">Private</span> mDataSize &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' Размер данных в байтах</span>
<span class="kw2">Private</span> bufIdx &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' Текущая позиция во входном буфере</span>
<span class="kw2">Private</span> buffer() &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Byte</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' Буфер</span>
<span class="kw2">Private</span> hStream &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' Описатель потока сжатия</span>
<span class="kw2">Private</span> mInit &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Boolean</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">' Инициализирован ли ACM</span>
&nbsp;
<span class="co1">' // Входной формат</span>
<span class="kw2">Public</span> <span class="kw4">Property</span> <span class="kw4">Get</span> InputNumOfChannels() <span class="kw4">As</span> <span class="kw1">Integer</span>
&nbsp; &nbsp; InputNumOfChannels = mInpFmt.nChannels
<span class="kw3">End</span> <span class="kw4">Property</span>
<span class="kw2">Public</span> <span class="kw4">Property</span> <span class="kw4">Get</span> InputSamplesPerSecond() <span class="kw4">As</span> <span class="kw1">Integer</span>
&nbsp; &nbsp; InputSamplesPerSecond = mInpFmt.nSamplesPerSec
<span class="kw3">End</span> <span class="kw4">Property</span>
<span class="kw2">Public</span> <span class="kw4">Property</span> <span class="kw4">Get</span> InputBitPerSample() <span class="kw4">As</span> <span class="kw1">Integer</span>
&nbsp; &nbsp; InputBitPerSample = mInpFmt.wBitsPerSample
<span class="kw3">End</span> <span class="kw4">Property</span>
&nbsp;
<span class="co1">' // Размер входных данных</span>
<span class="kw2">Public</span> <span class="kw4">Property</span> <span class="kw4">Get</span> InputDataSize() <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; InputDataSize = mDataSize
<span class="kw3">End</span> <span class="kw4">Property</span>
&nbsp;
<span class="co1">' // Текущая позиция в файле в отсчетах</span>
<span class="kw2">Public</span> <span class="kw4">Property</span> <span class="kw4">Get</span> InputCurrentPosition() <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; InputCurrentPosition = bufIdx / mInpFmt.nBlockAlign
<span class="kw3">End</span> <span class="kw4">Property</span>
<span class="kw2">Public</span> <span class="kw4">Property</span> <span class="kw4">Let</span> InputCurrentPosition(<span class="kw4">ByVal</span> Value <span class="kw4">As</span> <span class="kw1">Long</span>)
&nbsp; &nbsp; <span class="kw4">Dim</span> index <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; index = Value * mInpFmt.nBlockAlign
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> index &gt;= mDataSize <span class="kw3">Or</span> index &lt; 0 <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; err.Raise 5
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">Exit</span> <span class="kw4">Property</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; bufIdx = index
<span class="kw3">End</span> <span class="kw4">Property</span>
&nbsp;
<span class="co1">' // Выходной формат</span>
<span class="kw2">Public</span> <span class="kw4">Property</span> <span class="kw4">Get</span> OutputNumOfChannels() <span class="kw4">As</span> <span class="kw1">Integer</span>
&nbsp; &nbsp; OutputNumOfChannels = mOutFmt.nChannels
<span class="kw3">End</span> <span class="kw4">Property</span>
<span class="kw2">Public</span> <span class="kw4">Property</span> <span class="kw4">Get</span> OutputSamplesPerSecond() <span class="kw4">As</span> <span class="kw1">Integer</span>
&nbsp; &nbsp; OutputSamplesPerSecond = mOutFmt.nSamplesPerSec
<span class="kw3">End</span> <span class="kw4">Property</span>
<span class="kw2">Public</span> <span class="kw4">Property</span> <span class="kw4">Get</span> OutputBitPerSample() <span class="kw4">As</span> <span class="kw1">Integer</span>
&nbsp; &nbsp; OutputBitPerSample = mOutFmt.wBitsPerSample
<span class="kw3">End</span> <span class="kw4">Property</span>
&nbsp;
<span class="co1">' // Отношение размеров</span>
<span class="kw2">Public</span> <span class="kw4">Property</span> <span class="kw4">Get</span> Rate() <span class="kw4">As</span> <span class="kw1">Single</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> outLen &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="co1">' Проверка на инициализированность</span>
 &nbsp; &nbsp;<span class="kw3">If</span> <span class="kw3">Not</span> mInit <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> <span class="kw3">Not</span> Init() <span class="kw3">Then</span> <span class="kw2">Exit</span> <span class="kw4">Property</span>
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; acmStreamSize hStream, mDataSize, outLen, ACM_STREAMSIZEF_SOURCE
&nbsp; &nbsp; Rate = outLen / mDataSize
<span class="kw3">End</span> <span class="kw4">Property</span>
&nbsp;
<span class="co1">' // Задать формат</span>
<span class="kw2">Public</span> <span class="kw2">Function</span> SetFormat(<span class="kw4">ByVal</span> NumOfChannels <span class="kw4">As</span> <span class="kw1">Integer</span>, <span class="kw4">ByVal</span> SamplesPerSecond <span class="kw4">As</span> <span class="kw1">Long</span>, <span class="kw4">ByVal</span> BitPerSample <span class="kw4">As</span> <span class="kw1">Integer</span>) <span class="kw4">As</span> <span class="kw1">Boolean</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> outFmt &nbsp;<span class="kw4">As</span> WAVEFORMATEX
&nbsp; &nbsp; <span class="kw4">Dim</span> ret &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="co1">' Проверяем формат</span>
 &nbsp; &nbsp;<span class="kw3">With</span> outFmt
&nbsp; &nbsp; &nbsp; &nbsp; .wFormatTag = 1
&nbsp; &nbsp; &nbsp; &nbsp; .nChannels = NumOfChannels
&nbsp; &nbsp; &nbsp; &nbsp; .nSamplesPerSec = SamplesPerSecond
&nbsp; &nbsp; &nbsp; &nbsp; .wBitsPerSample = BitPerSample
&nbsp; &nbsp; &nbsp; &nbsp; .nBlockAlign = .wBitsPerSample \ 8 * .nChannels
&nbsp; &nbsp; &nbsp; &nbsp; .nAvgBytesPerSec = .nSamplesPerSec * .nBlockAlign
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">With</span>
&nbsp; &nbsp; <span class="co1">' Если открыт файл</span>
 &nbsp; &nbsp;<span class="kw3">If</span> mDataSize <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' Запрашиваем у менеджера сжатия, может ли он преобразовать этот формат в нужный нам</span>
 &nbsp; &nbsp; &nbsp; &nbsp;ret = acmStreamOpen(<span class="kw4">ByVal</span> 0&amp;, 0, mInpFmt, outFmt, <span class="kw4">ByVal</span> 0&amp;, <span class="kw4">ByVal</span> 0&amp;, <span class="kw4">ByVal</span> 0&amp;, ACM_STREAMOPENF_QUERY)
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> ret <span class="kw3">Then</span> <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' Закрываем активный поток</span>
 &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw3">If</span> hStream <span class="kw3">Then</span> acmStreamClose hStream, 0
&nbsp; &nbsp; &nbsp; &nbsp; mInit = <span class="kw5">False</span>
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp;
&nbsp; &nbsp; mOutFmt = outFmt
&nbsp; &nbsp; SetFormat = <span class="kw5">True</span>
&nbsp; &nbsp; 
<span class="kw3">End</span> <span class="kw2">Function</span>
&nbsp;
<span class="co1">' // Читает Wav файл и проверяет возможность перекодировать в выходной формат</span>
<span class="kw2">Public</span> <span class="kw2">Function</span> ReadWaveFile(strFileName <span class="kw4">As</span> <span class="kw1">String</span>) <span class="kw4">As</span> <span class="kw1">Boolean</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> hIn &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> inf &nbsp; &nbsp; <span class="kw4">As</span> MMCKINFO
&nbsp; &nbsp; <span class="kw4">Dim</span> sInf &nbsp; &nbsp;<span class="kw4">As</span> MMCKINFO
&nbsp; &nbsp; <span class="kw4">Dim</span> inpFmt &nbsp;<span class="kw4">As</span> WAVEFORMATEX
&nbsp; &nbsp; <span class="kw4">Dim</span> ret &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="co1">' Читаем файл</span>
 &nbsp; &nbsp;hIn = mmioOpen(strFileName, <span class="kw4">ByVal</span> 0, MMIO_READ)
&nbsp; &nbsp; <span class="kw3">If</span> (hIn = 0) <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; MsgBox <span class="st0">&quot;Error opening file&quot;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; <span class="co1">' Ищем чанк WAVE</span>
 &nbsp; &nbsp;inf.fccType = mmioStringToFOURCC(<span class="st0">&quot;WAVE&quot;</span>, 0)
&nbsp; &nbsp; <span class="kw3">If</span> mmioDescend(hIn, inf, <span class="kw4">ByVal</span> 0, MMIO_FINDRIFF) <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; mmioClose hIn, 0
&nbsp; &nbsp; &nbsp; &nbsp; MsgBox <span class="st0">&quot;Is not valid file&quot;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; <span class="co1">' Ищем чанк fmt, определяющий формат данных</span>
 &nbsp; &nbsp;sInf.ckid = mmioStringToFOURCC(<span class="st0">&quot;fmt&quot;</span>, 0)
&nbsp; &nbsp; <span class="kw3">If</span> mmioDescend(hIn, sInf, inf, MMIO_FINDCHUNK) <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; mmioClose hIn, 0
&nbsp; &nbsp; &nbsp; &nbsp; MsgBox <span class="st0">&quot;Format chunk not found&quot;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; <span class="co1">' Проверяем размер</span>
 &nbsp; &nbsp;<span class="kw3">If</span> sInf.ckSize &gt; Len(inpFmt) <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; mmioClose hIn, 0
&nbsp; &nbsp; &nbsp; &nbsp; MsgBox <span class="st0">&quot;Not supported format&quot;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; <span class="co1">' Читаем формат</span>
 &nbsp; &nbsp;<span class="kw3">If</span> mmioRead(hIn, inpFmt, sInf.ckSize) = -1 <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; mmioClose hIn, 0
&nbsp; &nbsp; &nbsp; &nbsp; MsgBox <span class="st0">&quot;Can't read format&quot;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; <span class="co1">' Запрашиваем у менеджера сжатия, может ли он преобразовать этот формат в нужный нам</span>
 &nbsp; &nbsp;ret = acmStreamOpen(<span class="kw4">ByVal</span> 0&amp;, 0, inpFmt, mOutFmt, <span class="kw4">ByVal</span> 0&amp;, <span class="kw4">ByVal</span> 0&amp;, <span class="kw4">ByVal</span> 0&amp;, ACM_STREAMOPENF_QUERY)
&nbsp; &nbsp; <span class="kw3">If</span> ret <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; mmioClose hIn, 0
&nbsp; &nbsp; &nbsp; &nbsp; MsgBox <span class="st0">&quot;Can't convert wav file&quot;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; <span class="co1">' Выходим из чанка fmt</span>
 &nbsp; &nbsp;mmioAscend hIn, sInf, 0
&nbsp; &nbsp; <span class="co1">' Ищем чанк data с данными</span>
 &nbsp; &nbsp;sInf.ckid = mmioStringToFOURCC(<span class="st0">&quot;data&quot;</span>, 0)
&nbsp; &nbsp; <span class="kw3">If</span> mmioDescend(hIn, sInf, inf, MMIO_FINDCHUNK) <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; mmioClose hIn, 0
&nbsp; &nbsp; &nbsp; &nbsp; MsgBox <span class="st0">&quot;Wave data not found&quot;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; <span class="co1">' Проверяем размер</span>
 &nbsp; &nbsp;<span class="kw3">If</span> sInf.ckSize &lt;= 0 <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; mmioClose hIn, 0
&nbsp; &nbsp; &nbsp; &nbsp; MsgBox <span class="st0">&quot;Invalid data size&quot;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; <span class="co1">' Выделяем буфер и читаем данные</span>
 &nbsp; &nbsp;<span class="kw4">ReDim</span> buffer(sInf.ckSize - 1)
&nbsp; &nbsp; <span class="kw3">If</span> mmioRead(hIn, buffer(0), sInf.ckSize) = -1 <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; mmioClose hIn, 0
&nbsp; &nbsp; &nbsp; &nbsp; MsgBox <span class="st0">&quot;Can't read data&quot;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; <span class="co1">' Закрываем файл</span>
 &nbsp; &nbsp;mmioClose hIn, 0
&nbsp; &nbsp; <span class="co1">' Инициализация переменных</span>
 &nbsp; &nbsp;mDataSize = sInf.ckSize
&nbsp; &nbsp; bufIdx = 0
&nbsp; &nbsp; mInpFmt = inpFmt
&nbsp; &nbsp; ReadWaveFile = <span class="kw5">True</span>
&nbsp; &nbsp; 
<span class="kw3">End</span> <span class="kw2">Function</span>
&nbsp;
<span class="co1">' // Получить сконвертированные данные</span>
<span class="kw2">Public</span> <span class="kw2">Function</span> Convert(<span class="kw4">ByVal</span> lpOutData <span class="kw4">As</span> <span class="kw1">Long</span>, <span class="kw4">ByVal</span> dwCountBytes <span class="kw4">As</span> <span class="kw1">Long</span>, dwCountRead <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> <span class="kw1">Boolean</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> ret &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> inpCountBytes &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> acmHdr &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> ACMSTREAMHEADER
&nbsp; &nbsp; <span class="co1">' Проверка на инициализированность</span>
 &nbsp; &nbsp;<span class="kw3">If</span> <span class="kw3">Not</span> mInit <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> <span class="kw3">Not</span> Init() <span class="kw3">Then</span> <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; <span class="co1">' Узнаем нужное количество данных во входном буфере для текущего запроса</span>
 &nbsp; &nbsp;ret = acmStreamSize(hStream, dwCountBytes, inpCountBytes, ACM_STREAMSIZEF_DESTINATION)
&nbsp; &nbsp; <span class="kw3">If</span> ret <span class="kw3">Then</span> <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; <span class="co1">' Корректируем размер с учетом выхода за пределы</span>
 &nbsp; &nbsp;<span class="kw3">If</span> inpCountBytes + bufIdx &gt;= mDataSize <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; inpCountBytes = mDataSize - bufIdx
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> inpCountBytes &lt;= 0 <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Convert = <span class="kw5">True</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; dwCountRead = 0
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; <span class="co1">' Заполняем заголовок преобразования</span>
 &nbsp; &nbsp;<span class="kw3">With</span> acmHdr
&nbsp; &nbsp; &nbsp; &nbsp; .cbStruct = Len(acmHdr)
&nbsp; &nbsp; &nbsp; &nbsp; .lppbDst = lpOutData
&nbsp; &nbsp; &nbsp; &nbsp; .lppbSrc = VarPtr(buffer(bufIdx))
&nbsp; &nbsp; &nbsp; &nbsp; .cbDstLength = dwCountBytes
&nbsp; &nbsp; &nbsp; &nbsp; .cbSrcLength = inpCountBytes
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">With</span>
&nbsp; &nbsp; <span class="co1">' Подготавливаем к перекодировке</span>
 &nbsp; &nbsp;ret = acmStreamPrepareHeader(hStream, acmHdr, 0)
&nbsp; &nbsp; <span class="kw3">If</span> ret <span class="kw3">Then</span> <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; <span class="co1">' Перекодируем</span>
 &nbsp; &nbsp;ret = acmStreamConvert(hStream, acmHdr, ACM_STREAMCONVERTF_BLOCKALIGN)
&nbsp; &nbsp; <span class="co1">' Освобождаем</span>
 &nbsp; &nbsp;acmStreamUnprepareHeader hStream, acmHdr, 0
&nbsp; &nbsp; <span class="kw3">If</span> ret <span class="kw3">Then</span> <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; <span class="co1">' Возвращаем реальное число прочитанных байт</span>
 &nbsp; &nbsp;dwCountRead = acmHdr.cbDstLengthUsed
&nbsp; &nbsp; bufIdx = bufIdx + acmHdr.cbSrcLengthUsed
&nbsp; &nbsp; <span class="co1">' Успех</span>
 &nbsp; &nbsp;Convert = <span class="kw5">True</span>
&nbsp; &nbsp; 
<span class="kw3">End</span> <span class="kw2">Function</span>
&nbsp;
<span class="co1">' // Инициализация потока ACM</span>
<span class="kw2">Private</span> <span class="kw2">Function</span> Init() <span class="kw4">As</span> <span class="kw1">Boolean</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> ret <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="co1">' Открываем поток для нужного преобразования</span>
 &nbsp; &nbsp;ret = acmStreamOpen(hStream, 0, mInpFmt, mOutFmt, <span class="kw4">ByVal</span> 0&amp;, <span class="kw4">ByVal</span> 0&amp;, <span class="kw4">ByVal</span> 0&amp;, 0)
&nbsp; &nbsp; <span class="kw3">If</span> ret <span class="kw3">Then</span> <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; Init = <span class="kw5">True</span>
&nbsp; &nbsp; mInit = <span class="kw5">True</span>
<span class="kw3">End</span> <span class="kw2">Function</span>
&nbsp;
<span class="kw2">Private</span> <span class="kw2">Sub</span> Class_Initialize()
&nbsp; &nbsp; <span class="co1">' Выходной формат по умолчанию</span>
 &nbsp; &nbsp;<span class="kw3">With</span> mOutFmt
&nbsp; &nbsp; &nbsp; &nbsp; .wFormatTag = 1
&nbsp; &nbsp; &nbsp; &nbsp; .nChannels = 1
&nbsp; &nbsp; &nbsp; &nbsp; .nSamplesPerSec = SampleRate
&nbsp; &nbsp; &nbsp; &nbsp; .wBitsPerSample = 16
&nbsp; &nbsp; &nbsp; &nbsp; .nBlockAlign = .wBitsPerSample \ 8 * .nChannels
&nbsp; &nbsp; &nbsp; &nbsp; .nAvgBytesPerSec = .nSamplesPerSec * .nBlockAlign
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">With</span>
<span class="kw3">End</span> <span class="kw2">Sub</span>
&nbsp;
<span class="kw2">Private</span> <span class="kw2">Sub</span> Class_Terminate()
&nbsp; &nbsp; <span class="kw3">If</span> hStream <span class="kw3">Then</span> acmStreamClose hStream, 0
<span class="kw3">End</span> <span class="kw2">Sub</span></pre></td></tr></table></div></td></tr></tbody></table></div>Разберем подробно код. Для открытия файла служит метод <b>ReadWaveFile</b>, в качестве аргумента он принимает имя wav-файла. Файл с расширением <b>.wav</b> представляет собой файл в формате <a rel="nofollow noopener noreferrer" href="https://ru.wikipedia.org/wiki/RIFF" target="_blank" title="https://ru.wikipedia.org/wiki/RIFF">RIFF</a>, который в свою очередь состоит из блоков, называемых чанками (<b>chunk</b>). Итак мы открываем файл с помощью функции <b>mmioOpen</b>, которая возвращает хендл файла, который можно использовать с функциями работы с <b>RIFF</b> файлами. Если все прошло успешно, то мы начинаем поиск чанка с типом <b>WAVE</b>, для этого мы вызываем функцию <b>mmioDescend</b>, которая заполняет структуру <b>MMCKINFO</b> информацией о чанке, если он найден. В качестве идентификатора чанка используется структура <b>FOURCC</b>, которая представляет собой 4 <b>ASCII</b> символа, которые упакованы в 32-разрядное число (в нашем случае Long). В качестве родительского чанка используем <b>NULL</b>, т.к. у нас не вложенный чанк, а в качестве флага передаем <b>MMIO_FINDRIFF</b>, который задает поиск чанка <b>RIFF</b> с заданным типом (в нашем случае <b>WAVE</b>). Итак, если функция <b>mmioDescend</b> отработала успешно, то наш <b>RIFF</b>-файл является <b>WAVE</b>-файлом, и можно переходить к получению формата данных. Формат данных хранится в чанке <b>fmt</b>, внутри чанка <b>WAVE</b> (вложенный чанк). Для получения этого чанка, мы вызываем опять-таки <b>mmioDescend</b>, только в качестве родительского чанка передаем только что найденный <b>WAVE</b>-чанк, а в качестве флага - <b>MMIO_FINDCHUNK</b>, который заставляет искать указанный чанк. В случае успеха, проверяем размер чанка, он должен соответствовать размеру структуры <b>WAVEFORMATEX</b>, и если все нормально читаем данные чанка (которые представляют собой структуру <b>WAVEFORMATEX</b>) посредством вызова <b>mmioRead</b>. Итак, теперь нам нужно убедиться, сможет ли <b>ACM</b> конвертировать данные из этого формата в нужный нам. Для этого мы вызываем функцию <b>acmStreamOpen</b> с флагом <b>ACM_STREAMOPENF_QUERY</b>, который позволяет запросить сможет ли <b>ACM</b> преобразовать данные между двумя форматами. В случае успеха начинаем разбор дальше. Итак мы сейчас находимся внутри <b>fmt</b> чанка, нам нужно опять вернуться в <b>WAVE</b> чанк, чтобы запросить чанк с данными. Для этого мы вызываем функцию <b>mmioAscend</b>. Далее, также как мы делали с <b>fmt</b> чанком, такую же последовательность действий повторяем для <b>data</b> чанка, который содержит непосредственно данные в формате <b>fmt</b> чанка. Данные читаем в буфер <b>buffer()</b>, обнуляем указатель в массиве на начало данных (<b>bufIdx</b>) и заполняем структуру с исходным форматом.<br />
Для задания выходного формата служит метод <b>SetFormat</b>, который проверяет возможность конвертирования в формат файла, если он был открыт. Основная функция класса <b>clsTrickWavConverter</b> - <b>Convert</b>, которая конвертирует данные из буфера по смещению <b>bufIdx</b> в нужный нам формат. Рассмотрим подробнее как она работает. При первом конвертировании поток преобразования еще не открыт (переменная <b>mInit</b> определяет инициализированность потока преобразования), поэтому мы вызываем метод <b>Init</b> который открывает поток преобразования через <b>acmStreamOpen</b>. Первым параметром передается указатель на хендл потока (<b>hStream</b>) - в него функция вернет хендл в случае успеха и его мы будем использовать для конвертации. В случае успешной инициализации потока мы определяем размер данных, необходимых что-бы произвести конвертацию. Т.к. вызывающая сторона передает указатель на буфер и его длину в байтах, нам нужно корректно заполнить буфер, не выходя за пределы. Для этого мы вызываем функцию <b>acmStreamSize</b>, которая возвращает необходимый размер данных для конвертации. В качестве флага мы передаем <b>ACM_STREAMSIZEF_DESTINATION</b>, что обозначает получение размера данных в байтах исходного буфера на основании размера выходного буфера. Далее мы корректируем размер с учетом выхода за пределы исходного буфера, т.к. возможно что исходный файл например слишком короткий или мы читаем данные около конца буфера. Далее мы заполняем заголовок <b>ACMSTREAMHEADER</b> описывающий данные преобразования и подготавливаем (фиксируем) его к конвертации с помощью функции <b>acmStreamPrepareHeader</b>. После этого мы вызываем <b>acmStreamConvert</b>, которая выполняет конвертацию. Флаг <b>ACM_STREAMCONVERTF_BLOCKALIGN</b> обозначает то, что мы конвертируем целое число блоков, в данном случае размер блока  - <b>mInpFmt.nBlockAlign</b>. После конвертации мы должны отменить фиксацию через <b>acmStreamUnprepareHeader</b> и возвращаем число возвращенных байтов, также передвигаем указатель в исходном буфере на число обработанных байт.<br />
В качестве захвата/воспроизведения звука используем класс <b>clsTrickSound</b> для работы со звуком посредством <b>winmm</b>:<br />
<div class="codeblock"><table class="vb"><thead><tr><td colspan="2" id="71375908"  class="head">Visual Basic</td></tr></thead><tbody><tr class="li1"><td><div id="71375908" style="height: 350px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
</pre></td><td class="de1"><pre class="de1"><span class="co1">' clsTrickSound - класс для захвата и воспроизведения звука</span>
<span class="co1">' © Кривоус Анатолий Анатольевич (The trick), 2014</span>
&nbsp;
<span class="kw2">Option</span> <span class="kw2">Explicit</span>
&nbsp;
<span class="kw2">Private</span> <span class="kw1">Enum</span> MMRESULT
&nbsp; &nbsp; MMSYSERR_NOERROR = 0
&nbsp; &nbsp; MMSYSERR_ERROR = 1
&nbsp; &nbsp; MMSYSERR_BADDEVICEID = 2
&nbsp; &nbsp; MMSYSERR_NOTENABLED = 3
&nbsp; &nbsp; MMSYSERR_ALLOCATED = 4
&nbsp; &nbsp; MMSYSERR_INVALHANDLE = 5
&nbsp; &nbsp; MMSYSERR_NODRIVER = 6
&nbsp; &nbsp; MMSYSERR_NOMEM = 7
&nbsp; &nbsp; MMSYSERR_NOTSUPPORTED = 8
&nbsp; &nbsp; MMSYSERR_BADERRNUM = 9
&nbsp; &nbsp; MMSYSERR_INVALFLAG = 10
&nbsp; &nbsp; MMSYSERR_INVALPARAM = 11
&nbsp; &nbsp; MMSYSERR_HANDLEBUSY = 12
&nbsp; &nbsp; MMSYSERR_INVALIDALIAS = 13
&nbsp; &nbsp; MMSYSERR_BADDB = 14
&nbsp; &nbsp; MMSYSERR_KEYNOTFOUND = 15
&nbsp; &nbsp; MMSYSERR_READERROR = 16
&nbsp; &nbsp; MMSYSERR_WRITEERROR = 17
&nbsp; &nbsp; MMSYSERR_DELETEERROR = 18
&nbsp; &nbsp; MMSYSERR_VALNOTFOUND = 19
&nbsp; &nbsp; MMSYSERR_NODRIVERCB = 20
&nbsp; &nbsp; WAVERR_BADFORMAT = 32
&nbsp; &nbsp; WAVERR_STILLPLAYING = 33
&nbsp; &nbsp; WAVERR_UNPREPARED = 34
&nbsp; &nbsp; MMRESULT_END
<span class="kw3">End</span> <span class="kw1">Enum</span>
&nbsp;
<span class="kw2">Public</span> <span class="kw1">Enum</span> Errors
&nbsp; &nbsp; CAPTURE_IS_ALREADY_RUNNING = vbObjectError <span class="kw3">Or</span> (MMRESULT_END)
&nbsp; &nbsp; INVALID_BUFFERS_COUNT
&nbsp; &nbsp; NOT_INITIALIZE
&nbsp; &nbsp; ERROR_UNAVAILABLE
&nbsp; &nbsp; ERROR_OBJECT_FAILED
&nbsp; &nbsp; ERROR_OPEN_DEVICE = vbObjectError <span class="kw3">Or</span> (2 * &amp;H100)
&nbsp; &nbsp; ERROR_PREPARE_BUFFERS = vbObjectError <span class="kw3">Or</span> (3 * &amp;H100)
&nbsp; &nbsp; ERROR_ADD_BUFFERS = vbObjectError <span class="kw3">Or</span> (4 * &amp;H100)
&nbsp; &nbsp; ERROR_STARTUP = vbObjectError <span class="kw3">Or</span> (5 * &amp;H100)
&nbsp; &nbsp; ERROR_STOP = vbObjectError <span class="kw3">Or</span> (6 * &amp;H100)
<span class="kw3">End</span> <span class="kw1">Enum</span>
&nbsp;
<span class="kw2">Private</span> <span class="kw4">Type</span> WNDCLASSEX
&nbsp; &nbsp; cbSize &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; style &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; lpfnwndproc &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; cbClsextra &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; cbWndExtra2 &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; hInstance &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; hIcon &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; hCursor &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; hbrBackground &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; lpszMenuName &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; lpszClassName &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; hIconSm &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw3">End</span> <span class="kw4">Type</span>
&nbsp;
<span class="kw2">Private</span> <span class="kw4">Type</span> WAVEFORMATEX
&nbsp; &nbsp; wFormatTag &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Integer</span>
&nbsp; &nbsp; nChannels &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Integer</span>
&nbsp; &nbsp; nSamplesPerSec &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; nAvgBytesPerSec &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; nBlockAlign &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Integer</span>
&nbsp; &nbsp; wBitsPerSample &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Integer</span>
&nbsp; &nbsp; cbSize &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Integer</span>
<span class="kw3">End</span> <span class="kw4">Type</span>
&nbsp;
<span class="kw2">Private</span> <span class="kw4">Type</span> WAVEINCAPS
&nbsp; &nbsp; wMid &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Integer</span>
&nbsp; &nbsp; wPid &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Integer</span>
&nbsp; &nbsp; vDriverVersion &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; szPname(31) &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Integer</span>
&nbsp; &nbsp; dwFormats &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; wChannels &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Integer</span>
&nbsp; &nbsp; wReserved1 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Integer</span>
<span class="kw3">End</span> <span class="kw4">Type</span>
<span class="kw2">Private</span> <span class="kw4">Type</span> WAVEOUTCAPS
&nbsp; &nbsp; wMid &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Integer</span>
&nbsp; &nbsp; wPid &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Integer</span>
&nbsp; &nbsp; vDriverVersion &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; szPname(31) &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Integer</span>
&nbsp; &nbsp; dwFormats &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; wChannels &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Integer</span>
&nbsp; &nbsp; wReserved &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Integer</span>
&nbsp; &nbsp; dwSupport &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw3">End</span> <span class="kw4">Type</span>
&nbsp;
<span class="kw2">Private</span> <span class="kw4">Type</span> WAVEHDR
&nbsp; &nbsp; lpData &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; dwBufferLength &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; dwBytesRecorded &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; dwUser &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; dwFlags &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; dwLoops &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; lpNext &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; Reserved &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw3">End</span> <span class="kw4">Type</span>
&nbsp;
<span class="kw2">Private</span> <span class="kw4">Type</span> buffer
&nbsp; &nbsp; data() &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Byte</span>
&nbsp; &nbsp; Header &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> WAVEHDR
&nbsp; &nbsp; Status &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Boolean</span>
<span class="kw3">End</span> <span class="kw4">Type</span>
&nbsp;
<span class="kw2">Private</span> <span class="kw4">Type</span> PROCESS_HEAP_ENTRY
&nbsp; &nbsp; lpData &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; cbData &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; cbOverhead &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Byte</span>
&nbsp; &nbsp; iRegionIndex &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Byte</span>
&nbsp; &nbsp; wFlags &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Integer</span>
&nbsp; &nbsp; dwCommittedSize &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; dwUnCommittedSize &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; lpFirstBlock &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; lpLastBlock &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw3">End</span> <span class="kw4">Type</span>
&nbsp;
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Function</span> DefWindowProc Lib <span class="st0">&quot;user32&quot;</span> Alias <span class="st0">&quot;DefWindowProcW&quot;</span> (<span class="kw4">ByVal</span> hwnd <span class="kw4">As</span> <span class="kw1">Long</span>, <span class="kw4">ByVal</span> wMsg <span class="kw4">As</span> <span class="kw1">Long</span>, <span class="kw4">ByVal</span> wParam <span class="kw4">As</span> <span class="kw1">Long</span>, <span class="kw4">ByVal</span> lParam <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Function</span> GetModuleHandle Lib <span class="st0">&quot;kernel32&quot;</span> Alias <span class="st0">&quot;GetModuleHandleW&quot;</span> (<span class="kw4">ByVal</span> lpModuleName <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Function</span> GetProcAddress Lib <span class="st0">&quot;kernel32&quot;</span> (<span class="kw4">ByVal</span> hModule <span class="kw4">As</span> <span class="kw1">Long</span>, <span class="kw4">ByVal</span> lpProcName <span class="kw4">As</span> <span class="kw1">String</span>) <span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Function</span> HeapCreate Lib <span class="st0">&quot;kernel32&quot;</span> (<span class="kw4">ByVal</span> flOptions <span class="kw4">As</span> <span class="kw1">Long</span>, <span class="kw4">ByVal</span> dwInitialSize <span class="kw4">As</span> <span class="kw1">Long</span>, <span class="kw4">ByVal</span> dwMaximumSize <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Function</span> HeapDestroy Lib <span class="st0">&quot;kernel32&quot;</span> (<span class="kw4">ByVal</span> hHeap <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Function</span> HeapAlloc Lib <span class="st0">&quot;kernel32&quot;</span> (<span class="kw4">ByVal</span> hHeap <span class="kw4">As</span> <span class="kw1">Long</span>, <span class="kw4">ByVal</span> dwFlags <span class="kw4">As</span> <span class="kw1">Long</span>, <span class="kw4">ByVal</span> dwBytes <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Function</span> HeapFree Lib <span class="st0">&quot;kernel32&quot;</span> (<span class="kw4">ByVal</span> hHeap <span class="kw4">As</span> <span class="kw1">Long</span>, <span class="kw4">ByVal</span> dwFlags <span class="kw4">As</span> <span class="kw1">Long</span>, lpMem <span class="kw4">As</span> Any) <span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Function</span> HeapWalk Lib <span class="st0">&quot;kernel32&quot;</span> (<span class="kw4">ByVal</span> hHeap <span class="kw4">As</span> <span class="kw1">Long</span>, <span class="kw4">ByRef</span> lpEntry <span class="kw4">As</span> PROCESS_HEAP_ENTRY) <span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Function</span> HeapLock Lib <span class="st0">&quot;kernel32&quot;</span> (<span class="kw4">ByVal</span> hHeap <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Function</span> HeapUnlock Lib <span class="st0">&quot;kernel32&quot;</span> (<span class="kw4">ByVal</span> hHeap <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Function</span> SetEnvironmentVariable Lib <span class="st0">&quot;kernel32&quot;</span> Alias <span class="st0">&quot;SetEnvironmentVariableW&quot;</span> (<span class="kw4">ByVal</span> lpName <span class="kw4">As</span> <span class="kw1">Long</span>, <span class="kw4">ByVal</span> lpValue <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Function</span> GetEnvironmentVariable Lib <span class="st0">&quot;kernel32&quot;</span> Alias <span class="st0">&quot;GetEnvironmentVariableW&quot;</span> (<span class="kw4">ByVal</span> lpName <span class="kw4">As</span> <span class="kw1">Long</span>, <span class="kw4">ByVal</span> lpBuffer <span class="kw4">As</span> <span class="kw1">Long</span>, <span class="kw4">ByVal</span> nSize <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Function</span> GetMem4 Lib <span class="st0">&quot;msvbvm60&quot;</span> (pSrc <span class="kw4">As</span> Any, pDst <span class="kw4">As</span> Any) <span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Function</span> GetClassInfoEx Lib <span class="st0">&quot;user32&quot;</span> Alias <span class="st0">&quot;GetClassInfoExW&quot;</span> (<span class="kw4">ByVal</span> hInstance <span class="kw4">As</span> <span class="kw1">Long</span>, <span class="kw4">ByVal</span> lpClassName <span class="kw4">As</span> <span class="kw1">Long</span>, lpWndClassEx <span class="kw4">As</span> WNDCLASSEX) <span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Function</span> UnregisterClass Lib <span class="st0">&quot;user32&quot;</span> Alias <span class="st0">&quot;UnregisterClassW&quot;</span> (<span class="kw4">ByVal</span> lpClassName <span class="kw4">As</span> <span class="kw1">Long</span>, <span class="kw4">ByVal</span> hInstance <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Function</span> RegisterClassEx Lib <span class="st0">&quot;user32&quot;</span> Alias <span class="st0">&quot;RegisterClassExW&quot;</span> (pcWndClassEx <span class="kw4">As</span> WNDCLASSEX) <span class="kw4">As</span> <span class="kw1">Integer</span>
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Function</span> CreateWindowEx Lib <span class="st0">&quot;user32&quot;</span> Alias <span class="st0">&quot;CreateWindowExW&quot;</span> (<span class="kw4">ByVal</span> dwExStyle <span class="kw4">As</span> <span class="kw1">Long</span>, <span class="kw4">ByVal</span> lpClassName <span class="kw4">As</span> <span class="kw1">Long</span>, <span class="kw4">ByVal</span> lpWindowName <span class="kw4">As</span> <span class="kw1">Long</span>, <span class="kw4">ByVal</span> dwStyle <span class="kw4">As</span> <span class="kw1">Long</span>, <span class="kw4">ByVal</span> x <span class="kw4">As</span> <span class="kw1">Long</span>, <span class="kw4">ByVal</span> y <span class="kw4">As</span> <span class="kw1">Long</span>, <span class="kw4">ByVal</span> nWidth <span class="kw4">As</span> <span class="kw1">Long</span>, <span class="kw4">ByVal</span> nHeight <span class="kw4">As</span> <span class="kw1">Long</span>, <span class="kw4">ByVal</span> hWndParent <span class="kw4">As</span> <span class="kw1">Long</span>, <span class="kw4">ByVal</span> hMenu <span class="kw4">As</span> <span class="kw1">Long</span>, <span class="kw4">ByVal</span> hInstance <span class="kw4">As</span> <span class="kw1">Long</span>, lpParam <span class="kw4">As</span> Any) <span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Function</span> DestroyWindow Lib <span class="st0">&quot;user32&quot;</span> (<span class="kw4">ByVal</span> hwnd <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Function</span> lstrlen Lib <span class="st0">&quot;kernel32&quot;</span> Alias <span class="st0">&quot;lstrlenW&quot;</span> (lpString <span class="kw4">As</span> Any) <span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Function</span> lstrcpyn Lib <span class="st0">&quot;kernel32&quot;</span> Alias <span class="st0">&quot;lstrcpynW&quot;</span> (lpString1 <span class="kw4">As</span> Any, lpString2 <span class="kw4">As</span> Any, <span class="kw4">ByVal</span> iMaxLength <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Function</span> SetWindowLong Lib <span class="st0">&quot;user32&quot;</span> Alias <span class="st0">&quot;SetWindowLongA&quot;</span> (<span class="kw4">ByVal</span> hwnd <span class="kw4">As</span> <span class="kw1">Long</span>, <span class="kw4">ByVal</span> nIndex <span class="kw4">As</span> <span class="kw1">Long</span>, <span class="kw4">ByVal</span> dwNewLong <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Sub</span> ZeroMemory Lib <span class="st0">&quot;kernel32&quot;</span> Alias <span class="st0">&quot;RtlZeroMemory&quot;</span> (Destination <span class="kw4">As</span> Any, <span class="kw4">ByVal</span> Length <span class="kw4">As</span> <span class="kw1">Long</span>)
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Sub</span> memcpy Lib <span class="st0">&quot;kernel32&quot;</span> Alias <span class="st0">&quot;RtlMoveMemory&quot;</span> (Destination <span class="kw4">As</span> Any, Source <span class="kw4">As</span> Any, <span class="kw4">ByVal</span> Length <span class="kw4">As</span> <span class="kw1">Long</span>)
&nbsp;
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Function</span> waveInGetNumDevs Lib <span class="st0">&quot;winmm.dll&quot;</span> () <span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Function</span> waveInGetID Lib <span class="st0">&quot;winmm.dll&quot;</span> (<span class="kw4">ByVal</span> hWaveIn <span class="kw4">As</span> <span class="kw1">Long</span>, lpuDeviceID <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Function</span> waveInGetDevCaps Lib <span class="st0">&quot;winmm.dll&quot;</span> Alias <span class="st0">&quot;waveInGetDevCapsW&quot;</span> (<span class="kw4">ByVal</span> uDeviceID <span class="kw4">As</span> <span class="kw1">Long</span>, lpCaps <span class="kw4">As</span> WAVEINCAPS, <span class="kw4">ByVal</span> uSize <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Function</span> waveInOpen Lib <span class="st0">&quot;winmm.dll&quot;</span> (lphWaveIn <span class="kw4">As</span> <span class="kw1">Long</span>, <span class="kw4">ByVal</span> uDeviceID <span class="kw4">As</span> <span class="kw1">Long</span>, lpFormat <span class="kw4">As</span> WAVEFORMATEX, <span class="kw4">ByVal</span> dwCallback <span class="kw4">As</span> <span class="kw1">Long</span>, <span class="kw4">ByVal</span> dwInstance <span class="kw4">As</span> <span class="kw1">Long</span>, <span class="kw4">ByVal</span> dwFlags <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> MMRESULT
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Function</span> waveInPrepareHeader Lib <span class="st0">&quot;winmm.dll&quot;</span> (<span class="kw4">ByVal</span> hWaveIn <span class="kw4">As</span> <span class="kw1">Long</span>, lpWaveInHdr <span class="kw4">As</span> WAVEHDR, <span class="kw4">ByVal</span> uSize <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> MMRESULT
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Function</span> waveInReset Lib <span class="st0">&quot;winmm.dll&quot;</span> (<span class="kw4">ByVal</span> hWaveIn <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> MMRESULT
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Function</span> waveInStart Lib <span class="st0">&quot;winmm.dll&quot;</span> (<span class="kw4">ByVal</span> hWaveIn <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> MMRESULT
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Function</span> waveInStop Lib <span class="st0">&quot;winmm.dll&quot;</span> (<span class="kw4">ByVal</span> hWaveIn <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> MMRESULT
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Function</span> waveInUnprepareHeader Lib <span class="st0">&quot;winmm.dll&quot;</span> (<span class="kw4">ByVal</span> hWaveIn <span class="kw4">As</span> <span class="kw1">Long</span>, lpWaveInHdr <span class="kw4">As</span> WAVEHDR, <span class="kw4">ByVal</span> uSize <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> MMRESULT
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Function</span> waveInClose Lib <span class="st0">&quot;winmm.dll&quot;</span> (<span class="kw4">ByVal</span> hWaveIn <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> MMRESULT
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Function</span> waveInGetErrorText Lib <span class="st0">&quot;winmm.dll&quot;</span> Alias <span class="st0">&quot;waveInGetErrorTextW&quot;</span> (<span class="kw4">ByVal</span> err <span class="kw4">As</span> <span class="kw1">Long</span>, <span class="kw4">ByVal</span> lpText <span class="kw4">As</span> <span class="kw1">Long</span>, <span class="kw4">ByVal</span> uSize <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> MMRESULT
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Function</span> waveInAddBuffer Lib <span class="st0">&quot;winmm.dll&quot;</span> (<span class="kw4">ByVal</span> hWaveIn <span class="kw4">As</span> <span class="kw1">Long</span>, lpWaveInHdr <span class="kw4">As</span> WAVEHDR, <span class="kw4">ByVal</span> uSize <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> MMRESULT
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Function</span> waveOutGetDevCaps Lib <span class="st0">&quot;winmm.dll&quot;</span> Alias <span class="st0">&quot;waveOutGetDevCapsW&quot;</span> (<span class="kw4">ByVal</span> uDeviceID <span class="kw4">As</span> <span class="kw1">Long</span>, lpCaps <span class="kw4">As</span> WAVEOUTCAPS, <span class="kw4">ByVal</span> uSize <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Function</span> waveOutGetNumDevs Lib <span class="st0">&quot;winmm.dll&quot;</span> () <span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Function</span> waveOutGetID Lib <span class="st0">&quot;winmm.dll&quot;</span> (<span class="kw4">ByVal</span> hWaveOut <span class="kw4">As</span> <span class="kw1">Long</span>, lpuDeviceID <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Function</span> waveOutOpen Lib <span class="st0">&quot;winmm.dll&quot;</span> (lphWaveOut <span class="kw4">As</span> <span class="kw1">Long</span>, <span class="kw4">ByVal</span> uDeviceID <span class="kw4">As</span> <span class="kw1">Long</span>, lpFormat <span class="kw4">As</span> WAVEFORMATEX, <span class="kw4">ByVal</span> dwCallback <span class="kw4">As</span> <span class="kw1">Long</span>, <span class="kw4">ByVal</span> dwInstance <span class="kw4">As</span> <span class="kw1">Long</span>, <span class="kw4">ByVal</span> dwFlags <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> MMRESULT
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Function</span> waveOutPrepareHeader Lib <span class="st0">&quot;winmm.dll&quot;</span> (<span class="kw4">ByVal</span> hWaveOut <span class="kw4">As</span> <span class="kw1">Long</span>, lpWaveOutHdr <span class="kw4">As</span> WAVEHDR, <span class="kw4">ByVal</span> uSize <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> MMRESULT
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Function</span> waveOutWrite Lib <span class="st0">&quot;winmm.dll&quot;</span> (<span class="kw4">ByVal</span> hWaveOut <span class="kw4">As</span> <span class="kw1">Long</span>, lpWaveOutHdr <span class="kw4">As</span> WAVEHDR, <span class="kw4">ByVal</span> uSize <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> MMRESULT
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Function</span> waveOutUnprepareHeader Lib <span class="st0">&quot;winmm.dll&quot;</span> (<span class="kw4">ByVal</span> hWaveOut <span class="kw4">As</span> <span class="kw1">Long</span>, lpWaveOutHdr <span class="kw4">As</span> WAVEHDR, <span class="kw4">ByVal</span> uSize <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> MMRESULT
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Function</span> waveOutClose Lib <span class="st0">&quot;winmm.dll&quot;</span> (<span class="kw4">ByVal</span> hWaveOut <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> MMRESULT
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Function</span> waveOutReset Lib <span class="st0">&quot;winmm.dll&quot;</span> (<span class="kw4">ByVal</span> hWaveOut <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> MMRESULT
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Function</span> waveOutPause Lib <span class="st0">&quot;winmm.dll&quot;</span> (<span class="kw4">ByVal</span> hWaveOut <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> MMRESULT
<span class="kw2">Private</span> <span class="kw4">Declare</span> <span class="kw2">Function</span> waveOutRestart Lib <span class="st0">&quot;winmm.dll&quot;</span> (<span class="kw4">ByVal</span> hWaveOut <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> MMRESULT
&nbsp;
<span class="kw2">Private</span> Const SndClass &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">String</span> = <span class="st0">&quot;TrickSoundClass&quot;</span>
<span class="kw2">Private</span> Const HWND_MESSAGE &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span> = -3
<span class="kw2">Private</span> Const WAVE_MAPPER &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span> = -1&amp;
<span class="kw2">Private</span> Const CALLBACK_WINDOW &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span> = &amp;H10000
<span class="kw2">Private</span> Const WAVE_FORMAT_PCM &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span> = 1
<span class="kw2">Private</span> Const MM_WIM_DATA &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span> = &amp;H3C0
<span class="kw2">Private</span> Const MM_WOM_DONE &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span> = &amp;H3BD
<span class="kw2">Private</span> Const WNDPROCINDEX &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span> = 18
<span class="kw2">Private</span> Const HEAP_CREATE_ENABLE_EXECUTE &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span> = &amp;H40000
<span class="kw2">Private</span> Const HEAP_NO_SERIALIZE &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span> = &amp;H1
<span class="kw2">Private</span> Const HEAP_ZERO_MEMORY &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span> = &amp;H8
<span class="kw2">Private</span> Const PROCESS_HEAP_ENTRY_BUSY &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span> = &amp;H4
<span class="kw2">Private</span> Const GWL_WNDPROC &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span> = (-4)
&nbsp;
<span class="kw2">Private</span> Init &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Boolean</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">' Корректно ли инициализирован класс</span>
<span class="kw2">Private</span> hwnd &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' Хендл окна приемника сообщений</span>
<span class="kw2">Private</span> mActive &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Boolean</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">' Активен ли процесс захвата/воспроизведения</span>
<span class="kw2">Private</span> mSmpCount &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' Размер буфера в семплах</span>
<span class="kw2">Private</span> mFormat &nbsp; &nbsp; <span class="kw4">As</span> WAVEFORMATEX &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' Формат</span>
<span class="kw2">Private</span> hWaveIn &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' Хендл устройства захвата</span>
<span class="kw2">Private</span> hWaveOut &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' Хендл устройства воспроизведения</span>
<span class="kw2">Private</span> Buffers() &nbsp; <span class="kw4">As</span> buffer &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' Буфера</span>
<span class="kw2">Private</span> bufCount &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' Количество буферов</span>
<span class="kw2">Private</span> unavailable <span class="kw4">As</span> <span class="kw1">Boolean</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">' Если недоступен, то True</span>
<span class="kw2">Private</span> paused &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Boolean</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">' Если пауза</span>
<span class="kw2">Private</span> devCap &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> Collection &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' Устройства захвата</span>
<span class="kw2">Private</span> devPlay &nbsp; &nbsp; <span class="kw4">As</span> Collection &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' Устройства воспроизведения</span>
&nbsp;
<span class="kw4">Dim</span> hHeap &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
<span class="kw4">Dim</span> lpAsm &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp;
<span class="co1">' // Событие возникающее при запросе нового буфера</span>
<span class="kw2">Public</span> Event NewData(<span class="kw4">ByVal</span> DataPtr <span class="kw4">As</span> <span class="kw1">Long</span>, <span class="kw4">ByVal</span> CountBytes <span class="kw4">As</span> <span class="kw1">Long</span>)
&nbsp;
<span class="co1">' // Если активен захват/воспроизведение то True</span>
<span class="kw2">Public</span> <span class="kw4">Property</span> <span class="kw4">Get</span> IsActive() <span class="kw4">As</span> <span class="kw1">Boolean</span>
&nbsp; &nbsp; IsActive = mActive
<span class="kw3">End</span> <span class="kw4">Property</span>
&nbsp;
<span class="co1">' // Если инициализация захвата/воспроизведения успешна то True</span>
<span class="kw2">Public</span> <span class="kw4">Property</span> <span class="kw4">Get</span> IsUnavailable() <span class="kw4">As</span> <span class="kw1">Boolean</span>
&nbsp; &nbsp; IsUnavailable = unavailable
<span class="kw3">End</span> <span class="kw4">Property</span>
&nbsp;
<span class="co1">' // Если ошибка инициализации объекта то True</span>
<span class="kw2">Public</span> <span class="kw4">Property</span> <span class="kw4">Get</span> IsFailed() <span class="kw4">As</span> <span class="kw1">Boolean</span>
&nbsp; &nbsp; IsFailed = <span class="kw3">Not</span> Init
<span class="kw3">End</span> <span class="kw4">Property</span>
&nbsp;
<span class="co1">' // Размер буфера в секундах</span>
<span class="kw2">Public</span> <span class="kw4">Property</span> <span class="kw4">Get</span> BufferLengthSec() <span class="kw4">As</span> <span class="kw1">Single</span>
&nbsp; &nbsp; BufferLengthSec = mSmpCount / mFormat.nSamplesPerSec
<span class="kw3">End</span> <span class="kw4">Property</span>
&nbsp;
<span class="co1">' // Размер буфера в семплах</span>
<span class="kw2">Public</span> <span class="kw4">Property</span> <span class="kw4">Get</span> BufferLengthSamples() <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; BufferLengthSamples = mSmpCount
<span class="kw3">End</span> <span class="kw4">Property</span>
&nbsp;
<span class="co1">' // Частота дискретизации</span>
<span class="kw2">Public</span> <span class="kw4">Property</span> <span class="kw4">Get</span> SampleRate() <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; SampleRate = mFormat.nSamplesPerSec
<span class="kw3">End</span> <span class="kw4">Property</span>
&nbsp;
<span class="co1">' // Разрядность</span>
<span class="kw2">Public</span> <span class="kw4">Property</span> <span class="kw4">Get</span> BitsPerSample() <span class="kw4">As</span> <span class="kw1">Integer</span>
&nbsp; &nbsp; BitsPerSample = mFormat.wBitsPerSample
<span class="kw3">End</span> <span class="kw4">Property</span>
&nbsp;
<span class="co1">' // Количество каналов</span>
<span class="kw2">Public</span> <span class="kw4">Property</span> <span class="kw4">Get</span> Channels() <span class="kw4">As</span> <span class="kw1">Integer</span>
&nbsp; &nbsp; Channels = mFormat.nChannels
<span class="kw3">End</span> <span class="kw4">Property</span>
&nbsp;
<span class="co1">' // Количество буферов</span>
<span class="kw2">Public</span> <span class="kw4">Property</span> <span class="kw4">Get</span> BuffersCount() <span class="kw4">As</span> <span class="kw1">Byte</span>
&nbsp; &nbsp; BuffersCount = bufCount
<span class="kw3">End</span> <span class="kw4">Property</span>
&nbsp;
<span class="co1">' // Текущий идентификатор устройства захвата</span>
<span class="kw2">Public</span> <span class="kw4">Property</span> <span class="kw4">Get</span> CurrentCaptureDeviceID() <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw3">If</span> hWaveIn <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; waveInGetID hWaveIn, CurrentCaptureDeviceID
&nbsp; &nbsp; <span class="kw3">Else</span>
&nbsp; &nbsp; &nbsp; &nbsp; err.Raise 5
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
<span class="kw3">End</span> <span class="kw4">Property</span>
&nbsp;
<span class="co1">' // Текущий идентификатор устройства воспроизведения</span>
<span class="kw2">Public</span> <span class="kw4">Property</span> <span class="kw4">Get</span> CurrentPlaybackDeviceID() <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw3">If</span> hWaveOut <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; waveOutGetID hWaveOut, CurrentPlaybackDeviceID
&nbsp; &nbsp; <span class="kw3">Else</span>
&nbsp; &nbsp; &nbsp; &nbsp; err.Raise 5
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
<span class="kw3">End</span> <span class="kw4">Property</span>
&nbsp;
<span class="co1">' // Коллекция доступных устройств захвата</span>
<span class="kw2">Public</span> <span class="kw4">Property</span> <span class="kw4">Get</span> CaptureDevices() <span class="kw4">As</span> Collection
&nbsp; &nbsp; <span class="kw4">Dim</span> devCount &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> caps &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> WAVEINCAPS
&nbsp; &nbsp; <span class="kw4">Dim</span> idx &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> strLen &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> tmpStr &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">String</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> devCap <span class="kw3">Is</span> <span class="kw5">Nothing</span> <span class="kw3">Then</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; devCount = waveInGetNumDevs()
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">Set</span> devCap = <span class="kw2">New</span> Collection
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">For</span> idx = 0 <span class="kw3">To</span> devCount - 1
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; waveInGetDevCaps idx, caps, Len(caps)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; strLen = lstrlen(caps.szPname(0))
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; tmpStr = Space(strLen)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; lstrcpyn <span class="kw4">ByVal</span> StrPtr(tmpStr), caps.szPname(0), strLen + 1
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; devCap.Add tmpStr
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">Next</span>
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw4">Set</span> CaptureDevices = devCap
&nbsp; &nbsp; 
<span class="kw3">End</span> <span class="kw4">Property</span>
&nbsp;
<span class="co1">' // Коллекция доступных устройств воспроизведения</span>
<span class="kw2">Public</span> <span class="kw4">Property</span> <span class="kw4">Get</span> PlaybackDevices() <span class="kw4">As</span> Collection
&nbsp; &nbsp; <span class="kw4">Dim</span> devCount &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> caps &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> WAVEOUTCAPS
&nbsp; &nbsp; <span class="kw4">Dim</span> idx &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> strLen &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> tmpStr &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">String</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> devPlay <span class="kw3">Is</span> <span class="kw5">Nothing</span> <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; devCount = waveOutGetNumDevs()
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">Set</span> devPlay = <span class="kw2">New</span> Collection
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">For</span> idx = 0 <span class="kw3">To</span> devCount - 1
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; waveOutGetDevCaps idx, caps, Len(caps)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; strLen = lstrlen(caps.szPname(0))
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; tmpStr = Space(strLen)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; lstrcpyn <span class="kw4">ByVal</span> StrPtr(tmpStr), caps.szPname(0), strLen + 1
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; devPlay.Add tmpStr
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">Next</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw4">Set</span> PlaybackDevices = devPlay
&nbsp; &nbsp; 
<span class="kw3">End</span> <span class="kw4">Property</span>
&nbsp;
<span class="co1">' // Запустить захват/воспроизведение</span>
<span class="kw2">Public</span> <span class="kw2">Function</span> StartProcess() <span class="kw4">As</span> <span class="kw1">Boolean</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> ret <span class="kw4">As</span> MMRESULT
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> mActive <span class="kw3">And</span> <span class="kw3">Not</span> paused <span class="kw3">Then</span> <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> <span class="kw3">Not</span> Init <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; err.Raise Errors.ERROR_OBJECT_FAILED
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> <span class="kw3">Not</span> unavailable <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; err.Raise Errors.NOT_INITIALIZE
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> hWaveIn <span class="kw3">Then</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; ret = waveInStart(hWaveIn)
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> ret <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; err.Raise ERROR_STARTUP <span class="kw3">Or</span> ret
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">Else</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">Dim</span> idx <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> paused <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ret = waveOutRestart(hWaveOut)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> ret <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; err.Raise ERROR_STARTUP <span class="kw3">Or</span> ret
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; paused = <span class="kw5">False</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">Else</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">For</span> idx = 0 <span class="kw3">To</span> bufCount - 1
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">RaiseEvent</span> NewData(Buffers(idx).Header.lpData, <span class="kw4">UBound</span>(Buffers(idx).data) + 1)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ret = waveOutWrite(hWaveOut, Buffers(idx).Header, Len(Buffers(idx).Header))
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> ret <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; err.Raise ERROR_STARTUP <span class="kw3">Or</span> ret
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">Next</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp;
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; StartProcess = <span class="kw5">True</span>
&nbsp; &nbsp; mActive = <span class="kw5">True</span>
&nbsp;
<span class="kw3">End</span> <span class="kw2">Function</span>
&nbsp;
<span class="co1">' // Приостановить воспроизведение</span>
<span class="kw2">Public</span> <span class="kw2">Function</span> PauseProcess() <span class="kw4">As</span> <span class="kw1">Boolean</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> ret <span class="kw4">As</span> MMRESULT
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> <span class="kw3">Not</span> Init <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; err.Raise Errors.ERROR_OBJECT_FAILED
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> <span class="kw3">Not</span> unavailable <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; err.Raise Errors.NOT_INITIALIZE
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> <span class="kw3">Not</span> mActive <span class="kw3">Then</span> <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> hWaveOut <span class="kw3">Then</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; paused = <span class="kw5">True</span>
&nbsp; &nbsp; &nbsp; &nbsp; waveOutPause hWaveOut
&nbsp; &nbsp; &nbsp; &nbsp; mActive = <span class="kw5">False</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; PauseProcess = <span class="kw5">True</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; 
<span class="kw3">End</span> <span class="kw2">Function</span>
&nbsp;
<span class="co1">' // Остановить захват/воспроизведение</span>
<span class="kw2">Public</span> <span class="kw2">Function</span> StopProcess() <span class="kw4">As</span> <span class="kw1">Boolean</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> ret <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> <span class="kw3">Not</span> Init <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; err.Raise Errors.ERROR_OBJECT_FAILED
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> <span class="kw3">Not</span> unavailable <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; err.Raise Errors.NOT_INITIALIZE
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> <span class="kw3">Not</span> mActive <span class="kw3">Then</span> <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> hWaveIn <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; ret = waveInStop(hWaveIn)
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> ret <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; err.Raise ERROR_STOP <span class="kw3">Or</span> ret
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp;
&nbsp; &nbsp; <span class="kw3">Else</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; ret = waveOutReset(hWaveOut)
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> ret <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; err.Raise ERROR_STOP <span class="kw3">Or</span> ret
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; mActive = <span class="kw5">False</span>
&nbsp; &nbsp; paused = <span class="kw5">False</span>
&nbsp; &nbsp; StopProcess = <span class="kw5">True</span>
&nbsp; &nbsp; 
<span class="kw3">End</span> <span class="kw2">Function</span>
&nbsp;
<span class="co1">' // Инициализация воспроизведения</span>
<span class="kw2">Public</span> <span class="kw2">Function</span> InitPlayback(<span class="kw4">ByVal</span> NumOfChannels <span class="kw4">As</span> <span class="kw1">Integer</span>, _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">ByVal</span> SamplesPerSec <span class="kw4">As</span> <span class="kw1">Long</span>, _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">ByVal</span> BitsPerSample <span class="kw4">As</span> <span class="kw1">Integer</span>, _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">ByVal</span> BufferSampleCount <span class="kw4">As</span> <span class="kw1">Long</span>, _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">Optional</span> <span class="kw4">ByVal</span> DeviceID <span class="kw4">As</span> <span class="kw1">Long</span> = WAVE_MAPPER, _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">Optional</span> <span class="kw4">ByVal</span> BuffersCount <span class="kw4">As</span> <span class="kw1">Byte</span> = 4) <span class="kw4">As</span> <span class="kw1">Boolean</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> ret <span class="kw4">As</span> MMRESULT
&nbsp; &nbsp; <span class="kw4">Dim</span> idx <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> <span class="kw3">Not</span> Init <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; err.Raise Errors.ERROR_OBJECT_FAILED
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> unavailable <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; err.Raise Errors.ERROR_UNAVAILABLE
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> BuffersCount &lt; 1 <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; err.Raise Errors.INVALID_BUFFERS_COUNT
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; unavailable = <span class="kw5">True</span>
&nbsp;
&nbsp; &nbsp; <span class="kw3">With</span> mFormat
&nbsp; &nbsp; &nbsp; &nbsp; .cbSize = 0
&nbsp; &nbsp; &nbsp; &nbsp; .wFormatTag = WAVE_FORMAT_PCM
&nbsp; &nbsp; &nbsp; &nbsp; .wBitsPerSample = BitsPerSample
&nbsp; &nbsp; &nbsp; &nbsp; .nSamplesPerSec = SamplesPerSec
&nbsp; &nbsp; &nbsp; &nbsp; .nChannels = NumOfChannels
&nbsp; &nbsp; &nbsp; &nbsp; .nBlockAlign = .nChannels * .wBitsPerSample \ 8
&nbsp; &nbsp; &nbsp; &nbsp; .nAvgBytesPerSec = .nSamplesPerSec * .nBlockAlign
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">With</span>
&nbsp;
&nbsp; &nbsp; mSmpCount = BufferSampleCount - (BufferSampleCount <span class="kw4">Mod</span> mFormat.nBlockAlign)
&nbsp; &nbsp; 
&nbsp; &nbsp; ret = waveOutOpen(hWaveOut, DeviceID, mFormat, hwnd, 0, CALLBACK_WINDOW)
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> ret <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; err.Raise ERROR_OPEN_DEVICE <span class="kw3">Or</span> ret
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; bufCount = BuffersCount
&nbsp; &nbsp; <span class="kw4">ReDim</span> Buffers(BuffersCount - 1)
&nbsp;
&nbsp; &nbsp; <span class="kw3">For</span> idx = 0 <span class="kw3">To</span> BuffersCount - 1
&nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">With</span> Buffers(idx)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">ReDim</span> .data(mSmpCount * mFormat.nBlockAlign - 1)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .Header.lpData = VarPtr(.data(0))
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .Header.dwBufferLength = <span class="kw4">UBound</span>(.data) + 1
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .Header.dwFlags = 0
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .Header.dwLoops = 0
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ret = waveOutPrepareHeader(hWaveOut, .Header, Len(.Header))
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .Status = ret = MMSYSERR_NOERROR
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">With</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> ret <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Clear
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; err.Raise ERROR_PREPARE_BUFFERS <span class="kw3">Or</span> ret
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; <span class="kw3">Next</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; InitPlayback = <span class="kw5">True</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
<span class="kw3">End</span> <span class="kw2">Function</span>
&nbsp;
<span class="co1">' // Инициализация захвата</span>
<span class="kw2">Public</span> <span class="kw2">Function</span> InitCapture(<span class="kw4">ByVal</span> NumOfChannels <span class="kw4">As</span> <span class="kw1">Integer</span>, _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">ByVal</span> SamplesPerSec <span class="kw4">As</span> <span class="kw1">Long</span>, _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">ByVal</span> BitsPerSample <span class="kw4">As</span> <span class="kw1">Integer</span>, _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">ByVal</span> BufferSampleCount <span class="kw4">As</span> <span class="kw1">Long</span>, _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">Optional</span> <span class="kw4">ByVal</span> DeviceID <span class="kw4">As</span> <span class="kw1">Long</span> = WAVE_MAPPER, _
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">Optional</span> <span class="kw4">ByVal</span> BuffersCount <span class="kw4">As</span> <span class="kw1">Byte</span> = 4) <span class="kw4">As</span> <span class="kw1">Boolean</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> ret <span class="kw4">As</span> MMRESULT
&nbsp; &nbsp; <span class="kw4">Dim</span> idx <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> <span class="kw3">Not</span> Init <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; err.Raise Errors.ERROR_OBJECT_FAILED
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> unavailable <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; err.Raise Errors.ERROR_UNAVAILABLE
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> BuffersCount &lt; 1 <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; err.Raise Errors.INVALID_BUFFERS_COUNT
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; unavailable = <span class="kw5">True</span>
&nbsp;
&nbsp; &nbsp; <span class="kw3">With</span> mFormat
&nbsp; &nbsp; &nbsp; &nbsp; .cbSize = 0
&nbsp; &nbsp; &nbsp; &nbsp; .wFormatTag = WAVE_FORMAT_PCM
&nbsp; &nbsp; &nbsp; &nbsp; .wBitsPerSample = BitsPerSample
&nbsp; &nbsp; &nbsp; &nbsp; .nSamplesPerSec = SamplesPerSec
&nbsp; &nbsp; &nbsp; &nbsp; .nChannels = NumOfChannels
&nbsp; &nbsp; &nbsp; &nbsp; .nBlockAlign = .nChannels * .wBitsPerSample \ 8
&nbsp; &nbsp; &nbsp; &nbsp; .nAvgBytesPerSec = .nSamplesPerSec * .nBlockAlign
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">With</span>
&nbsp;
&nbsp; &nbsp; mSmpCount = BufferSampleCount - (BufferSampleCount <span class="kw4">Mod</span> mFormat.nBlockAlign)
&nbsp; &nbsp; 
&nbsp; &nbsp; ret = waveInOpen(hWaveIn, DeviceID, mFormat, hwnd, 0, CALLBACK_WINDOW)
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> ret <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; err.Raise ERROR_OPEN_DEVICE <span class="kw3">Or</span> ret
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; bufCount = BuffersCount
&nbsp; &nbsp; <span class="kw4">ReDim</span> Buffers(BuffersCount - 1)
&nbsp;
&nbsp; &nbsp; <span class="kw3">For</span> idx = 0 <span class="kw3">To</span> BuffersCount - 1
&nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">With</span> Buffers(idx)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">ReDim</span> .data(mSmpCount * mFormat.nBlockAlign - 1)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .Header.lpData = VarPtr(.data(0))
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .Header.dwBufferLength = <span class="kw4">UBound</span>(.data) + 1
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .Header.dwFlags = 0
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .Header.dwLoops = 0
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ret = waveInPrepareHeader(hWaveIn, .Header, Len(.Header))
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .Status = ret = MMSYSERR_NOERROR
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">With</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> ret <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Clear
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; err.Raise ERROR_PREPARE_BUFFERS <span class="kw3">Or</span> ret
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; <span class="kw3">Next</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">For</span> idx = 0 <span class="kw3">To</span> BuffersCount - 1
&nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; ret = waveInAddBuffer(hWaveIn, Buffers(idx).Header, Len(Buffers(idx).Header))
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> ret <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Clear
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; err.Raise ERROR_PREPARE_BUFFERS <span class="kw3">Or</span> ret
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">Next</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; InitCapture = <span class="kw5">True</span>
&nbsp; &nbsp; 
<span class="kw3">End</span> <span class="kw2">Function</span>
&nbsp;
<span class="co1">' // ------------------------------------------------------------------------------------------------------------</span>
&nbsp;
<span class="kw2">Private</span> <span class="kw2">Function</span> WndProc(<span class="kw4">ByVal</span> hwnd <span class="kw4">As</span> <span class="kw1">Long</span>, <span class="kw4">ByVal</span> Msg <span class="kw4">As</span> <span class="kw1">Long</span>, <span class="kw4">ByVal</span> wParam <span class="kw4">As</span> <span class="kw1">Long</span>, <span class="kw4">ByVal</span> lParam <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> idx <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> hdr <span class="kw4">As</span> WAVEHDR
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> unavailable <span class="kw3">Then</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">Select</span> <span class="kw3">Case</span> Msg
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">Case</span> MM_WIM_DATA
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; memcpy hdr, <span class="kw4">ByVal</span> lParam, Len(hdr)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; idx = GetBufferIndex(hdr.lpData)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> idx = -1 <span class="kw3">Then</span> <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">RaiseEvent</span> NewData(hdr.lpData, mSmpCount * mFormat.nBlockAlign)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; waveInAddBuffer hWaveIn, Buffers(idx).Header, Len(Buffers(idx).Header)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">Case</span> MM_WOM_DONE
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; memcpy hdr, <span class="kw4">ByVal</span> lParam, Len(hdr)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; idx = GetBufferIndex(hdr.lpData)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> idx = -1 <span class="kw3">Then</span> <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">RaiseEvent</span> NewData(hdr.lpData, mSmpCount * mFormat.nBlockAlign)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; waveOutWrite hWaveOut, Buffers(idx).Header, Len(Buffers(idx).Header)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">Select</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; WndProc = DefWindowProc(hwnd, Msg, wParam, lParam)
&nbsp; &nbsp; 
<span class="kw3">End</span> <span class="kw2">Function</span>
&nbsp;
<span class="kw2">Private</span> <span class="kw2">Function</span> CreateAsm() <span class="kw4">As</span> <span class="kw1">Boolean</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> inIDE &nbsp; <span class="kw4">As</span> <span class="kw1">Boolean</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> AsmSize <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> ptr &nbsp; &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> isFirst <span class="kw4">As</span> <span class="kw1">Boolean</span>
&nbsp;
&nbsp; &nbsp; Debug.Assert MakeTrue(inIDE)
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> lpAsm = 0 <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> inIDE <span class="kw3">Then</span> AsmSize = &amp;H2C <span class="kw3">Else</span> AsmSize = &amp;H20
&nbsp; &nbsp; &nbsp; &nbsp; hHeap = GetPrevHeap()
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> hHeap = 0 <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; hHeap = HeapCreate(HEAP_CREATE_ENABLE_EXECUTE <span class="kw3">Or</span> HEAP_NO_SERIALIZE, 0, 0)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> hHeap = 0 <span class="kw3">Then</span> err.Raise 7: <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> <span class="kw3">Not</span> SaveCurHeap() <span class="kw3">Then</span> HeapDestroy hHeap: hHeap = 0: err.Raise 7: <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; isFirst = <span class="kw5">True</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; lpAsm = HeapAlloc(hHeap, HEAP_NO_SERIALIZE <span class="kw3">Or</span> HEAP_ZERO_MEMORY, AsmSize)
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> lpAsm = 0 <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> isFirst <span class="kw3">Then</span> HeapDestroy hHeap
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; hHeap = 0
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; err.Raise 7
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; ptr = lpAsm
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> inIDE <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; CreateIDEStub (ptr): ptr = ptr + &amp;HD
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; CreateStackConv ptr
&nbsp; &nbsp; CreateAsm = <span class="kw5">True</span>
&nbsp; &nbsp; 
<span class="kw3">End</span> <span class="kw2">Function</span>
&nbsp;
<span class="kw2">Private</span> <span class="kw2">Function</span> SaveCurHeap() <span class="kw4">As</span> <span class="kw1">Boolean</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> i &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> out <span class="kw4">As</span> <span class="kw1">String</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; out = Hex(hHeap)
&nbsp; &nbsp; <span class="kw3">For</span> i = Len(out) + 1 <span class="kw3">To</span> 8: out = <span class="st0">&quot;0&quot;</span> &amp; out: <span class="kw3">Next</span>
&nbsp; &nbsp; SaveCurHeap = SetEnvironmentVariable(StrPtr(SndClass), StrPtr(out))
&nbsp; &nbsp; 
<span class="kw3">End</span> <span class="kw2">Function</span>
&nbsp;
<span class="kw2">Private</span> <span class="kw2">Function</span> GetPrevHeap() <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> out &nbsp;<span class="kw4">As</span> <span class="kw1">String</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; out = Space(&amp;H8)
&nbsp; &nbsp; <span class="kw3">If</span> GetEnvironmentVariable(StrPtr(SndClass), StrPtr(out), LenB(out)) <span class="kw3">Then</span> GetPrevHeap = Val(<span class="st0">&quot;&amp;H&quot;</span> &amp; out)
&nbsp; &nbsp; 
<span class="kw3">End</span> <span class="kw2">Function</span>
&nbsp;
<span class="kw2">Private</span> <span class="kw2">Function</span> CreateStackConv(<span class="kw4">ByVal</span> ptr <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> <span class="kw1">Boolean</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> lpMeth &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> vTable &nbsp; &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; GetMem4 <span class="kw4">ByVal</span> ObjPtr(Me), vTable
&nbsp; &nbsp; GetMem4 <span class="kw4">ByVal</span> vTable + WNDPROCINDEX * 4 + &amp;H1C, lpMeth
&nbsp; &nbsp; 
&nbsp; &nbsp; GetMem4 &amp;H5450C031, <span class="kw4">ByVal</span> ptr + &amp;H0: &nbsp; &nbsp;GetMem4 &amp;H488DE409, <span class="kw4">ByVal</span> ptr + &amp;H4: &nbsp; &nbsp;GetMem4 &amp;H2474FF04, <span class="kw4">ByVal</span> ptr + &amp;H8
&nbsp; &nbsp; GetMem4 &amp;H68FAE018, <span class="kw4">ByVal</span> ptr + &amp;HC: &nbsp; &nbsp;GetMem4 &amp;H12345678, <span class="kw4">ByVal</span> ptr + &amp;H10: &nbsp; GetMem4 &amp;HFFFFDAE8, <span class="kw4">ByVal</span> ptr + &amp;H14
&nbsp; &nbsp; GetMem4 &amp;H10C258FF, <span class="kw4">ByVal</span> ptr + &amp;H18: &nbsp; GetMem4 &amp;H0, <span class="kw4">ByVal</span> ptr + &amp;H1C
&nbsp; &nbsp; 
&nbsp; &nbsp; GetMem4 ObjPtr(Me), <span class="kw4">ByVal</span> ptr + &amp;H10 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">' Push Me</span>
 &nbsp; &nbsp;GetMem4 lpMeth - (ptr + &amp;H14) - 5, <span class="kw4">ByVal</span> ptr + &amp;H14 + 1 <span class="co1">' Call WndProc</span>
 &nbsp; &nbsp;
<span class="kw3">End</span> <span class="kw2">Function</span>
&nbsp;
<span class="kw2">Private</span> <span class="kw2">Function</span> CreateIDEStub(<span class="kw4">ByVal</span> ptr <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> <span class="kw1">Boolean</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> hInstVB6 &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> lpEbMode &nbsp; &nbsp;<span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> hInstUser32 <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> lpDefProc &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; hInstVB6 = GetModuleHandle(StrPtr(<span class="st0">&quot;vba6&quot;</span>))
&nbsp; &nbsp; <span class="kw3">If</span> hInstVB6 = 0 <span class="kw3">Then</span> <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; hInstUser32 = GetModuleHandle(StrPtr(<span class="st0">&quot;user32&quot;</span>))
&nbsp; &nbsp; <span class="kw3">If</span> hInstUser32 = 0 <span class="kw3">Then</span> <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; lpEbMode = GetProcAddress(hInstVB6, <span class="st0">&quot;EbMode&quot;</span>)
&nbsp; &nbsp; <span class="kw3">If</span> lpEbMode = 0 <span class="kw3">Then</span> <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; lpDefProc = GetProcAddress(hInstUser32, <span class="st0">&quot;DefWindowProcW&quot;</span>)
&nbsp; &nbsp; <span class="kw3">If</span> lpDefProc = 0 <span class="kw3">Then</span> <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp;
&nbsp;
&nbsp; &nbsp; GetMem4 &amp;HFFFFFBE8, <span class="kw4">ByVal</span> ptr + &amp;H0: &nbsp; &nbsp;GetMem4 &amp;HFC8FEFF, <span class="kw4">ByVal</span> ptr + &amp;H4
&nbsp; &nbsp; GetMem4 &amp;H34566B85, <span class="kw4">ByVal</span> ptr + &amp;H8: &nbsp; &nbsp;GetMem4 &amp;H12, <span class="kw4">ByVal</span> ptr + &amp;HC
&nbsp;
&nbsp; &nbsp; GetMem4 lpEbMode - ptr - 5, <span class="kw4">ByVal</span> ptr + 1 + 0 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">' Call EbMode</span>
 &nbsp; &nbsp;GetMem4 lpDefProc - (ptr + &amp;HD), <span class="kw4">ByVal</span> ptr + &amp;H9 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">' JNE &nbsp;DefWindowProcW</span>
 &nbsp; &nbsp;
&nbsp; &nbsp; CreateIDEStub = <span class="kw5">True</span>
&nbsp; &nbsp; 
<span class="kw3">End</span> <span class="kw2">Function</span>
&nbsp;
<span class="kw2">Private</span> <span class="kw2">Function</span> MakeTrue(Value <span class="kw4">As</span> <span class="kw1">Boolean</span>) <span class="kw4">As</span> <span class="kw1">Boolean</span>
&nbsp;
&nbsp; &nbsp; Value = <span class="kw5">True</span>
&nbsp; &nbsp; MakeTrue = <span class="kw5">True</span>
&nbsp; &nbsp; 
<span class="kw3">End</span> <span class="kw2">Function</span>
&nbsp;
<span class="kw2">Private</span> <span class="kw2">Sub</span> Clear()
&nbsp; &nbsp; <span class="kw4">Dim</span> idx <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; unavailable = <span class="kw5">False</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> hWaveIn <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; waveInReset hWaveIn
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">For</span> idx = 0 <span class="kw3">To</span> bufCount - 1
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> Buffers(idx).Status <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; waveInUnprepareHeader hWaveIn, Buffers(idx).Header, Len(Buffers(idx).Header)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">Next</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; waveInClose hWaveIn
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">Else</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; waveOutReset hWaveOut
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">For</span> idx = 0 <span class="kw3">To</span> bufCount - 1
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> Buffers(idx).Status <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; waveOutUnprepareHeader hWaveOut, Buffers(idx).Header, Len(Buffers(idx).Header)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">Next</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; waveOutClose hWaveOut
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; hWaveIn = 0
&nbsp; &nbsp; hWaveOut = 0
&nbsp; &nbsp; paused = <span class="kw5">False</span>
&nbsp; &nbsp; mActive = <span class="kw5">False</span>
&nbsp; &nbsp; bufCount = 0
&nbsp; &nbsp; Erase Buffers()
&nbsp; &nbsp; ZeroMemory mFormat, Len(mFormat)
&nbsp; &nbsp; 
<span class="kw3">End</span> <span class="kw2">Sub</span>
&nbsp;
<span class="kw2">Private</span> <span class="kw2">Function</span> GetBufferIndex(<span class="kw4">ByVal</span> ptr <span class="kw4">As</span> <span class="kw1">Long</span>) <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; <span class="kw4">Dim</span> idx <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">For</span> idx = 0 <span class="kw3">To</span> <span class="kw4">UBound</span>(Buffers)
&nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> Buffers(idx).Header.lpData = ptr <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; GetBufferIndex = idx
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">Exit</span> <span class="kw2">Function</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">Next</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; GetBufferIndex = -1
<span class="kw3">End</span> <span class="kw2">Function</span>
&nbsp;
<span class="kw2">Private</span> <span class="kw2">Sub</span> Class_Initialize()
&nbsp; &nbsp; <span class="kw4">Dim</span> cls &nbsp; &nbsp; <span class="kw4">As</span> WNDCLASSEX
&nbsp; &nbsp; <span class="kw4">Dim</span> hUser &nbsp; <span class="kw4">As</span> <span class="kw1">Long</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; cls.cbSize = Len(cls)
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> GetClassInfoEx(App.hInstance, StrPtr(SndClass), cls) = 0 <span class="kw3">Then</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; hUser = GetModuleHandle(StrPtr(<span class="st0">&quot;user32&quot;</span>))
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> hUser = 0 <span class="kw3">Then</span> <span class="kw2">Exit</span> <span class="kw2">Sub</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; cls.hInstance = App.hInstance
&nbsp; &nbsp; &nbsp; &nbsp; cls.lpfnwndproc = GetProcAddress(hUser, <span class="st0">&quot;DefWindowProcW&quot;</span>)
&nbsp; &nbsp; &nbsp; &nbsp; cls.lpszClassName = StrPtr(SndClass)
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">If</span> RegisterClassEx(cls) = 0 <span class="kw3">Then</span> <span class="kw2">Exit</span> <span class="kw2">Sub</span>
&nbsp;
&nbsp; &nbsp; <span class="kw3">End</span> <span class="kw3">If</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> <span class="kw3">Not</span> CreateAsm() <span class="kw3">Then</span> <span class="kw2">Exit</span> <span class="kw2">Sub</span>
&nbsp; &nbsp;
&nbsp; &nbsp; hwnd = CreateWindowEx(0, StrPtr(SndClass), 0, 0, 0, 0, 0, 0, HWND_MESSAGE, 0, App.hInstance, <span class="kw4">ByVal</span> 0&amp;)
&nbsp; &nbsp; <span class="kw3">If</span> hwnd = 0 <span class="kw3">Then</span> <span class="kw2">Exit</span> <span class="kw2">Sub</span>
&nbsp;
&nbsp; &nbsp; SetWindowLong hwnd, GWL_WNDPROC, lpAsm
&nbsp; &nbsp; 
&nbsp; &nbsp; Init = <span class="kw5">True</span>
&nbsp;
<span class="kw3">End</span> <span class="kw2">Sub</span>
&nbsp;
<span class="kw2">Private</span> <span class="kw2">Sub</span> Class_Terminate()
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> <span class="kw3">Not</span> Init <span class="kw3">Then</span> <span class="kw2">Exit</span> <span class="kw2">Sub</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; Clear
&nbsp; &nbsp; 
&nbsp; &nbsp; DestroyWindow hwnd
&nbsp; &nbsp; UnregisterClass StrPtr(SndClass), App.hInstance
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw3">If</span> hHeap = 0 <span class="kw3">Then</span> <span class="kw2">Exit</span> <span class="kw2">Sub</span>
&nbsp;
&nbsp; &nbsp; HeapFree hHeap, HEAP_NO_SERIALIZE, <span class="kw4">ByVal</span> lpAsm
&nbsp; &nbsp; 
<span class="kw3">End</span> <span class="kw2">Sub</span></pre></td></tr></table></div></td></tr></tbody></table></div>Описывать работу с <b>winmm</b> я не буду, скажу только что в качестве уведомлений используются оконные сообщения. Мы создаем для каждого экземпляра класса свое окно и <b>wave</b>-функции передают ему уведомления в виде сообщений, а мы, используя ассемблерную вставку, обрабатываем их в специальном методе класса, предварительно установив его в качестве оконной процедуры. Также я добавил туда проверку <b>EbMode</b>, что бы не было такого как в <b>DirectSound</b>, когда нельзя поставить нормально брейкпоинт при использовании циркулярного буфера. Класс генерирует событие <b>NewData</b> когда ему нужна очередная порция звуковых данных при воспроизведении и когда очередной буфер заполнен при захвате. Для инициализации воспроизведения используется метод <b>InitPlayback</b>, который инициализирует устройство воспроизведения (<b>DeviceID</b>) исходя из заданного формата и количества буферов в очереди. Список устройств получается свойством <b>PlaybackDevices</b>, которое представляет коллекцию устройств воспроизведения. Индекс устройства (от 0) соответствует нужному <b>DeviceID</b>. Чтобы предоставить функции выбирать само устройство по умолчанию для заданного формата, то передается константа <b>WAVE_MAPPER</b>. Инициализация захвата производится аналогично с помощью метода <b>InitCapture</b>; список устройств захвата получается с помощью метода <b>CaptureDevices</b>. Методы <b>StartProcess</b>, <b>StopProcess</b> соответственно запускают процесс воспроизведения/записи и останавливают; метод <b>PauseProcess</b> приостанавливает воспроизведение. Назначение остальных свойств понятно из комментариев в коде.<br />
<a href="https://www.cyberforum.ru/blogs/354370/blog3009.html">Продолжение...</a></div>

]]></content:encoded>
			<dc:creator>The trick</dc:creator>
			<guid isPermaLink="true">https://www.cyberforum.ru/blogs/354370/2998.html</guid>
		</item>
	</channel>
</rss>
