<?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;CkUHQns8eSp7ImA9WhRUGUw.&quot;"><id>tag:blogger.com,1999:blog-1688132520265708820</id><updated>2012-01-30T11:57:13.571+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><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>798</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;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><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><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><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><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><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;DUcASX0-fCp7ImA9WhdVFkQ.&quot;"><id>tag:blogger.com,1999:blog-1688132520265708820.post-735344658191301764</id><published>2011-09-03T22:10:00.000+04:00</published><updated>2011-09-22T17:10:48.354+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-09-22T17:10:48.354+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;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><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;CkcCRng7fSp7ImA9WhdVEkg.&quot;"><id>tag:blogger.com,1999:blog-1688132520265708820.post-871306012853550258</id><published>2011-09-02T22:09:00.000+04:00</published><updated>2011-09-17T13:01:07.605+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-09-17T13:01:07.605+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;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><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;CUENQHo4fCp7ImA9WhdVEk8.&quot;"><id>tag:blogger.com,1999:blog-1688132520265708820.post-2702370324084433719</id><published>2011-09-01T22:09:00.000+04:00</published><updated>2011-09-17T05:41:31.434+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-09-17T05:41:31.434+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;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><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;CEQMQ308fCp7ImA9WhdVEk8.&quot;"><id>tag:blogger.com,1999:blog-1688132520265708820.post-5477983680087185034</id><published>2011-08-31T22:08:00.000+04:00</published><updated>2011-09-17T05:19:42.374+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-09-17T05:19:42.374+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;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><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;DEQBR3w7fip7ImA9WhdVEkg.&quot;"><id>tag:blogger.com,1999:blog-1688132520265708820.post-3568786456412055911</id><published>2011-08-30T22:08:00.000+04:00</published><updated>2011-09-17T14:45:56.206+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-09-17T14:45:56.206+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. В следующий раз мы посмотрим на переход на 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><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;CEQNSHg_fip7ImA9WhdVEk8.&quot;"><id>tag:blogger.com,1999:blog-1688132520265708820.post-7449359055015592386</id><published>2011-08-29T22:07:00.000+04:00</published><updated>2011-09-17T05:19:59.646+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-09-17T05:19:59.646+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;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><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><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><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><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><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><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><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><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><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><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><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><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><entry gd:etag="W/&quot;CUABQn49fCp7ImA9WhdWFU0.&quot;"><id>tag:blogger.com,1999:blog-1688132520265708820.post-6946397201573791084</id><published>2011-08-17T20:48:00.000+04:00</published><updated>2011-09-08T21:42:33.064+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-09-08T21:42:33.064+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="fonts" /><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/14/630864.aspx" title="Pitfalls of transparent rendering of anti-aliased fonts"&gt;Pitfalls of transparent rendering of anti-aliased fonts&lt;/a&gt;. Автор: Реймонд Чен.&lt;br /&gt;
&lt;br /&gt;
Windows предоставляет несколько технологий рендеринга монохромного текста на цветных экранах, пользуясь преимуществами характеристик экрана для предоставления гладкого результата. Эти техники включают в себя сглаживание (grayscale anti-aliasing), а также и более продвинутую технику - &lt;a href="http://www.microsoft.com/typography/ClearTypeInfo.mspx" title="Microsoft Typogrphy: ClearType information"&gt;ClearType&lt;/a&gt;. Обе техники читают фоновые пиксели, чтобы определить, что рисовать. Это означает, что рендеринг текста требует дополнительного внимания.&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
Если вы рисуете текст вместе с фоном (обрамляющим прямоугольником), то тут нет никаких проблем, потому что вы выводите текст вместе с фоном, так что вы получаете согласованный результат, вне зависимости от предыдущего состояния холста (canvas). Но если вы рисуете текст поверх существующего фона, то вы должны убедиться, что на фоне находится именно ожидаемое вами изображение.&lt;br /&gt;
&lt;br /&gt;
К примеру, наиболее часто люди лажаются, выводя текст многократно. Я видел программы, которые рисуют всё более тёмный текст; чем дольше вы её используете - тем темнее становится текст. Давайте посмотрим на то, как это происходит, и как этого можно избежать. Создайте пустое VCL приложение с таким кодом:
&lt;pre class="brush:delphi"&gt;type
  TForm1 = class(TForm)
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure FormPaint(Sender: TObject);
  private
    FAntialias: TFont;
    FClearType: TFont;
    procedure MultiPaint(const X, Y, N: Integer);
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
 FAntialias := TFont.Create;
 FClearType := TFont.Create;

 FAntialias.Handle := CreateFont(-20, 0, 0, 0, FW_NORMAL, 0, 0, 0, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, ANTIALIASED_QUALITY, DEFAULT_PITCH, 'Tahoma');
 FClearType.Handle := CreateFont(-20, 0, 0, 0, FW_NORMAL, 0, 0, 0, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, CLEARTYPE_QUALITY, DEFAULT_PITCH, 'Tahoma');
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  FreeAndNil(FClearType);
  FreeAndNil(FAntialias);
