<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/atom10russianfull.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearch/1.1/" xmlns:georss="http://www.georss.org/georss" xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr="http://purl.org/syndication/thread/1.0" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" gd:etag="W/&quot;DkIFRHs9cCp7ImA9WhVTEk4.&quot;"><id>tag:blogger.com,1999:blog-1688132520265708820</id><updated>2012-02-26T09:35:15.568+04:00</updated><category term="не делай так" /><category term="прочее" /><category term="locale/cultures" /><category term="Vista" /><category term="ты можешь это сделать" /><category term="забавно" /><category term="клавиатуры" /><category term="Win16" /><category term="языки" /><category term="Совместимость" /><category term="fonts" /><category term="UI" /><category term="RTTI" /><category term="аудио" /><category term="collation/casing" /><category term="обработка ошибок" /><category term="серия" /><category term="История" /><category term="реестр" /><category term="справка" /><category term="дизайн/проектирование" /><category term="железо" /><category term="окна" /><category term="encoding/codepages" /><category term="Int'l Programming" /><category term="Win64" /><category term="LIP/MUI" /><category term="Potpourri" /><category term="дата/время" /><category term="Delphi" /><category term="Win32" /><category term="кодинг" /><category term="unicode" /><category term="linguistic" /><category term="хак" /><category term="MSLU" /><category term="безопасность" /><category term="фильмы" /><title>Блог GunSmoker-а</title><subtitle type="html">...when altering one's mind becomes as easy as programming a computer, what does it mean to be human?..</subtitle><link rel="http://schemas.google.com/g/2005#feed" type="application/atom+xml" href="http://www.transl-gunsmoker.ru/feeds/posts/default" /><link rel="alternate" type="text/html" href="http://www.transl-gunsmoker.ru/" /><link rel="next" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default?start-index=26&amp;max-results=25&amp;redirect=false&amp;v=2" /><author><name>Александр Алексеев</name><uri>https://profiles.google.com/113168002104297556003</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh3.googleusercontent.com/-aUMttZQBsuI/AAAAAAAAAAI/AAAAAAAAC3Y/QuZ7K9t_WzE/s512-c/photo.jpg" /></author><generator version="7.00" uri="http://www.blogger.com">Blogger</generator><openSearch:totalResults>801</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/atom+xml" href="http://feeds.feedburner.com/GunsmokersTranslations" /><feedburner:info uri="gunsmokerstranslations" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><feedburner:emailServiceId>GunsmokersTranslations</feedburner:emailServiceId><feedburner:feedburnerHostname>http://feedburner.google.com</feedburner:feedburnerHostname><feedburner:feedFlare href="http://add.my.yahoo.com/rss?url=http%3A%2F%2Ffeeds.feedburner.com%2FGunsmokersTranslations" src="http://us.i1.yimg.com/us.yimg.com/i/us/my/addtomyyahoo4.gif">Subscribe with My Yahoo!</feedburner:feedFlare><feedburner:feedFlare href="http://www.newsgator.com/ngs/subscriber/subext.aspx?url=http%3A%2F%2Ffeeds.feedburner.com%2FGunsmokersTranslations" src="http://www.newsgator.com/images/ngsub1.gif">Subscribe with NewsGator</feedburner:feedFlare><feedburner:feedFlare href="http://feeds.my.aol.com/add.jsp?url=http%3A%2F%2Ffeeds.feedburner.com%2FGunsmokersTranslations" src="http://o.aolcdn.com/favorites.my.aol.com/webmaster/ffclient/webroot/locale/en-US/images/myAOLButtonSmall.gif">Subscribe with My AOL</feedburner:feedFlare><feedburner:feedFlare href="http://www.bloglines.com/sub/http://feeds.feedburner.com/GunsmokersTranslations" src="http://www.bloglines.com/images/sub_modern11.gif">Subscribe with Bloglines</feedburner:feedFlare><feedburner:feedFlare href="http://www.netvibes.com/subscribe.php?url=http%3A%2F%2Ffeeds.feedburner.com%2FGunsmokersTranslations" src="http://www.netvibes.com/img/add2netvibes.gif">Subscribe with Netvibes</feedburner:feedFlare><feedburner:feedFlare href="http://fusion.google.com/add?feedurl=http%3A%2F%2Ffeeds.feedburner.com%2FGunsmokersTranslations" src="http://buttons.googlesyndication.com/fusion/add.gif">Subscribe with Google</feedburner:feedFlare><feedburner:feedFlare href="http://www.pageflakes.com/subscribe.aspx?url=http%3A%2F%2Ffeeds.feedburner.com%2FGunsmokersTranslations" src="http://www.pageflakes.com/ImageFile.ashx?instanceId=Static_4&amp;fileName=ATP_blu_91x17.gif">Subscribe with Pageflakes</feedburner:feedFlare><feedburner:feedFlare href="http://lenta.yandex.ru/settings.xml?name=feed&amp;url=http%3A%2F%2Ffeeds.feedburner.com%2FGunsmokersTranslations" src="http://lenta.yandex.ru/i/addfeed.gif">?????? ? ??????.?????</feedburner:feedFlare><feedburner:feedFlare href="http://www.plusmo.com/add?url=http%3A%2F%2Ffeeds.feedburner.com%2FGunsmokersTranslations" src="http://plusmo.com/res/graphics/fbplusmo.gif">Subscribe with Plusmo</feedburner:feedFlare><feedburner:feedFlare href="http://www.thefreedictionary.com/_/hp/AddRSS.aspx?http%3A%2F%2Ffeeds.feedburner.com%2FGunsmokersTranslations" src="http://img.tfd.com/hp/addToTheFreeDictionary.gif">Subscribe with The Free Dictionary</feedburner:feedFlare><feedburner:feedFlare href="http://www.bitty.com/manual/?contenttype=rssfeed&amp;contentvalue=http%3A%2F%2Ffeeds.feedburner.com%2FGunsmokersTranslations" src="http://www.bitty.com/img/bittychicklet_91x17.gif">Subscribe with Bitty Browser</feedburner:feedFlare><feedburner:feedFlare href="http://www.newsalloy.com/?rss=http%3A%2F%2Ffeeds.feedburner.com%2FGunsmokersTranslations" src="http://www.newsalloy.com/subrss3.gif">Subscribe with NewsAlloy</feedburner:feedFlare><feedburner:feedFlare href="http://www.live.com/?add=http%3A%2F%2Ffeeds.feedburner.com%2FGunsmokersTranslations" src="http://tkfiles.storage.msn.com/x1piYkpqHC_35nIp1gLE68-wvzLZO8iXl_JMledmJQXP-XTBOLfmQv4zhj4MhcWEJh_GtoBIiAl1Mjh-ndp9k47If7hTaFno0mxW9_i3p_5qQw">Subscribe with Live.com</feedburner:feedFlare><feedburner:feedFlare href="http://mix.excite.eu/add?feedurl=http%3A%2F%2Ffeeds.feedburner.com%2FGunsmokersTranslations" src="http://image.excite.co.uk/mix/addtomix.gif">Subscribe with Excite MIX</feedburner:feedFlare><feedburner:feedFlare href="http://download.attensa.com/app/get_attensa.html?feedurl=http%3A%2F%2Ffeeds.feedburner.com%2FGunsmokersTranslations" src="http://www.attensa.com/blogs/attensa/WindowsLiveWriter/BadgeredintoBadges_10C02/attensa_feed_button5.gif">Subscribe with Attensa for Outlook</feedburner:feedFlare><feedburner:feedFlare href="http://www.webwag.com/wwgthis.php?url=http%3A%2F%2Ffeeds.feedburner.com%2FGunsmokersTranslations" src="http://www.webwag.com/images/wwgthis.gif">Subscribe with Webwag</feedburner:feedFlare><feedburner:feedFlare href="http://www.podcastready.com/oneclick_bookmark.php?url=http%3A%2F%2Ffeeds.feedburner.com%2FGunsmokersTranslations" src="http://www.podcastready.com/images/podcastready_button.gif">Subscribe with Podcast Ready</feedburner:feedFlare><feedburner:feedFlare href="http://www.flurry.com/pushRssFeed.do?r=fb&amp;url=http%3A%2F%2Ffeeds.feedburner.com%2FGunsmokersTranslations" src="http://www.flurry.com/images/flurry_rss_logo2.gif">Subscribe with Flurry</feedburner:feedFlare><feedburner:feedFlare href="http://www.wikio.com/subscribe?url=http%3A%2F%2Ffeeds.feedburner.com%2FGunsmokersTranslations" src="http://www.wikio.com/shared/img/add2wikio.gif">Subscribe with Wikio</feedburner:feedFlare><feedburner:feedFlare href="http://www.dailyrotation.com/index.php?feed=http%3A%2F%2Ffeeds.feedburner.com%2FGunsmokersTranslations" src="http://www.dailyrotation.com/rss-dr2.gif">Subscribe with Daily Rotation</feedburner:feedFlare><entry gd:etag="W/&quot;AkYBQns6fyp7ImA9WhRaF04.&quot;"><id>tag:blogger.com,1999:blog-1688132520265708820.post-539405443253219257</id><published>2012-02-19T15:35:00.000+04:00</published><updated>2012-02-20T15:42:33.517+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-02-20T15:42:33.517+04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Delphi" /><category scheme="http://www.blogger.com/atom/ns#" term="кодинг" /><title>Используется ли файл?</title><content type="html">Это перевод &lt;a href="http://blog.delphi-jedi.net/2010/11/14/is-file-in-use/" title="Is File In Use"&gt;Is File In Use&lt;/a&gt;. Автор: Christian Wimmer.&lt;br /&gt;
&lt;br /&gt;
На форумах DelphiPraxis задали вопрос, который обычно всплывает несколько раз в год. Однако в этот раз я мог сказать, что появился API, который решает эту проблему. Вопрос был о том, как определить, кто держит файл. Главной проблемой стало то, что я никак не мог вспомнить название API, так что Assarbad пришлось постараться и поискать его.&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
И это имя - &lt;a href="http://msdn.microsoft.com/en-us/library/windows/desktop/bb775874(v=vs.85).aspx" title="MSDN: IFileIsInUse interface"&gt;&lt;code&gt;IFileIsInUse&lt;/code&gt;&lt;/a&gt;, интерфейс. Он объявлен в &lt;code&gt;Shobjidl.h&lt;/code&gt;, &lt;code&gt;Shobjidl.idl&lt;/code&gt; и, сейчас уже, в &lt;code&gt;JwaShlObj.pas&lt;/code&gt;. Вот вырезка кода:
&lt;pre class="brush:delphi"&gt;{$IFDEF WINVISTA_UP}
const
  IID_IFileIsInUse: TGUID = (
    D1:$64a1cbf0; D2:$3a1a; D3:$4461; D4:($91,$58,$37,$69,$69,$69,$39,$50));

type
  {$ALIGN 4}
  tagFILE_USAGE_TYPE = (
    FUT_PLAYING = 0,
    FUT_EDITING = 1,
    FUT_GENERIC = 2
  );
  FILE_USAGE_TYPE = tagFILE_USAGE_TYPE;
  TFileUsageType = FILE_USAGE_TYPE;

const
  OF_CAP_CANSWITCHTO     = $0001;
  OF_CAP_CANCLOSE        = $0002;

type
  IFileIsInUse = interface(IUnknown)
    ['{64a1cbf0-3a1a-4461-9158-376969693950}']
    function GetAppName(out ppszName: LPWSTR) : HRESULT; stdcall;
    function GetUsage(out pfut : FILE_USAGE_TYPE) : HRESULT; stdcall;
    function GetCapabilities(out pdwCapFlags : DWORD) : HRESULT; stdcall;
    function GetSwitchToHWND(out phwnd : HWND) : HRESULT; stdcall;
    function CloseFile() : HRESULT; stdcall;
  end;

{$ENDIF WINVISTA_UP}&lt;/pre&gt;
Интерфейс &lt;a title="Вы можете читать контракт и с другой стороны" href="http://www.transl-gunsmoker.ru/2008/12/blog-post_4027.html"&gt;может использоваться двояко&lt;/a&gt; - и клиентом и быть реализованным сервером. Клиент обычно проверяет, заблокирован ли файл, и получает ссылку на интерфейс для вызова его методов. Сервер же может держать блокировку на файле и реализовывать этот интерфейс, чтобы предоставить его услуги клиенту. Эта статья коснётся только стороны клиента.&lt;br /&gt;
&lt;br /&gt;
Вы увидите, что этот API будет работать только если обе стороны выполняют свою работу. Процесс, блокирующий файл, также должен реализовать этот интерфейс и зарегистрировать его, чтобы клиент мог получать информацию. Нет никакой прямой связи между блокировкой файла и именем процесса. Если процесс держит блокировку на файл, но не реализует интерфейс, то вы снова оказываетесь сами за себя. &lt;br /&gt;
&lt;br /&gt;
В конечном итоге, это просто помощник Оболочки для красивого диалога удаления файлов Windows:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;
&lt;a href="http://2.bp.blogspot.com/-Woz17UgovcA/T0IjuVGkvvI/AAAAAAAADWM/apa-e88Jf5o/s1600/FileInUse-2%2B%25281%2529.png" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="236" width="400" src="http://2.bp.blogspot.com/-Woz17UgovcA/T0IjuVGkvvI/AAAAAAAADWM/apa-e88Jf5o/s400/FileInUse-2%2B%25281%2529.png" /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
Итак, напомню вид самого интерфейса:
&lt;pre class="brush:delphi"&gt;IFileIsInUse = interface(IUnknown)
  function GetAppName(out ppszName: LPWSTR) : HRESULT; stdcall;
  function GetUsage(out pfut : FILE_USAGE_TYPE) : HRESULT; stdcall;
  function GetCapabilities(out pdwCapFlags : DWORD) : HRESULT; stdcall;
  function GetSwitchToHWND(out phwnd : HWND) : HRESULT; stdcall;
  function CloseFile() : HRESULT; stdcall;
end;&lt;/pre&gt;
Методы интерфейса достаточно просто понять:
&lt;ul&gt;
&lt;li&gt;&lt;a title="MSDN: IFileIsInUse::GetAppName method" href="http://msdn.microsoft.com/en-us/library/windows/desktop/bb775867(v=vs.85).aspx"&gt;&lt;code&gt;GetAppName&lt;/code&gt;&lt;/a&gt; получает имя процесса, который держит файл. Это будет произвольное имя, выбранное самим приложением. Всегда используйте &lt;code&gt;PWideChar&lt;/code&gt; для получения имени и не забывайте освободить память с помощью CoTaskMemFree (&lt;a title="В чём разница между SHGetMalloc, SHAlloc, CoGetMalloc и CoTaskMemAlloc?" href="http://www.transl-gunsmoker.ru/2009/05/shgetmalloc-shalloc-cogetmalloc.html"&gt;или через &lt;code&gt;IMalloc&lt;/code&gt;&lt;/a&gt;). Не забывайте проверять результат вызова. Если вам нравятся исключения, то вы можете изменить прототип метода на &lt;code&gt;safecall&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&lt;/li&gt;

&lt;li&gt;&lt;a title="MSDN: IFileIsInUse::GetUsage method" href="http://msdn.microsoft.com/en-us/library/windows/desktop/bb775873(v=vs.85).aspx"&gt;&lt;code&gt;GetUsage&lt;/code&gt;&lt;/a&gt; возвращает причину блокировки файла. Это может быть одна из констант в перечислении &lt;code&gt;TFileUsage&lt;/code&gt;. Это может быть проигрывание видео, музыки или редактирование файла. Либо же это может быть общая причина &lt;code&gt;FUT_GENERIC&lt;/code&gt;. Возможно, в будущем будет добавлено больше кодов.&lt;br /&gt;&amp;nbsp;&lt;/li&gt;

&lt;li&gt;&lt;code&gt;GetCapabilities&lt;/code&gt; возвращает набор флагов. Они указывают, можно ли попросить закрыть файл вызовом &lt;code&gt;CloseFile&lt;/code&gt; (&lt;code&gt;OF_CAP_CANCLOSE&lt;/code&gt;) или же мы можем активировать приложение, блокирующее файл (&lt;code&gt;OF_CAP_CANSWITCHTO&lt;/code&gt;). Лучше всего уведомить пользователя о переключении окон, иначе он может сильно расстроиться из-за внезапно пропавшего окна вашего приложения. Само переключение окон можно сделать вызовом &lt;a title="MSDN: SetForegroundWindow function" href="http://msdn.microsoft.com/en-us/library/windows/desktop/ms633539(v=vs.85).aspx"&gt;&lt;code&gt;SetForegroundWindow&lt;/code&gt;&lt;/a&gt;. Но &lt;a href="http://www.transl-gunsmoker.ru/2010/10/blog-post_25.html" title="Вынос окна на передний план - это как любовь: вы не можете её украсть, вам её должны подарить"&gt;для успешности выполнения этой операции ваше приложение должно иметь фокус&lt;/a&gt;.&lt;br /&gt;&amp;nbsp;&lt;/li&gt;

&lt;li&gt;&lt;a title="MSDN: IFileIsInUse::GetSwitchToHWND method" href="http://msdn.microsoft.com/en-us/library/windows/desktop/bb775870(v=vs.85).aspx"&gt;&lt;code&gt;GetSwitchToHWND&lt;/code&gt;&lt;/a&gt; возвращает описатель окна процесса, блокирующего файл. Используйте этот описатель только для переключения на окно. Вы понятия не имеете, что за окно вам могут тут вернуть. Не нужно пытаться его закрыть или делать что-то ещё - это просто будет плохим поведением. И всегда проверяйте на ошибки вызова. Иначе вы не узнаете, вернули ли вам корректный описатель.&lt;br /&gt;
&lt;br /&gt;
Конечно же, вы можете вызывать этот метод только если вызов &lt;code&gt;GetCapabilities&lt;/code&gt; вернул бит &lt;code&gt;OF_CAP_CANSWITCHTO&lt;/code&gt;.&lt;br /&gt;&amp;nbsp;&lt;/li&gt;

&lt;li&gt;&lt;a title="MSDN: IFileIsInUse::CloseFile method" href="http://msdn.microsoft.com/en-us/library/windows/desktop/bb775865(v=vs.85).aspx"&gt;&lt;code&gt;CloseFile&lt;/code&gt;&lt;/a&gt; просит сервер закрыть файл. Вы можете вызывать этот метод только если вызов &lt;code&gt;GetCapabilities&lt;/code&gt; вернул бит &lt;code&gt;OF_CAP_CANCLOSE&lt;/code&gt;. Ну, это действительно приятная возможность. Но не доверяйте ей слепо! Всегда перепроверьте блокировку файла после вызова прежде чем двигаться дальше.&lt;/li&gt;
&lt;/ul&gt;
Чтобы получить интерфейс для файла вам нужно сначала иметь заблокированный файл. Вы можете скачать и скомпилировать &lt;a title="MSDN: File Is In Use Sample" href="http://msdn.microsoft.com/en-us/library/ee330722(VS.85).aspx"&gt;пример из MSDN с именем IsFileInUse&lt;/a&gt; или вы можете просто открыть файл в приложении, которое реализует такие интерфейсы (например, MS Office).&lt;br /&gt;
&lt;br /&gt;
Следующий шаг - узнать, где размещаются заблокированные файлы. Это место - running object table, или ROT для краткости (её можно получить через ActiveX.&lt;a href="http://msdn.microsoft.com/en-us/library/windows/desktop/ms684004(v=vs.85).aspx" title="MSDN: GetRunningObjectTable function"&gt;GetRunningObjectTable&lt;/a&gt;()). Это глобальная* (для машины) таблица, которая хранит запущенные (running) COM объекты. Вы можете реализовать свой собственный интерфейс и поместить его в эту таблицу. Поскольку интерфейсы могут быть произвольными, к ним прикрепляются &lt;a title="Википедия: Моникер" href="http://ru.wikipedia.org/wiki/%D0%9C%D0%BE%D0%BD%D0%B8%D0%BA%D0%B5%D1%80"&gt;моникеры&lt;/a&gt; (&lt;a title="MSDN: IMoniker interface" href="http://msdn.microsoft.com/en-us/library/windows/desktop/ms679705(v=vs.85).aspx"&gt;IMoniker&lt;/a&gt;), которые уникально их описывают. Существует несколько типов моникеров вроде класса (class), элемента (item) и файла (file) - и нас интересует только последний тип.&lt;br /&gt;
&lt;br /&gt; 
Так что вся работа будет заключаться в переборе моникеров в ROT. Обычно их там не много (у меня было всего 5). Каждый моникер проверяется на тип (&lt;a title="MSDN: IMoniker::IsSystemMoniker method" href="http://msdn.microsoft.com/en-us/library/windows/desktop/ms690482(v=vs.85).aspx"&gt;&lt;code&gt;IsSystemMoniker&lt;/code&gt;&lt;/a&gt;) и то, является ли он файловым моникером (&lt;a href="http://msdn.microsoft.com/en-us/library/windows/desktop/ms694326(v=vs.85).aspx" title="MSDN: MKSYS enumeration"&gt;&lt;code&gt;MKSYS_FILEMONIKER&lt;/code&gt;&lt;/a&gt;), а затем путь к файлу сравнивается с нашим файлом. Если честно, то я не могу сказать, зачем пример сперва сравнивает префикс, а потом сам моникер - извините.&lt;br /&gt;
&lt;br /&gt; 
Сам объект получается по моникеру через вызов &lt;a title="MSDN: IRunningObjectTable::GetObject method" href="http://msdn.microsoft.com/en-us/library/windows/desktop/ms683841(v=vs.85).aspx"&gt;&lt;code&gt;GetObject&lt;/code&gt;&lt;/a&gt; у ROT. Этот вызов может завершиться с ошибкой &lt;code&gt;E_ACCESS_DENIED&lt;/code&gt;, так что проверяйте на ошибки. В итоге мы получаем интерфейс &lt;code&gt;IFileIsInUse&lt;/code&gt;. Поскольку файл мог быть зарегистрирован без реализации этого интерфейса, то нам снова нужна проверка на ошибки.&lt;br /&gt;
&lt;br /&gt;
Я не писал весь этот код сам. Фактически, я взял за основу уже упоминавшийся мною пример из MSDN. 
&lt;pre class="brush:delphi"&gt;function GetFileInUseInfo(const FileName : WideString) : IFileIsInUse;
var
  ROT : IRunningObjectTable;
  mFile, enumIndex, Prefix : IMoniker;
  enumMoniker : IEnumMoniker;
  MonikerType : LongInt;
  unkInt  : IInterface;
begin
  result := nil;

  OleCheck(GetRunningObjectTable(0, ROT));
  OleCheck(CreateFileMoniker(PWideChar(FileName), mFile));

  OleCheck(ROT.EnumRunning(enumMoniker));

  while (enumMoniker.Next(1, enumIndex, nil) = S_OK) do
  begin
    OleCheck(enumIndex.IsSystemMoniker(MonikerType));
    if MonikerType = MKSYS_FILEMONIKER then
    begin
      if Succeeded(mFile.CommonPrefixWith(enumIndex, Prefix)) and
         (mFile.IsEqual(Prefix) = S_OK) then
      begin
       if Succeeded(ROT.GetObject(enumIndex, unkInt)) then
        begin
          if Succeeded(unkInt.QueryInterface(IID_IFileIsInUse, result)) then
          begin
            result := unkInt as IFileIsInUse;
            exit;
          end;
        end;
      end;
    end;
  end;
end;&lt;/pre&gt;
&lt;br /&gt;
&lt;h1&gt;Заключение&lt;/h1&gt;
В целом этот API имеет такие недостатки:
&lt;ul&gt;
&lt;li&gt;Этот API рассчитывает на то, что программа, заблокировавшая файл, реализует и зарегистрирует специальный интерфейс. Если же приложение не озаботится этой задачей, то вы не сможете использовать этот метод.&lt;/li&gt;
&lt;li&gt;Если вы заблокируете файл на разделяемой сетевой папке, а затем попробуете обратиться к нему по локальному имени, то не сможете определить процесс, заблокировавший файл. Причина кроется в самой Windows. Windows является провайдером файлов внешнему миру (даже если это всего лишь петля обратно на локальную машину), так что Проводник Windows покажет просто "Система" в качестве блокиратора. Также, в ROT вы увидите только сетевой (UNC) путь вместо локального.&lt;/li&gt;
&lt;li&gt;(*) Примечание безопасности: ROT не является по настоящему глобальной. Фактически, в системе есть несколько ROT. ROT делятся индивидуально по пользователям, а затем по mandatory integrity control (MIC) или integrity levels (IL) - высокому (high), среднему (medium) и, вероятно, низкому (low) (я не проверял). Если вы запущены как обычный пользователь, ваши процессы будут иметь средний уровень, а процессы администратора - высокий. Программа со средним уровнем сможет зарегистрировать себя только в ROT для среднего уровня. Поэтому, если она хочет создать ключи реестра для COM (LOCAL_MACHINE\Classes\AppID\guid), то ей нужно запуститься под администратором хотя бы раз. Таким образом, она сможет стать видимой для всех ROT при желании. К сожалению, это ещё не всё. На многопользовательской системе каждый объект в моникере имеет дескриптор безопасности, который говорит COM, кто имеет к нему доступ. Если Алиса хочет получить доступ к ROT, созданной Бобом, то Боб должен явно разрешить Алисе доступ к ней. Как обычно в вопросах безопасности, по умолчанию доступ разрешается только системе, администраторам и создателю - эти настройки копируются из глобальных настроек безопасности COM и они могут быть изменены либо в реестре для AppID при запуске процесса, либо для каждого регистрируемого объекта сервером. JWSCL даёт доступ к обоим вариантам в файле JwsclComSecurity.pas (&gt;= 0.9.4). И таким образом сервер может изменить настройки, чтобы, скажем, дать всем прошедшим проверку пользователям доступ к объекту.&lt;/li&gt;
&lt;/ul&gt;
&lt;br /&gt;
&lt;h1&gt;Пример&lt;/h1&gt;
Вы можете скачать файл-пример напрямую с Subversion:&lt;br /&gt;
&lt;a href="https://jedi-apilib.svn.sourceforge.net/svnroot/jedi-apilib/jwapi/trunk/Examples/FileIsInUse/Client/FileIsInUseClientExample.dpr" title="FileIsInUseClientExample.dpr"&gt;https://jedi-apilib.svn.sourceforge.net/svnroot/jedi-apilib/jwapi/trunk/Examples/FileIsInUse/Client/FileIsInUseClientExample.dpr&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
См. также: &lt;a title="Как мне найти программу, которая держит этот файл?" href="http://www.transl-gunsmoker.ru/2012/02/blog-post_18.html"&gt;Как мне найти программу, которая держит этот файл?&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688132520265708820-539405443253219257?l=www.transl-gunsmoker.ru' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=b-tmZLxXB94:qqOHa2ycum4:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=b-tmZLxXB94:qqOHa2ycum4:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=b-tmZLxXB94:qqOHa2ycum4:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=b-tmZLxXB94:qqOHa2ycum4:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=b-tmZLxXB94:qqOHa2ycum4:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=b-tmZLxXB94:qqOHa2ycum4:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=b-tmZLxXB94:qqOHa2ycum4:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=b-tmZLxXB94:qqOHa2ycum4:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=b-tmZLxXB94:qqOHa2ycum4:-BTjWOF_DHI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=b-tmZLxXB94:qqOHa2ycum4:-BTjWOF_DHI" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/GunsmokersTranslations/~4/b-tmZLxXB94" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.transl-gunsmoker.ru/feeds/539405443253219257/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://www.transl-gunsmoker.ru/2012/02/blog-post_19.html#comment-form" title="Комментарии: 0" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/539405443253219257?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/539405443253219257?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/GunsmokersTranslations/~3/b-tmZLxXB94/blog-post_19.html" title="Используется ли файл?" /><author><name>Александр Алексеев</name><uri>https://profiles.google.com/113168002104297556003</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh3.googleusercontent.com/-aUMttZQBsuI/AAAAAAAAAAI/AAAAAAAAC3Y/QuZ7K9t_WzE/s512-c/photo.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://2.bp.blogspot.com/-Woz17UgovcA/T0IjuVGkvvI/AAAAAAAADWM/apa-e88Jf5o/s72-c/FileInUse-2%2B%25281%2529.png" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://www.transl-gunsmoker.ru/2012/02/blog-post_19.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DU4BRXcyfyp7ImA9WhRaF04.&quot;"><id>tag:blogger.com,1999:blog-1688132520265708820.post-5311522479826646968</id><published>2012-02-18T05:33:00.000+04:00</published><updated>2012-02-20T15:39:14.997+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-02-20T15:39:14.997+04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="кодинг" /><title>Как мне найти программу, которая держит этот файл?</title><content type="html">Это перевод &lt;a href="http://blogs.msdn.com/b/oldnewthing/archive/2012/02/17/10268840.aspx" title="How do I find out which process has a file open?"&gt;How do I find out which process has a file open?&lt;/a&gt; Автор: Реймонд Чен.&lt;br /&gt;
&lt;br /&gt;
Исторически, нет никакого специального способа найти процесс, который держит файл. Файловый объект имеет обычный счётчик ссылок объекта ядра и когда счётчик опускается до нуля - файл закрывается. Но в системе никто не отслеживает процессы, открывшие данный описатель, и сколько именно раз они его открыли (и это упрощённое изложение даже игнорирует тот факт, что счётчик может быть увеличен вовсе не процессом, а, скажем, драйвером режима ядра; или, быть может, изначально счётчик был увеличен процессом, который теперь уже закрыт, но &lt;a title="MSDN: ObReferenceObjectByHandle routine" href="http://msdn.microsoft.com/ru-ru/library/ff558679(en-us,VS.85).aspx"&gt;файл ещё держится драйвером ядра&lt;/a&gt;).&lt;br /&gt;
&lt;br /&gt;
Это состояние вещей согласуется с концепцией не хранить информацию, которая вам не нужна. Файловую систему не заботит, кто там держит её файлы. Её задача - закрыть файл, когда уйдёт последняя ссылка.&lt;br /&gt;
&lt;br /&gt;
Аналогичную ситуацию вы видите в COM. Всё, что вас заботит - когда же счётчик опустится до нуля (потому что в этот момент вам нужно удалить объект). Если позже вы обнаружите в своём процессе утечку, то у вас нет никакого волшебного запроса "Покажи мне всех, то вызывал _AddRef для моего объекта" - просто потому, что вы никогда и не вели учёт вызывающих _AddRef. Нет у вас и возможности сделать так: "Вот объект, который я хочу удалить. Покажи мне всех использующих его, так что я смогу их удалить".&lt;br /&gt;
&lt;br /&gt;
По крайней мере таким был классический сценарий.&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
А теперь познакомьтесь с &lt;a title="MSDN: Restart Manager" href="http://msdn.microsoft.com/ru-ru/library/cc948910(en-us,VS.85).aspx"&gt;Restart Manager&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
Официальная цель Restart Manager - оказать помощь в закрытии и перезапуске приложений, которые вы хотите обновить. Чтобы сделать это, вам нужно отслеживать, какие процессы держат ссылки и на какие файлы. И вот она та самая база данных, что нам нужна (почему это ядро хранит список процессов, открывших файл? Потому что это принцип, обратный к принципу не хранить вещи, которые вам не нужны: теперь ядру нужна эта информация!)&lt;br /&gt;
&lt;br /&gt;
Вот простая программа, которая принимает в командной строке имя файла и показывает список процессов, открывших этот файл.
&lt;pre class="brush:delphi"&gt;program Project78;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  Winapi.Windows,
  System.SysUtils;

{$A+,Z4}

const
  PROCESS_QUERY_LIMITED_INFORMATION = $1000;

  RstrtMgr = 'Rstrtmgr.dll';

  RM_SESSION_KEY_LEN = SizeOf(TGUID);          // RM_SESSION_KEY_LEN - size in bytes of binary session key
  CCH_RM_SESSION_KEY = RM_SESSION_KEY_LEN * 2; // CCH_RM_SESSION_KEY - character count of text-encoded session key
  CCH_RM_MAX_APP_NAME = 255;                   // CCH_RM_MAX_APP_NAME - maximum character count of application friendly name
  CCH_RM_MAX_SVC_NAME = 63;                    // CCH_RM_MAX_SVC_NAME - maximum character count of service short name
  RM_INVALID_TS_SESSION = -1;                  // Uninitialized value for TS Session ID
  RM_INVALID_PROCESS = -1;                     // Uninitialized value for Process ID

type
  TAppName = array[0..CCH_RM_MAX_APP_NAME] of WideChar;
  TServiceName = array[0..CCH_RM_MAX_SVC_NAME] of WideChar;
  TSessionKey = array[0..CCH_RM_SESSION_KEY] of WideChar;

  _RM_APP_TYPE = (
    RmUnknownApp = 0,   // Application type cannot be classified in known categories
    RmMainWindow = 1,   // Application is a windows application that displays a top-level window
    RmOtherWindow = 2,  // Application is a windows app but does not display a top-level window
    RmService = 3,      // Application is an NT service
    RmExplorer = 4,     // Application is Explorer
    RmConsole = 5,      // Application is Console application
    RmCritical = 1000   // Application is critical system process where a reboot is required to restart
  );
  RM_APP_TYPE = _RM_APP_TYPE;
  TRMAppType = RM_APP_TYPE;

  _RM_SHUTDOWN_TYPE = (
    RmForceShutdown = $1,          // Force app shutdown
    RmShutdownOnlyRegistered = $10 // Only shudown apps if all apps registered for restart
  );
  RM_SHUTDOWN_TYPE = _RM_SHUTDOWN_TYPE;
  TRMShutdownType = RM_SHUTDOWN_TYPE;

  _RM_APP_STATUS = (
    RmStatusUnknown = $0,          // Application in unknown state or state not important
    RmStatusRunning = $1,          // Application is currently running
    RmStatusStopped = $2,          // Application stopped by Restart Manager
    RmStatusStoppedOther = $4,     // Application detected stopped by outside action
    RmStatusRestarted = $8,        // Application restarted by Restart Manager
    RmStatusErrorOnStop = $10,     // An error occurred when stopping this application
    RmStatusErrorOnRestart = $20,  // An error occurred when restarting this application
    RmStatusShutdownMasked = $40,  // Shutdown action masked by filer
    RmStatusRestartMasked = $80    // Restart action masked by filter
  );
  RM_APP_STATUS = _RM_APP_STATUS;
  TRMAppStatus = RM_APP_STATUS;

  _RM_REBOOT_REASON = (
    RmRebootReasonNone = $0,               // Reboot not required
    RmRebootReasonPermissionDenied = $1,   // Current user does not have permission to shut down one or more detected processes
    RmRebootReasonSessionMismatch = $2,    // One or more processes are running in another TS session.
    RmRebootReasonCriticalProcess = $4,    // A critical process has been detected
    RmRebootReasonCriticalService = $8,    // A critical service has been detected
    RmRebootReasonDetectedSelf = $10       // The current process has been detected
  );
  RM_REBOOT_REASON = _RM_REBOOT_REASON;
  TRMRebootReason = RM_REBOOT_REASON;

  _RM_UNIQUE_PROCESS = record
    dwProcessId: DWORD;               // PID
    ProcessStartTime: TFileTime;      // Process creation time
  end;
  RM_UNIQUE_PROCESS = _RM_UNIQUE_PROCESS;
  PRM_UNIQUE_PROCESS = ^_RM_UNIQUE_PROCESS;
  TRMUniqueProcess = RM_UNIQUE_PROCESS;
  PRMUniqueProcess = PRM_UNIQUE_PROCESS;

  _RM_PROCESS_INFO = record
    Process: TRMUniqueProcess;         // Unique process identification
    strAppName: TAppName;              // Application friendly name
    strServiceShortName: TServiceName; // Service short name, if applicable
    ApplicationType: TRMAppType;       // Application type
    AppStatus: ULONG;                  // Bit mask of application status
    TSSessionId: DWORD;                // Terminal Service session ID of process (-1 if n/a)
    bRestartable: BOOL;                // Is application restartable?
  end;
  RM_PROCESS_INFO = _RM_PROCESS_INFO;
  PRM_PROCESS_INFO = ^_RM_PROCESS_INFO;
  TRMProcessInfo = RM_PROCESS_INFO;
  PRMProcessInfo = PRM_PROCESS_INFO;

function QueryFullProcessImageName(hProcess: THandle; dwFlags: DWORD; lpExeName: PChar; var lpdwSize: Integer): BOOL; stdcall; external kernel32 name {$IFDEF UNICODE}'QueryFullProcessImageNameW'{$ELSE}'QueryFullProcessImageNameA'{$ENDIF};

function RmStartSession(out pSessionHandle: DWORD; dwSessionFlags: DWORD; out strSessionKey: TSessionKey): DWORD; stdcall; external RstrtMgr;
function RmEndSession(dwSessionHandle: DWORD): DWORD; stdcall; external RstrtMgr;
function RmRegisterResources(dwSessionHandle: DWORD; nFiles: UINT; rgsFileNames: PPWideChar; nApplications: UINT; rgApplications: PRMUniqueProcess; nServices: UINT; rgsServiceNames: PPWideChar): DWORD; stdcall; external RstrtMgr;
function RmGetList(dwSessionHandle: DWORD; out pnProcInfoNeeded: UINT; var pnProcInfo: UINT; out rgAffectedApps: TRMProcessInfo; out lpdwRebootReasons: DWORD): DWORD; stdcall; external RstrtMgr;

procedure Run;

  function StrFromAppType(const AAppType: TRMAppType): String;
  begin
    case AAppType of
      RmMainWindow:  Result := 'Application is a windows application that displays a top-level window';
      RmOtherWindow: Result := 'Application is a windows app but does not display a top-level window';
      RmService:     Result := 'Application is an NT service';
      RmExplorer:    Result := 'Application is Explorer';
      RmConsole:     Result := 'Application is Console application';
      RmCritical:    Result := 'Application is critical system process where a reboot is required to restart';
    else
                     Result := 'Application type cannot be classified in known categories';
    end;
  end;

const
  Num = 10;
var
  dwSession: DWORD;
  szSessionKey: TSessionKey;
  pszFile: PPWideChar;
  P: PWideChar;
  FileName: WideString;
  dwReason: DWORD;
  i: Integer;
  nProcInfoNeeded: UINT;
  nProcInfo: UINT;
  rgpi: array[0..Num - 1] of TRMProcessInfo;
  hProcess: THandle;
  ftCreate, ftExit, ftKernel, ftUser: TFileTime;
  sz: String;
  cch: Integer;
begin
  FileName := ParamStr(1);

  FillChar(SessionKey, SizeOf(SessionKey), 0);
  SetLastError(RmStartSession(dwSession, 0, szSessionKey));
  Win32Check(GetLastError = ERROR_SUCCESS);
  try
    P := PWideChar(FileName);
    pszFile := @P;
    SetLastError(RmRegisterResources(dwSession, 1, pszFile, 0, nil, 0, nil));
    Win32Check(GetLastError = ERROR_SUCCESS);

    nProcInfo := Num;
    SetLastError(RmGetList(dwSession, nProcInfoNeeded, nProcInfo, rgpi[0], dwReason));
    Win32Check(GetLastError = ERROR_SUCCESS);

    for i := 0 to nProcInfo - 1 do
    begin
      WriteLn(Format('%d.ApplicationType = %d (%s)', [i, Ord(rgpi[i].ApplicationType), StrFromAppType(rgpi[i].ApplicationType)]));
      WriteLn(Format('%d.strAppName = %s', [i, rgpi[i].strAppName]));
      WriteLn(Format('%d.Process.dwProcessId = %d', [i, rgpi[i].Process.dwProcessId]));

      hProcess := OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, False, rgpi[i].Process.dwProcessId);
      if hProcess &lt;&gt; 0 then
      try
        if GetProcessTimes(hProcess, ftCreate, ftExit, ftKernel, ftUser) and
           (CompareFileTime(rgpi[i].Process.ProcessStartTime, ftCreate) = 0) then
        begin
          cch := MAX_PATH;
          SetLength(sz, cch);
          if QueryFullProcessImageName(hProcess, 0, PChar(sz), cch) and
             (cch &lt;= MAX_PATH) then
          begin
            SetLength(sz, cch);
            WriteLn(Format('%d.Process.Name = %s', [i, sz]));
          end;
        end;
      finally
        CloseHandle(hProcess);
      end;
      WriteLn;
    end;
  finally
    RmEndSession(dwSession);
  end;
end;

begin
  try
    if ParamCount = 0 then
      Exit;
    Run;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.&lt;/pre&gt;