end;

procedure TForm1.FormPaint(Sender: TObject);
var
  SavedFont: TFont;
  SavedBrushStyle: TBrushStyle;
begin
  SavedFont := TFont.Create;
  try
    SavedBrushStyle := Canvas.Brush.Style;
    SavedFont.Assign(Canvas.Font);
    try
      Canvas.Brush.Style := bsClear;

      Canvas.Font := FAntialias;
      MultiPaint(10,  0, 1);
      MultiPaint(10, 20, 2);
      MultiPaint(10, 40, 3);
      MultiPaint(10, 60, 4);

      Canvas.Font := FClearType;
      MultiPaint(10,100, 1);
      MultiPaint(10,120, 2);
      MultiPaint(10,140, 3);
      MultiPaint(10,160, 4);
    finally
      Canvas.Font.Assign(SavedFont);
      Canvas.Brush.Style := SavedBrushStyle;
    end;
  finally
    FreeAndNil(SavedFont);
  end;
end;

procedure TForm1.MultiPaint(const X, Y, N: Integer);
var
  Str: String;
  I: Integer;
begin
  for I := 0 to N - 1 do
    Canvas.TextOut(X, Y, 'Съешь еще этих мягких французских булок, да выпей чаю.');
end;&lt;/pre&gt;
Эта программа создаёт два шрифта: один вида anti-aliased, а другой - ClearType (&lt;a title="RE: Suggestion Box 3 by David Walker" href="http://blogs.msdn.com/b/oldnewthing/archive/2007/05/23/407234.aspx#535523"&gt;я понятия не имею, почему люди утверждают, что не существует безопасного способа включить ClearType в одном окне&lt;/a&gt;. Мы только что это сделали).&lt;br /&gt;
&lt;br /&gt;
Запустите эту программу и внимательно посмотрите на результаты. Заметьте, что каждый следующий ряд рисуется темнее предыдущего в обоих наборах. Чем больше мы делаем повторов вывода текста, тем темнее становится текст. В частности, заметьте, что повторный вывод текста с anti-aliased шрифтом делает результат всё уродливее и уродливее!&lt;br /&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;
&lt;img border="0" src="http://2.bp.blogspot.com/-tBQFKMYPC4Q/Tmj5gNN7lZI/AAAAAAAAC4E/WJCGGxH1LBM/s1600/Fonts.png" /&gt;&lt;/div&gt;
&lt;br /&gt;
Что же пошло не так?&lt;br /&gt;
&lt;br /&gt;
Когда мы рисуем текст первый раз, фон заполнен сплошным цветом окна. Но когда текст рисуется поверх повторно, то на фоновом изображении уже есть ранее написанный текст. Когда алгоритм решает что "Этот пиксель следует рисовать на 50% темнее", то по факту получается на 75% темнее, потому что пиксель затемняется дважды. А если вы рисуете три раза, то итоговое затемнение будет 88%.&lt;br /&gt;
&lt;br /&gt;
Когда вы рисуете текст ровно один раз, он рисуется поверх планируемого вами фонового изображения. Это позволяет движкам anti-aliasing сглаживания и ClearType выполнять их работу очень аккуратно.&lt;br /&gt;
&lt;br /&gt;
Те программы, которые рисуют чрезмерно тёмный текст, попались на эту проблему "печати поверх". Когда программа решает, что ей нужно обновить какое-то окно (к примеру, сместили фокус, так что фокусное выделение надо перерисовать - добавить или убрать), она "экономит время", не выполняя стирание фона, а просто рисует изображение поверх старого (текст, но с или без прямоугольника выделения). К сожалению, если вы не стираете фон, то текст будет выводиться поверх старого, приводя к затемнению.&lt;br /&gt;
&lt;br /&gt;
Решение заключается в рисовании текста на правильном фоне. Если вы не знаете, что за фон сейчас на экране - то вам нужно стереть его, чтобы быть уверенным в известном его состоянии. В противном случае вы будете миксировать пиксели текста с неизвестным содержимым, что может приводить к нежелательным (и уродливым) результатам.&lt;br /&gt;
&lt;br /&gt;
Если у вас орлиные глаза, то вы могли заметить и другой случай, когда люди делают эту же ошибку: когда текст в элементе управления (скажем, check box) становится темнее и темнее с каждым проходом через него по Tab. Это происходит из-за того, что люди не обращают внимание на флаги, передаваемые в &lt;a title="MSDN: DRAWITEMSTRUCT Structure" href="http://msdn.microsoft.com/en-us/library/bb775802(VS.85).aspx"&gt;записи &lt;code&gt;TDrawItemStruct&lt;/code&gt;&lt;/a&gt;, передаваемой с &lt;a href="http://msdn.microsoft.com/en-us/library/bb775923(VS.85).aspx" title="MSDN: WM_DRAWITEM Message"&gt;сообщением &lt;code&gt;WM_DRAWITEM&lt;/code&gt;&lt;/a&gt;. К примеру, некоторые люди просто рисуют элемент целиком, даже хотя оконный менеджер указывает флаг &lt;code&gt;ODA_FOCUS&lt;/code&gt;, указывающий на необходимость рисования или стирания только прямоугольника фокуса. Это не будет проблемой, если рисование элемента включает в себя стирание фона. Но если вместо этого вы не стираете фон, предполагая, что его стёрло &lt;a title="MSDN: WM_ERASEBKGND Message" href="http://msdn.microsoft.com/en-us/library/ms648055(VS.85).aspx"&gt;сообщение &lt;code&gt;WM_ERASEBKGND&lt;/code&gt;&lt;/a&gt;, то в итоге вы повторите проблему рисования текста многократно в случае, когда вас просят исправить только прямоугольник фокуса. В этом случае элемент управления не стирается; всё, что от вам требуется - перерисовать прямоугольник фокуса. Если вы при этом ещё и рисуете текст, то вы делаете то же, что и функция &lt;code&gt;MultiPaint&lt;/code&gt; из нашего примера выше: рисуете текст поверх текста, приводя к всё более тёмному тексту.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688132520265708820-6946397201573791084?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=-t-ADN-hZMA:EUATDGc_s78: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=-t-ADN-hZMA:EUATDGc_s78:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=-t-ADN-hZMA:EUATDGc_s78:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=-t-ADN-hZMA:EUATDGc_s78:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=-t-ADN-hZMA:EUATDGc_s78:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=-t-ADN-hZMA:EUATDGc_s78: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=-t-ADN-hZMA:EUATDGc_s78:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=-t-ADN-hZMA:EUATDGc_s78:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=-t-ADN-hZMA:EUATDGc_s78:-BTjWOF_DHI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=-t-ADN-hZMA:EUATDGc_s78:-BTjWOF_DHI" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/GunsmokersTranslations/~4/-t-ADN-hZMA" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.transl-gunsmoker.ru/feeds/6946397201573791084/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://www.transl-gunsmoker.ru/2011/08/pitfalls-of-transparent-rendering-of.html#comment-form" title="Комментарии: 0" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/6946397201573791084?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/6946397201573791084?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/GunsmokersTranslations/~3/-t-ADN-hZMA/pitfalls-of-transparent-rendering-of.html" title="Подводные камни вывода сглаженного текста с прозрачным фоном" /><author><name>Александр Алексеев</name><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/-tBQFKMYPC4Q/Tmj5gNN7lZI/AAAAAAAAC4E/WJCGGxH1LBM/s72-c/Fonts.png" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://www.transl-gunsmoker.ru/2011/08/pitfalls-of-transparent-rendering-of.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CkUMRns6eSp7ImA9WhdWFU0.&quot;"><id>tag:blogger.com,1999:blog-1688132520265708820.post-2275735095323024418</id><published>2011-08-16T02:00:00.000+04:00</published><updated>2011-09-08T20:44:47.511+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-09-08T20:44:47.511+04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Win32" /><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/13/629451.aspx" title="Fumbling around in the dark and stumbling across the wrong solution"&gt;Fumbling around in the dark and stumbling across the wrong solution&lt;/a&gt;. Автор: Реймонд Чен.&lt;br /&gt;
&lt;br /&gt;
Я не хотел придираться к &lt;a title="Victor Vogelpoel: Monitor Power Off" href="http://web.archive.org/web/20070408145818/http://dotnetjunkies.com/WebLog/victorv/archive/2004/09/13/25380.aspx"&gt;этой серии записей&lt;/a&gt;, но она иллюстрирует интересный шаблон спотыкания на неверном "решении".&lt;br /&gt;
&lt;br /&gt;
Эта серия пытается запустить системный триггер неактивности монитора отправкой сообщения окну рабочего стола (desktop window).&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
Как мы &lt;a title="Что такого особенного в окне рабочего стола?" href="http://www.transl-gunsmoker.ru/2009/01/blog-post_3072.html"&gt;видели ранее&lt;/a&gt;, окно рабочего стола является очень особенным окном, и его, как правило, нужно избегать, поскольку оно не ведёт себя так же, как обычные окна приложений. В частности, в коде поста автор пытается отправить (post) сообщение окну рабочего стола. Это работало раньше в &lt;a title="Но почему вообще возможно отключить рабочий стол?" href="http://www.transl-gunsmoker.ru/2010/05/blog-post_06.html"&gt;исторически открытом мире оконного менеджера&lt;/a&gt;, но требования безопасности и надёржности взяли приоритет над обратной совместимостью. В Windows XP SP2 окно рабочего стола не чувствительно к отключению, потому что программы продолжали неосознанно делать это, и оно блокирует отправленные ему сообщения. Я думаю, что это было сделано для противодействия &lt;a title="Википедия: Подрывная атака" href="http://ru.wikipedia.org/wiki/Shatter_attack"&gt;shatter атакам&lt;/a&gt;. Да, это улучшило надёжность и стабильность, но &lt;a title="Monitor Power Off II" href="http://web.archive.org/web/20060619174847/http://dotnetjunkies.com/WebLog/victorv/archive/2004/09/13/25386.aspx"&gt;также это сломало хак&lt;/a&gt; с &lt;a title="MSDN: PostMessage Function" href="http://msdn.microsoft.com/en-us/library/ms644944(VS.85).aspx"&gt;&lt;code&gt;PostMessage&lt;/code&gt;&lt;/a&gt;, на который рассчитывал код.&lt;br /&gt;
&lt;br /&gt;
Начинается &lt;a title="Monitor Power Off III: the solution" href="http://web.archive.org/web/20060628052249/http://dotnetjunkies.com/WebLog/victorv/archive/2004/09/13/25394.aspx"&gt;раунд 3&lt;/a&gt;, где автор ищет другие окна, которым можно было бы отправить сообщение, и в итоге он находит загадочное окно, которое, кажется, работает: &lt;code&gt;HWND_TOPMOST = -1&lt;/code&gt;.&lt;br /&gt;
&lt;br /&gt;
Я спинным мозгом чувствовал, что люди пишут программы именно таким способом, но оптимистическая часть меня надеялась, что это происходит в выдуманном мной мире, а не в реальном. Перебор параметров и их комбинаций (некоторые из которых очевидно являются недопустимыми), передача их в функцию, чтобы увидеть, что произойдёт - это попадает в категорию программирования методом тыка, а не программного проектирования и дизайна. Даже если таким образом вы нашли что-то работающее, будет безрассудно использовать это в своей программе!&lt;br /&gt;
&lt;br /&gt;
(Аналогично, я видел, как люди задают вопросы вроде "Что означает сообщение 49251?" Это обратный случай: люди увидели какое-то число и пытаются присвоить ему смысл. Номера сообщений, начинающиеся с $C000 (десятичное - 49152), являются сообщениями, регистрируемыми через &lt;a title="MSDN: RegisterWindowMessage Function" href="http://msdn.microsoft.com/en-us/library/ms644947(VS.85).aspx"&gt;&lt;code&gt;RegisterWindowMessage&lt;/code&gt;&lt;/a&gt;. Числовое значение сообщения, ассоциированного с зарегистрированным сообщением, меняется от запуска к запуску, его нельзя предсказать. Единственная гарантия - это постоянство в рамках одного рабочего стола).&lt;br /&gt;
&lt;br /&gt;
Если вы посмотрите более внимательно на итоговый код, вы увидите, что "решение", на самом деле, является ещё одним багом. Так получилось, что значение -1 оконного описателя подозрительно похоже на значение &lt;code&gt;HWND_BROADCAST&lt;/code&gt;:
&lt;pre class="brush:delphi"&gt;const
  HWND_BROADCAST = $FFFF;&lt;/pre&gt;