Итак, первой строкой в этом коде... нет, постойте - ещё до вызова функции &lt;a title="MSDN: Rm­Start­Session function" href="http://msdn.microsoft.com/en-us/library/windows/desktop/aa373668(v=vs.85).aspx"&gt;&lt;code&gt;Rm­Start­Session&lt;/code&gt;&lt;/a&gt; у нас есть строка
&lt;pre class="brush:delphi"&gt;FillChar(SessionKey, SizeOf(SessionKey), 0);&lt;/pre&gt; 
Одна эта строка кода решает аж два бага!&lt;br /&gt;
&lt;br /&gt;
Первый из них - баг в документации. Документация по функции &lt;code&gt;Rm­Start­Session&lt;/code&gt; не указывает, насколько большим должен быть буфер для ключа сессии. Правильный ответ - CCH_RM_SESSION_KEY + 1.&lt;br /&gt;
&lt;br /&gt;
Второй баг - в коде. Функция &lt;code&gt;Rm­Start­Session&lt;/code&gt; не завершает ключ терминатором, даже хотя прототип функции описан как возвращающий нуль-терминированную строку. Чтобы обойти эту проблему, мы очищаем буфер перед использованием, заполняя его нулями, так что то, что будет записано в ключ сессии, автоматически получит корректный терминатор (а именно - один из тех нулей, что мы записали).&lt;br /&gt;
&lt;br /&gt;
Прим. пер.: при переводе с C на Delphi прототип функции был существенно изменён. Так что теперь ключ сессии передаётся как фиксированный массив.&lt;br /&gt;
&lt;br /&gt;
Окей, эти проблемы ушли с дороги. Теперь, базовый алгоритм:
&lt;ol&gt;
&lt;li&gt;Создать сессию Restart Manager.&lt;/li&gt;
&lt;li&gt;Добавить интересующий нас файл в сессию.&lt;/li&gt;
&lt;li&gt;Запросить список процессов, влияющих на ресурс.&lt;/li&gt;
&lt;li&gt;Напечатать немного информации по каждому процессу.&lt;/li&gt;
&lt;li&gt;Закрыть сессию.&lt;/li&gt;
&lt;/ol&gt;
Я уже упомянул, что вы создаёте сессию вызовом &lt;code&gt;Rm­Start­Session&lt;/code&gt;. Следующим шагом мы добавляем в сессию единственный файл вызовом &lt;a title="MSDN: Rm­Register­Resources function" href="http://msdn.microsoft.com/en-us/library/windows/desktop/aa373663(v=vs.85).aspx"&gt;&lt;code&gt;Rm­Register­Resources&lt;/code&gt;&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
Теперь начинается веселье. Получение списка процессов обычно происходит в два этапа. Сначала вы запрашиваете число доступных процессов (передавая 0 в &lt;code&gt;nProcInfo&lt;/code&gt;), затем выделяете память и вызываете функцию второй раз для получения данных. Но поскольку это просто пример, я вшил в программу фиксированное число процессов. Если файл открыт более 10 процессами, то я просто сдамся (вы можете проверить это, запустив программу и указав файл вроде kernel32.dll).&lt;br /&gt;
&lt;br /&gt;
Прим.пер.: и если какой-то сильно умный читатель вздумает &lt;a href="http://www.gunsmoker.ru/2010/05/90.html" title="90% кода в интернете - говно"&gt;скопировать этот код и использовать "как есть"&lt;/a&gt;...&lt;br /&gt;
&lt;br /&gt;
Вторая хитрая часть заключается в поиске процесса по &lt;a title="MSDN: RM_PROCESS_INFO structure" href="http://msdn.microsoft.com/en-us/library/windows/desktop/aa373674(v=vs.85).aspx"&gt;&lt;code&gt;TRMProcessInfo&lt;/code&gt;&lt;/a&gt;. Поскольку ID процессов могут использоваться заново (recycle), то структура &lt;code&gt;TRMProcessInfo&lt;/code&gt; идентифицирует процесс комбинацией ID и времени его запуска. Такая комбинация является уникальной в рамках одной машины, потому что два процесса не могут иметь одинаковые ID в одно и то же время. Поэтому мы открываем процесс по его ID, а затем проверяем, что это именно тот процесс, что нам нужен (а если нет - то &lt;a title="RE: How do I recover the window handle passed to ShellExecute? by Anonymous" href="http://blogs.msdn.com/b/oldnewthing/archive/2010/08/26/10054386.aspx#10054750"&gt;ID ссылается на процесс, который уже завершил работу&lt;/a&gt; с того момента, когда мы запрашивали список). Если же все данные совпали, то мы выводим путь к .exe файлу процесса.&lt;br /&gt;
&lt;br /&gt;
Вот и всё, что нужно, чтобы перечислить все процессы, открывшие какой-то конкретный файл. &lt;br /&gt;
&lt;br /&gt;
Конечно же, более выразительным интерфейсом для управления используемыми файлами является &lt;a title="MSDN: IFileIsInUse interface" href="http://msdn.microsoft.com/en-us/library/windows/desktop/bb775874(v=vs.85).aspx"&gt;&lt;code&gt;IFileIsInUse&lt;/code&gt;&lt;/a&gt;, который &lt;a title="Некоторые папки двигать нельзя - и вам придётся научиться с этим жить" href="http://www.transl-gunsmoker.ru/2012/02/blog-post.html"&gt;я упоминал не так давно&lt;/a&gt;. Этот интерфейс скажет вам не только какие приложения открыли файл (и в более дружелюбном формате, чем просто путь к .exe), но вы также сможете переключиться на это приложение и даже попросить его закрыть файл (если оно поддерживает эту возможность). Сама Windows 7 сначала пытается использовать &lt;code&gt;IFileIsInUse&lt;/code&gt;, и лишь если он не сумел освободить файл - обращается к Restart Manager.&lt;br /&gt;
&lt;br /&gt;
Читать далее: &lt;a title="Используется ли файл?" href="http://www.transl-gunsmoker.ru/2012/02/blog-post_19.html"&gt;использование &lt;code&gt;IFileIsInUse&lt;/code&gt;&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688132520265708820-5311522479826646968?l=www.transl-gunsmoker.ru' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=g3yyaGW447o:J9e2AXkvEQI:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=g3yyaGW447o:J9e2AXkvEQI:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=g3yyaGW447o:J9e2AXkvEQI:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=g3yyaGW447o:J9e2AXkvEQI:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=g3yyaGW447o:J9e2AXkvEQI:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=g3yyaGW447o:J9e2AXkvEQI:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=g3yyaGW447o:J9e2AXkvEQI:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=g3yyaGW447o:J9e2AXkvEQI:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=g3yyaGW447o:J9e2AXkvEQI:-BTjWOF_DHI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=g3yyaGW447o:J9e2AXkvEQI:-BTjWOF_DHI" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/GunsmokersTranslations/~4/g3yyaGW447o" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.transl-gunsmoker.ru/feeds/5311522479826646968/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://www.transl-gunsmoker.ru/2012/02/blog-post_18.html#comment-form" title="Комментарии: 0" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/5311522479826646968?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/5311522479826646968?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/GunsmokersTranslations/~3/g3yyaGW447o/blog-post_18.html" title="Как мне найти программу, которая держит этот файл?" /><author><name>Александр Алексеев</name><uri>https://profiles.google.com/113168002104297556003</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh3.googleusercontent.com/-aUMttZQBsuI/AAAAAAAAAAI/AAAAAAAAC3Y/QuZ7K9t_WzE/s512-c/photo.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://www.transl-gunsmoker.ru/2012/02/blog-post_18.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CE8BQng5cCp7ImA9WhRaFU8.&quot;"><id>tag:blogger.com,1999:blog-1688132520265708820.post-3299934105999946592</id><published>2012-02-17T03:52:00.000+04:00</published><updated>2012-02-18T03:54:13.628+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-02-18T03:54:13.628+04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="кодинг" /><title>Некоторые папки двигать нельзя - и вам придётся научиться с этим жить</title><content type="html">Это перевод &lt;a href="http://blogs.msdn.com/b/oldnewthing/archive/2010/08/06/10046812.aspx" title="Some known folders cannot be moved, but others can, and you'll just have to accept that"&gt;Some known folders cannot be moved, but others can, and you'll just have to accept that&lt;/a&gt;. Автор: Реймонд Чен.&lt;br /&gt;
&lt;br /&gt;
Некоторые из "известных папок" оболочки (Shell known folders) могут быть помечены как &lt;a title="MSDN: KF_CATEGORY enumeration" href="http://msdn.microsoft.com/en-us/library/bb762512(VS.85).aspx"&gt;&lt;code&gt;KF_CATEGORY_FIXED&lt;/code&gt;&lt;/a&gt;, что делает их неперемещаемыми. И наоборот, если папка файловой системы не является "неподвижной", то она может быть перемещена.&lt;br /&gt;
&lt;br /&gt;
Эта дихотомия представляется простой и недостойной отдельного обсуждения - за исключением того, что у некоторых клиентов иногда возникают проблемы включения этого понятия в своё мировоззрение.&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;
&lt;blockquote&gt;У меня есть код, который вызывает &lt;a title="MSDN: SHSetFolderPath function" href="http://msdn.microsoft.com/en-us/library/windows/desktop/bb762247(v=vs.85).aspx"&gt;&lt;code&gt;SHSet­Folder­Path&lt;/code&gt;&lt;/a&gt;, и он работает для большинства папок, но для некоторых значений CSIDL вроде &lt;code&gt;CSIDL_COMMON_APPDATA&lt;/code&gt; код завершается с ошибкой &lt;code&gt;E_INVALIDARG&lt;/code&gt;. При этом не имеет значения, вызываю ли я его с повышением привилегий или нет. Что я делаю не так?&lt;/blockquote&gt;
Разница в том, что &lt;code&gt;CSIDL_COMMON_APPDATA&lt;/code&gt; (известная в &lt;i&gt;Новом Мировом Порядке&lt;/i&gt; как &lt;code&gt;FOLDERID_ProgramData&lt;/code&gt;) помечена как &lt;code&gt;KF_CATEGORY_FIXED&lt;/code&gt;, так что её нельзя переместить.
&lt;blockquote&gt;А есть ли способ игнорировать флаг &lt;code&gt;KF_CATEGORY_FIXED&lt;/code&gt; и переместить её?&lt;/blockquote&gt;
Нет. Она неперемещаема. Извините. Вам просто остаётся принять тот факт, что эта папка не поменяет своё расположение по вашему желанию.&lt;br /&gt;
&lt;br /&gt;
И буквально на следующий день мы получили такой вопрос от совершенно другого клиента:
&lt;blockquote&gt;У нас есть программа, которая следит за "известной папкой", но наш код вылетает, если пользователь меняет расположение папки в то время, пока программа за ней следит. Есть ли какой-нибудь способ сделать так, чтобы пользователь не мог двигать папку?&lt;/blockquote&gt;
Если папка может быть перемещена, то вам нужно принять тот факт, что она может быть перемещена. Вы не можете изменить порядок вещей только потому, что это сделает легче жизнь лично вам.&lt;br /&gt;
&lt;br /&gt;
Я нахожу любопытным, что мы получили два запроса подряд, которые просят о полностью противоположных вещах: "Я хочу сделать эту папку перемещаемой!" и "Я хочу сделать эту папку неперемещаемой!". Я могу только догадываться о &lt;a title="Что если две программы сделают это?" href="http://www.transl-gunsmoker.ru/2010/06/blog-post_11.html"&gt;том хаосе, который вызовут эти программы, будучи запущенными одновременно на одной машине&lt;/a&gt;!&lt;br /&gt;
&lt;br /&gt;
Что может сделать программа - зарегистрировать &lt;a title="MSDN: IFile­Is­In­Use interface" href="http://msdn.microsoft.com/en-us/library/windows/desktop/bb775874(v=vs.85).aspx"&gt;&lt;code&gt;IFile­Is­In­Use&lt;/code&gt;&lt;/a&gt; для каталога, так что оболочка вызовет её, когда кто-то захочет переместить папку. И тогда, по крайней мере, программа будет знать, когда будут происходить &lt;i&gt;страшные вещи&lt;/i&gt;, и подготовиться к ним. Мне сказали, что &lt;a href="http://msdn.microsoft.com/en-us/library/ee330722(VS.85).aspx" title="MSDN: File Is In Use Sample"&gt;простой пример использования &lt;code&gt;IFile­Is­In­Use&lt;/code&gt;&lt;/a&gt; можно найти в SDK Windows 7 SDK в каталоге winui\Shell\AppPlatform\FileIsInUse. И ещё есть &lt;a title="Your File Is In Use… Demystified" href="http://wayback.archive.org/web/jsp/Interstitial.jsp?seconds=5&amp;date=1177850876000&amp;url=http%3A%2F%2Fshellrevealed.com%2Fblogs%2Fshellblog%2Farchive%2F2007%2F03%2F29%2FYour-File-Is-In-Use_2620_-Demystified.aspx&amp;target=http%3A%2F%2Fweb.archive.org%2Fweb%2F20070429124756%2Fhttp%3A%2F%2Fshellrevealed.com%2Fblogs%2Fshellblog%2Farchive%2F2007%2F03%2F29%2FYour-File-Is-In-Use_2620_-Demystified.aspx"&gt;старая статья&lt;/a&gt; по этой теме в сейчас-уже-не-работающем блоге Shell Revealed.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688132520265708820-3299934105999946592?l=www.transl-gunsmoker.ru' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=5-74wKDK0B0:ZeYGKeD_qnk:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=5-74wKDK0B0:ZeYGKeD_qnk:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=5-74wKDK0B0:ZeYGKeD_qnk:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=5-74wKDK0B0:ZeYGKeD_qnk:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=5-74wKDK0B0:ZeYGKeD_qnk:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=5-74wKDK0B0:ZeYGKeD_qnk:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=5-74wKDK0B0:ZeYGKeD_qnk:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=5-74wKDK0B0:ZeYGKeD_qnk:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=5-74wKDK0B0:ZeYGKeD_qnk:-BTjWOF_DHI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=5-74wKDK0B0:ZeYGKeD_qnk:-BTjWOF_DHI" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/GunsmokersTranslations/~4/5-74wKDK0B0" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.transl-gunsmoker.ru/feeds/3299934105999946592/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://www.transl-gunsmoker.ru/2012/02/blog-post.html#comment-form" title="Комментарии: 1" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/3299934105999946592?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/3299934105999946592?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/GunsmokersTranslations/~3/5-74wKDK0B0/blog-post.html" title="Некоторые папки двигать нельзя - и вам придётся научиться с этим жить" /><author><name>Александр Алексеев</name><uri>https://profiles.google.com/113168002104297556003</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh3.googleusercontent.com/-aUMttZQBsuI/AAAAAAAAAAI/AAAAAAAAC3Y/QuZ7K9t_WzE/s512-c/photo.jpg" /></author><thr:total>1</thr:total><feedburner:origLink>http://www.transl-gunsmoker.ru/2012/02/blog-post.html</feedburner:origLink></entry><entry gd:etag="W/&quot;Ak4GSHwyeip7ImA9WhdUEEo.&quot;"><id>tag:blogger.com,1999:blog-1688132520265708820.post-757251907260489577</id><published>2011-09-08T02:59:00.000+04:00</published><updated>2011-09-27T03:15:29.292+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-09-27T03:15:29.292+04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="История" /><category scheme="http://www.blogger.com/atom/ns#" term="Win32" /><category scheme="http://www.blogger.com/atom/ns#" term="серия" /><title>Почему функция SetWindowsHookEx принимает параметр HINSTANCE?</title><content type="html">Это перевод &lt;a href="http://blogs.msdn.com/b/oldnewthing/archive/2006/08/11/695514.aspx" title="Why does SetWindowsHookEx take an HINSTANCE parameter?"&gt;Why does SetWindowsHookEx take an HINSTANCE parameter?&lt;/a&gt; Автор: Реймонд Чен.&lt;br /&gt;
&lt;br /&gt;
&lt;a href="http://blogs.msdn.com/b/oldnewthing/archive/2005/04/25/411741.aspx#412529" title="RE: What is the HINSTANCE passed to SetWindowsHookEx used for? by Anonymous"&gt;Анонимный комментатор спросил&lt;/a&gt;, почему функция &lt;code&gt;SetWindowsHookEx&lt;/code&gt; принимает параметр &lt;code&gt;HINSTANCE&lt;/code&gt;, если она всё равно сконвертирует это значение в имя файла.&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
Потому что ловушки работали иначе в 16-битных Windows.&lt;br /&gt;
&lt;br /&gt;
В мире 16-битных Windows не было такой вещи как внедрение ловушки. Все 16-битные приложения Windows работали в едином адресном пространстве, так что не было никакой необходимости внедрять ловушку - она и так была доступна всем процессам. Соответственно, не было никакой необходимости конвертировать описатель в имя файла.&lt;br /&gt;
&lt;br /&gt;
Но что там с описателем? Он был нужен, чтобы увеличить счётчик загрузок модуля, чтобы модуль не выгрузился бы в то время, пока установлена и работает ловушка. Когда ловушка удалялась, то счётчик ссылок уменьшался.&lt;br /&gt;
&lt;br /&gt;
И даже в 32-битных Windows оконному менеджеру всё ещё нужен описатель. Зачем? Чтобы сконвертировать адрес функции в DLL в RVA (прим.пер.: относительное смещение) - чтобы эту функцию можно было потом найти в DLL, когда она будет загружена в другой процесс (прим.пер.: и по другому адресу). Так что если бы вы передавали имя фала вместо описателя, то оконному менеджеру всё равно пришлось бы вызывать &lt;code&gt;GetModuleHandle&lt;/code&gt; для получения описателя. Поскольку большинство программ хранит описатели, а не имена, то выбор параметра-описателя был естественным выбором (не говоря уже о сохранении совместимости с 16-битными Windows - а это очень важный критерий, когда ты пытаешься убедить людей писать программы под Win32).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688132520265708820-757251907260489577?l=www.transl-gunsmoker.ru' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=wHc6933CS4M:vXOSRAmI7TA:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=wHc6933CS4M:vXOSRAmI7TA:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=wHc6933CS4M:vXOSRAmI7TA:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=wHc6933CS4M:vXOSRAmI7TA:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=wHc6933CS4M:vXOSRAmI7TA:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=wHc6933CS4M:vXOSRAmI7TA:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=wHc6933CS4M:vXOSRAmI7TA:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=wHc6933CS4M:vXOSRAmI7TA:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=wHc6933CS4M:vXOSRAmI7TA:-BTjWOF_DHI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=wHc6933CS4M:vXOSRAmI7TA:-BTjWOF_DHI" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/GunsmokersTranslations/~4/wHc6933CS4M" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.transl-gunsmoker.ru/feeds/757251907260489577/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://www.transl-gunsmoker.ru/2011/09/why-does-setwindowshookex-take.html#comment-form" title="Комментарии: 0" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/757251907260489577?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/757251907260489577?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/GunsmokersTranslations/~3/wHc6933CS4M/why-does-setwindowshookex-take.html" title="Почему функция SetWindowsHookEx принимает параметр HINSTANCE?" /><author><name>Александр Алексеев</name><uri>https://profiles.google.com/113168002104297556003</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh3.googleusercontent.com/-aUMttZQBsuI/AAAAAAAAAAI/AAAAAAAAC3Y/QuZ7K9t_WzE/s512-c/photo.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://www.transl-gunsmoker.ru/2011/09/why-does-setwindowshookex-take.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DUQBQ3Y6fip7ImA9WhdUEEo.&quot;"><id>tag:blogger.com,1999:blog-1688132520265708820.post-8509687284284189844</id><published>2011-09-07T02:55:00.000+04:00</published><updated>2011-09-27T02:49:12.816+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-09-27T02:49:12.816+04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="История" /><category scheme="http://www.blogger.com/atom/ns#" term="серия" /><category scheme="http://www.blogger.com/atom/ns#" term="Win16" /><title>Как злоупотребляли оконными ловушками в Win16</title><content type="html">Это перевод &lt;a href="http://blogs.msdn.com/b/oldnewthing/archive/2006/08/10/694362.aspx" title="One way people abused hooks in 16-bit Windows"&gt;One way people abused hooks in 16-bit Windows&lt;/a&gt;. Автор: Реймонд Чен.&lt;br /&gt;
&lt;br /&gt;
В &lt;a title="Как были реализованы оконные ловушки в 16-битных Windows?" href="http://www.transl-gunsmoker.ru/2011/09/how-were-window-hooks-implemented-in-16.html"&gt;прошлый раз&lt;/a&gt; мы увидели, как в 16-битных Windows реализуются оконные ловушки. Даже хотя значения &lt;code&gt;HHOOK&lt;/code&gt; являются не прозрачными типами данных ("чёрным ящиком"), с которыми нужно работать как с описателями, многие программы "знали слишком много, чтобы стать опасными" - и воспользовались тем фактом, что значения &lt;code&gt;HHOOK&lt;/code&gt; были просто указателями на предыдущую процедуру ловушки.&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
Самый частый способ использования этой информации во зло заключается в снятии ловушки неверным способом. Вместо вызова функции &lt;code&gt;UnhookWindowsHook&lt;/code&gt; снова вызывалась &lt;code&gt;SetWindowsHook&lt;/code&gt;! Более конкретно: текущая ловушка удалялась установкой на вершину цепочки предыдущей ловушки:
&lt;pre class="brush:delphi"&gt;var
  g_hhkPrev: HHOOK;

// Установка ловушки
g_hhkPrev := SetWindowsHook(WH_KEYBOARD, MyHookProc);
...

// Безумие! Удаление ловушки установкой предыдущей ловушки "обратно"
SetWindowsHook(WH_KEYBOARD, g_hhkPrev);&lt;/pre&gt;
Несмотря на это вопиюще неверное поведение, код работал; это вроде как две неверные вещи при сложении дали почти правильное. Если никто не трогал цепочку ловушек между установкой и таким "снятием" ловушки, то переустановка старой ловушки в вершину списка восстанавливала список ловушек в то же состояние, в которое он приходит при правильном удалении ловушки.&lt;br /&gt;
&lt;br /&gt;
Но если между этими двумя вызовами кто-то другой установит их собственную ловушку, то установка старой ловушки заново приведёт к "удалению" не только &lt;code&gt;MyHookProc&lt;/code&gt;, но и &lt;b&gt;всех ловушек, которые были установлены после неё&lt;/b&gt; (это в точности та же проблема, которая получается, если &lt;a title="Homework assignment about window subclassing" href="http://blogs.msdn.com/b/oldnewthing/archive/2003/11/10/55647.aspx"&gt;вы неосторожно удаляете саб-классированные оконные процедуры&lt;/a&gt;).&lt;br /&gt;
&lt;br /&gt;
Я до сих пор понятия не имею, почему кому-то взбрело в голову удалять ловушку именно таким способом вместо правильного - они ничего не сэкономили, а просто заменили одну строчку на другую:
&lt;pre class="brush:delphi"&gt;UnhookWindowsHook(WH_KEYBOARD, MyHookProc);&lt;/pre&gt;
Windows 3.1 ввела модель с &lt;code&gt;SetWindowsHookEx&lt;/code&gt;/&lt;code&gt;CallNextHookEx&lt;/code&gt;, которая не использовала технику внешнего связанного списка, а хранила список сама, внутренне. Это защитило цепочку ловушек от программ, которые портили её неверным управлением списком, но это также означало, что теперь все эти сумасшедшие программы, которые пытались удалить ловушку повторной установкой старой ловушки, теперь будут просто портить внутренний список ловушек. Нам пришлось написать специальный код, который определял такие случаи и заменял их на корректные вызовы.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688132520265708820-8509687284284189844?l=www.transl-gunsmoker.ru' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=NDA3RtiRuuc:NGYRCi0dRLo:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=NDA3RtiRuuc:NGYRCi0dRLo:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=NDA3RtiRuuc:NGYRCi0dRLo:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=NDA3RtiRuuc:NGYRCi0dRLo:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=NDA3RtiRuuc:NGYRCi0dRLo:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=NDA3RtiRuuc:NGYRCi0dRLo:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=NDA3RtiRuuc:NGYRCi0dRLo:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=NDA3RtiRuuc:NGYRCi0dRLo:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=NDA3RtiRuuc:NGYRCi0dRLo:-BTjWOF_DHI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=NDA3RtiRuuc:NGYRCi0dRLo:-BTjWOF_DHI" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/GunsmokersTranslations/~4/NDA3RtiRuuc" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.transl-gunsmoker.ru/feeds/8509687284284189844/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://www.transl-gunsmoker.ru/2011/09/one-way-people-abused-hooks-in-16-bit.html#comment-form" title="Комментарии: 0" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/8509687284284189844?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/8509687284284189844?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/GunsmokersTranslations/~3/NDA3RtiRuuc/one-way-people-abused-hooks-in-16-bit.html" title="Как злоупотребляли оконными ловушками в Win16" /><author><name>Александр Алексеев</name><uri>https://profiles.google.com/113168002104297556003</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh3.googleusercontent.com/-aUMttZQBsuI/AAAAAAAAAAI/AAAAAAAAC3Y/QuZ7K9t_WzE/s512-c/photo.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://www.transl-gunsmoker.ru/2011/09/one-way-people-abused-hooks-in-16-bit.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DUIBQn8zfCp7ImA9WhdUEEo.&quot;"><id>tag:blogger.com,1999:blog-1688132520265708820.post-3284805136637551190</id><published>2011-09-06T02:55:00.000+04:00</published><updated>2011-09-27T02:52:33.184+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-09-27T02:52:33.184+04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="История" /><category scheme="http://www.blogger.com/atom/ns#" term="серия" /><category scheme="http://www.blogger.com/atom/ns#" term="Win16" /><title>Как были реализованы оконные ловушки в 16-битных Windows?</title><content type="html">Это перевод &lt;a href="http://blogs.msdn.com/b/oldnewthing/archive/2006/08/09/693280.aspx" title="How were window hooks implemented in 16-bit Windows?"&gt;How were window hooks implemented in 16-bit Windows?&lt;/a&gt; Автор: Реймонд Чен.&lt;br /&gt;
&lt;br /&gt;
Механизм отслеживания оконных ловушек был реализован существенно иначе в 16-битных Windows.&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
При этом использовались функции &lt;code&gt;SetWindowsHook&lt;/code&gt;, &lt;code&gt;UnhookWindowsHook&lt;/code&gt; и &lt;code&gt;DefHookProc&lt;/code&gt;. Первые две функции существуют даже сегодня, но третья заменена простым макросом:
&lt;pre class="brush:cpp"&gt;// 16-битный прототип
DWORD WINAPI DefHookProc(int nCode, WPARAM wParam,
                         LPARAM lParam, HHOOK FAR *phk);

// 32-битный макрос
#define DefHookProc(nCode, wParam, lParam, phhk)\
        CallNextHookEx(*phhk, nCode, wParam, lParam)&lt;/pre&gt;
Предупреждение: весь код реконструируется по памяти. Его дух не тронут, но точные детали могут быть указаны не верно.&lt;br /&gt;
&lt;br /&gt;
Чтобы установить ловушку в 16-битных Windows, вы сначала вызываете &lt;code&gt;SetWindowsHook&lt;/code&gt;:
&lt;pre class="brush:delphi"&gt;var
  g_hhkPrev: HHOOK;
...
  g_hhkPrev := SetWindowsHook(WH_WHATEVER, MyHookProc);&lt;/pre&gt;
Возвращаемое значение от &lt;code&gt;SetWindowsHook&lt;/code&gt; должно быть сохранено в глобальной переменной, которой мы дали несколько провокационное имя &lt;code&gt;g_hhkPrev&lt;/code&gt;. Процедура ловушки выглядела вот так:
&lt;pre class="brush:delphi"&gt;// В Win16 процедура ловушки возвращала DWORD, а не LRESULT
function MyHookProc(nCode: Integer; wParam: WinTypes.WPARAM; lParam: WinTypes.LPARAM): DWord;
begin
  if nCode &gt;= 0 then
  begin
    ...
  end;
  Result := DefHookProc(nCode, wParam, lParam, g_hhkPrev);
end;&lt;/pre&gt;
А когда вы заканчивали работу, то вы удаляли ловушку вызовом &lt;code&gt;UnhookWindowsHook&lt;/code&gt;:
&lt;pre class="brush:delphi"&gt;UnhookWindowsHook(WH_WHATEVER, MyhookProc);
g_hhkPrev := 0;&lt;/pre&gt;
Внутренне, цепочка функций ловушек управлялась как связанный список, но вместо использования для хранения списка каких-то внутренних структур, связанный список управлялся через сами переменные &lt;code&gt;HHOOK&lt;/code&gt;.&lt;br /&gt;
&lt;br /&gt;
Внутренняя реализация &lt;code&gt;SetWindowsHook&lt;/code&gt; была довольно простой:
&lt;pre class="brush:delphi"&gt;// Этот массив инициализирован процедурами вида "ничего не делай"
var
  g_rgHook: array[0..NUMHOOKS - 1] of HOOKPROC;