Внутренне оконный менеджер принимает -1 (что есть $FFFFFFFF) за &lt;code&gt;HWND_BROADCAST&lt;/code&gt; (объяснить, почему это так, - я оставляю вам в качестве упражнения; мы можете это сделать на основе наших предыдущих обсуждений). В результате, на самом деле, автор делает широковещательную рассылку сообщения всем окнам верхнего уровня! Как мы не раз видели ранее, &lt;a title="Помните, что происходит, когда вы делаете широковещательную рассылку" href="http://www.transl-gunsmoker.ru/2011/08/remember-what-happens-when-you.html"&gt;широковещательная рассылка - это довольно опасное занятие&lt;/a&gt;, но в этом случае автору повезло в том, что все окна интерпретируют это сообщение одинаково - способом, который допускает многократную обработку сообщения, и ни одно окно не выполняет никакой особой фильтрации для этого сообщения (&lt;a title="Turn on/off monitor" href="http://www.codeproject.com/KB/system/display_states.aspx"&gt;ещё один автор набрёл на это же неверное "решение"&lt;/a&gt;, но не предоставил никаких деталей о процессе, в результате которого он пришёл к этому решению. &lt;a href="http://www.codeproject.com/KB/cs/Monitor_management_guide.aspx" title="Complete Guide on How To Turn A Monitor On/Off/Standby"&gt;И ещё один автор обнаружил некоторые проблемы, но не сложил все кусочки вместе&lt;/a&gt;).&lt;br /&gt;
&lt;br /&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-2275735095323024418?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=-pm7bFkAC7Q:1jjNpbdpjcg: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=-pm7bFkAC7Q:1jjNpbdpjcg:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=-pm7bFkAC7Q:1jjNpbdpjcg:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=-pm7bFkAC7Q:1jjNpbdpjcg:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=-pm7bFkAC7Q:1jjNpbdpjcg:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=-pm7bFkAC7Q:1jjNpbdpjcg: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=-pm7bFkAC7Q:1jjNpbdpjcg:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=-pm7bFkAC7Q:1jjNpbdpjcg:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=-pm7bFkAC7Q:1jjNpbdpjcg:-BTjWOF_DHI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=-pm7bFkAC7Q:1jjNpbdpjcg:-BTjWOF_DHI" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/GunsmokersTranslations/~4/-pm7bFkAC7Q" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.transl-gunsmoker.ru/feeds/2275735095323024418/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://www.transl-gunsmoker.ru/2011/08/fumbling-around-in-dark-and-stumbling.html#comment-form" title="Комментарии: 0" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/2275735095323024418?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/2275735095323024418?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/GunsmokersTranslations/~3/-pm7bFkAC7Q/fumbling-around-in-dark-and-stumbling.html" title="Блуждая в темноте и спотыкаясь о неверное решение" /><author><name>Александр Алексеев</name><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/fumbling-around-in-dark-and-stumbling.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DU4HQ3Y7cCp7ImA9WhdWE04.&quot;"><id>tag:blogger.com,1999:blog-1688132520265708820.post-2581619160517987693</id><published>2011-08-15T02:00:00.000+04:00</published><updated>2011-09-06T23:38:52.808+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-09-06T23:38:52.808+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="не делай так" /><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/12/628193.aspx" title="Remember what happens when you broadcast a message"&gt;Remember what happens when you broadcast a message&lt;/a&gt;. Автор: Реймонд Чен.&lt;br /&gt;
&lt;br /&gt;
Иногда я вижу, как люди делают вещи вроде широковещательной рассылки (broadcasting) &lt;a title="MSDN: WM_COMMAND Message" href="http://msdn.microsoft.com/en-us/library/ms647591(VS.85).aspx"&gt;сообщения &lt;code&gt;WM_COMMAND&lt;/code&gt;&lt;/a&gt; всем окнам верхнего уровня (top-level windows). Это одна из вещей, которые настолько очевидно неправильные, что я не понимаю, как кому-то вообще может приходить в голову идея, что это будет работать.&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
Предположим, вы делаете такую рассылку:
&lt;pre class="brush:delphi"&gt;SendMessage(HWND_BROADCAST, WM_COMMAND, 100, 0);&lt;/pre&gt;
&lt;br /&gt;
Что произойдёт?&lt;br /&gt;
&lt;br /&gt;
Каждое окно верхнего уровня получает сообщение с одними и теми же параметрами, и каждое окно верхнего уровня начинает интерпретировать эти параметры на своё собственное усмотрение. Как вы знаете (поскольку вы же писали подобное), каждая оконная процедура определяет свои собственные элементы меню и дочерние окна, так что нет гарантий, что команда 100 будет делать одну и ту же вещь в каждом окне. Диалоговое окно с таким шаблоном:
&lt;pre&gt;#define IDC_USEDEFAULT 100
...
    AUTORADIOBUTTON "Use &amp;default color",
                    IDC_USEDEFAULT, 14, 38, 68, 10, WS_TABSTOP&lt;/pre&gt;