function SetWindowsHook(nType: Integer; pfnHookProc: HOOKPROC): HHOOK; 
var
  hhkPrev: HHOOK;
begin
  hhkPrev := HHOOK(g_rgHook[nType]);
  g_rgHook[nType] := pfnHookProc;
  Result := hhkPrev;
end;&lt;/pre&gt;
Установка ловушки просто записывала вашу процедуру ловушки в заголовок списка (цепочки ловушек), и возвращала предыдущее начало списка. Вызов ловушки заключался просто в вызове первой ловушки (головы списка):
&lt;pre class="brush:delphi"&gt;function CallHook(nType, nCode: Integer; wParam: WinTypes.WPARAM; lParam: WinTypes.LPARAM): DWORD;
begin
  Result := g_rgHook[nType](nCode, wParam, lParam);
end;&lt;/pre&gt;
Каждая процедура ловушки сначала выполняла свою работу, а затем передавала управление следующему элементу в цепочке, вызывая &lt;code&gt;DefHookProc&lt;/code&gt; и передавая ему &lt;code&gt;HHOOK&lt;/code&gt;:
&lt;pre class="brush:delphi"&gt;function DefHookProc(nCode: Integer; wParam: WinTypes.WPARAM; lParam: WinTypes.LPARAM; var phk: HHOOK): DWord;
var
  pfnNext: HOOKPROC;
begin
  pfnNext := HOOKPROC(phk);
  if nCode &gt;=0 then
  begin 
    Result := pfnNext(nCode, wParam, lParam);
    Exit;
  end;
  ... ещё код ...
end;&lt;/pre&gt;
Как вы можете видеть, это чрезвычайно примитивно: вызов ловушки вызывает первую процедуру в списке, которая затем вызывает &lt;code&gt;DefHookProc&lt;/code&gt;, которая знает, что &lt;code&gt;HHOOK&lt;/code&gt; - это просто &lt;code&gt;HOOKPROC&lt;/code&gt;, и она просто перенаправляет вызов вниз по списку, прямо вызывая эту самую функцию.&lt;br /&gt;
&lt;br /&gt;
А волшебство здесь заключается в последнем шаге, когда вы снимаете ловушку. Вспомните, что правило вызова ловушек таково, что отрицательные коды должны передаваться в &lt;code&gt;DefHookProc&lt;/code&gt; (или, по современному: &lt;code&gt;CallNextHookEx&lt;/code&gt;) напрямую. Это соглашение позволяет подсистеме ловушек использовать отрицательные коды для ведения учёта. В данном случае мы использовали -1 как код "снять эту ловушку":
&lt;pre class="brush:delphi"&gt;function UnhookWindowsHook(nType: Integer; pfnHookProc: HOOKPROC): BOOL;
begin
  Result := DefHookProc(-1, 0, LPARAM(pfnHookProc), HHOOK(g_rgHook[nType]));
end;&lt;/pre&gt;
А затем начинается магия:
&lt;pre class="brush:delphi"&gt;function DefHookProc(nCode: Integer; wParam: WinTypes.WPARAM; lParam: WinTypes.LPARAM; var phk: HHOOK): DWord;
var
  pfnNext: HOOKPROC;
begin
  pfnNext := HOOKPROC(phk);
  if nCode &gt;=0 then
  begin 
    Result := pfnNext(nCode, wParam, lParam);
    Exit;
  end;
  case nCode of 
    -1: // пытаемся снять ловушку
    begin
      if pfnNext = HOOKPROC(lParam) then // нашли ловушку
      begin
        phk := HHOOK(pfnNext(-2, 0, 0));
        Result := Ord(True);
      end
      else
        // иначе - продолжаем искать
        Result := pfnNext(nCode, wParam, lParam);
    end;
    -2: // сообщить о следующей процедуре ловушке
      Result := DWORD(@phk);
  else
    Result := 0;
  end;
end;&lt;/pre&gt;
Вот так - вся система оконных ловушек занимает всего несколько строк. Вам надо отдать должное Win16 за её компактный размер.&lt;br /&gt;
&lt;br /&gt;
Прим.пер.: вырезан разбор примера работы.&lt;br /&gt;
&lt;br /&gt;
Каждый раз, когда я говорю об этом, я восхищаюсь, как компактно была написана 16-битная Windows. Всего в двух десятках строк мы создали систему управления связанным списком функций, включающую в себя диспетчеризацию, а также поддержку удаления произвольного элемента из середины списка - и всё это без единого выделения памяти.&lt;br /&gt;
&lt;br /&gt;
В &lt;a title="Как злоупотребляли оконными ловушками в Win16" href="http://www.transl-gunsmoker.ru/2011/09/one-way-people-abused-hooks-in-16-bit.html"&gt;следующий раз&lt;/a&gt; мы посмотрим на то, как люди злоупотребляли этой простой системой.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688132520265708820-3284805136637551190?l=www.transl-gunsmoker.ru' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=83Wl_1bc-Qs:mB6kCA-Endk:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=83Wl_1bc-Qs:mB6kCA-Endk:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=83Wl_1bc-Qs:mB6kCA-Endk:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=83Wl_1bc-Qs:mB6kCA-Endk:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=83Wl_1bc-Qs:mB6kCA-Endk:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=83Wl_1bc-Qs:mB6kCA-Endk:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=83Wl_1bc-Qs:mB6kCA-Endk:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=83Wl_1bc-Qs:mB6kCA-Endk:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=83Wl_1bc-Qs:mB6kCA-Endk:-BTjWOF_DHI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=83Wl_1bc-Qs:mB6kCA-Endk:-BTjWOF_DHI" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/GunsmokersTranslations/~4/83Wl_1bc-Qs" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.transl-gunsmoker.ru/feeds/3284805136637551190/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://www.transl-gunsmoker.ru/2011/09/how-were-window-hooks-implemented-in-16.html#comment-form" title="Комментарии: 0" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/3284805136637551190?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/3284805136637551190?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/GunsmokersTranslations/~3/83Wl_1bc-Qs/how-were-window-hooks-implemented-in-16.html" title="Как были реализованы оконные ловушки в 16-битных Windows?" /><author><name>Александр Алексеев</name><uri>https://profiles.google.com/113168002104297556003</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh3.googleusercontent.com/-aUMttZQBsuI/AAAAAAAAAAI/AAAAAAAAC3Y/QuZ7K9t_WzE/s512-c/photo.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://www.transl-gunsmoker.ru/2011/09/how-were-window-hooks-implemented-in-16.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DEEERXo6fip7ImA9WhdVGU0.&quot;"><id>tag:blogger.com,1999:blog-1688132520265708820.post-7872144256622663884</id><published>2011-09-05T02:54:00.000+04:00</published><updated>2011-09-25T03:23:24.416+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-09-25T03:23:24.416+04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="клавиатуры" /><title>Кандидат на звание самого непонятного сочетания клавиш: Shift+F8</title><content type="html">Это перевод &lt;a href="http://blogs.msdn.com/b/oldnewthing/archive/2006/08/08/692129.aspx" title="Candidate for most obscure keyboard shortcut: Shift+F8"&gt;Candidate for most obscure keyboard shortcut: Shift+F8&lt;/a&gt;. Автор: Реймонд Чен.&lt;br /&gt;
&lt;br /&gt;
Так получилось, что одной из самых непонятных сочетаний клавиш является Shift+F8, которая используется в listbox для несмежного расширенного выбора. Господи, сколько же слов.&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
&lt;a title="Список используемых в Windows XP сочетаний клавиш" href="http://support.microsoft.com/default.aspx?scid=301583"&gt;Статья KB Q301583&lt;/a&gt; не очень помогает в понимании, перечисляя это сочетание в разделе "Сочетания клавиш для диалоговых окон", даже хотя это не диалоговая комбинация клавиш. Это сочетание клавиш listbox-а.&lt;br /&gt;
&lt;br /&gt;
Если в listbox включен режим расширенного выделения, то вы можете использовать комбинацию Shift+F8, чтобы создать несмежное расширенное выделение с клавиатуры (с помощью мыши вы можете сделать это просто делая Ctrl+щелчок). Нажмите Shift+F8 первый раз, чтобы войти в режим расширенного выделения, затем используйте стрелочки для перемещения по списку и нажмите Ctrl+Space или Shift+Space для выделения (или снятия выделения). Когда вы закончите - нажмите Shift+F8 ещё раз (или просто переместите фокус к следующему элементу управления).&lt;br /&gt;
&lt;br /&gt;
И да, конкретно этот клавиатурный интерфейс просто кошмарен. Более естественный механизм использовал бы Ctrl+Стрелка для перемещения фокуса без изменения выбора, и Ctrl+Пробел для выбора (или отмены выбора) элемента. К счастью, элемент управления listview использовал именно этот подход вместо подражания сумасшедшему сочетанию клавиш Shift+F8.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688132520265708820-7872144256622663884?l=www.transl-gunsmoker.ru' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=ffsMYf88BfM:YGWqz_oxEQ8:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=ffsMYf88BfM:YGWqz_oxEQ8:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=ffsMYf88BfM:YGWqz_oxEQ8:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=ffsMYf88BfM:YGWqz_oxEQ8:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=ffsMYf88BfM:YGWqz_oxEQ8:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=ffsMYf88BfM:YGWqz_oxEQ8:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=ffsMYf88BfM:YGWqz_oxEQ8:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=ffsMYf88BfM:YGWqz_oxEQ8:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=ffsMYf88BfM:YGWqz_oxEQ8:-BTjWOF_DHI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=ffsMYf88BfM:YGWqz_oxEQ8:-BTjWOF_DHI" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/GunsmokersTranslations/~4/ffsMYf88BfM" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.transl-gunsmoker.ru/feeds/7872144256622663884/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://www.transl-gunsmoker.ru/2011/09/candidate-for-most-obscure-keyboard.html#comment-form" title="Комментарии: 0" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/7872144256622663884?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/7872144256622663884?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/GunsmokersTranslations/~3/ffsMYf88BfM/candidate-for-most-obscure-keyboard.html" title="Кандидат на звание самого непонятного сочетания клавиш: Shift+F8" /><author><name>Александр Алексеев</name><uri>https://profiles.google.com/113168002104297556003</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh3.googleusercontent.com/-aUMttZQBsuI/AAAAAAAAAAI/AAAAAAAAC3Y/QuZ7K9t_WzE/s512-c/photo.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://www.transl-gunsmoker.ru/2011/09/candidate-for-most-obscure-keyboard.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DEAERXkyfyp7ImA9WhdaEEs.&quot;"><id>tag:blogger.com,1999:blog-1688132520265708820.post-8928091661563735431</id><published>2011-09-04T22:10:00.000+04:00</published><updated>2011-10-20T03:25:04.797+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-10-20T03:25:04.797+04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Win32" /><category scheme="http://www.blogger.com/atom/ns#" term="серия" /><title>Как менее наивный компилятор вызывает импортируемую функцию</title><content type="html">Это перевод &lt;a href="http://blogs.msdn.com/b/oldnewthing/archive/2006/07/24/676669.aspx" title="How a less naive compiler calls an imported function"&gt;How a less naive compiler calls an imported function&lt;/a&gt;. Автор: Реймонд Чен.&lt;br /&gt;
&lt;br /&gt;
Если функция объявлена со спецификатором &lt;code&gt;dllimport&lt;/code&gt;, то это указывает компилятору Visual Studio C/C++, что эта функция импортируется из другого (исполняемого) модуля, а не является обычной функцией в этом же исполняемом модуле. Имея на руках эту информацию, компилятор генерирует немного другой код, поскольку теперь он осведомлён об особенностях импортируемых функций.&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
Во-первых, теперь больше нет необходимости в функции-заглушке, потому что компилятор может сгенерировать инструкцию call [__imp__FunctionName]. Кроме того, компилятор знает, что этот адрес (адрес импортируемой функции) никогда не меняется, и, соответственно, он может оптимизировать многократное использование этого адреса, например:
&lt;pre class="brush:asm"&gt;    mov   ebx, [__imp__FunctionName]
    push  1
    call  ebx ; FunctionName(1)
    push  2
    call  ebx ; FunctionName(2)&lt;/pre&gt;
(Примечание к сумасшедшим людям: подобная оптимизация означает, что у вас могут возникнуть проблемы, если вы исправляете таблицу импорта модуля после того, как код в модуле начал работу - потому что указатель на функцию может быть сохранён в регистр до того, как вы начали править импорт. Рассмотрите случай с примером выше, когда вы изменили запись в таблице __imp__FunctionName после выполнения инструкции mov ebx, [__imp__FunctionName]: ваша функция-перехватчик не будет вызвана, потому что старый указатель на функцию сохранён в регистре ebx).&lt;br /&gt;
&lt;br /&gt;
Аналогично, если ваша программа попытается взять адрес импортируемой функции, которая была объявлена со спецификатором dllimport, то компилятор распознает эту операцию и преобразует её в загрузку адреса из таблицы адресов импортируемых функций.&lt;br /&gt;
&lt;br /&gt;
В результате этого дополнительного знания, сообщаемого компилятору, функции-заглушки больше не нужны; компилятор знает, что ему надо идти прямо к таблице адресов импортированных функций.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688132520265708820-8928091661563735431?l=www.transl-gunsmoker.ru' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=_sd7mkdZpWs:IcXXMx1d9ro:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=_sd7mkdZpWs:IcXXMx1d9ro:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=_sd7mkdZpWs:IcXXMx1d9ro:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=_sd7mkdZpWs:IcXXMx1d9ro:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=_sd7mkdZpWs:IcXXMx1d9ro:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=_sd7mkdZpWs:IcXXMx1d9ro:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=_sd7mkdZpWs:IcXXMx1d9ro:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=_sd7mkdZpWs:IcXXMx1d9ro:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=_sd7mkdZpWs:IcXXMx1d9ro:-BTjWOF_DHI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=_sd7mkdZpWs:IcXXMx1d9ro:-BTjWOF_DHI" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/GunsmokersTranslations/~4/_sd7mkdZpWs" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.transl-gunsmoker.ru/feeds/8928091661563735431/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://www.transl-gunsmoker.ru/2011/09/how-less-naive-compiler-calls-imported.html#comment-form" title="Комментарии: 2" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/8928091661563735431?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/8928091661563735431?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/GunsmokersTranslations/~3/_sd7mkdZpWs/how-less-naive-compiler-calls-imported.html" title="Как менее наивный компилятор вызывает импортируемую функцию" /><author><name>Александр Алексеев</name><uri>https://profiles.google.com/113168002104297556003</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh3.googleusercontent.com/-aUMttZQBsuI/AAAAAAAAAAI/AAAAAAAAC3Y/QuZ7K9t_WzE/s512-c/photo.jpg" /></author><thr:total>2</thr:total><feedburner:origLink>http://www.transl-gunsmoker.ru/2011/09/how-less-naive-compiler-calls-imported.html</feedburner:origLink></entry><entry gd:etag="W/&quot;A0cNQns_eyp7ImA9WhRaFU8.&quot;"><id>tag:blogger.com,1999:blog-1688132520265708820.post-735344658191301764</id><published>2011-09-03T22:10:00.000+04:00</published><updated>2012-02-18T05:38:13.543+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-02-18T05:38:13.543+04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Win32" /><category scheme="http://www.blogger.com/atom/ns#" term="серия" /><title>Вызов импортируемой функции, наивный способ</title><content type="html">Это перевод &lt;a href="http://blogs.msdn.com/b/oldnewthing/archive/2006/07/21/673830.aspx" title="Calling an imported function, the naive way"&gt;Calling an imported function, the naive way&lt;/a&gt;. Автор: Реймонд Чен.&lt;br /&gt;
&lt;br /&gt;
Библиотека импорта (import library) разрешает (resolve) символы импортируемых функций, но к ней не обращаются до этапа компоновки. Давайте посмотрим на наивную реализацию, когда компилятор слепо не осведомлён о существовании импортируемых функций.&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
В 16-битном мире это не вызывало никаких проблем. Компилятор генерировал дальний (far) вызов функции и оставлял внешнюю запись в объектном файле, указывающую, что вот этот адрес функции требует заполнения компоновщиком. На стадии компоновки линкер осознаёт, что этот внешний символ соответствует импортируемой функции, так что он &lt;a href="http://www.transl-gunsmoker.ru/2011/08/how-were-dll-functions-imported-in-16.html" title="Как импортировались DLL функции в 16-битных Windows?"&gt;собирает вместе все места её вызова в цепочку&lt;/a&gt; и создаёт запись импорта функции в таблице импорта модуля. В run-time эти записи исправляются загрузчиком ОС и все счастливы.&lt;br /&gt;
&lt;br /&gt;
Давайте посмотрим, как справится с этой же ситуацией наивный 32-битный компилятор. Компилятор сгенерирует обычную инструкцию вызова функции, перекладывая разрешение адреса на компоновщик. Линкер увидит, что этот внешний символ, на самом деле, является импортируемой функций, и, ой, прямой вызов нужно переделать в косвенный. Но компоновщик не может переписать код, сгенерированный компилятором. Что же делать компоновщику?&lt;br /&gt;
&lt;br /&gt;
Решение заключается во вставке ещё одно уровня косвенности (предупреждение: информация ниже не верна буквально, но она "достаточно правдоподобна". Мы копнём детали в следующих постах).&lt;br /&gt;
&lt;br /&gt;
Для каждой экспортируемой функции создаётся два символа. Первый из них - запись в таблице импортируемых функций. Назовём его __imp__FunctionName. Конечно же, наивный компилятор не знает ни про какой префикс __imp__. Он просто генерирует код для инструкции call FunctionName, ожидая что компоновщик подставит нужный адрес.&lt;br /&gt;
&lt;br /&gt;
Тут на сцену выходит второй символ. Он имеет имя FunctionName и является однострочной функцией, состоящей из одной-единственной инструкции: jmp [__imp__FunctionName] (генерируется компоновщиком). Эта крохотная заглушка предназначена для удовлетворения внешних ссылок на функцию FunctionName, и в свою очередь она генерирует ссылку на __imp__FunctionName, которая разрешается записью в таблице импортируемых функций.&lt;br /&gt;
&lt;br /&gt;
Когда модуль загружается - его зависимости импорта будут разрешены, и реальный адрес функции будет записан в __imp__FunctionName. Затем, когда код вызовет импортируемую функцию, то сгенерированный компилятором код вызовет функцию FunctionName, которая является заглушкой, которая и вызовет целевую функцию через косвенный вызов.&lt;br /&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;
&lt;a href="http://3.bp.blogspot.com/-_1g_6FMRM6o/TnsxZkkTlFI/AAAAAAAAC4g/CQIe-1tnjHo/s1600/x86_call.png" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="400" width="314" src="http://3.bp.blogspot.com/-_1g_6FMRM6o/TnsxZkkTlFI/AAAAAAAAC4g/CQIe-1tnjHo/s400/x86_call.png" /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
Заметьте, что с наивным компилятором, если ваш код попытается взять адрес импортируемой функции, то он получит адрес заглушки, поскольку наивный компилятор оперирует с адресом функции FunctionName, не зная о том, что она импортируется из другого модуля и для неё, на самом деле, создаётся заглушка.&lt;br /&gt;
&lt;br /&gt;
В &lt;a title="Как менее наивный компилятор вызывает импортируемую функцию" href="http://www.transl-gunsmoker.ru/2011/09/how-less-naive-compiler-calls-imported.html"&gt;следующий раз&lt;/a&gt; мы увидим, что с этой ситуацией сможет сделать менее наивный компилятор.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688132520265708820-735344658191301764?l=www.transl-gunsmoker.ru' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=2ymklcqcx08:SUIVWzYeMhY:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=2ymklcqcx08:SUIVWzYeMhY:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=2ymklcqcx08:SUIVWzYeMhY:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=2ymklcqcx08:SUIVWzYeMhY:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=2ymklcqcx08:SUIVWzYeMhY:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=2ymklcqcx08:SUIVWzYeMhY:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=2ymklcqcx08:SUIVWzYeMhY:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=2ymklcqcx08:SUIVWzYeMhY:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=2ymklcqcx08:SUIVWzYeMhY:-BTjWOF_DHI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=2ymklcqcx08:SUIVWzYeMhY:-BTjWOF_DHI" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/GunsmokersTranslations/~4/2ymklcqcx08" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.transl-gunsmoker.ru/feeds/735344658191301764/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://www.transl-gunsmoker.ru/2011/09/calling-imported-function-naive-way.html#comment-form" title="Комментарии: 5" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/735344658191301764?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/735344658191301764?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/GunsmokersTranslations/~3/2ymklcqcx08/calling-imported-function-naive-way.html" title="Вызов импортируемой функции, наивный способ" /><author><name>Александр Алексеев</name><uri>https://profiles.google.com/113168002104297556003</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh3.googleusercontent.com/-aUMttZQBsuI/AAAAAAAAAAI/AAAAAAAAC3Y/QuZ7K9t_WzE/s512-c/photo.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://3.bp.blogspot.com/-_1g_6FMRM6o/TnsxZkkTlFI/AAAAAAAAC4g/CQIe-1tnjHo/s72-c/x86_call.png" height="72" width="72" /><thr:total>5</thr:total><feedburner:origLink>http://www.transl-gunsmoker.ru/2011/09/calling-imported-function-naive-way.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DUMGQX04fSp7ImA9WhVTEEQ.&quot;"><id>tag:blogger.com,1999:blog-1688132520265708820.post-871306012853550258</id><published>2011-09-02T22:09:00.000+04:00</published><updated>2012-02-24T19:30:20.335+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-02-24T19:30:20.335+04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Win32" /><category scheme="http://www.blogger.com/atom/ns#" term="серия" /><title>Смена дизайна импорта в 32-х битных Windows</title><content type="html">Это перевод &lt;a href="http://blogs.msdn.com/b/oldnewthing/archive/2006/07/20/672695.aspx" title="Rethinking the way DLL exports are resolved for 32-bit Windows"&gt;Rethinking the way DLL exports are resolved for 32-bit Windows&lt;/a&gt;. Автор: Реймонд Чен.&lt;br /&gt;
&lt;br /&gt;
За последние дни мы узнали, как работает &lt;a title="Как экспортировались DLL функции в 16-битных Windows?" href="http://www.transl-gunsmoker.ru/2011/08/how-were-dll-functions-exported-in-16.html"&gt;экспорт&lt;/a&gt; и &lt;a title="Как импортировались DLL функции в 16-битных Windows?" href="http://www.transl-gunsmoker.ru/2011/08/how-were-dll-functions-imported-in-16.html"&gt;импорт&lt;/a&gt; функций в 16-битных Windows, а также что &lt;a href="http://www.transl-gunsmoker.ru/2011/08/how-are-dll-functions-exported-in-32.html" title="Как экспортируются DLL функции в 32-битных Windows?"&gt;экспорт функций в Win32 очень похож на экспорт функций в 16-битных Windows&lt;/a&gt;. Но 16-битный импорт был полностью переписан в мире 32-битных Windows.&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
Вспомните, что в 16-битных Windows правки для импортируемой функции размещались по всему сегменту цепочкой. Это прекрасно работало в Win16, потому что там было глобальное адресное пространство: сегменты кода разделялись глобально, так что будучи единожды загруженным, сегмент мог быть использован любым процессом. Но 32-битные Windows использовали изолированные адресные пространства. Если правки были расположены по всему сегменту, то загрузка сегмента кода с диска (вернее - страницы сегмента кода) означала его модификацию, что не давало разделять один сегмент несколькими процессами (механизм copy-on-write). Даже если бы таблица правок хранилась бы во внешнем сегменте, вам всё равно пришлось бы править сегмент кода, чтобы указать цели для переходов (если вы достаточно умны, то вы могли бы разделять страницы между процессами, если все правки были бы одинаковы для всех этих процессов; но учёт изменений для этой модели был бы полным кошмаром).&lt;br /&gt;
&lt;br /&gt;
Но помимо простой неэффективности, идея применения правок импорта к сегменту кода просто невозможна. К примеру, Alpha AXP имеет инструкцию "call direct" (прямой вызов), но она ограничена функциями, которые находятся лишь на расстоянии не более 128 Кб от места вызова. Если вы хотите вызвать функцию, которая находится дальше от вас, то вы должны загрузить адрес назначения во временный регистр и вызвать функцию через этот регистр. И, &lt;a href="http://www.transl-gunsmoker.ru/2008/11/64.html" title="Почему гранулярность выделения адресного пространства равна 64 Кб?"&gt;как мы видели ранее&lt;/a&gt;, загрузка 32-битного значения в регистр на Alpha AXP является двухступенчатой операцией, которая зависит от того, установлен или очищен 15-й бит того значения, которое вы хотите загрузить. Поскольку это импортируемая функция, то на этапе компиляции или компоновки вы понятия не имеете будет ли адрес целевой функции иметь 15-й бит или нет.&lt;br /&gt;
&lt;br /&gt;
И Alpha AXP была далеко не единственной архитектурой, которая ограничивала расстояние для прямых вызовов. К примеру, Intel ia64 (Itanium) мог делать прямые вызовы функций только в пределах 4 Мб, а популярные сегодня архитектуры AMD x86-64 и Intel EM64T ограничивают вас 2 Гб. Да, это не звучит как ограничение пока вы не вспомните, что это - 64-разрядные процессоры с 16 эксабайтами адресного пространства. И снова мы видим, что &lt;a title="Архитектура x86 - очень странная" href="http://www.transl-gunsmoker.ru/2009/07/x86.html"&gt;архитектура x86 является странной&lt;/a&gt; (прим.пер.: в том смысле, что на ней нет ограничения на прямые вызовы в отличие от многих других архитектур).&lt;br /&gt;
&lt;br /&gt;
Оба момента выше сделали правки импорта в сегменте кода нежелательными (и невозможными). Вместо этого правки импорта пришлось применять к данным. Вместо того, чтобы применять правку к каждому месту, где вызывается импортируемая функция, применяется всего одна правка в таблице указателей на функции. Это означает, что вызовы импортируемых функций на самом деле являются косвенными вызовами через указатель. К примеру, на x86 это означает, что вместо &lt;code&gt;call ImportedFunction&lt;/code&gt; генерируется код, который говорит &lt;code&gt;call [__imp__ImportedFunction]&lt;/code&gt;, где __imp__ImportedFunction - это имя переменной (места в таблице), которая содержит указатель на эту импортируемую функцию.&lt;br /&gt;
&lt;br /&gt;
Это означает, что правки импортируемых функций заключаются в простом поиске целевых адресов и их записи в таблицу импортируемых адресов (прим.пер.: хранимую в сегменте данных). Код не изменяется; он просто читает указатель на функцию в run-time и вызывает функцию через указатель.&lt;br /&gt;
&lt;br /&gt;
С этой информацией мы теперь вооружены для более глубокого погружения в кое-какие последствия этого дизайна - что мы и сделаем в &lt;a title="Вызов импортируемой функции, наивный способ" href="http://www.transl-gunsmoker.ru/2011/09/calling-imported-function-naive-way.html"&gt;следующий раз&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688132520265708820-871306012853550258?l=www.transl-gunsmoker.ru' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=cEfLWysfvaI:xeGz1LsBrng:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=cEfLWysfvaI:xeGz1LsBrng:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=cEfLWysfvaI:xeGz1LsBrng:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=cEfLWysfvaI:xeGz1LsBrng:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=cEfLWysfvaI:xeGz1LsBrng:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=cEfLWysfvaI:xeGz1LsBrng:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=cEfLWysfvaI:xeGz1LsBrng:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=cEfLWysfvaI:xeGz1LsBrng:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=cEfLWysfvaI:xeGz1LsBrng:-BTjWOF_DHI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=cEfLWysfvaI:xeGz1LsBrng:-BTjWOF_DHI" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/GunsmokersTranslations/~4/cEfLWysfvaI" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.transl-gunsmoker.ru/feeds/871306012853550258/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://www.transl-gunsmoker.ru/2011/09/rethinking-way-dll-exports-are-resolved.html#comment-form" title="Комментарии: 0" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/871306012853550258?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/871306012853550258?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/GunsmokersTranslations/~3/cEfLWysfvaI/rethinking-way-dll-exports-are-resolved.html" title="Смена дизайна импорта в 32-х битных Windows" /><author><name>Александр Алексеев</name><uri>https://profiles.google.com/113168002104297556003</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh3.googleusercontent.com/-aUMttZQBsuI/AAAAAAAAAAI/AAAAAAAAC3Y/QuZ7K9t_WzE/s512-c/photo.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://www.transl-gunsmoker.ru/2011/09/rethinking-way-dll-exports-are-resolved.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DUUAQHwzeCp7ImA9WhVTEEQ.&quot;"><id>tag:blogger.com,1999:blog-1688132520265708820.post-2702370324084433719</id><published>2011-09-01T22:09:00.000+04:00</published><updated>2012-02-24T19:27:21.280+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-02-24T19:27:21.280+04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Win32" /><category scheme="http://www.blogger.com/atom/ns#" term="серия" /><title>Экспортируемые функции, являющиеся forwarder-ами</title><content type="html">Это перевод &lt;a href="http://blogs.msdn.com/b/oldnewthing/archive/2006/07/19/671238.aspx" title="Exported functions that are really forwarders"&gt;Exported functions that are really forwarders&lt;/a&gt;. Автор: Реймонд Чен.&lt;br /&gt;
&lt;br /&gt;
В &lt;a title="Как экспортируются DLL функции в 32-битных Windows?" href="http://www.transl-gunsmoker.ru/2011/08/how-are-dll-functions-exported-in-32.html"&gt;прошлый раз&lt;/a&gt; мы видели как экспортируемые функции в Win32 ведут себя похоже на экспорт функций в 16-битных Windows - только сместился акцент использования с импорта по номеру на импорт по имени. Этот акцент никак не выражен в формате исполняемого файла; и 16-битные и 32-битные DLL могут экспортировать функции по номеру и имени (и обоим одновременно).&lt;br /&gt;
&lt;br /&gt;
Но в Win32 появляется новый тип экспортируемых функций, известный как forwarder. &lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
Forwarder выглядит как обычная экспортируемая функция, за исключением того что запись в таблице экспорта по номерам говорит "Ох, на самом деле, я не функция в этой DLL. На самом деле - я функция вон в той DLL". К примеру, если вы сделаете &lt;pre&gt;link /dump /exports kernel32.dll&lt;/pre&gt;
То вы увидите что-то такое:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;151   EnterCriticalSection (forwarded to NTDLL.RtlEnterCriticalSection)&lt;/pre&gt;
&lt;br /&gt;
Это означает, что если программа связана с &lt;code&gt;KERNEL32.EnterCriticalSection&lt;/code&gt;, то загрузчик ОС молчаливо перенаправит функцию на &lt;code&gt;NTDLL.RtlEnterCriticalSection&lt;/code&gt;. Forwarder-ы являются удобным способом сохранить внешний интерфейс, изменив функциональность выносом в отдельные DLL. Старая DLL продолжает экспортировать функцию, но сама функция была перемещена в новую DLL.&lt;br /&gt;
&lt;br /&gt;
Трюк с forwarder - это лучше, чем иметь пустую функцию-заглушку в старой DLL, которая просто вызывает функцию в новой DLL: потому что функция заглушка создаёт зависимость между новой и старой DLL (в конце концов, старую DLL нужно связать с новой DLL, чтобы была возможность вызвать функцию!). А при использовании forwarder новая DLL не будет загружаться, если только кто-то не попросит перенаправленную функцию из старой DLL. В результате вы не платите за загрузку новой DLL пока кто-то её явно не попросит.&lt;br /&gt;
&lt;br /&gt;
Okей, как мы увидели, с введением forwarder Win32 отошла от 16-битных Windows, но как только мы посмотрим на импорт, то увидим, что тут изменилось вообще всё. Мы посмотрим на эту историю в &lt;a href="http://www.transl-gunsmoker.ru/2011/09/rethinking-way-dll-exports-are-resolved.html" title="Смена дизайна импорта в 32-х битных Windows"&gt;следующий раз&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688132520265708820-2702370324084433719?l=www.transl-gunsmoker.ru' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=z48Etnt8mbs:-2aqWjW8yP4:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=z48Etnt8mbs:-2aqWjW8yP4:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=z48Etnt8mbs:-2aqWjW8yP4:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=z48Etnt8mbs:-2aqWjW8yP4:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=z48Etnt8mbs:-2aqWjW8yP4:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=z48Etnt8mbs:-2aqWjW8yP4:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=z48Etnt8mbs:-2aqWjW8yP4:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=z48Etnt8mbs:-2aqWjW8yP4:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=z48Etnt8mbs:-2aqWjW8yP4:-BTjWOF_DHI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=z48Etnt8mbs:-2aqWjW8yP4:-BTjWOF_DHI" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/GunsmokersTranslations/~4/z48Etnt8mbs" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.transl-gunsmoker.ru/feeds/2702370324084433719/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://www.transl-gunsmoker.ru/2011/09/exported-functions-that-are-really.html#comment-form" title="Комментарии: 0" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/2702370324084433719?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/2702370324084433719?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/GunsmokersTranslations/~3/z48Etnt8mbs/exported-functions-that-are-really.html" title="Экспортируемые функции, являющиеся forwarder-ами" /><author><name>Александр Алексеев</name><uri>https://profiles.google.com/113168002104297556003</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh3.googleusercontent.com/-aUMttZQBsuI/AAAAAAAAAAI/AAAAAAAAC3Y/QuZ7K9t_WzE/s512-c/photo.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://www.transl-gunsmoker.ru/2011/09/exported-functions-that-are-really.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DUYDQXsyfSp7ImA9WhVTEEQ.&quot;"><id>tag:blogger.com,1999:blog-1688132520265708820.post-5477983680087185034</id><published>2011-08-31T22:08:00.000+04:00</published><updated>2012-02-24T19:26:10.595+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-02-24T19:26:10.595+04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Win32" /><category scheme="http://www.blogger.com/atom/ns#" term="серия" /><title>Как экспортируются DLL функции в 32-битных Windows?</title><content type="html">Это перевод &lt;a href="http://blogs.msdn.com/b/oldnewthing/archive/2006/07/18/669668.aspx" title="How are DLL functions exported in 32-bit Windows?"&gt;How are DLL functions exported in 32-bit Windows?&lt;/a&gt; Автор: Реймонд Чен.&lt;br /&gt;
&lt;br /&gt;
Дизайнерам 32-битных Windows не нужно было беспокоиться о втискивании в 256 Кб памяти. Поскольку модули в Win32 построены на загрузке по запросу, то всё, что вам нужно сделать - спроецировать весь образ в память, а затем коснуться тех частей, которые вам нужны. Поэтому нет никакого различия между резидентной и не резидентной таблицами, так что имена экспортируемых функций просто записываются в исполняемый образ вместе с указателем (вернее относительным виртуальным адресом) на имя, хранимое в таблице экспорта.&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
В отличие от &lt;a title="Как экспортировались DLL функции в 16-битных Windows?" href="http://www.transl-gunsmoker.ru/2011/08/how-were-dll-functions-exported-in-16.html"&gt;главной 16-битной таблицы экспорта&lt;/a&gt;, 32-битная таблица не является разрежённой. Иными словами, если ваша DLL экспортирует две функции с номерами 10 и 1000 - у вас будет таблица экспорта на 991 записей, состоящая из двух функций и кучи нулей. Поэтому вам не следует назначать номера функциям с большими пробелами в нумерации - иначе вы будете зря тратить пространство в таблице экспорта.&lt;br /&gt;
&lt;br /&gt;
Как я заметил выше, теперь есть только одна таблица экспортируемых имён, поскольку вам не нужно делать отличия между резидентными и не резидентными именами. Таблица экспортируемых имён работает в той же манере, что и в 16-битных Windows, проецируя имена на номера. В отличие от 16-битных таблиц, где порядок не имеет значения, таблица экспортируемых имён в 32-битных Windows должна быть отсортирована, чтобы система могла воспользоваться (эффективным) двоичным поиском для ускорения поиска имён.&lt;br /&gt;
&lt;br /&gt;
Как и в 16-битных Windows, каждому имени присваивается номер. Если программист не указывает номер, то его назначает компоновщик, и, как и в 16-битных Windows, автоматически присваиваемые значения могут отличаться в сборках. Однако тут есть существенное отличие в моделях использования: вспомните, что в 16-битных Windows импорт по имени не поощрялся (по соображениям производительности), и в результате обычно каждой экспортируемой функции явно присваивался номер, что и было предпочтительным способом связывания функций. С другой стороны, именованный импорт в Win32 является нормой, а явное присваивание номеров функций ушло в прошлое. Это означает, что &lt;b&gt;номера в таблице экспорта не фиксированы&lt;/b&gt;. К примеру, посмотрите на номера, присваиваемые функции &lt;code&gt;LocalAlloc&lt;/code&gt; из kernel32 за эти годы:&lt;br /&gt;
&lt;br /&gt;
&lt;TABLE&gt;&lt;TR&gt;&lt;TD&gt;Windows NT 3.1&lt;/TD&gt;&lt;TD&gt;314&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;Windows NT 3.5&lt;/TD&gt;&lt;TD&gt;372&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;Windows 95&lt;/TD&gt;&lt;TD&gt;501&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;Windows NT 4.0&lt;/TD&gt;&lt;TD&gt;407&lt;/TD&gt;&lt;/TR&gt;&lt;/TABLE&gt; 
&lt;br /&gt;
Но некоторые люди имеют привычку "взламывать" (reverse-engineer) библиотеки импорта - вероятно потому, что им лень качать Platform SDK чтобы получить &lt;b&gt;официальные&lt;/b&gt; библиотеки импорта. Проблема с генерацией библиотеки импорта заключается в том, что вы не можете сказать, был ли номер, скажем, функции &lt;code&gt;LoadLibrary&lt;/code&gt; присвоен явно программистом (и, значит, будет постоянен) или же был назначен автоматически компоновщиком (и в этом случае он &lt;b&gt;будет&lt;/b&gt; меняться). Утилиты генерации библиотек импорта могут просто перестраховаться и всегда использовать именованный импорт, но по какой-то причине они часто предпочитают импорт по номерам (вероятно, это остаток из времён 16-битных Windows, где такой способ импорта был предпочитаемым).&lt;br /&gt;
&lt;br /&gt;
Этот неудачный выбор части утилит по генерации библиотек импорта жить опасной жизнью создал проблемы совместимости команде DirectX (я не знаю, почему именно DirectX оказался под ударом больше остальных. Возможно, потому что у разработчиков игр нет времени учить детали Win32; они просто хотят писать их игры). Как только кто-то использовал такую утилиту, он оказался привязанным к функциям DirectX вроде &lt;code&gt;DirectDrawCreate&lt;/code&gt; по номеру, а не по имени. А когда вышла следующая версия DirectX и этому имени был присвоен другой номер компоновщиком, их программы стали вылетать. Команде DirectX пришлось поднять из архива старые DLL, тщательно выписать все номера функций (присвоенные случайным образом компоновщиком) и в точности повторить это присвоение вручную, так чтобы эти номера более не менялись бы в будущем.&lt;br /&gt;
&lt;br /&gt;
Есть и другие причины, почему вы не можете сгенерировать библиотеку импорта по DLL; я подниму эти причины в следующий раз, когда буду обсуждать импорт из DLL.&lt;br /&gt;
&lt;br /&gt;
В &lt;a  title="Экспортируемые функции, являющиеся forwarder-ами" href="http://www.transl-gunsmoker.ru/2011/09/exported-functions-that-are-really.html"&gt;следующий раз&lt;/a&gt; - переходники.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688132520265708820-5477983680087185034?l=www.transl-gunsmoker.ru' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=q46XlZKNi6o:TH_06LUKk-c:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=q46XlZKNi6o:TH_06LUKk-c:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=q46XlZKNi6o:TH_06LUKk-c:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=q46XlZKNi6o:TH_06LUKk-c:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=q46XlZKNi6o:TH_06LUKk-c:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=q46XlZKNi6o:TH_06LUKk-c:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=q46XlZKNi6o:TH_06LUKk-c:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=q46XlZKNi6o:TH_06LUKk-c:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=q46XlZKNi6o:TH_06LUKk-c:-BTjWOF_DHI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=q46XlZKNi6o:TH_06LUKk-c:-BTjWOF_DHI" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/GunsmokersTranslations/~4/q46XlZKNi6o" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.transl-gunsmoker.ru/feeds/5477983680087185034/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://www.transl-gunsmoker.ru/2011/08/how-are-dll-functions-exported-in-32.html#comment-form" title="Комментарии: 0" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/5477983680087185034?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/5477983680087185034?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/GunsmokersTranslations/~3/q46XlZKNi6o/how-are-dll-functions-exported-in-32.html" title="Как экспортируются DLL функции в 32-битных Windows?" /><author><name>Александр Алексеев</name><uri>https://profiles.google.com/113168002104297556003</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh3.googleusercontent.com/-aUMttZQBsuI/AAAAAAAAAAI/AAAAAAAAC3Y/QuZ7K9t_WzE/s512-c/photo.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://www.transl-gunsmoker.ru/2011/08/how-are-dll-functions-exported-in-32.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DUcGQXg9fyp7ImA9WhVTEEQ.&quot;"><id>tag:blogger.com,1999:blog-1688132520265708820.post-3568786456412055911</id><published>2011-08-30T22:08:00.000+04:00</published><updated>2012-02-24T19:23:40.667+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-02-24T19:23:40.667+04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="серия" /><category scheme="http://www.blogger.com/atom/ns#" term="Win16" /><title>Как импортировались DLL функции в 16-битных Windows?</title><content type="html">Это перевод &lt;a href="http://blogs.msdn.com/b/oldnewthing/archive/2006/07/17/668284.aspx" title="How were DLL functions imported in 16-bit Windows?"&gt;How were DLL functions imported in 16-bit Windows?&lt;/a&gt; Автор: Реймонд Чен.&lt;br /&gt;
&lt;br /&gt;
В &lt;a title="Как экспортировались DLL функции в 16-битных Windows?" href="http://www.transl-gunsmoker.ru/2011/08/how-were-dll-functions-exported-in-16.html"&gt;прошлый раз&lt;/a&gt; я рассказал о том, как экспортировались функции в 16-битных Windows. Сегодня мы посмотрим на их импорт.&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
Каждый раз, когда сегмент загружается с диска в память, сначала читается raw-содержимое сегмента с диска, а затем применяются relocation-правки. Правка для импортируемой функции состоит из имени целевой DLL, целевой функции (либо её имени, либо номера), и позиции первого места в сегменте, где нужно произвести правку. Все импортируемые адреса являются far-адресами, поскольку они ссылаются на другой сегмент (почему? если бы они были в том же сегменте, то это означало бы, что они расположены в той же DLL, так что это получился бы импорт функции из самого себя!). На 16-битных Windows дальний указатель занимает 4 байта (двухбайтовый селектор и двухбайтовое смещение), а поскольку точный адрес функции не известен в момент компиляции DLL, то эти 4 байта являются просто заглушками - они помещаются в файл с тем, чтобы быть заполненными настоящими значениями в run-time, когда будет получен адрес импортируемой функции. И эти байты заглушек служат двойной цели.&lt;br /&gt;
&lt;br /&gt;
Все вызовы сегмента одной и той же импортируемой функции связываются в список; запись в relocation таблице ссылается только на первый элемент этого списка. А что насчёт остальных вызовов? На них указывают заглушки! К примеру, предположим, что у нас есть сегмент, который требует две правки для функции &lt;code&gt;GetPrivateProfileInt&lt;/code&gt;, которая является функцией ядра с номером 127. Тогда в relocation таблице будет создана одна запись, которая говорит: "этот сегмент требует функцию номер 127 из KERNEL; начало по смещению 01D1". Копия сегмента на диске может выглядеть так:
&lt;TABLE&gt;&lt;TR&gt;&lt;TD&gt;...&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;&lt;TT&gt;01D0&lt;/TT&gt;&lt;/TD&gt;&lt;TD&gt;&lt;TT&gt;9A&lt;/TT&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;&lt;TT&gt;01D1&lt;/TT&gt;&lt;/TD&gt;&lt;TD&gt;&lt;TT&gt;FE&lt;/TT&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;&lt;TT&gt;01D2&lt;/TT&gt;&lt;/TD&gt;&lt;TD&gt;&lt;TT&gt;01&lt;/TT&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;&lt;TT&gt;01D3&lt;/TT&gt;&lt;/TD&gt;&lt;TD&gt;&lt;TT&gt;00&lt;/TT&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;&lt;TT&gt;01D0&lt;/TT&gt;&lt;/TD&gt;&lt;TD&gt;&lt;TT&gt;00&lt;/TT&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;...&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;&lt;TT&gt;01FD&lt;/TT&gt;&lt;/TD&gt;&lt;TD&gt;&lt;TT&gt;9A&lt;/TT&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;&lt;TT&gt;01FE&lt;/TT&gt;&lt;/TD&gt;&lt;TD&gt;&lt;TT&gt;FF&lt;/TT&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;&lt;TT&gt;01FF&lt;/TT&gt;&lt;/TD&gt;&lt;TD&gt;&lt;TT&gt;FF&lt;/TT&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;&lt;TT&gt;0200&lt;/TT&gt;&lt;/TD&gt;&lt;TD&gt;&lt;TT&gt;00&lt;/TT&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;&lt;TT&gt;0201&lt;/TT&gt;&lt;/TD&gt;&lt;TD&gt;&lt;TT&gt;00&lt;/TT&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;...&lt;/TD&gt;&lt;/TR&gt;&lt;/TABLE&gt;
Чтобы применить правку, мы вызываем &lt;code&gt;GetProcAddress&lt;/code&gt; для получения настоящего адреса функции 127. Затем мы идём к месту первой правки (это место со смещением $01D1), записываем в него адрес, а затем смотрим на значение, которое мы только что перезаписали (конечно, мы должны его сохранить перед записью). Это значение равно $01FE, поэтому мы идём к месту по смещению $01FE и снова записываем адрес. Перезаписанное значение равно $FFFF, что означает конец цепочки правок.&lt;br /&gt;
&lt;br /&gt;
Но что если вызов &lt;code&gt;GetProcAddress&lt;/code&gt; будет неудачным? (к примеру - в KERNEL нет никакой функции 127). Тогда вместо записи адреса целевой функции, загрузчик запишет адрес специальной функции-заглушки, которая вызывает фатальную ошибку "Call to Undefined Dynalink".&lt;br /&gt;
&lt;br /&gt;
Okей, это было краткое введение в импорт и экспорт функций в 16-битных Windows. В &lt;a title="Как экспортируются DLL функции в 32-битных Windows?"  href="http://www.transl-gunsmoker.ru/2011/08/how-are-dll-functions-exported-in-32.html"&gt;следующий раз&lt;/a&gt; мы посмотрим на переход на 32-битные Windows и решения дизайна, которые сформировали новую модель экспорта-импорта.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688132520265708820-3568786456412055911?l=www.transl-gunsmoker.ru' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=TjRoF4u2lh8:Ca6BxspHBdw:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=TjRoF4u2lh8:Ca6BxspHBdw:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=TjRoF4u2lh8:Ca6BxspHBdw:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=TjRoF4u2lh8:Ca6BxspHBdw:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=TjRoF4u2lh8:Ca6BxspHBdw:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=TjRoF4u2lh8:Ca6BxspHBdw:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=TjRoF4u2lh8:Ca6BxspHBdw:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=TjRoF4u2lh8:Ca6BxspHBdw:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=TjRoF4u2lh8:Ca6BxspHBdw:-BTjWOF_DHI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=TjRoF4u2lh8:Ca6BxspHBdw:-BTjWOF_DHI" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/GunsmokersTranslations/~4/TjRoF4u2lh8" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.transl-gunsmoker.ru/feeds/3568786456412055911/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://www.transl-gunsmoker.ru/2011/08/how-were-dll-functions-imported-in-16.html#comment-form" title="Комментарии: 0" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/3568786456412055911?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/3568786456412055911?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/GunsmokersTranslations/~3/TjRoF4u2lh8/how-were-dll-functions-imported-in-16.html" title="Как импортировались DLL функции в 16-битных Windows?" /><author><name>Александр Алексеев</name><uri>https://profiles.google.com/113168002104297556003</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh3.googleusercontent.com/-aUMttZQBsuI/AAAAAAAAAAI/AAAAAAAAC3Y/QuZ7K9t_WzE/s512-c/photo.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://www.transl-gunsmoker.ru/2011/08/how-were-dll-functions-imported-in-16.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DE4ERHkyfyp7ImA9WhVTEEQ.&quot;"><id>tag:blogger.com,1999:blog-1688132520265708820.post-7449359055015592386</id><published>2011-08-29T22:07:00.000+04:00</published><updated>2012-02-24T19:21:45.797+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-02-24T19:21:45.797+04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="серия" /><category scheme="http://www.blogger.com/atom/ns#" term="Win16" /><title>Как экспортировались DLL функции в 16-битных Windows?</title><content type="html">Это перевод &lt;a href="http://blogs.msdn.com/b/oldnewthing/archive/2006/07/14/665669.aspx" title="How were DLL functions exported in 16-bit Windows?"&gt;How were DLL functions exported in 16-bit Windows?&lt;/a&gt; Автор: Реймонд Чен.&lt;br /&gt;
&lt;br /&gt;
Весь смысл динамических библиотек (DLL) заключается в динамическом связывании. В то время как статические библиотеки встраиваются в конечный продукт, модуль, который использует динамическую библиотеку, просто говорит: "мне, пожалуйста, функцию X из модуля Y, спасибо". Эта техника имеет преимущества и недостатки. Одно из преимуществ - эффективное использование места на диске и в памяти, поскольку существует только одна копия модуля, вместо отдельных копий для каждого модуля. Второе преимущество - обновление DLL может быть выполнено без перекомпиляции всех программ, её использующих. С другой стороны, возможность менять функциональность также является и одним из недостатков DLL, поскольку одна программа может изменить DLL и вызвать этим каскадные эффекты в других клиентах DLL.&lt;br /&gt;
&lt;br /&gt;
В любом случае, давайте начнём с того, как 16-битные Windows управляли импортом и экспортом. После этого мы посмотрим что было изменено при миграции на 32-битные Windows, а затем мы посмотрим на импорт с точки зрения компилятора.&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
16-битная DLL имела не одну, а целых три таблицы экспорта (на самом деле всё было немного сложнее, чем я описываю здесь, но я собираюсь опустить некоторые занудные детали, чтобы ваши головы не взорвались от переизбытка информации). Самая важная таблица представляла собой разрежённый массив функций, проиндексированных с 1("ordinal"). Это и есть таблица функций, которая является мастер-списком всех экспортируемых функций. Если вы запрашивали функцию по номеру (ordinal), этот номер искался именно в этой таблице. Физически таблица устроена не так просто - из-за разрежённости, но логически она выглядит так:&lt;br /&gt;
&lt;br /&gt;
&lt;TABLE RULES=cols&gt;&lt;TR&gt;&lt;TH&gt;Номер&amp;nbsp;&lt;/TH&gt;&lt;TH&gt;Адрес&lt;/TH&gt;&lt;TH&gt;прочее&lt;/TH&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;1&lt;/TD&gt;&lt;TD&gt;02:0014&lt;/TD&gt;&lt;TD&gt;...&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;2&lt;/TD&gt;&lt;TD&gt;04:0000&lt;/TD&gt;&lt;TD&gt;...&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;5&lt;/TD&gt;&lt;TD&gt;02:02C8&lt;/TD&gt;&lt;TD&gt;...&lt;/TD&gt;&lt;/TR&gt;&lt;/TABLE&gt; 
&lt;br /&gt;
Первая колонка в таблице представляет собой номер функции, а вторая описывает размещение функции (заметьте, что в этой DLL нет функций 3 и 4).&lt;br /&gt;
&lt;br /&gt;
Интересные вещи начинаются, когда вы хотите получить функцию по её имени. Таблица экспортируемых имён представляет собой список имён функций с ассоциированными номерами. К примеру, секция экспортируемых имён для 16-битного оконного менеджера выглядела так:&lt;br /&gt;
&lt;br /&gt;
&lt;TABLE&gt;&lt;TR&gt;&lt;TD COLSPAN=2&gt;...&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;&lt;CODE&gt;ClipCursor&lt;/CODE&gt;&lt;/TD&gt;&lt;TD&gt;16&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;&lt;CODE&gt;GetCursorPos&lt;/CODE&gt;&lt;/TD&gt;&lt;TD&gt;17&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;&lt;CODE&gt;SetCapture&lt;/CODE&gt;&lt;/TD&gt;&lt;TD&gt;18&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD COLSPAN=2&gt;...&lt;/TD&gt;&lt;/TR&gt;&lt;/TABLE&gt; 
&lt;br /&gt;
Если кто-то просил адрес функции "ClipCursor", то сначала проверялась таблица экспортируемых имён и из неё извлекался номер 16, после чего этот номер использовался для обращения к мастер-таблице, упомянутой выше. Хотя вы не видите этого здесь, но не существовало никакого требования, чтобы имена в таблице экспортируемых имён шли в каком-то определённом порядке или чтобы каждая функция с номером имела бы экспортируемое имя.&lt;br /&gt;
&lt;br /&gt;
Подождите-ка, я сказал "таблица экспортируемых имён"? Ох, извините, это было сильное упрощение. На самом деле было две таблицы экспортируемых имён: резидентная таблица имён и не резидентная таблица имён. Как следует из их названий, имена в резидентной таблице оставались в памяти всё время, пока DLL была загружена, а имена из не резидентной таблицы загружались в память только когда кто-то вызывал &lt;a title="MSDN: GetProcAddress Function" href="http://msdn.microsoft.com/en-us/library/ms683212(VS.85).aspx"&gt;&lt;code&gt;GetProcAddress&lt;/code&gt;&lt;/a&gt; (или её эквивалент). Это различие появилось из-за очень жёстких ограничений на память, с которыми Windows приходилось работать в те дни. К примеру, оконный менеджер имел около шестисот экспортируемых функций; если бы все эти функции были резидентными, то они занимали бы более 10 Кб памяти. Это означает, что вы тратите 4 процента вашей памяти (машины с 256 Кб), чтобы хранить вещи, которые вам не нужны 99% процентов времени.&lt;br /&gt;
&lt;br /&gt;
Большой размер таблицы экспортируемых имён означал, что только функции, часто передаваемые в &lt;code&gt;GetProcAddress&lt;/code&gt;, заслуживают появления в резидентной таблице. Для большинства DLL таких функций не было вообще, так что таблица резидентных имён была обычно пустой (для пущей безопасности удалены голово-взрывные детали).&lt;br /&gt;
&lt;br /&gt;
Поскольку получение функции по имени было таким дорогим (загрузка не резидентной таблицы с диска), то все функции ОС экспортировались по именам и номеру. Импорт функции по номеру был предпочтителен - это позволяло полностью избегать таблицы имён.&lt;br /&gt;
&lt;br /&gt;
Заметьте, что каждая функция с именем имела соответствующий номер. Если вы не присваивали номер функции явно, то это делал за вас компоновщик (линкер), однако автоматически сгенерированное значение не обязано было сохраняться одинаковым при каждой сборке. Обычно эта ситуация не возникала на практике, потому что все старались явно присваивать номера функциям по причинам, указанным выше - чтобы избежать огромных накладных расходов импорта функции по имени.&lt;br /&gt;
&lt;br /&gt;
Это был краткий обзор экспорта функций в 16-битных Windows. В &lt;a title="Как импортировались DLL функции в 16-битных Windows?" href="http://www.transl-gunsmoker.ru/2011/08/how-were-dll-functions-imported-in-16.html"&gt;следующий раз&lt;/a&gt; мы посмотрим на то, как они импортировались.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688132520265708820-7449359055015592386?l=www.transl-gunsmoker.ru' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=HBcj1E6vZzY:pKf81ynTNY0:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=HBcj1E6vZzY:pKf81ynTNY0:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=HBcj1E6vZzY:pKf81ynTNY0:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=HBcj1E6vZzY:pKf81ynTNY0:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=HBcj1E6vZzY:pKf81ynTNY0:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=HBcj1E6vZzY:pKf81ynTNY0:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=HBcj1E6vZzY:pKf81ynTNY0:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=HBcj1E6vZzY:pKf81ynTNY0:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=HBcj1E6vZzY:pKf81ynTNY0:-BTjWOF_DHI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=HBcj1E6vZzY:pKf81ynTNY0:-BTjWOF_DHI" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/GunsmokersTranslations/~4/HBcj1E6vZzY" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.transl-gunsmoker.ru/feeds/7449359055015592386/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://www.transl-gunsmoker.ru/2011/08/how-were-dll-functions-exported-in-16.html#comment-form" title="Комментарии: 0" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/7449359055015592386?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/7449359055015592386?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/GunsmokersTranslations/~3/HBcj1E6vZzY/how-were-dll-functions-exported-in-16.html" title="Как экспортировались DLL функции в 16-битных Windows?" /><author><name>Александр Алексеев</name><uri>https://profiles.google.com/113168002104297556003</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh3.googleusercontent.com/-aUMttZQBsuI/AAAAAAAAAAI/AAAAAAAAC3Y/QuZ7K9t_WzE/s512-c/photo.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://www.transl-gunsmoker.ru/2011/08/how-were-dll-functions-exported-in-16.html</feedburner:origLink></entry><entry gd:etag="W/&quot;D0QFR3c4eSp7ImA9WhdWGEw.&quot;"><id>tag:blogger.com,1999:blog-1688132520265708820.post-6457972541294089131</id><published>2011-08-28T22:07:00.000+04:00</published><updated>2011-09-12T12:15:16.931+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-09-12T12:15:16.931+04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="прочее" /><title>Pidl и моникеры делают примерно одно и то же, только по-разному</title><content type="html">Это перевод &lt;a href="http://blogs.msdn.com/b/oldnewthing/archive/2006/07/12/663365.aspx" title="Pidls and monikers do roughly the same thing, just backwards"&gt;Pidls and monikers do roughly the same thing, just backwards&lt;/a&gt;. Автор: Реймонд Чен.&lt;br /&gt;
&lt;br /&gt;
Работая с Оболочкой (Shell) Windows, вам наверняка в какой-то момент пришлось работать с указателем на список ID элементов (pointer to item ID list), известным как "pidl" (рифмуется с "middle"). С другой стороны, работая с OLE, вы могли работать с &lt;a title="Википедия: Моникер" href="http://ru.wikipedia.org/wiki/%D0%9C%D0%BE%D0%BD%D0%B8%D0%BA%D0%B5%D1%80"&gt;моникерами&lt;/a&gt; (moniker). В каком-то смысле они делают одно и то же. Они позволяют вам ссылаться на какой-то объект внутри пространства имён (с которым вы как-то работаете), они имеют иерархическую структуру, вы можете их сериализовать и т.п.&lt;br /&gt;
&lt;br /&gt;
Но почему тогда команда Оболочки Windows изобрела pidl-ы, если моникеры делали ровно это же?&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
Фундаментальное отличие между pidl и моникерами - не в том, что они делают, а скорее в их "акценте". Моникер - это COM объект. Это означает, что они существуют в форме COM интерфейса (&lt;a title="MSDN: IMoniker Interface" href="http://msdn.microsoft.com/en-us/library/ms679705(VS.85).aspx"&gt;&lt;code&gt;IMoniker&lt;/code&gt;&lt;/a&gt;), что означает, что код, стоящий за объектом, должен быть загружен и готов к работе. Вы можете сконвертировать моникер в данные, но это не то, как обычно проводят свою жизнь моникеры. Если у вас есть массив моникеров, то весь код под капотом должен быть проинициализирован.&lt;br /&gt;
&lt;br /&gt;
Pidl, с другой стороны, проводит большую часть своей жизни будучи засериализован в данные. COM объект появляется только когда вы производите связывание (bind). Рассмотрите, к примеру, случай, когда вы перечисляете содержимое папки. Эта операция генерирует pidl для каждого элемента в папке, но создание и хранение этих pidl не требует выполнения кода каждого элемента. Папка с тысячей элементов создаст тысячу кусков данных, а не тысячу COM объектов. Рассмотрим, к примеру, папку с тысячей таблиц Excel. Если вы открываете эту папку, то вам бы не хотелось запускать Excel для каждой из тысячи таблиц в папке (для обслуживания элементов). На этом этапе вы пока просто говорите &lt;b&gt;про&lt;/b&gt; файлы. И лишь когда пользователь щёлкает на файле, вы хотели бы запускать Excel.&lt;br /&gt;
&lt;br /&gt;
Поскольку Оболочка большую часть времени проводит в "разговоре &lt;b&gt;о&lt;/b&gt; вещах", а не "разговорах &lt;b&gt;с&lt;/b&gt; вещами", то объект, который большую часть времени "мёртв", является более предпочтительным. Вы можете сказать, что разница между pidl и моникерами является вопросом жизни и смерти (прим.пер.: это юмор, если кто не понял).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688132520265708820-6457972541294089131?l=www.transl-gunsmoker.ru' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=vRC8DxLBkfw:kVueaLLFl_I:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=vRC8DxLBkfw:kVueaLLFl_I:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=vRC8DxLBkfw:kVueaLLFl_I:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=vRC8DxLBkfw:kVueaLLFl_I:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=vRC8DxLBkfw:kVueaLLFl_I:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=vRC8DxLBkfw:kVueaLLFl_I:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=vRC8DxLBkfw:kVueaLLFl_I:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=vRC8DxLBkfw:kVueaLLFl_I:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=vRC8DxLBkfw:kVueaLLFl_I:-BTjWOF_DHI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=vRC8DxLBkfw:kVueaLLFl_I:-BTjWOF_DHI" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/GunsmokersTranslations/~4/vRC8DxLBkfw" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.transl-gunsmoker.ru/feeds/6457972541294089131/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://www.transl-gunsmoker.ru/2011/08/pidls-and-monikers-do-roughly-same.html#comment-form" title="Комментарии: 0" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/6457972541294089131?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/6457972541294089131?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/GunsmokersTranslations/~3/vRC8DxLBkfw/pidls-and-monikers-do-roughly-same.html" title="Pidl и моникеры делают примерно одно и то же, только по-разному" /><author><name>Александр Алексеев</name><uri>https://profiles.google.com/113168002104297556003</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh3.googleusercontent.com/-aUMttZQBsuI/AAAAAAAAAAI/AAAAAAAAC3Y/QuZ7K9t_WzE/s512-c/photo.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://www.transl-gunsmoker.ru/2011/08/pidls-and-monikers-do-roughly-same.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DkcNR3c4cSp7ImA9WhdWGEw.&quot;"><id>tag:blogger.com,1999:blog-1688132520265708820.post-6249901763765167057</id><published>2011-08-27T22:06:00.000+04:00</published><updated>2011-09-12T11:54:56.939+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-09-12T11:54:56.939+04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="прочее" /><title>Не всякий человек с не-Windows разделом является гиком</title><content type="html">Это перевод &lt;a href="http://blogs.msdn.com/b/oldnewthing/archive/2006/07/11/662325.aspx" title="Not everybody with a non-Windows partition type is a geek"&gt;Not everybody with a non-Windows partition type is a geek&lt;/a&gt;. Автор: Реймонд Чен.&lt;br /&gt;
&lt;br /&gt;
В обсуждении поста "Почему установщик Windows записывает новый загрузочный сектор?" (прим.пер. имеется в виду &lt;a href="http://blogs.msdn.com/b/oldnewthing/archive/2005/12/20/505887.aspx" title="Why does Windows setup lay down a new boot sector?"&gt;оригинал&lt;/a&gt;) некоторые комментаторы предложили такое поведение: если на машине обнаруживается не-Windows раздел, то предполагать, что машина принадлежит &lt;a title="Википедия: Гик" href="http://ru.wikipedia.org/wiki/%D0%93%D0%B8%D0%BA_(%D1%81%D0%BB%D0%B5%D0%BD%D0%B3)"&gt;техно-гику&lt;/a&gt;. Таким образом, типичный пользователь компьютера может быть спасён от лицезрения технического диалога с &lt;a href="http://www.transl-gunsmoker.ru/2009/09/blog-post_10.html" title="Чтобы продемонстрировать вам превосходство нашего интеллекта, мы сейчас зададим вам вопрос, на который вы не сможете ответить"&gt;гик-вопросом, на которой они не смогут ответить&lt;/a&gt;.&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
Проблема в этом плане в том, что не всякий человек с не-Windows разделом на жёстком диске обязательно является техно-гиком. Многие OEM-машины (особенно ноутбуки - прим.пер.) поставляются с диском, разбитым на два раздела. На первом стоит Windows, а второй - не-Windows раздел, который имеет небольшой размер и содержит утилиты диагностики и восстановления системы (иногда - дистрибутивный образ мастер-копии системы). Наличие этого небольшого раздела не слишком известно, поскольку он работает только в редких случаях загрузки с "system recovery CD" от поставщика машины.&lt;br /&gt;
&lt;br /&gt;
Следствие из этого состоит в том, что если бы установщик Windows принял бы этот подход "человек с не-Windows разделом должен быть гиком", то в итоге мы бы пометили гиками ужасно много людей (многие из которых гиками не являются).&lt;br /&gt;
&lt;br /&gt;
Вы также можете сказать: "ну, только гики устанавливают операционные системы. Обычные люди покупают компьютер с предустановленной системой. Тот факт, что они вообще запустили установщик Windows, доказывает, что они - гики. Поэтому, установщик Windows должен быть оптимизирован для использования гиками". Действительно, предпосылки этого аргумента (что только гики устанавливают Windows) верны - но только если вы достигли устойчивого состояния. В течение нескольких месяцев сразу после выпуска новой версии Windows, Windows устанавливают все подряд - как гики, так и простые люди (кроме того, наплыв установок Windows приходится на каждое Рождество). Рецензенты журналов, пишущие обзоры новой операционной системы, будут устанавливать Windows для пробы. Так что впечатление от установки - это первое, что они увидят. Так что установке лучше бы быть гладкой и безболезненной.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688132520265708820-6249901763765167057?l=www.transl-gunsmoker.ru' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=YdXD2id5Bg8:i-24xKO80zo:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=YdXD2id5Bg8:i-24xKO80zo:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=YdXD2id5Bg8:i-24xKO80zo:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=YdXD2id5Bg8:i-24xKO80zo:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=YdXD2id5Bg8:i-24xKO80zo:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=YdXD2id5Bg8:i-24xKO80zo:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=YdXD2id5Bg8:i-24xKO80zo:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=YdXD2id5Bg8:i-24xKO80zo:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=YdXD2id5Bg8:i-24xKO80zo:-BTjWOF_DHI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=YdXD2id5Bg8:i-24xKO80zo:-BTjWOF_DHI" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/GunsmokersTranslations/~4/YdXD2id5Bg8" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.transl-gunsmoker.ru/feeds/6249901763765167057/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://www.transl-gunsmoker.ru/2011/08/not-everybody-with-non-windows.html#comment-form" title="Комментарии: 0" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/6249901763765167057?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/6249901763765167057?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/GunsmokersTranslations/~3/YdXD2id5Bg8/not-everybody-with-non-windows.html" title="Не всякий человек с не-Windows разделом является гиком" /><author><name>Александр Алексеев</name><uri>https://profiles.google.com/113168002104297556003</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh3.googleusercontent.com/-aUMttZQBsuI/AAAAAAAAAAI/AAAAAAAAC3Y/QuZ7K9t_WzE/s512-c/photo.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://www.transl-gunsmoker.ru/2011/08/not-everybody-with-non-windows.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CE8GQXs4fSp7ImA9WhdWGEw.&quot;"><id>tag:blogger.com,1999:blog-1688132520265708820.post-8552443543260493264</id><published>2011-08-26T22:06:00.000+04:00</published><updated>2011-09-12T11:33:40.535+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-09-12T11:33:40.535+04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="обработка ошибок" /><category scheme="http://www.blogger.com/atom/ns#" term="кодинг" /><title>Прежде чем учиться опознавать неправильное, вам нужно узнать, что есть правильное</title><content type="html">Это перевод &lt;a href="http://blogs.msdn.com/b/oldnewthing/archive/2006/07/10/661389.aspx" title="Before you can learn to recognize what's wrong, you must learn to recognize what's right"&gt;Before you can learn to recognize what's wrong, you must learn to recognize what's right&lt;/a&gt;. Автор: Реймонд Чен.&lt;br /&gt;
&lt;br /&gt;
Иногда, когда я отлаживаю проблему, я могу проигнорировать какой-то поток, а кто-то спросит: "а что делает этот поток? Почему ты его игнорируешь?"&lt;br /&gt;
&lt;br /&gt;
Мой ответ: "Я понятия не имею, что он делает, но что бы он ни делал, это - нормально".&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
&lt;a title="MSDN Blogs: If broken it is, fix it you should" href="http://blogs.msdn.com/b/tess/"&gt;Tess&lt;/a&gt; публикует отличную серию постов, посвящённых отладке CLR, но один из самых важных уроков - это &lt;a title="Things to ignore when debugging an ASP.NET hang" href="http://blogs.msdn.com/b/tess/archive/2005/12/20/505862.aspx"&gt;где вы учитесь игнорировать вещи, когда отлаживаете зависание в ASP.NET&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
Зависания и блокировки могут быть сложны в отладке, потому что у вас нет исключения, которое кричит: "эй, смотри на меня, вот она проблема, я - это баг!". Вместо этого программа просто замирает и вам нужно понять почему. При этом вам нужно искать что-то неординарное, необычное, но чтобы вы могли опознать это что-то - вам нужно знать, как обычно выглядят вещи в вашем процессе.&lt;br /&gt;
&lt;br /&gt;
Ну так сделайте это. Запустите программу, пусть она работает, а вы остановите её в отладчике и просто посмотрите на неё. Отметьте то, что вы увидите. Это - вещи, которые происходят в вашей программе, когда всё идёт своим чередом. Так выглядит ваша программа, когда она работает в нормальном режиме. Теперь, когда вы знаете как выглядят нормальные вещи в вашей программе, вы можете опознать не нормальные.&lt;br /&gt;
&lt;br /&gt;
Заметьте, что вам даже не нужно понимать, что все эти нормальные вещи значат или делают. К примеру, когда я подключаю отладчик к процессу, я часто вижу потоки, ожидающие RPC или спящие в пуле потоков ядра. Я понятия не имею, что они делают, но раз уж они тут и раньше были, то я их игнорирую.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688132520265708820-8552443543260493264?l=www.transl-gunsmoker.ru' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=zxEoDkKqE7I:ELo2W2SVQ3o:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=zxEoDkKqE7I:ELo2W2SVQ3o:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=zxEoDkKqE7I:ELo2W2SVQ3o:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=zxEoDkKqE7I:ELo2W2SVQ3o:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=zxEoDkKqE7I:ELo2W2SVQ3o:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=zxEoDkKqE7I:ELo2W2SVQ3o:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=zxEoDkKqE7I:ELo2W2SVQ3o:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=zxEoDkKqE7I:ELo2W2SVQ3o:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=zxEoDkKqE7I:ELo2W2SVQ3o:-BTjWOF_DHI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=zxEoDkKqE7I:ELo2W2SVQ3o:-BTjWOF_DHI" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/GunsmokersTranslations/~4/zxEoDkKqE7I" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.transl-gunsmoker.ru/feeds/8552443543260493264/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://www.transl-gunsmoker.ru/2011/08/before-you-can-learn-to-recognize-whats.html#comment-form" title="Комментарии: 0" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/8552443543260493264?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/8552443543260493264?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/GunsmokersTranslations/~3/zxEoDkKqE7I/before-you-can-learn-to-recognize-whats.html" title="Прежде чем учиться опознавать неправильное, вам нужно узнать, что есть правильное" /><author><name>Александр Алексеев</name><uri>https://profiles.google.com/113168002104297556003</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh3.googleusercontent.com/-aUMttZQBsuI/AAAAAAAAAAI/AAAAAAAAC3Y/QuZ7K9t_WzE/s512-c/photo.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://www.transl-gunsmoker.ru/2011/08/before-you-can-learn-to-recognize-whats.html</feedburner:origLink></entry><entry gd:etag="W/&quot;C0AHRXoyfCp7ImA9WhdWGEw.&quot;"><id>tag:blogger.com,1999:blog-1688132520265708820.post-4720696836164099851</id><published>2011-08-25T22:05:00.000+04:00</published><updated>2011-09-12T11:15:34.494+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-09-12T11:15:34.494+04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="История" /><category scheme="http://www.blogger.com/atom/ns#" term="окна" /><title>Нет, а почему именно 160x31?</title><content type="html">Это перевод &lt;a href="http://blogs.msdn.com/b/oldnewthing/archive/2006/07/07/659013.aspx" title="No, really, why is it 160x31?"&gt;No, really, why is it 160x31?&lt;/a&gt; Автор: Реймонд Чен.&lt;br /&gt;
&lt;br /&gt;
Когда я ранее пояснял, &lt;a title="Почему все свёрнутые окна имеют размер 160x31?" href="http://www.transl-gunsmoker.ru/2010/02/160x31.html"&gt;почему свёрнутые (минимизированные) окна имеют размер 160x31&lt;/a&gt;, я не пояснил, почему размером выбрано именно ровно 160 и 31.&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
Ширина минимизированного окна определяется полем &lt;code&gt;iWidth&lt;/code&gt; в &lt;a title="MSDN: MINIMIZEDMETRICS Structure" href="http://msdn.microsoft.com/en-us/library/ms724500(VS.85).aspx"&gt;записи &lt;code&gt;TMinimizedMetrics&lt;/code&gt;&lt;/a&gt;. Вы можете получить и изменить эту запись с помощью &lt;a title="MSDN: SystemParametersInfo Function" href="http://msdn.microsoft.com/en-us/library/ms724947(VS.85).aspx"&gt;функции &lt;code&gt;SystemParametersInfo&lt;/code&gt;&lt;/a&gt; (вас будут интересовать флаги &lt;code&gt;SPI_GETMINIMIZEDMETRICS&lt;/code&gt; и &lt;code&gt;SPI_SETMINIMIZEDMETRICS&lt;/code&gt;). Некоторые люди упоминают использование значение &lt;code&gt;MinWidth&lt;/code&gt; в реестре, но &lt;a href="http://www.transl-gunsmoker.ru/2010/03/systemparametersinfo.html" title="Использование SystemParametersInfo для получения настроек интерфейса пользователя"&gt;они не правы&lt;/a&gt;. К примеру, заметьте, что исправление &lt;code&gt;MinWidth&lt;/code&gt; требует выхода из системы, а использование &lt;code&gt;SPI_SETMINIMIZEDMETRICS&lt;/code&gt; действует немедленно. Это потому что &lt;code&gt;SPI_SETMINIMIZEDMETRICS&lt;/code&gt; обновляет внутреннее состояние системы, а правки реестра просто меняют значение в БД, на которую никто не смотрит (после запуска).&lt;br /&gt;
&lt;br /&gt;
Что насчёт высоты? А это просто высота строки-заголовка (caption bar), которую вы можете изменить на вкладке "Оформление" апплета "Экран" панели управления (программно вы можете использовать эту же полезную функцию &lt;code&gt;SystemParametersInfo&lt;/code&gt;, но в этот раз используя поле &lt;code&gt;iCaptionHeight&lt;/code&gt; &lt;a href="http://msdn.microsoft.com/en-us/library/ff729175(VS.85).aspx" title="MSDN: NONCLIENTMETRICS Structure"&gt;записи &lt;code&gt;TNonClientMetrics&lt;/code&gt;&lt;/a&gt;).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688132520265708820-4720696836164099851?l=www.transl-gunsmoker.ru' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=aIaExmMygo8:PauMbLoT8d0:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=aIaExmMygo8:PauMbLoT8d0:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=aIaExmMygo8:PauMbLoT8d0:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=aIaExmMygo8:PauMbLoT8d0:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=aIaExmMygo8:PauMbLoT8d0:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=aIaExmMygo8:PauMbLoT8d0:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=aIaExmMygo8:PauMbLoT8d0:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=aIaExmMygo8:PauMbLoT8d0:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=aIaExmMygo8:PauMbLoT8d0:-BTjWOF_DHI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=aIaExmMygo8:PauMbLoT8d0:-BTjWOF_DHI" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/GunsmokersTranslations/~4/aIaExmMygo8" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.transl-gunsmoker.ru/feeds/4720696836164099851/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://www.transl-gunsmoker.ru/2011/08/no-really-why-is-it-160x31.html#comment-form" title="Комментарии: 0" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/4720696836164099851?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/4720696836164099851?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/GunsmokersTranslations/~3/aIaExmMygo8/no-really-why-is-it-160x31.html" title="Нет, а почему именно 160x31?" /><author><name>Александр Алексеев</name><uri>https://profiles.google.com/113168002104297556003</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh3.googleusercontent.com/-aUMttZQBsuI/AAAAAAAAAAI/AAAAAAAAC3Y/QuZ7K9t_WzE/s512-c/photo.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://www.transl-gunsmoker.ru/2011/08/no-really-why-is-it-160x31.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DUIDQ3YycSp7ImA9WhdWGE0.&quot;"><id>tag:blogger.com,1999:blog-1688132520265708820.post-6564820483730097774</id><published>2011-08-24T22:04:00.000+04:00</published><updated>2011-09-12T10:06:12.899+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-09-12T10:06:12.899+04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="кодинг" /><category scheme="http://www.blogger.com/atom/ns#" term="Win32" /><title>Максимальный размер переменных окружения - это 32 или 64 Кб?</title><content type="html">Это перевод &lt;a href="http://blogs.msdn.com/b/oldnewthing/archive/2006/07/06/657868.aspx" title="Is the maximum size of the environment 32K or 64K?"&gt;Is the maximum size of the environment 32K or 64K?&lt;/a&gt; Автор: Реймонд Чен.&lt;br /&gt;
&lt;br /&gt;
Кажется, есть некоторая путаница в максимальном размере переменных окружения: 32 Кб или 64 Кб. Какое значение правильное?&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
Оба.&lt;br /&gt;
&lt;br /&gt;
Ограничение - 32'767 Unicode символов, что есть 65'534 байт. Назовите это 32 Кб или 64 Кб - как вам угодно. Но только убедитесь, что вы включили единицу измерения в ваши утверждения, если она не следует из контекста.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688132520265708820-6564820483730097774?l=www.transl-gunsmoker.ru' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=rqIRtqIfOWU:hEc3RQjTiCo:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=rqIRtqIfOWU:hEc3RQjTiCo:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=rqIRtqIfOWU:hEc3RQjTiCo:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=rqIRtqIfOWU:hEc3RQjTiCo:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=rqIRtqIfOWU:hEc3RQjTiCo:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=rqIRtqIfOWU:hEc3RQjTiCo:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=rqIRtqIfOWU:hEc3RQjTiCo:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=rqIRtqIfOWU:hEc3RQjTiCo:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=rqIRtqIfOWU:hEc3RQjTiCo:-BTjWOF_DHI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=rqIRtqIfOWU:hEc3RQjTiCo:-BTjWOF_DHI" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/GunsmokersTranslations/~4/rqIRtqIfOWU" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.transl-gunsmoker.ru/feeds/6564820483730097774/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://www.transl-gunsmoker.ru/2011/08/is-maximum-size-of-environment-32k-or.html#comment-form" title="Комментарии: 0" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/6564820483730097774?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/6564820483730097774?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/GunsmokersTranslations/~3/rqIRtqIfOWU/is-maximum-size-of-environment-32k-or.html" title="Максимальный размер переменных окружения - это 32 или 64 Кб?" /><author><name>Александр Алексеев</name><uri>https://profiles.google.com/113168002104297556003</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh3.googleusercontent.com/-aUMttZQBsuI/AAAAAAAAAAI/AAAAAAAAC3Y/QuZ7K9t_WzE/s512-c/photo.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://www.transl-gunsmoker.ru/2011/08/is-maximum-size-of-environment-32k-or.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CEcGSHk5eSp7ImA9WhdWFk8.&quot;"><id>tag:blogger.com,1999:blog-1688132520265708820.post-1270620744194217875</id><published>2011-08-23T22:04:00.000+04:00</published><updated>2011-09-10T06:33:49.721+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-09-10T06:33:49.721+04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="безопасность" /><title>Безопасность: не забывайте обнулять вещи, на которые вам наплевать</title><content type="html">Это перевод &lt;a href="http://blogs.msdn.com/b/oldnewthing/archive/2006/07/03/655251.aspx" title="Security: Don't forget to initialize the stuff you don't care about"&gt;Security: Don't forget to initialize the stuff you don't care about&lt;/a&gt;. Автор: Реймонд Чен.&lt;br /&gt;
&lt;br /&gt;
Затерянным в волнении уязвимостей повышения привилегий является простое раскрытие информации через отсутствующую инициализацию мусора (прим.пер.: понятия не имею, зачем оригинальное предложение так построено).&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
Сегодня каждый знаком с использованием &lt;a title="MSDN: SecureZeroMemory Function" href="http://msdn.microsoft.com/en-us/library/aa366877(VS.85).aspx"&gt;функции &lt;code&gt;SecureZeroMemory&lt;/code&gt;&lt;/a&gt; для гарантии того, что буфера, содержащие чувствительную (важную) информацию, очищены после работы с ними (*), но вы также должны очищать буфера перед их записью в другое место. Рассмотрим, к примеру, такой формат данных:
&lt;pre class="brush:delphi"&gt;type
  TFileHeader = packed record
    dwMagic: DWORD;
    dwVersion: DWORD;
    wszComment: array[0..256-1] of WideChar;
    cbData: DWORD;
    // далее следует cbData байт данных
  end;&lt;/pre&gt;