проинтерпретирует это сообщение как:
&lt;pre&gt;id 	= 	IDC_USEDEFAULT (100)
command 	= 	BN_CLICKED (0)
window 	= 	NULL (0) — недопустимый параметр&lt;/pre&gt;
В зависимости от того, как написана оконная процедура, она может попытаться отправить сообщение кнопке (ей это не удастся, поскольку вы передали 0 в качестве оконного описателя), или она может обновить состояние диалога вроде отключения элементов управления (поскольку ей сказали, что пользователь щёлкнул на кнопку "User default color").&lt;br /&gt;
&lt;br /&gt;
Какой-то другой диалог может иметь такой шаблон:
&lt;pre&gt;#define IDC_CHANGE 100
...
    PUSHBUTTON      "C&amp;hange", IDC_CHANGE, 88, 95, 50, 14&lt;/pre&gt;
Его диалоговая процедура проинтерпретирует сообщение как:
&lt;pre&gt;id 	= 	IDC_CHANGE (100)
command 	= 	BN_CLICKED (0)
window 	= 	NULL (0) — недопустимый параметр&lt;/pre&gt;
Реакцией, вероятно, будет применение текущих изменений в диалоге.&lt;br /&gt;
&lt;br /&gt;
А в это время ещё какое-то окно имеет меню, заданное так:
&lt;pre&gt;#define IDC_REFRESH 100
...
        MENUITEM "&amp;Refresh", IDC_REFRESH&lt;/pre&gt;