Код, который заполняет такие файлы, мог бы выглядеть так:
&lt;pre class="brush:delphi"&gt;function SaveToFile(hFile: THandle; pszComment: PWideChar; cbData: DWORD; pbData: PByte): Boolean;
var
  cbWritten: DWORD;
  fh: TFileHeader;
begin
  fh.dwMagic := FILE_MAGICNUMBER;
  fh.dwVersion := FILE_CURRENTVERSION;
  fh.cbData := cbData;
  Result := SUCCEEDED(StringCchCopyW(fh.wszComment, 256, pszComment)) and
                  WriteFile(hFile, @fh, SizeOf(fh), cbWritten, nil) and
                  (cbWritten = SizeOf(fh)) and
                  WriteFile(hFile, pbData, cbData, cbWritten, nil) and
                  (cbWritten = cbData);
end;&lt;/pre&gt;
Увидели баг безопасности?&lt;br /&gt;
&lt;br /&gt;
Если комментарий к файлу будет менее 255 символов, то байты после терминирующего нуля будут содержать не инициализированный мусор со стека. Мусор со стека может содержать интересную информацию, которую вы бы не хотели выкладывать в файл. Конечно, он не будет содержать информацию, которую вы уже идентифицировали как чувствительную (вроде паролей) - потому что вы уже подчистили её; но мусор может содержать информацию, хотя и менее критическую, но всё же привлекательную для злоумышленника. К примеру, у вас может утечь имя аккаунта.&lt;br /&gt;
&lt;br /&gt;
Мне рассказали, что одна сетевая программа известной компании содержала аналогичный баг. Они использовали очень продвинутый алгоритм "смены пароля", детали которого сейчас не важны. Дизайном было предусмотрено, что по сети передавались только сложно зашифрованные данные. Таким образом, если кто-то просматривал сетевые пакеты, то он не мог увидеть ничего интересного. Вот только у них был баг в их клиентской программе: когда они отправляли зашифрованный пароль на сервер, они забыли обнулить не используемые байты в пакете "смена пароля". А в этих данных была... ага, верно: не зашифрованная копия пароля.&lt;br /&gt;
&lt;br /&gt;
Примечание переводчика:&lt;br /&gt;
(*) &lt;code&gt;SecureZeroMemory&lt;/code&gt; практически не известна в Delphi, потому что предназначена для обхода оптимизатора C++ (видимо, по причине того, что &lt;a title="MSDN: ZeroMemory Function" href="http://msdn.microsoft.com/en-us/library/aa366920(VS.85).aspx"&gt;&lt;code&gt;ZeroMemory&lt;/code&gt;&lt;/a&gt; в C++ является макросом, а не функцией; поэтому её "вызов" последним действием на переменную может быть выброшен оптимизатором). Вместо неё в Delphi используется стандартная системная &lt;code&gt;ZeroMemory&lt;/code&gt; (которая в Delphi является обычной функцией; фактически - inline-переходником к &lt;a title="System.FillChar" href="http://docwiki.embarcadero.com/VCL/en/System.FillChar"&gt;&lt;code&gt;FillChar&lt;/code&gt;&lt;/a&gt;) или чисто Delphi-ёвая &lt;code&gt;FillChar&lt;/code&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688132520265708820-1270620744194217875?l=www.transl-gunsmoker.ru' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=twnktCXNSks:irNuEEcp3nQ:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=twnktCXNSks:irNuEEcp3nQ:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=twnktCXNSks:irNuEEcp3nQ:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=twnktCXNSks:irNuEEcp3nQ:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=twnktCXNSks:irNuEEcp3nQ:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=twnktCXNSks:irNuEEcp3nQ:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=twnktCXNSks:irNuEEcp3nQ:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=twnktCXNSks:irNuEEcp3nQ:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=twnktCXNSks:irNuEEcp3nQ:-BTjWOF_DHI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=twnktCXNSks:irNuEEcp3nQ:-BTjWOF_DHI" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/GunsmokersTranslations/~4/twnktCXNSks" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.transl-gunsmoker.ru/feeds/1270620744194217875/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://www.transl-gunsmoker.ru/2011/08/security-dont-forget-to-initialize.html#comment-form" title="Комментарии: 0" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/1270620744194217875?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/1270620744194217875?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/GunsmokersTranslations/~3/twnktCXNSks/security-dont-forget-to-initialize.html" title="Безопасность: не забывайте обнулять вещи, на которые вам наплевать" /><author><name>Александр Алексеев</name><uri>https://profiles.google.com/113168002104297556003</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh3.googleusercontent.com/-aUMttZQBsuI/AAAAAAAAAAI/AAAAAAAAC3Y/QuZ7K9t_WzE/s512-c/photo.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://www.transl-gunsmoker.ru/2011/08/security-dont-forget-to-initialize.html</feedburner:origLink></entry><entry gd:etag="W/&quot;A0UFSX85fip7ImA9WhdWFkw.&quot;"><id>tag:blogger.com,1999:blog-1688132520265708820.post-6019062231079066604</id><published>2011-08-22T22:03:00.000+04:00</published><updated>2011-09-10T05:46:58.126+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-09-10T05:46:58.126+04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="кодинг" /><title>Событие с авто-сбросом - это просто глупый семафор</title><content type="html">Это перевод &lt;a href="http://blogs.msdn.com/b/oldnewthing/archive/2006/06/22/642849.aspx" title="An auto-reset event is just a stupid semaphore"&gt;An auto-reset event is just a stupid semaphore&lt;/a&gt;. Автор: Реймонд Чен.&lt;br /&gt;
&lt;br /&gt;
Когда вы создаёте событие с помощью &lt;a title="MSDN: CreateEvent Function" href="http://msdn.microsoft.com/en-us/library/ms682396(VS.85).aspx"&gt;функции &lt;code&gt;CreateEvent&lt;/code&gt;&lt;/a&gt;, вы указываете, хотите ли вы создать событие с авто-сбросом (auto-reset event) или с ручным сбросом (manual-reset event).&lt;br /&gt;
&lt;br /&gt;
Событие с ручным сбросом прозрачно для понимания: если событие сброшено, то вы ждёте сигнального состояния; если событие установлено (в сигнальном состоянии), то ожидания не происходит (ожидание удовлетворяется сразу). Не играет значения, сколько человек ждут события; они все обрабатываются одинаково, а состояние события не зависит от числа его ожидающих.&lt;br /&gt;
&lt;br /&gt;
Событие с авто-сбросом - более запутывающая штука.&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
Вероятно, проще всего думать о событии с авто-сбросом как о семафоре с максимумом счётчика равным единице. Если событие сброшено, то вы ждёте сигнального состояния. Если событие устанавливается, то один из ожидающих будет отпущен, а остальные продолжат ждать (и из &lt;a href="http://www.transl-gunsmoker.ru/2010/02/pulseevent.html" title="PulseEvent имеет фундаментальный изъян"&gt;нашего обсуждения &lt;code&gt;PulseEvent&lt;/code&gt;&lt;/a&gt; вы уже знаете, что то, кто именно будет освобождён, - не определено).&lt;br /&gt;
&lt;br /&gt;
"Ага!" в случае событий с авто-сбросом заключается в случае, когда вы устанавливаете уже установленное событие. Поскольку у события есть только два состояния (сброшенное и сигнальное), то установка события, которое и так находится в сигнальном состоянии, не даст никакого эффекта, ничего не изменит. Если вы используете событие, чтобы контролировать ресурс в модели producer/consumer, то "установка уже установленного события" приведёт к потере токена. Рассмотрим такой шаблон:&lt;br /&gt;
&lt;br /&gt;
&lt;TABLE RULES=cols BORDER=1 BORDERCOLOR=black CELLPADDING=3 STYLE="border-collapse: collapse"&gt;&lt;TR&gt;&lt;TH&gt;Producer&lt;/TH&gt;&lt;TH&gt;Consumer&lt;/TH&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;        &lt;/TD&gt;&lt;TD&gt;Ожидание&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;Задать работу&lt;/TD&gt;&lt;TD&gt;&lt;/TR&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;&lt;CODE&gt;SetEvent&lt;/CODE&gt;&lt;/TD&gt;&lt;TD&gt;&lt;/TR&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;        &lt;/TD&gt;&lt;TD&gt;Проснуться и сбросить событие&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;        &lt;/TD&gt;&lt;TD&gt;Работа&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;Задать работу&lt;/TD&gt;&lt;TD&gt;&lt;/TR&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;        &lt;/TD&gt;&lt;TD&gt;Wait&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;&lt;CODE&gt;SetEvent&lt;/CODE&gt;&lt;/TD&gt;&lt;TD&gt;&lt;/TR&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;        &lt;/TD&gt;&lt;TD&gt;Проснуться и сбросить событие&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;        &lt;/TD&gt;&lt;TD&gt;Работа&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;...     &lt;/TD&gt;&lt;TD&gt;...&lt;/TD&gt;&lt;/TR&gt;&lt;/TABLE&gt; 
&lt;br /&gt;
Но что если тайминг немного не совпадёт? Что если потребитель (consumer) будет медленно делать работу (либо же producer будет слишком быстро её генерировать)?&lt;br /&gt;
&lt;br /&gt;
&lt;TABLE RULES=cols BORDER=1 BORDERCOLOR=black CELLPADDING=3 STYLE="border-collapse: collapse"&gt;&lt;TR&gt;&lt;TH&gt;Producer&lt;/TH&gt;&lt;TH&gt;Consumer&lt;/TH&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;        &lt;/TD&gt;&lt;TD&gt;Ожидание&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;Задать работу&lt;/TD&gt;&lt;TD&gt;&lt;/TR&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;&lt;CODE&gt;SetEvent&lt;/CODE&gt;&lt;/TD&gt;&lt;TD&gt;&lt;/TR&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;        &lt;/TD&gt;&lt;TD&gt;Проснуться и сбросить событие&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;Задать работу&lt;/TD&gt;&lt;TD&gt;&lt;/TR&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;&lt;CODE&gt;SetEvent&lt;/CODE&gt;&lt;/TD&gt;&lt;TD&gt;&lt;/TR&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;        &lt;/TD&gt;&lt;TD&gt;Работа&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;Задать работу&lt;/TD&gt;&lt;TD&gt;&lt;/TR&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;&lt;CODE&gt;SetEvent&lt;/CODE&gt; (без эффекта)&lt;/TD&gt;&lt;TD&gt;&lt;/TR&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;        &lt;/TD&gt;&lt;TD&gt;Ожидание удовлетворено сразу&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;        &lt;/TD&gt;&lt;TD&gt;Сброс события&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;        &lt;/TD&gt;&lt;TD&gt;Работа&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;        &lt;/TD&gt;&lt;TD&gt;Ожидание&lt;/TD&gt;&lt;/TR&gt;&lt;/TABLE&gt;
&lt;br /&gt;
Заметьте, что producer создал три задания на работу, но consumer обработал только два. Третий &lt;code&gt;SetEvent&lt;/code&gt; просто ничего не сделал, поскольку событие было уже установлено (вы увидите ту же проблему, если попытаетесь увеличить счётчик семафора за его максимум). Если вы хотите, чтобы оба числа соответствовали друг другу, то вам нужно использовать семафор с максимальным значением счётчика выше чем максимум элементов в очереди (прим.пер.: либо же consumer при выходе из ожидания может обрабатывать не один элемент, а все в очереди).&lt;br /&gt;
&lt;br /&gt;
Мораль истории: знайте ваши инструменты, знайте их ограничения и используйте правильный инструмент для правильной задачи.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688132520265708820-6019062231079066604?l=www.transl-gunsmoker.ru' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=XB013wWlDZM:8g3dsEerWUw:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=XB013wWlDZM:8g3dsEerWUw:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=XB013wWlDZM:8g3dsEerWUw:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=XB013wWlDZM:8g3dsEerWUw:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=XB013wWlDZM:8g3dsEerWUw:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=XB013wWlDZM:8g3dsEerWUw:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=XB013wWlDZM:8g3dsEerWUw:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=XB013wWlDZM:8g3dsEerWUw:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=XB013wWlDZM:8g3dsEerWUw:-BTjWOF_DHI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=XB013wWlDZM:8g3dsEerWUw:-BTjWOF_DHI" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/GunsmokersTranslations/~4/XB013wWlDZM" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.transl-gunsmoker.ru/feeds/6019062231079066604/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://www.transl-gunsmoker.ru/2011/08/auto-reset-event-is-just-stupid.html#comment-form" title="Комментарии: 0" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/6019062231079066604?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/6019062231079066604?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/GunsmokersTranslations/~3/XB013wWlDZM/auto-reset-event-is-just-stupid.html" title="Событие с авто-сбросом - это просто глупый семафор" /><author><name>Александр Алексеев</name><uri>https://profiles.google.com/113168002104297556003</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh3.googleusercontent.com/-aUMttZQBsuI/AAAAAAAAAAI/AAAAAAAAC3Y/QuZ7K9t_WzE/s512-c/photo.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://www.transl-gunsmoker.ru/2011/08/auto-reset-event-is-just-stupid.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CE4DSH89cSp7ImA9WhdWFkw.&quot;"><id>tag:blogger.com,1999:blog-1688132520265708820.post-9158012274920786985</id><published>2011-08-21T22:02:00.000+04:00</published><updated>2011-09-10T04:02:59.169+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-09-10T04:02:59.169+04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="безопасность" /><title>Одноэкземплярная программа является своим собственным отказом в обслуживании</title><content type="html">Это перевод &lt;a href="http://blogs.msdn.com/b/oldnewthing/archive/2006/06/20/639479.aspx" title="A single-instance program is its own denial of service"&gt;A single-instance program is its own denial of service&lt;/a&gt;. Автор: Реймонд Чен.&lt;br /&gt;
&lt;br /&gt;
Есть много способов, которыми программа может достичь поведения запуска в едином экземпляре; я не буду разбирать их. Но как только люди встают на путь программы с одним запущенным экземпляром, они тут же спотыкаются о другую проблему при анализе безопасности: отказ в обслуживании (denial of service attack).
&lt;blockquote&gt;Мы используем мьютекс с фиксированным именем, чтобы определить, не запущена ли уже копия программы. Но это также означает, что злоумышленник может создать мьютекс с таким именем до запуска программы, блокируя, таким образом, запуск нашей программы! Как я могу предотвратить этот тип отказа в обслуживании?&lt;/blockquote&gt;
&lt;a name='more'&gt;&lt;/a&gt;
Если атакующий запущен в том же контексте безопасности, что и ваша программа (ну, или &lt;i&gt;будет&lt;/i&gt; запущена ваша программа), то вы ничего не можете сделать с этой ситуацией. Какое бы "секретное рукопожатие" вы ни придумали бы для определения запуска второго экземпляра - атакующий всегда может просто повторить его. Поскольку он запущен в том же контексте безопасности - он может ровно столько же, сколько и ваша программа.&lt;br /&gt;
&lt;br /&gt;
Но посмотрите на этот вопрос шире: одноэкземплярная программа является "своим собственным отказом в обслуживании"! В конце концов, первый экземпляр программы блокирует последующие запуски программы. Ваши требования к программе изначально являются уязвимостью в безопасности. Соответственно, вы не можете защитить себя от отказа в обслуживании, потому что вы &lt;b&gt;хотите&lt;/b&gt; иметь отказ в обслуживании.&lt;br /&gt;
&lt;br /&gt;
Но что вы можете сделать - понять и сузить область уязвимости. Очевидно, вы не можете защитить себя от атакующего, работающего на том же уровне привилегий, что и вы. Но вы можете защитить себя от атакующих, работающих под другими (не привилегированными) учётными записями. Для этого вам нужно включить защищаемые объекты в процесс вашего программного "рукопожатия". Тогда простые пользователи (не администраторы) не смогут заблокировать запуск вашей программы для других пользователей. Худшей вещью, которой допускается делать не администратору - делать свою жизнь жалкой, но не жизнь других пользователей.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688132520265708820-9158012274920786985?l=www.transl-gunsmoker.ru' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=wYm5AmwLqXg:Yj7BN1ygOJw:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=wYm5AmwLqXg:Yj7BN1ygOJw:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=wYm5AmwLqXg:Yj7BN1ygOJw:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=wYm5AmwLqXg:Yj7BN1ygOJw:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=wYm5AmwLqXg:Yj7BN1ygOJw:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=wYm5AmwLqXg:Yj7BN1ygOJw:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=wYm5AmwLqXg:Yj7BN1ygOJw:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=wYm5AmwLqXg:Yj7BN1ygOJw:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=wYm5AmwLqXg:Yj7BN1ygOJw:-BTjWOF_DHI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=wYm5AmwLqXg:Yj7BN1ygOJw:-BTjWOF_DHI" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/GunsmokersTranslations/~4/wYm5AmwLqXg" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.transl-gunsmoker.ru/feeds/9158012274920786985/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://www.transl-gunsmoker.ru/2011/08/single-instance-program-is-its-own.html#comment-form" title="Комментарии: 0" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/9158012274920786985?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/9158012274920786985?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/GunsmokersTranslations/~3/wYm5AmwLqXg/single-instance-program-is-its-own.html" title="Одноэкземплярная программа является своим собственным отказом в обслуживании" /><author><name>Александр Алексеев</name><uri>https://profiles.google.com/113168002104297556003</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh3.googleusercontent.com/-aUMttZQBsuI/AAAAAAAAAAI/AAAAAAAAC3Y/QuZ7K9t_WzE/s512-c/photo.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://www.transl-gunsmoker.ru/2011/08/single-instance-program-is-its-own.html</feedburner:origLink></entry><entry gd:etag="W/&quot;C08NQHw-eSp7ImA9WhdWFkw.&quot;"><id>tag:blogger.com,1999:blog-1688132520265708820.post-3886498928189991429</id><published>2011-08-20T22:01:00.000+04:00</published><updated>2011-09-10T03:44:51.251+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-09-10T03:44:51.251+04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="История" /><category scheme="http://www.blogger.com/atom/ns#" term="не делай так" /><title>Почему нет программного способа изменить порядок элементов в меню Пуск?</title><content type="html">Это перевод &lt;a href="http://blogs.msdn.com/b/oldnewthing/archive/2006/06/19/636823.aspx" title="Why can't you programmatically reorder the items on the Start menu?"&gt;Why can't you programmatically reorder the items on the Start menu?&lt;/a&gt; Автор: Реймонд Чен.&lt;br /&gt;
&lt;br /&gt;
Классическое меню Пуск и часть "Все программы" меню Пуск в стиле Windows XP позволяют вам изменить порядок ярлыков, показываемых в них. Вы можете использовать перетаскивание для его упорядочивания, либо же просто использовать сортировку по имени. Но почему нет никакого программного интерфейса для выполнения этих операций?&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
Потому что эта сила может использоваться во зло чаще, чем во имя добра.&lt;br /&gt;
&lt;br /&gt;
Как я &lt;a title="Почему к часто используемым программам в меню Пуск нет программного доступа?" href="http://www.transl-gunsmoker.ru/2008/11/blog-post_9947.html"&gt;отмечал ранее&lt;/a&gt;, меню Пуск является крайне ценным местом. Если бы к порядку элементов меню Пуск был дан программный интерфейс, то программы бы просто использовали его для своего пиара, помещая себя на вершину (и, вероятно, понижая своих конкурентов). Как только вы осознаете, что программы собираются делать с меню Пуск, вы поймёте, что пользователь сам потеряет способность упорядочивать элементы меню - потому что программы просто будут замещать выбор пользователя своими установками.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688132520265708820-3886498928189991429?l=www.transl-gunsmoker.ru' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=zlWhO0YtGQ4:xxHH2DnUmkw:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=zlWhO0YtGQ4:xxHH2DnUmkw:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=zlWhO0YtGQ4:xxHH2DnUmkw:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=zlWhO0YtGQ4:xxHH2DnUmkw:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=zlWhO0YtGQ4:xxHH2DnUmkw:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=zlWhO0YtGQ4:xxHH2DnUmkw:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=zlWhO0YtGQ4:xxHH2DnUmkw:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=zlWhO0YtGQ4:xxHH2DnUmkw:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=zlWhO0YtGQ4:xxHH2DnUmkw:-BTjWOF_DHI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=zlWhO0YtGQ4:xxHH2DnUmkw:-BTjWOF_DHI" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/GunsmokersTranslations/~4/zlWhO0YtGQ4" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.transl-gunsmoker.ru/feeds/3886498928189991429/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://www.transl-gunsmoker.ru/2011/08/why-cant-you-programmatically-reorder.html#comment-form" title="Комментарии: 0" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/3886498928189991429?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/3886498928189991429?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/GunsmokersTranslations/~3/zlWhO0YtGQ4/why-cant-you-programmatically-reorder.html" title="Почему нет программного способа изменить порядок элементов в меню Пуск?" /><author><name>Александр Алексеев</name><uri>https://profiles.google.com/113168002104297556003</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh3.googleusercontent.com/-aUMttZQBsuI/AAAAAAAAAAI/AAAAAAAAC3Y/QuZ7K9t_WzE/s512-c/photo.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://www.transl-gunsmoker.ru/2011/08/why-cant-you-programmatically-reorder.html</feedburner:origLink></entry><entry gd:etag="W/&quot;C0QBQXw9fCp7ImA9WhdWFkw.&quot;"><id>tag:blogger.com,1999:blog-1688132520265708820.post-507614984179056483</id><published>2011-08-19T22:01:00.000+04:00</published><updated>2011-09-10T03:35:50.264+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-09-10T03:35:50.264+04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="прочее" /><title>Разбираемся, что на самом деле значит "значимые цифры"</title><content type="html">Это перевод &lt;a href="http://blogs.msdn.com/b/oldnewthing/archive/2006/06/16/634078.aspx" title="Understanding what significant digits really mean"&gt;Understanding what significant digits really mean&lt;/a&gt;. Автор: Реймонд Чен.&lt;br /&gt;
&lt;br /&gt;
Числа с плавающей запятой двойной точности (double-precision floating point number) содержат 15 значащих цифр. Что это значит на самом деле?
&lt;blockquote&gt;Я умножил 0,619207 на 10'000'000 и получил 6'192'069,999999991 вместо 6'192'070. Это же только шесть цифр; где мои обещанные пятнадцать?&lt;/blockquote&gt;
&lt;a name='more'&gt;&lt;/a&gt;
Разговор о значащих цифрах - это просто сокращение для относительной погрешности. Это не означает, что первые пятнадцать цифр в точности совпадают с результатом (если вы проведёте больше времени в численном анализе, то услышите как люди говорят о вещах вроде "пять с половиной значащих цифр". Если бы значением "значащих цифр" было таким буквальным, то как вы могли бы иметь половину цифры?).&lt;br /&gt;
&lt;br /&gt;
Относительная ошибка в данном вычислении равна 9 / 6'192'070'000'000'000 = 1,5 × 10&lt;sup&gt;-15&lt;/sup&gt;, что согласуется с точностью в пятнадцать значащих цифр.&lt;br /&gt;
&lt;br /&gt;
Но даже если вы примете буквальное значение слов, то числа примера всё ещё равны друг другу до пятнадцати знаков. Вспомните, что 0,99999... = 1, и поэтому значения
&lt;pre&gt;6192069,999999991
6192069,999999999...&lt;/pre&gt;
согласуются на пятнадцать значащих цифр - как и было обещано.&lt;br /&gt;
&lt;br /&gt;
А если вы адепт гипер-буквального чтения и отказываетесь принимать трактовку 0,99999... = 1, то вам придётся признать, что единственными возможными значениями для числа значащих цифр являются ноль и бесконечность! &lt;br /&gt;
&lt;br /&gt;
Рассмотрите пример вычислений, в результате которых получается ровно 1, а сами вычисления выполняются с точностью N значащих цифр (где N &gt; 0). Поскольку вы не принимаете, что 0,9 согласуется с 1,0 в одной значащей цифре, то единственными значениями, которые согласуются с 1,0 хотя бы по одной значащей цифре, будут числа в форме "один, запятая, что-то". Обозначим результат вычислений через 1 + ε, где 0 ≤ ε &lt; 1. Теперь вычтем это из 2.0, что даст 1 − ε. Этот результат тоже должен иметь N значащих цифр. И снова, поскольку вы не принимаете, что 0,9 согласуется с 1,0 по одной значащей цифре, то чтобы этот результат был бы хорош для N значащих цифр (N &gt; 0), результат должен иметь форму "один, запятая, что-то". Обозначим результат как 1 + δ, где 0 ≤ δ &lt; 1.&lt;br /&gt;
&lt;br /&gt;
Итак, мы получили 1 − ε = 1 + δ, откуда следует ε = −δ. Поскольку и δ, и ε больше или равны нуля, то единственное решение этого уравнения: ε = δ = 0. Соответственно, единственным числом, которое равно единице до какого-то ненулевого числа значащих цифр (если вы подписались на гипер-буквальную трактовку) будет сама единица. Другими словами, единственное возможное (не нулевое) число значащих цифр - бесконечность. И я думаю, что мы все согласны в том, что если единственными значениями количества значащих цифр были бы ноль с бесконечностью, то вся концепция значимых цифр была бы глупой и бесполезной.&lt;br /&gt;
&lt;br /&gt;
Вот почему значимые цифры не используют гипер-буквальное определение.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688132520265708820-507614984179056483?l=www.transl-gunsmoker.ru' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=tuJeE7f1x6U:WRv5ctS1oPQ:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=tuJeE7f1x6U:WRv5ctS1oPQ:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=tuJeE7f1x6U:WRv5ctS1oPQ:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=tuJeE7f1x6U:WRv5ctS1oPQ:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=tuJeE7f1x6U:WRv5ctS1oPQ:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=tuJeE7f1x6U:WRv5ctS1oPQ:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=tuJeE7f1x6U:WRv5ctS1oPQ:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=tuJeE7f1x6U:WRv5ctS1oPQ:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=tuJeE7f1x6U:WRv5ctS1oPQ:-BTjWOF_DHI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=tuJeE7f1x6U:WRv5ctS1oPQ:-BTjWOF_DHI" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/GunsmokersTranslations/~4/tuJeE7f1x6U" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.transl-gunsmoker.ru/feeds/507614984179056483/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://www.transl-gunsmoker.ru/2011/08/understanding-what-significant-digits.html#comment-form" title="Комментарии: 2" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/507614984179056483?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/507614984179056483?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/GunsmokersTranslations/~3/tuJeE7f1x6U/understanding-what-significant-digits.html" title="Разбираемся, что на самом деле значит &quot;значимые цифры&quot;" /><author><name>Александр Алексеев</name><uri>https://profiles.google.com/113168002104297556003</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh3.googleusercontent.com/-aUMttZQBsuI/AAAAAAAAAAI/AAAAAAAAC3Y/QuZ7K9t_WzE/s512-c/photo.jpg" /></author><thr:total>2</thr:total><feedburner:origLink>http://www.transl-gunsmoker.ru/2011/08/understanding-what-significant-digits.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CEMHQn8zeCp7ImA9WhdWFUw.&quot;"><id>tag:blogger.com,1999:blog-1688132520265708820.post-2874105027947987698</id><published>2011-08-18T22:00:00.000+04:00</published><updated>2011-09-09T00:07:13.180+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-09-09T00:07:13.180+04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Win32" /><category scheme="http://www.blogger.com/atom/ns#" term="окна" /><title>Свойства оконных классов применяются ко всем окнам этого класса</title><content type="html">Это перевод &lt;a href="http://blogs.msdn.com/b/oldnewthing/archive/2006/06/15/632243.aspx" title="Window class properties apply to all windows that belong to the class"&gt;Window class properties apply to all windows that belong to the class&lt;/a&gt;. Автор: Реймонд Чен.&lt;br /&gt;
&lt;br /&gt;
Свойства оконных классов применяются ко всем окнам этого класса. Именно поэтому они и называются классовыми свойствами. Это выглядит как очевидная вещь, когда её облекают в слова, но я видел множество "решений", которые теряют из вида этот простой факт.&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
Все свойства, устанавливаемые в записях &lt;a title="MSDN: WNDCLASS Structure" href="http://msdn.microsoft.com/en-us/library/ms633576(VS.85).aspx"&gt;&lt;code&gt;TWndClass&lt;/code&gt;&lt;/a&gt; (или &lt;a title="MSDN: WNDCLASSEX Structure" href="http://msdn.microsoft.com/en-us/library/ms633577(VS.85).aspx"&gt;&lt;code&gt;TWndClassEx&lt;/code&gt;&lt;/a&gt;) являются свойствами оконного класса, равно как и свойства, доступ к которым происходит через &lt;a title="MSDN: SetClassLongPtr Function" href="http://msdn.microsoft.com/en-us/library/ms633589(VS.85).aspx"&gt;&lt;code&gt;Get&lt;/code&gt;/&lt;code&gt;SetClassWord&lt;/code&gt;/&lt;code&gt;Long&lt;/code&gt;/&lt;code&gt;LongPtr&lt;/code&gt;&lt;/a&gt;. Это означает, что когда вы меняете эти свойства, то это изменение затрагивает весь оконный класс. К примеру, если вы пишете:
&lt;pre class="brush:delphi"&gt;SetClassLongPtr(hwnd, GCLP_HCURSOR, LONG_PTR(hcurNew));&lt;/pre&gt;
то вы не просто меняете курсор окна &lt;code&gt;hwnd&lt;/code&gt;. Вы меняете курсор всех окон того же оконного класса, что и оконный класс &lt;code&gt;hwnd&lt;/code&gt;. К примеру, если &lt;code&gt;hwnd&lt;/code&gt; является описателем элементом управления edit, то этим вы измените курсор во всех edit-ах!&lt;br /&gt;
&lt;br /&gt;
Но что если вам нужно изменить классовое свойство только для одного окна, вместо всех окон?&lt;br /&gt;
&lt;br /&gt;
Если вы хотите изменить меню, фон, курсор или значок конкретного окна, то вы можете заместить умолчания класса на уровне окна:&lt;br /&gt;
&lt;br /&gt;
&lt;TABLE BORDER=1 BORDERCOLOR=black STYLE="border-collapse: collapse"&gt;&lt;TR&gt;&lt;TH&gt;Свойство&lt;/TH&gt;&lt;TH&gt;Метод&lt;/TH&gt;&lt;/TR&gt; 
&lt;TR&gt;&lt;TD VALIGN=BASELINE&gt;Меню&lt;/TD&gt;&lt;TD VALIGN=BASELINE&gt;&lt;CODE&gt;SetMenu(hwnd, hmenuNew)&lt;/CODE&gt; + удалить старое меню&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD VALIGN=BASELINE&gt;Фон&lt;/TD&gt;&lt;TD VALIGN=BASELINE&gt;Заместить &lt;CODE&gt;WM_ERASEBKGND&lt;/CODE&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD VALIGN=BASELINE&gt;Курсор&lt;/TD&gt;&lt;TD VALIGN=BASELINE&gt;Заместить &lt;CODE&gt;WM_SETCURSOR&lt;/CODE&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD VALIGN=BASELINE&gt;Иконка&lt;/TD&gt;&lt;TD VALIGN=BASELINE&gt;&lt;CODE&gt;SendMessage(hwnd, WM_SETICON, iconSize, LPARAM(hiconNew));&lt;/CODE&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;/TABLE&gt;
&lt;br /&gt;
Прим.пер.: вероятно, людей смущает тот факт, что функции Get/SetClassXYZ принимают аргументом окно, а не оконный класс.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688132520265708820-2874105027947987698?l=www.transl-gunsmoker.ru' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=wCLS_ujZHE8:QJ_sQ0t07JM:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=wCLS_ujZHE8:QJ_sQ0t07JM:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=wCLS_ujZHE8:QJ_sQ0t07JM:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=wCLS_ujZHE8:QJ_sQ0t07JM:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=wCLS_ujZHE8:QJ_sQ0t07JM:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=wCLS_ujZHE8:QJ_sQ0t07JM:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=wCLS_ujZHE8:QJ_sQ0t07JM:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=wCLS_ujZHE8:QJ_sQ0t07JM:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=wCLS_ujZHE8:QJ_sQ0t07JM:-BTjWOF_DHI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=wCLS_ujZHE8:QJ_sQ0t07JM:-BTjWOF_DHI" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/GunsmokersTranslations/~4/wCLS_ujZHE8" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.transl-gunsmoker.ru/feeds/2874105027947987698/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://www.transl-gunsmoker.ru/2011/08/window-class-properties-apply-to-all.html#comment-form" title="Комментарии: 0" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/2874105027947987698?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/2874105027947987698?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/GunsmokersTranslations/~3/wCLS_ujZHE8/window-class-properties-apply-to-all.html" title="Свойства оконных классов применяются ко всем окнам этого класса" /><author><name>Александр Алексеев</name><uri>https://profiles.google.com/113168002104297556003</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh3.googleusercontent.com/-aUMttZQBsuI/AAAAAAAAAAI/AAAAAAAAC3Y/QuZ7K9t_WzE/s512-c/photo.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://www.transl-gunsmoker.ru/2011/08/window-class-properties-apply-to-all.html</feedburner:origLink></entry></feed>