Оно проинтерпретирует сообщение как выбор пользователем элемента меню "Refresh".
&lt;pre&gt;id 	= 	IDC_REFRESH (100)
command 	= 	0 — недопустимый параметр, должно быть равно 1 для элементов меню
window 	= 	NULL (0)&lt;/pre&gt;
Это не только будет являться не корректной командой для меню, но к тому же окно может находится в состоянии, когда программа отключила пункт "Refresh". И тем не менее вы отправили ей сообщение, как если бы пользователь выбрал пункт меню, что невозможно. Поздравляю, вы только что поставили программу в невозможную ситуацию, и она вполне может вылететь в результате этих действий. К примеру, программа могла заблокировать пункт меню потому что у неё нет объектов для обновления. Когда вы отправите ей команду обновления, она попытается обновить несуществующий объект и вылетит с разыменованием nil-указателя.&lt;br /&gt;
&lt;br /&gt;
Очевидно, вы не можете делать широковещательную рассылку сообщения &lt;code&gt;WM_COMMAND&lt;/code&gt;, потому что не существует его универсальной интерпретации в программах. Сообщение, которое означает команду "Обновить" для одного окна, вполне может означать команду "Изменить" для другого окна.&lt;br /&gt;
&lt;br /&gt;
Та же логика применима почти ко всем стандартным сообщениям Windows. Вот сообщения, которые специально разработаны для широковещательной рассылки:
&lt;ul&gt;
&lt;li&gt;&lt;a title="MSDN: WM_SYSCOLORCHANGE Message" href="http://msdn.microsoft.com/en-us/library/dd145223(VS.85).aspx"&gt;WM_SYSCOLORCHANGE&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a title="MSDN: WM_SETTINGCHANGE Message" href="http://msdn.microsoft.com/en-us/library/ms725497(VS.85).aspx"&gt;WM_SETTINGCHANGE&lt;/a&gt; (= &lt;a title="MSDN: WM_WININICHANGE Message" href="http://msdn.microsoft.com/en-us/library/ms725499(VS.85).aspx"&gt;WM_WININICHANGE&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&lt;a title="MSDN: WM_DEVMODECHANGE Message" href="http://msdn.microsoft.com/en-us/library/dd145209(VS.85).aspx"&gt;WM_DEVMODECHANGE&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a title="MSDN: WM_FONTCHANGE Message" href="http://msdn.microsoft.com/en-us/library/dd145211(VS.85).aspx"&gt;WM_FONTCHANGE&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a title="MSDN: WM_TIMECHANGE Message" href="http://msdn.microsoft.com/en-us/library/ms725498(VS.85).aspx"&gt;WM_TIMECHANGE&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a title="MSDN: WM_DDE_INITIATE Message" href="http://msdn.microsoft.com/en-us/library/ms648996(VS.85).aspx"&gt;WM_DDE_INITIATE&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
Если вы попытаетесь сделать широковещательную рассылку сообщения в диапазоне от &lt;a href="http://msdn.microsoft.com/en-us/library/ms644931(VS.85).aspx" title="MSDN: WM_USER Message"&gt;&lt;code&gt;WM_USER&lt;/code&gt;&lt;/a&gt; до &lt;a href="http://msdn.microsoft.com/en-us/library/ms644930(VS.85).aspx" title="MSDN: WM_APP Message"&gt;&lt;code&gt;WM_APP&lt;/code&gt;&lt;/a&gt;, то вы ещё безумнее, чем я думал. Как мы уже видели, &lt;a title="Кому принадлежат разные типы сообщений?" href="http://www.transl-gunsmoker.ru/2008/12/blog-post_01.html"&gt;смысл оконных сообщений в этом диапазоне определяется оконным классом или приложением, которое создало окно&lt;/a&gt;. Здесь уже контекстно-чувствительны не только параметры сообщения, но и само сообщение! Это означает, что отправка произвольному окну сообщения (к примеру) &lt;code&gt;WM_USER + 1&lt;/code&gt; приведёт к полностью случайному поведению системы (мы видели это ранее &lt;a title="Отправка широковещательных сообщений, определяемых пользователем" href="http://www.transl-gunsmoker.ru/2009/03/blog-post_28.html"&gt;в контексте широковещательной рассылки&lt;/a&gt;, но это так же работает и для прямой доставки сообщения). Если это диалоговое окно, оно решит, что вы отправили ему &lt;a title="MSDN: DM_SETDEFID Message" href="http://msdn.microsoft.com/en-us/library/ms645413(VS.85).aspx"&gt;сообщение &lt;code&gt;DM_SETDEFID&lt;/code&gt;&lt;/a&gt;, и в итоге вы смените действие по умолчанию в диалоге. Если это общий диалог (common dialog), он решит, что это &lt;a href="http://msdn.microsoft.com/en-us/library/ms646880(VS.85).aspx" title="MSDN: WM_CHOOSEFONT_GETLOGFONT Message" title="MSDN: LOGFONT Structure"&gt;сообщение &lt;code&gt;WM_CHOOSEFONT_GETLOGFONT&lt;/code&gt;&lt;/a&gt;, и если вам повезёт, то он вылетит при попытке доступа к &lt;a href="http://msdn.microsoft.com/en-us/library/dd145037(VS.85).aspx"&gt;&lt;code&gt;TLogFont&lt;/code&gt;&lt;/a&gt; по недопустимому указателю (если вам НЕ повезёт, то параметры, которые вы передали с сообщением, позволят коду выполнится, приводя к работе кода программы над случайными данными, приводя к их порче и странному поведению программы). Если это элемент управления tooltip, то вы отправили ему &lt;a href="http://msdn.microsoft.com/en-us/library/bb760326(VS.85).aspx" title="MSDN: TTM_ACTIVATE Message"&gt;&lt;code&gt;TTM_ACTIVATE&lt;/code&gt;&lt;/a&gt; и, значит, что-то сделали с его состоянием активации.&lt;br /&gt;
&lt;br /&gt;
Эти же предупреждения с использованием той же логики применимы и к отправке сообщений без универсального смысла окнам, с чьим оконным классом у вас нет интерфейсного контракта. К примеру, я видел как программисты отправляют &lt;a href="http://msdn.microsoft.com/en-us/library/bb774597(VS.85).aspx" title="MSDN: PSM_PRESSBUTTON Message"&gt;сообщение &lt;code&gt;PSM_PRESSBUTTON&lt;/code&gt;&lt;/a&gt; окну, которое, как они полагают без всяких на то оснований, является вкладкой свойств (property sheet).&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-2581619160517987693?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=9AMXM1xBCQo:dURzPBABMkE: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=9AMXM1xBCQo:dURzPBABMkE:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=9AMXM1xBCQo:dURzPBABMkE:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=9AMXM1xBCQo:dURzPBABMkE:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=9AMXM1xBCQo:dURzPBABMkE:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=9AMXM1xBCQo:dURzPBABMkE: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=9AMXM1xBCQo:dURzPBABMkE:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=9AMXM1xBCQo:dURzPBABMkE:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=9AMXM1xBCQo:dURzPBABMkE:-BTjWOF_DHI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=9AMXM1xBCQo:dURzPBABMkE:-BTjWOF_DHI" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/GunsmokersTranslations/~4/9AMXM1xBCQo" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.transl-gunsmoker.ru/feeds/2581619160517987693/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://www.transl-gunsmoker.ru/2011/08/remember-what-happens-when-you.html#comment-form" title="Комментарии: 0" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/2581619160517987693?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/2581619160517987693?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/GunsmokersTranslations/~3/9AMXM1xBCQo/remember-what-happens-when-you.html" title="Помните, что происходит, когда вы делаете широковещательную рассылку" /><author><name>Александр Алексеев</name><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/remember-what-happens-when-you.html</feedburner:origLink></entry></feed>

