<?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;DUUHR347eSp7ImA9WhVbE0w.&quot;"><id>tag:blogger.com,1999:blog-1688132520265708820</id><updated>2012-05-29T22:47:16.001+04:00</updated><category term="не делай так" /><category term="прочее" /><category term="locale/cultures" /><category term="Vista" /><category term="ты можешь это сделать" /><category term="забавно" /><category term="клавиатуры" /><category term="Win16" /><category term="языки" /><category term="Совместимость" /><category term="fonts" /><category term="UI" /><category term="RTTI" /><category term="аудио" /><category term="collation/casing" /><category term="обработка ошибок" /><category term="серия" /><category term="История" /><category term="реестр" /><category term="справка" /><category term="дизайн/проектирование" /><category term="железо" /><category term="окна" /><category term="encoding/codepages" /><category term="Int'l Programming" /><category term="Win64" /><category term="LIP/MUI" /><category term="Potpourri" /><category term="дата/время" /><category term="Delphi" /><category term="Win32" /><category term="кодинг" /><category term="unicode" /><category term="linguistic" /><category term="хак" /><category term="MSLU" /><category term="безопасность" /><category term="фильмы" /><title>Блог GunSmoker-а</title><subtitle type="html">...when altering one's mind becomes as easy as programming a computer, what does it mean to be human?..</subtitle><link rel="http://schemas.google.com/g/2005#feed" type="application/atom+xml" href="http://www.transl-gunsmoker.ru/feeds/posts/default" /><link rel="alternate" type="text/html" href="http://www.transl-gunsmoker.ru/" /><link rel="next" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default?start-index=26&amp;max-results=25&amp;redirect=false&amp;v=2" /><author><name>Александр Алексеев</name><uri>https://profiles.google.com/113168002104297556003</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh3.googleusercontent.com/-aUMttZQBsuI/AAAAAAAAAAI/AAAAAAAAC3Y/QuZ7K9t_WzE/s512-c/photo.jpg" /></author><generator version="7.00" uri="http://www.blogger.com">Blogger</generator><openSearch:totalResults>804</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;Ak4AQ3Y_fyp7ImA9WhVQFE4.&quot;"><id>tag:blogger.com,1999:blog-1688132520265708820.post-3984829993570054445</id><published>2012-04-01T11:19:00.000+04:00</published><updated>2012-04-03T11:42:22.847+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-04-03T11:42:22.847+04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="кодинг" /><category scheme="http://www.blogger.com/atom/ns#" term="окна" /><title>Возможности окон Windows</title><content type="html">Это перевод &lt;a href="http://msdn.microsoft.com/en-us/library/ms632599(VS.85).aspx" title="Window Features"&gt;Window Features&lt;/a&gt;. Автор: MSDN.&lt;br /&gt;
&lt;br /&gt;
В этом обзоре рассматриваются особенности и возможности окон Windows - типы, состояния, размеры и положение.&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
Содержание:
&lt;ul&gt;
&lt;li&gt;&lt;a href="#types"&gt;Типы окон&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#overlapped"&gt;Overlapped окна&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#popup"&gt;Pop-up окна&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#child"&gt;Child окна&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#layered"&gt;Layered окна&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#message_only"&gt;Message-Only окна&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#relationships"&gt;Отношения между окнами&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#foreground"&gt;Окна первого и заднего плана&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#owned_windows"&gt;Owned окна&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#zorder"&gt;Z-порядок&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#show_states"&gt;Состояния отображения окон&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#active"&gt;Активное окно&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#disabled"&gt;Отключенные окна&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#visibility"&gt;Видимость окон&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#min_max"&gt;Свёрнутые, максимизированные и развёрнутые окна&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#size_position"&gt;Размеры окон и их положение&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#default"&gt;Размер и положение по умолчанию&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#tracking"&gt;Отслеживание размера&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#system_commands"&gt;Системные команды&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#functions"&gt;Функции управления размером и положением&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#messages"&gt;Сообщения размера и положения&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#animation"&gt;Анимация окон&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#layout_mirroring"&gt;Компоновка и отражение&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#mirroring_boxes"&gt;Отражение диалоговых окон и окон-сообщений&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#mirroring_dc"&gt;Отражение контекстов устройств, не ассоциированных с окном&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#destruction"&gt;Уничтожение окон&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;br /&gt;
&lt;h3&gt;&lt;a id="types"&gt;&lt;/a&gt;Типы окон&lt;/h3&gt;
Этот раздел содержит следующие подразделы, описывающие типы окон:
&lt;ul&gt;
&lt;li&gt;&lt;a href="#overlapped"&gt;Overlapped окна&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#popup"&gt;Pop-up окна&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#child"&gt;Child окна&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#layered"&gt;Layered окна&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#message_only"&gt;Message-Only окна&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;br /&gt;
&lt;h4&gt;&lt;a id="overlapped"&gt;&lt;/a&gt;Overlapped окна&lt;/h4&gt;
&lt;em&gt;Overlapped окно&lt;/em&gt; ("перекрывающее окно") - это окно верхнего уровня (top-level window), у которого есть заголовок (title bar), рамка (border) и клиентская область (client area); предполагается, что окно этого типа будет использоваться в качестве главного окна приложения. У него также могут быть системное меню, кнопки максимизации и минимизации и полосы прокрутки. Overlapped окно обычно включает в себя все эти компоненты, когда используется в качестве главного окна приложения.&lt;br /&gt;
&lt;br /&gt;
Приложение может создать overlapped окно, указывая стиль &lt;a href="http://msdn.microsoft.com/en-us/library/ms632600(VS.85).aspx" title="MSDN: Window Styles"&gt;&lt;code&gt;WS_OVERLAPPED&lt;/code&gt;&lt;/a&gt; или &lt;code&gt;WS_OVERLAPPEDWINDOW&lt;/code&gt; в &lt;a href="http://msdn.microsoft.com/en-us/library/ms632680(VS.85).aspx" title="MSDN: CreateWindowEx Function"&gt;функции &lt;code&gt;CreateWindowEx&lt;/code&gt;&lt;/a&gt;. Если вы использовали стиль &lt;code&gt;WS_OVERLAPPED&lt;/code&gt;, то окно будет также иметь заголовок и рамку. Если вы использовали стиль &lt;code&gt;WS_OVERLAPPEDWINDOW&lt;/code&gt;, то окно будет иметь заголовок, изменяющую размеры рамку, системное меню и кнопки минимизации и максимизации.&lt;br /&gt;
&lt;blockquote&gt;Примечания переводчика: флаг &lt;code&gt;WS_OVERLAPPED&lt;/code&gt; равен нулю, поэтому если вы не укажете иной стиль, то окно по умолчанию будет иметь стиль &lt;code&gt;WS_OVERLAPPED&lt;/code&gt;. Стиль &lt;code&gt;WS_OVERLAPPEDWINDOW&lt;/code&gt; - это не самостоятельный стиль, он является простой комбинацией стилей &lt;code&gt;WS_OVERLAPPED&lt;/code&gt;, &lt;code&gt;WS_CAPTION&lt;/code&gt;, &lt;code&gt;WS_SYSMENU&lt;/code&gt;, &lt;code&gt;WS_THICKFRAME&lt;/code&gt;, &lt;code&gt;WS_MINIMIZEBOX&lt;/code&gt; и &lt;code&gt;WS_MAXIMIZEBOX&lt;/code&gt;.&lt;br /&gt;
&lt;br /&gt;
Delphi создаёт overlapped-окна при &lt;a href="http://docwiki.embarcadero.com/VCL/en/Forms.TCustomForm.BorderStyle" title="Forms.TCustomForm.BorderStyle"&gt;&lt;code&gt;BorderStyle&lt;/code&gt;&lt;/a&gt; = &lt;code&gt;bsSingle&lt;/code&gt;, &lt;code&gt;bsToolWindow&lt;/code&gt;, &lt;code&gt;bsSizeable&lt;/code&gt; или &lt;code&gt;bsSizeToolWin&lt;/code&gt;. Значение &lt;code&gt;bsSizeable&lt;/code&gt; будет эквивалентно стилю &lt;code&gt;WS_OVERLAPPEDWINDOW&lt;/code&gt;, если вы не отключали кнопки маскимизации/минимизации.&lt;/blockquote&gt;
&lt;br /&gt;
&lt;h4&gt;&lt;a id="popup"&gt;&lt;/a&gt;Pop-up окна&lt;/h4&gt;
&lt;em&gt;Pop-up окно&lt;/em&gt; ("всплывающее окно") - это разновидность overlapped окна, используемая для диалоговых окон, окон-сообщений и других аналогичных временных окон, которые появляются вне главного окна приложения. Для них заголовок не обязателен; в противном случае pop-up окна не отличаются от overlapped окон со &lt;a href="http://msdn.microsoft.com/en-us/library/ms632600(VS.85).aspx" title="MSDN: Window Styles"&gt;стилем &lt;code&gt;WS_OVERLAPPED&lt;/code&gt;&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
Вы можете создать pop-up окно, указывая &lt;a href="http://msdn.microsoft.com/en-us/library/ms632600(VS.85).aspx" title="MSDN: Window Styles"&gt;&lt;code&gt;стиль WS_POPUP&lt;/code&gt;&lt;/a&gt; в &lt;a href="http://msdn.microsoft.com/en-us/library/ms632680(VS.85).aspx" title="MSDN: CreateWindowEx Function"&gt;&lt;code&gt;функции CreateWindowEx&lt;/code&gt;&lt;/a&gt;. Чтобы окно имело заголовок, вам нужно отдельно указать стиль &lt;code&gt;WS_CAPTION&lt;/code&gt;. Для создания pop-up окна с рамкой и системным меню используйте стиль &lt;code&gt;WS_POPUPWINDOW&lt;/code&gt;. Чтобы системное меню можно было увидеть, вам также нужно включить стиль &lt;code&gt;WS_CAPTION&lt;/code&gt; вместе со стилем &lt;code&gt;WS_POPUPWINDOW&lt;/code&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;blockquote&gt;Примечания переводчика: стиль &lt;code&gt;WS_POPUPWINDOW&lt;/code&gt; - это не самостоятельный стиль, он является простой комбинацией стилей &lt;code&gt;WS_POPUP&lt;/code&gt;, &lt;code&gt;WS_BORDER&lt;/code&gt; и &lt;code&gt;WS_SYSMENU&lt;/code&gt;.&lt;br /&gt;
&lt;br /&gt;
Delphi создаёт pop-up-окна при &lt;a href="http://docwiki.embarcadero.com/VCL/en/Forms.TCustomForm.BorderStyle" title="Forms.TCustomForm.BorderStyle"&gt;&lt;code&gt;BorderStyle&lt;/code&gt;&lt;/a&gt; = &lt;code&gt;bsNone&lt;/code&gt; или &lt;code&gt;bsDialog&lt;/code&gt;. Стиль &lt;code&gt;WS_POPUPWINDOW&lt;/code&gt; не используется Delphi, поскольку она никогда не указывает стиль &lt;code&gt;WS_BORDER&lt;/code&gt; при установленном стиле &lt;code&gt;WS_POPUP&lt;/code&gt; (вместо этого используется &lt;code&gt;WS_THICKFRAME&lt;/code&gt;).&lt;/blockquote&gt;
&lt;br /&gt;
&lt;h4&gt;&lt;a id="child"&gt;&lt;/a&gt;Child окна&lt;/h4&gt;
&lt;em&gt;Child окно&lt;/em&gt; ("дочернее окно") - имеет установленный стиль &lt;a href="http://msdn.microsoft.com/en-us/library/ms632600(VS.85).aspx" title="MSDN: Window Styles"&gt;&lt;code&gt;WS_CHILD&lt;/code&gt;&lt;/a&gt; и ограничивается клиентской областью его родительского окна (parent window). Как правило, child окна используются приложением, чтобы разделить клиентскую область окна по функциональным зонам. Вы можете создать child окно, указывая стиль &lt;code&gt;WS_CHILD&lt;/code&gt; в &lt;a href="http://msdn.microsoft.com/en-us/library/ms632680(VS.85).aspx" title="MSDN: CreateWindowEx Function"&gt;функции &lt;code&gt;CreateWindowEx&lt;/code&gt;&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
У child окна обязательно должно быть родительское окно. Родительское окно может быть overlapped окном, pop-up окном или даже другим child окном. Вы указываете его во время вызова &lt;a href="http://msdn.microsoft.com/en-us/library/ms632680(VS.85).aspx" title="MSDN: CreateWindowEx Function"&gt;функции &lt;code&gt;CreateWindowEx&lt;/code&gt;&lt;/a&gt; в параметре &lt;code&gt;hWndParent&lt;/code&gt;. Если же вы укажете стиль &lt;a href="http://msdn.microsoft.com/en-us/library/ms632600(VS.85).aspx" title="MSDN: Window Styles"&gt;&lt;code&gt;WS_CHILD&lt;/code&gt;&lt;/a&gt; в &lt;code&gt;CreateWindowEx&lt;/code&gt;, но не укажете родительское окно, то система откажется создавать окно.&lt;br /&gt;
&lt;br /&gt;
Child окно имеет клиентскую область, но больше никаких других дополнительных возможностей, если только вы их явно не запросите. Вы можете добавить заголовок, системное меню, кнопки минимизации и максимизации, рамку и полосы прокрутки, но child окно не может иметь меню. Если вы укажете меню при создании child окна - оно будет проигнорировано. Если вы не укажете тип рамки, то система создаст окно вообще без рамки (borderless window). Приложение может использовать такие окна для разделения клиентской области родительского окна на части без видимого указания разделения пользователю.&lt;br /&gt;
&lt;br /&gt;
&lt;blockquote&gt;Примечания переводчика: форма Delphi является pop-up или overlapped окном в 99% случаев. Вы можете положить форму на другую форму (отношения child-parent), но это используется крайне редко. Примером child окон могут служить фреймы (&lt;code&gt;TFrame&lt;/code&gt;), панели (&lt;code&gt;TPanel&lt;/code&gt;) и вообще практически любые другие оконные компоненты - например, кнопка (&lt;code&gt;TButton&lt;/code&gt;). Тут нужно помнить, что "окно" в смысле пользователя - это "форма" в Delphi. Но термин "окно" в смысле системы - это вовсе не обязательно "окно" в смысле пользователя, это вообще некоторый элемент управления. Все элементы управления в Windows являются окнами в смысле системы. Кнопки, списки, строки ввода, полосы прокрутки, панели и тулбары - всё это "окна".&lt;/blockquote&gt;
&lt;br /&gt;
Кроме того, в этом разделе:
&lt;ul&gt;
&lt;li&gt;&lt;a href="#positioning"&gt;Позиционирование&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#Clipping"&gt;Обрезание&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#relationship"&gt;Отношение с родительским окном&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#Messages"&gt;Сообщения&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;br /&gt;
&lt;h2&gt;&lt;a id="positioning"&gt;&lt;/a&gt;Позиционирование&lt;/h2&gt;
Система всегда размещает child окно, используя относительные координаты - относительно левого-верхнего угла клиентской области родительского окна. Child окно не может выступать за границы своего родительского окна. Если приложение создаёт child окно больше размеров своего родительского окна, либо же размещает его так, что какая-то его часть выходит за границы родителя, то система обрезает child окно так, что часть child окна вне его родительского окна будет просто невидима.&lt;br /&gt;
&lt;br /&gt;
Кроме того, действия с родительским окном также будут иметь эффект на все его child окна, как указано в следующей таблице:&lt;br /&gt;
&lt;br /&gt;
&lt;table border=1 cellpadding=4&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th align="center"&gt;Parent окно&lt;/th&gt;&lt;th align="center"&gt;Child окно&lt;/th&gt;&lt;/tr&gt;
&lt;tbody&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;Скрывается непосредственно перед скрытием своего родительского окна. Всякое child окно видимо только если его родительское окно тоже видимо.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Перемещается&lt;/td&gt;&lt;td&gt;Перемещается вместе с клиентской областью своего родителя так, что его относительное положение сохраняется. Child окно ответственно за перерисовку своей клиентской области после перемещения.&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;/tbody&gt;&lt;/table&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;h2&gt;&lt;a id="Clipping"&gt;&lt;/a&gt;Обрезание&lt;/h2&gt;
По умолчанию система не обрезает child окно, если оно размещено вне клиентской области своего родителя (если клиентская область родителя меньше размеров самого родительского окна, поскольку система всегда обрезает child окно, вылезающее за размеры своего родителя, как указано выше). Это означает, что родительское окно будет рисовать поверх child окна, если оно производит любую операцию рисования в той же области, что и child окно. Однако систему можно попросить обрезать child окно по клиентской области своего родителя, указав &lt;a href="http://msdn.microsoft.com/en-us/library/ms632600(VS.85).aspx" title="MSDN: Window Styles"&gt;стиль &lt;code&gt;WS_CLIPCHILDREN&lt;/code&gt;&lt;/a&gt; родительскому окну (не дочернему). Если child окно обрезается, то родительское окно не будет рисовать поверх его.&lt;br /&gt;
&lt;br /&gt;
Child окно может пересекать другие child окна в той же клиентской области. Child окна, которые разделяют одного родителя, называются &lt;em&gt;sibling окнами&lt;/em&gt; ("братские окна"). Пересекающиеся sibling окна могут рисовать в клиентской области друг друга, если только одно из child окон не имеет &lt;a href="http://msdn.microsoft.com/en-us/library/ms632600(VS.85).aspx" title="MSDN: Window Styles"&gt;стиль &lt;code&gt;WS_CLIPSIBLINGS&lt;/code&gt;&lt;/a&gt;. Если child окно указывает этот стиль, то любая часть любого другого sibling окна, лежащая в рамках этого child окна, будет обрезаться.&lt;br /&gt;
&lt;br /&gt;
Установка стилей &lt;a href="http://msdn.microsoft.com/en-us/library/ms632600(VS.85).aspx"&gt;&lt;code&gt;WS_CLIPCHILDREN&lt;/code&gt;&lt;/a&gt; или &lt;code&gt;WS_CLIPSIBLINGS&lt;/code&gt; приводит к небольшому падению производительности. Каждое окно занимает системные ресурсы, так что приложение не должно использовать дочерние окна без разбора. &lt;a title="В не оконных элементах управления нет ничего волшебного" href="http://www.transl-gunsmoker.ru/2010/02/blog-post_25.html"&gt;Для достижения оптимальной производительности приложения, которые должны логически разделять своё главное окно, должны делать это в оконной процедуре главного окна, а не с помощью дочерних окон&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;h2&gt;&lt;a id="relationship"&gt;&lt;/a&gt;Отношение с родительским окном&lt;/h2&gt;
Приложение может изменить родительское окно уже созданного child окна вызовом &lt;a href="http://msdn.microsoft.com/en-us/library/ms633541(VS.85).aspx" title="MSDN: SetParent Function"&gt;функции &lt;code&gt;SetParent&lt;/code&gt;&lt;/a&gt;. В этом случае система удаляет child окно из клиентской области старого родительского окна и размещает его в клиентской области нового родительского окна. Если в &lt;code&gt;SetParent&lt;/code&gt; указать &lt;code&gt;0&lt;/code&gt; в качестве родителя, то новым родительским окном станет рабочий стол (desktop window). В этом случае child окно рисуется на рабочем столе, вне границ любого другого окна. &lt;a href="http://msdn.microsoft.com/en-us/library/ms633510(VS.85).aspx" title="MSDN: GetParent Window"&gt;Функция &lt;code&gt;GetParent&lt;/code&gt;&lt;/a&gt; возвращает описатель родительского окна child окна.&lt;br /&gt;
&lt;br /&gt;
Родительское окно уступает часть своей клиентской области child окну, и child окно получает весь ввод (input) с этой области. Родительское и child окно не обязаны иметь один и тот же оконный класс (window class). Это означает, что приложение может заполнить родительское окна child окнами, которые выглядят по-разному и выполняют разные задачи. Например, диалоговое окно может содержать несколько типов элементов управления, каждый из которых является child окном, которое принимает различные типы данных от пользователя.&lt;br /&gt;
&lt;br /&gt;
Child окно имеет одно и только одно родительское окно, но родительское окно может иметь сколько угодно child окон. Каждое child окно, в свою очередь, тоже может иметь сколько угодно child окон. В такой цепочке окон каждое child окно называется descendant окном (окном-потомком) исходного родительского окна. Приложение может использовать &lt;a href="http://msdn.microsoft.com/en-us/library/ms633524(VS.85).aspx" title="MSDN: IsChild Function"&gt;функцию &lt;code&gt;IsChild&lt;/code&gt;&lt;/a&gt;, чтобы определить, является ли заданное окно child окном или descendant окном другого окна.&lt;br /&gt;
&lt;br /&gt;
&lt;a href="http://msdn.microsoft.com/en-us/library/ms633494(VS.85).aspx" title="MSDN: EnumChildWindows Function"&gt;Функция &lt;code&gt;EnumChildWindows&lt;/code&gt;&lt;/a&gt; перечисляет все дочерние окна родительского окна. Функция &lt;code&gt;EnumChildWindows&lt;/code&gt; передаёт описатель каждого найденного child окна в функцию обратного вызова приложения. Функция работает рекурсивно, поэтому она также перечисляет все descendant окна заданного родительского окна.&lt;br /&gt;
&lt;br /&gt;
&lt;h2&gt;&lt;a id="Messages"&gt;&lt;/a&gt;Сообщения&lt;/h2&gt;
Система передаёт все сообщения ввода для child окна самому child окну напрямую, минуя окно родителя. Исключением для этого правила является случай, если дочернее окно было отключено вызовом &lt;a href="http://msdn.microsoft.com/en-us/library/ms646291(VS.85).aspx" title="MSDN: EnableWindow Function"&gt;функции &lt;code&gt;EnableWindow&lt;/code&gt;&lt;/a&gt;. В этом случае система передаёт сообщения, предназначенные child окну, его родителю. Это позволяет родительскому окну проверить сообщения ввода и при необходимости включить child окно.&lt;br /&gt;
&lt;br /&gt;
Child окно также может иметь уникальный числовой идентификатор. Идентификаторы child окон важны, если вы работаете с сообщениями. Приложение управляет элементами управления, отправляя им сообщения. Приложение может использовать идентификатор child окна для отправки в него сообщений. Кроме того, если элемент управления шлёт сообщения-уведомления своему родителю, то эти сообщения будут включать в себя идентификатор child-окна, что позволит родителю идентифицировать отправителя сообщения. Приложение указывает идентификатор child окна установкой параметра &lt;code&gt;hMenu&lt;/code&gt; &lt;a href="http://msdn.microsoft.com/en-us/library/ms632680(VS.85).aspx" title="MSDN: CreateWindowEx Function"&gt;функции &lt;code&gt;CreateWindowEx&lt;/code&gt;&lt;/a&gt; в число - значение идентификатора (&lt;a href="http://www.transl-gunsmoker.ru/2012/03/windows.html#menu_handle_child_id" title="Об окнах Windows"&gt;а не описатель меню&lt;/a&gt;).&lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;&lt;a id="layered"&gt;&lt;/a&gt;Layered окна&lt;/h4&gt;
Использование &lt;em&gt;layered окна&lt;/em&gt; ("окна со слоями") может здорово улучшить производительность и визуальные эффекты для окон, которые имеют сложную форму, анимацию или применяют альфа-каналы. Система автоматически производит композицию и перерисовку layered окон и окон под ним. В результате layered окна рисуются гладко, без эффектов мерцания на сложных регионах. Кроме того, layered окна могут быть сделаны частично прозрачными.&lt;br /&gt;
&lt;br /&gt;
Чтобы создать layered окно, вам нужно указать флаг &lt;code&gt;WS_EX_LAYERED&lt;/code&gt; расширенного стиля окна при вызове &lt;a href="http://msdn.microsoft.com/en-us/library/ms632680(VS.85).aspx" title="MSDN: CreateWindowEx Function"&gt;функции &lt;code&gt;CreateWindowEx&lt;/code&gt;&lt;/a&gt; или вызвать &lt;a href="http://msdn.microsoft.com/en-us/library/ms633591(VS.85).aspx" title="MSDN: SetWindowLong Function"&gt;функцию &lt;code&gt;SetWindowLong&lt;/code&gt;&lt;/a&gt; уже после создания окна. После вызова &lt;code&gt;CreateWindowEx&lt;/code&gt; layered окно не будет видимо до тех пор, пока вы не вызовите для него &lt;a href="http://msdn.microsoft.com/en-us/library/ms633540(VS.85).aspx" title="MSDN: SetLayeredWindowAttributes Function"&gt;функцию &lt;code&gt;SetLayeredWindowAttributes&lt;/code&gt;&lt;/a&gt; или &lt;a href="http://msdn.microsoft.com/en-us/library/ms633556(VS.85).aspx" title="MSDN: UpdateLayeredWindow Function"&gt;функцию &lt;code&gt;UpdateLayeredWindow&lt;/code&gt;&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;strong&gt;Примечание&lt;/strong&gt;: начиная с Windows 8 &lt;code&gt;WS_EX_LAYERED&lt;/code&gt; может использоваться как для окон верхнего уровня, так и для дочерних окон (child окон). Предыдущие версии Windows поддерживают стиль &lt;code&gt;WS_EX_LAYERED&lt;/code&gt; только для окон верхнего уровня.&lt;br /&gt;
&lt;br /&gt;
Чтобы установить степень непрозрачности (opacity level) или цвет-маску (transparency color key) для заданного layered окна - вызовите &lt;a href="http://msdn.microsoft.com/en-us/library/ms633540(VS.85).aspx" title="MSDN: SetLayeredWindowAttributes Function"&gt;функцию &lt;code&gt;SetLayeredWindowAttributes&lt;/code&gt;&lt;/a&gt;. После этого вызова система всё ещё может запросить окно нарисовать себя при показе или перемещении. Однако, поскольку система запоминает растровое содержимое layered окна, то она не будет просить окно перерисовать себя при частичном перекрытии окна другими окнами или при его перемещении по рабочему столу. При этом устаревшим приложениям не нужно переделывать их код рисования, если они хотят добавить прозрачность или эффекты - потому что система перенаправляет рисование на окнах, которые вызвали &lt;code&gt;SetLayeredWindowAttributes&lt;/code&gt;, на внеэкранный растр.&lt;br /&gt;
&lt;br /&gt;
Чтобы реализовать более эффективную анимацию или если вам нужно попиксельное альфа-смешение цветов - вы можете вызвать &lt;a href="http://msdn.microsoft.com/en-us/library/ms633556(VS.85).aspx" title="MSDN: UpdateLayeredWindow Function"&gt;&lt;code&gt;UpdateLayeredWindow&lt;/code&gt;&lt;/a&gt;. &lt;code&gt;UpdateLayeredWindow&lt;/code&gt; следует использовать, когда приложение хочет работать напрямую с формой и содержимым layered окна, минуя внеэкранный буфер, который система предоставляет через вызов &lt;a href="http://msdn.microsoft.com/en-us/library/ms633540(VS.85).aspx" title="MSDN: SetLayeredWindowAttributes Function"&gt;&lt;code&gt;SetLayeredWindowAttributes&lt;/code&gt;&lt;/a&gt;. Кроме того, использование &lt;code&gt;UpdateLayeredWindow&lt;/code&gt; более эффективно, потому что системе не нужно выделять дополнительную память для хранения изображения перенаправленного окна. Обратите внимание, что если вы вызвали &lt;code&gt;SetLayeredWindowAttributes&lt;/code&gt;, то все последующие вызовы &lt;code&gt;UpdateLayeredWindow&lt;/code&gt; завершатся с ошибкой - до тех пор, пока вы не переустановите (сбросите и заново установите) флаг &lt;code&gt;WS_EX_LAYERED&lt;/code&gt;.&lt;br /&gt;
&lt;br /&gt;
Тестирование на попадание мышью (hit testing) для layered окна основывается на форме и прозрачности окна. Это означает, что зоны окна, которые раскрашены цветовым ключом или же их альфа-канал равен нулю, пропустят щелчки мыши мимо себя. Однако если в окне дополнительно установлен флаг &lt;code&gt;WS_EX_TRANSPARENT&lt;/code&gt;, то всё окно целиком будет пропускать щелчки мыши сквозь себя.&lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;&lt;a id="message_only"&gt;&lt;/a&gt;Message-Only окна&lt;/h4&gt;
&lt;em&gt;Message-only окно&lt;/em&gt; ("окно для сообщений") позволяет вам принимать и отправлять оконные сообщения. Оно не видимо, не имеет Z-порядка, не появляется в перечислениях (enum) и не принимает широковещательные сообщения. Это окно просто диспетчеризирует сообщения.&lt;br /&gt;
&lt;br /&gt;
Чтобы создать message-only окно, укажите константу &lt;a href="#message_only"&gt;&lt;code&gt;HWND_MESSAGE&lt;/code&gt;&lt;/a&gt; или описатель на другое message-only окно в параметре &lt;code&gt;hWndParent&lt;/code&gt; &lt;a href="http://msdn.microsoft.com/en-us/library/ms632680(VS.85).aspx" title="MSDN: CreateWindowEx Function"&gt;функции &lt;code&gt;CreateWindowEx&lt;/code&gt;&lt;/a&gt;. Вы также можете сконвертировать любое существующее окно в message-only окно, указав &lt;code&gt;HWND_MESSAGE&lt;/code&gt; в качестве параметра &lt;code&gt;hWndNewParent&lt;/code&gt; &lt;a href="http://msdn.microsoft.com/en-us/library/ms633541(VS.85).aspx" title="MSDN: SetParent Function"&gt;функции &lt;code&gt;SetParent&lt;/code&gt;&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
Чтобы найти message-only окна - укажите &lt;a href="#message_only"&gt;&lt;code&gt;HWND_MESSAGE&lt;/code&gt;&lt;/a&gt; в параметре &lt;code&gt;hwndParent&lt;/code&gt; &lt;a href="http://msdn.microsoft.com/en-us/library/ms633500(VS.85).aspx" title="MSDN: FindWindowEx Function"&gt;функции &lt;code&gt;FindWindowEx&lt;/code&gt;&lt;/a&gt;. Кроме того, функция &lt;code&gt;FindWindowEx&lt;/code&gt; будет искать message-only окна наравне с окнами верхнего уровня, если оба параметра &lt;code&gt;hwndParent&lt;/code&gt; и &lt;code&gt;hwndChildAfter&lt;/code&gt; будут равны нулю.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;&lt;a id="relationships"&gt;&lt;/a&gt;Отношения между окнами&lt;/h3&gt;
Существует много способов, которыми одно окно может относится к пользователю или другому окну. Окно может быть владеемым, окном переднего или заднего плана, а также у него может быть задан Z-порядок по отношению к другим окнам:
&lt;ul&gt;
&lt;li&gt;&lt;a href="#foreground"&gt;Окна первого и заднего плана&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#owned_windows"&gt;Owned окна&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#zorder"&gt;Z-порядок&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;br /&gt;
&lt;h4&gt;&lt;a id="foreground"&gt;&lt;/a&gt;Окна первого и заднего плана&lt;/h4&gt;
Каждый процесс может иметь несколько потоков для выполнения, и каждый поток может создавать окна. Поток, который создал окно, с которым сейчас работает пользователь, называется потоком первого плана (foreground thread), а окно называется &lt;code&gt;foreground окном&lt;/code&gt; (окном первого плана). Все прочие потоки называются потоками заднего плана (background thread), а все прочие окна - окнами заднего плана (&lt;em&gt;background окнами&lt;/em&gt;).&lt;br /&gt;
&lt;br /&gt;
Каждый поток имеет уровень приоритета, который определяет количество времени процессора, получаемое этим потоком. Хотя приложение может менять уровень приоритета своих потоков, обычно поток первого плана получает небольшую прибавку приоритета по отношению к потокам заднего плана. Поэтому поток первого плана будет получать больше процессорного времени, чем потоки заднего плана. Обычно поток первого плана имеет приоритет 9, в то время как обычное значение приоритета потока заднего плана - 7.&lt;br /&gt;
&lt;br /&gt;
Пользователь задаёт окно переднего плана, щёлкая по окну, либо используя комбинации клавиш ALT+TAB или ALT+ESC. Чтобы получить описатель окна переднего плана - используйте &lt;a href="http://msdn.microsoft.com/en-us/library/ms633505(VS.85).aspx" title="MSDN: GetForegroundWindow Function"&gt;функцию &lt;code&gt;GetForegroundWindow&lt;/code&gt;&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
Приложение может установить окно переднего плана, используя функцию &lt;a href="http://msdn.microsoft.com/en-us/library/ms633539(VS.85).aspx" title="MSDN: SetForegroundWindow Function"&gt;&lt;code&gt;SetForegroundWindow&lt;/code&gt;&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
Система ограничивает процессы, которым разрешено менять окно переднего плана. Процесс сможет сделать это только если верны условия ниже:
&lt;ul&gt;
&lt;li&gt;процесс является процессом первого плана (т.е. окно первого плана принадлежит ему).&lt;/li&gt;
&lt;li&gt;процесс был запущен процессом переднего плана.&lt;/li&gt;
&lt;li&gt;процесс получил последнее сообщение ввода.&lt;/li&gt;
&lt;li&gt;в системе нет процесса переднего плана.&lt;/li&gt;
&lt;li&gt;процесс первого плана сейчас отлаживается.&lt;/li&gt;
&lt;li&gt;смена окна первого плана не заблокирована (см. &lt;a href="http://msdn.microsoft.com/en-us/library/ms633532(VS.85).aspx" title="MSDN: LockSetForegroundWindow Function"&gt;&lt;code&gt;LockSetForegroundWindow&lt;/code&gt;&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;истёк таймаут блокировки окна (см. &lt;code&gt;SPI_GETFOREGROUNDLOCKTIMEOUT&lt;/code&gt; в &lt;a href="http://msdn.microsoft.com/en-us/library/ms724947(VS.85).aspx" title="MSDN: SystemParametersInfo Function"&gt;&lt;code&gt;SystemParametersInfo&lt;/code&gt;&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;нет активных меню.&lt;/li&gt;
&lt;/ul&gt;
Процесс, которому разрешено менять окно первого плана, может передать это право другому процессу вызовом &lt;a href="http://msdn.microsoft.com/en-us/library/ms632668(VS.85).aspx" title="MSDN: AllowSetForegroundWindow Function"&gt;функции &lt;code&gt;AllowSetForegroundWindow&lt;/code&gt;&lt;/a&gt; или &lt;a href="http://msdn.microsoft.com/en-us/library/ms644932(VS.85).aspx" title="MSDN: BroadcastSystemMessage Function"&gt;функции &lt;code&gt;BroadcastSystemMessage&lt;/code&gt;&lt;/a&gt; с флагом &lt;code&gt;BSF_ALLOWSFW&lt;/code&gt;. Процесс первого плана также может отключить вызовы &lt;a href="http://msdn.microsoft.com/en-us/library/ms633539(VS.85).aspx" title="MSDN: SetForegroundWindow Function"&gt;&lt;code&gt;SetForegroundWindow&lt;/code&gt;&lt;/a&gt;, вызвав &lt;a href="http://msdn.microsoft.com/en-us/library/ms633532(VS.85).aspx" title="MSDN: LockSetForegroundWindow Function"&gt;&lt;code&gt;LockSetForegroundWindow&lt;/code&gt;&lt;/a&gt; function.&lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;&lt;a id="owned_windows"&gt;&lt;/a&gt;Owned окна&lt;/h4&gt;
Overlapped или pop-up окно может владеть другим overlapped или pop-up окном. Отношение "владелец - owned окно" накладывают ограничения на последнее:
&lt;ul&gt;
&lt;li&gt;Owned окно всегда находится поверх своего владельца в Z-порядке.&lt;/li&gt;
&lt;li&gt;Система автоматически уничтожает owned окна при уничтожении их владельца.&lt;/li&gt;
&lt;li&gt;Owned окно скрывается при минимизации своего владельца.&lt;/li&gt;
&lt;/ul&gt;
Владельцем может быть только overlapped или pop-up окно; child окна владельцами быть не могут. Приложение может создать owned окно указанием описателя окна-владельца в параметре &lt;code&gt;hwndParent&lt;/code&gt; &lt;a href="http://msdn.microsoft.com/en-us/library/ms632680(VS.85).aspx" title="MSDN: CreateWindowEx Function"&gt;функции &lt;code&gt;CreateWindowEx&lt;/code&gt;&lt;/a&gt;, когда она создаёт окно со стилями &lt;a href="http://msdn.microsoft.com/en-us/library/ms632600(VS.85).aspx" title="MSDN: Window Styles"&gt;&lt;code&gt;WS_OVERLAPPED&lt;/code&gt;&lt;/a&gt; или &lt;code&gt;WS_POPUP&lt;/code&gt;. Параметр &lt;code&gt;hwndParent&lt;/code&gt; должен идентифицировать overlapped или pop-up окно. После создания owned окна приложение не может передать отношение владения другому окну.&lt;br /&gt;
&lt;br /&gt;
Диалоговые окна и окна-сообщения (message box) являются owned окнами. Приложение указывает окно-владельца при вызове функции, создающей диалог или окно-сообщение.&lt;br /&gt;
&lt;br /&gt;
Приложение может использовать &lt;a href="http://msdn.microsoft.com/en-us/library/ms633515(VS.85).aspx" id="MSDN: GetWindow Function"&gt;функцию &lt;code&gt;GetWindow&lt;/code&gt;&lt;/a&gt; с флагом &lt;code&gt;GW_OWNER&lt;/code&gt;, чтобы получить описатель окна-владельца.&lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;&lt;a id="zorder"&gt;&lt;/a&gt;Z-порядок&lt;/h4&gt;
&lt;em&gt;Z-order&lt;/em&gt; (Z-порядок) окна указывает положение окна в стопке overlapped окон. Эта стопка окон ориентируется вдоль воображаемой оси (оси Z), идущей от экрана. Окно на вершине Z оси (Z-порядка) перекрывает все прочие окна. Окно на дне Z-порядка может быть перекрыто любым другим окном.&lt;br /&gt;
&lt;br /&gt;
Система хранит Z-порядок в едином списке. Она добавляет окна в список в зависимости от их типа: topmost окна, top-level окна и child окна. &lt;em&gt;Окно "поверх всех"&lt;/em&gt; (topmost окно) перекрывает все прочие окна (не topmost окна), вне зависимости от того, является ли topmost окно активным. У topmost окон устанавливается стиль &lt;code&gt;WS_EX_TOPMOST&lt;/code&gt;. Все topmost окна всегда располагаются в списке Z-порядка перед любыми другими не topmost окнами. Child окна группируются вместе с их родителями.&lt;br /&gt;
&lt;br /&gt;
Когда приложение создаёт окно, система помещает окно в список Z-order - на вершину цепочки окон того же типа. Вы можете использовать &lt;a href="http://msdn.microsoft.com/en-us/library/ms632673(VS.85).aspx" title="MSDN: BringWindowToTop Function"&gt;функцию &lt;code&gt;BringWindowToTop&lt;/code&gt;&lt;/a&gt;, чтобы переместить окно на вершину списка Z-order (но только в рамках окон того же типа). Вы можете переупорядочивать окна, используя функции &lt;a href="http://msdn.microsoft.com/en-us/library/ms633545(VS.85).aspx" title="MSDN: SetWindowPos Function"&gt;&lt;code&gt;SetWindowPos&lt;/code&gt;&lt;/a&gt; или &lt;a href="http://msdn.microsoft.com/en-us/library/ms632681(VS.85).aspx" title="MSDN: DeferWindowPos Function"&gt;&lt;code&gt;DeferWindowPos&lt;/code&gt;&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
Пользователь может изменить Z-порядок активируя различные окна. Система перемещает активное окно на вершину Z-порядка для окон того же типа. Child окна перемещаются на вершину вместе со своим родителем. Вы можете использовать &lt;a href="http://msdn.microsoft.com/en-us/library/ms633514(VS.85).aspx" title="MSDN: GetTopWindow Function"&gt;функцию &lt;code&gt;GetTopWindow&lt;/code&gt;&lt;/a&gt; для поиска всех child окон родительского окна и получения описателя child окна на вершине Z-порядка. &lt;a href="http://msdn.microsoft.com/en-us/library/ms633509(VS.85).aspx" title="MSDN: GetNextWindow Function"&gt;Функция &lt;code&gt;GetNextWindow&lt;/code&gt;&lt;/a&gt; возвращает описатель следующего или предыдущего окна в Z-порядке.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;&lt;a id="show_states"&gt;&lt;/a&gt;Состояния отображения окон&lt;/h3&gt;
В любой момент времени произвольное окно может быть активным или не активным; скрытым или видимым; и либо минимизированным, либо максимизированным, либо развёрнутым. Все эти характеристики называются &lt;em&gt;window show state&lt;/em&gt; (состояния отображения окон). Они обсуждаются ниже:&lt;br /&gt;
&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#active"&gt;Активное окно&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#disabled"&gt;Отключенные окна&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#visibility"&gt;Видимость окон&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#min_max"&gt;Минимизированные, максимизированные и развёрнутые окна&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
&lt;a id="active"&gt;&lt;/a&gt;Активное окно&lt;/h4&gt;
&lt;em&gt;Активное окно&lt;/em&gt; (active window) - это окно верхнего уровня (top-level) приложения, с которым сейчас работает пользователь. Чтобы пользователь мог опознать активное окно, система размещает активное окно на вершине Z-порядка, а также изменяет вид заголовка (выделяя его) и рамки окна. Активным окном может быть только окно верхнего уровня. Если пользователь работает с child окном, то система активирует окно верхнего уровня, ассоциированное с текущим child окном.&lt;br /&gt;
&lt;br /&gt;
В любой момент времени в системе может быть только одно активное окно. Пользователь может активировать окно (верхнего уровня) просто щёлкая по нему (или по одному из его child окон), либо используя комбинации клавиш для переключения окон (например, ALT+ESC или ALT+TAB). Приложение может сделать окно  (верхнего уровня) активным, вызывая &lt;a href="http://msdn.microsoft.com/en-us/library/ms646311(VS.85).aspx" title="MSDN: SetActiveWindow Function"&gt;функцию &lt;code&gt;SetActiveWindow&lt;/code&gt;&lt;/a&gt;. Активация окна также может происходить при использовании других функций, включая &lt;a href="http://msdn.microsoft.com/en-us/library/ms633545(VS.85).aspx" title="MSDN: SetWindowPos Function"&gt;&lt;code&gt;SetWindowPos&lt;/code&gt;&lt;/a&gt;, &lt;a href="http://msdn.microsoft.com/en-us/library/ms632681(VS.85).aspx" title="MSDN: DeferWindowPos Function"&gt;&lt;code&gt;DeferWindowPos&lt;/code&gt;&lt;/a&gt;, &lt;a href="http://msdn.microsoft.com/en-us/library/ms633544(VS.85).aspx" title="MSDN: SetWindowPlacement Function"&gt;&lt;code&gt;SetWindowPlacement&lt;/code&gt;&lt;/a&gt; и &lt;a href="http://msdn.microsoft.com/en-us/library/ms632682(VS.85).aspx" title="MSDN: DestroyWindow Function"&gt;&lt;code&gt;DestroyWindow&lt;/code&gt;&lt;/a&gt;. Хотя приложение может менять активное окно в любое время, но чтобы не смущать пользователя, лучше всего это делать в ответ на действия пользователя. Приложение может узнать текущее активное окно вызовом &lt;a href="http://msdn.microsoft.com/en-us/library/ms646292(VS.85).aspx" title="MSDN: GetActiveWindow Function"&gt;функции &lt;code&gt;GetActiveWindow&lt;/code&gt;&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
Когда активное окно изменяется с окна одного приложения на окно другого приложения, система посылает &lt;a href="http://msdn.microsoft.com/en-us/library/ms632614(VS.85).aspx" title="MSDN: WM_ACTIVATEAPP Function"&gt;сообщение &lt;code&gt;WM_ACTIVATEAPP&lt;/code&gt;&lt;/a&gt; обоим приложениям, уведомляя их об изменении активного окна. Если же активное окно изменяется с одного окна приложения на другое окно этого же приложения, то система посылает &lt;a href="http://msdn.microsoft.com/en-us/library/ms646274(VS.85).aspx" title="MSDN: WM_ACTIVATE Function"&gt;сообщение &lt;code&gt;WM_ACTIVATE&lt;/code&gt;&lt;/a&gt; обоим окнам этого приложения.&lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;&lt;a id="disabled"&gt;&lt;/a&gt;Отключенные окна&lt;/h4&gt;
Окно может быть отключено. &lt;em&gt;Отключенное окно&lt;/em&gt; (disabled window) не может получать ввод пользователя (сообщения клавиатуры и мыши), но может получать сообщения от других окон, приложений и системы. Обычно приложение отключает окно для предотвращения взаимодействия с ним пользователя. К примеру, приложение может отключить кнопку в диалоге, чтобы не дать пользователю на неё нажать. Приложение может включить (enable) отключенное окно в любое время; включение окна восстановит возможность ввода.&lt;br /&gt;
&lt;br /&gt;
По умолчанию окно создаётся в доступном состоянии. Но приложение может указать &lt;a href="http://msdn.microsoft.com/en-us/library/ms632600(VS.85).aspx" title="MSDN: Window Styles"&gt;стиль &lt;code&gt;WS_DISABLED&lt;/code&gt;&lt;/a&gt; для создания окна в отключенном состоянии. Приложение может включить или отключить окно вызовом &lt;a href="http://msdn.microsoft.com/en-us/library/ms646291(VS.85).aspx" title="MSDN: EnableWindow Function"&gt;функции &lt;code&gt;EnableWindow&lt;/code&gt;&lt;/a&gt;. Непосредственно перед изменением состояния окна система отправляет ему &lt;a href="http://msdn.microsoft.com/en-us/library/ms632621(VS.85).aspx" title="MSDN: WM_ENABLE Message"&gt;сообщение &lt;code&gt;WM_ENABLE&lt;/code&gt;&lt;/a&gt;. Приложение может узнать статус окна вызовом &lt;a href="http://msdn.microsoft.com/en-us/library/ms646303(VS.85).aspx" title="MSDN: IsWindowEnabled Function"&gt;функции &lt;code&gt;IsWindowEnabled&lt;/code&gt;&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
Когда child окно отключается, система передаёт сообщения мыши от child окна его родителю. Родительское окно может использовать эти сообщения для определения того, не нужно ли включить child окно. Подробнее - см. &lt;a href="http://msdn.microsoft.com/en-us/library/ms645533(VS.85).aspx" title="MSDN: Mouse Input"&gt;Mouse Input&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
В каждый момент времени только одно окно в системе может принимать клавиатурный ввод; говорят, что такое &lt;em&gt;окно имеет фокус&lt;/em&gt; (keyboard focus). Если приложение использует &lt;a href="http://msdn.microsoft.com/en-us/library/ms646291(VS.85).aspx" title="MSDN: EnableWindow Function"&gt;функцию &lt;code&gt;EnableWindow&lt;/code&gt;&lt;/a&gt; для отключения окна, которое имеет фокус ввода, то, кроме собственно отключения, окно также потеряет фокус ввода. Функция &lt;code&gt;EnableWindow&lt;/code&gt; установит клавиатурный фокус в 0, что означает, что в системе нет окна с фокусом ввода. Аналогично, если фокус имеет child окно или descendant окно, а родитель отключается, то descendant окно теряет фокус ввода. Подробнее - см. &lt;a href="http://msdn.microsoft.com/en-us/library/ms645530(VS.85).aspx" title="MSDN: Keyboard Input"&gt;Keyboard Input&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;&lt;a id="visibility"&gt;&lt;/a&gt;Видимость окон&lt;/h4&gt;
Окно может быть либо видимым, либо невидимым (скрытым). Система показывает на экране только &lt;em&gt;видимые окна&lt;/em&gt; (visible window). Все &lt;em&gt;скрытые окна&lt;/em&gt; (hidden window) не отображаются (не рисуются). Если окно видимо, то пользователь может взаимодействовать с окном (ввод и чтение содержимого). Если окно скрыто, то оно отключается. Скрытое окно может получать сообщения от других окон, приложений или системы, но оно не может получать ввод от пользователя или отображать себя на экране (рисовать). Приложение указывает видимость окон на экране при их создании. Приложение также может менять видимость окон после создания.&lt;br /&gt;
&lt;br /&gt;
Окно видимо, если у него установлен &lt;a href="http://msdn.microsoft.com/en-us/library/ms632600(VS.85).aspx" title="MSDN: Window Styles"&gt;стиль &lt;code&gt;WS_VISIBLE&lt;/code&gt;&lt;/a&gt;. По умолчанию &lt;a href="http://msdn.microsoft.com/en-us/library/ms632680(VS.85).aspx" title="MSDN: CreateWindowEx Function"&gt;функция &lt;code&gt;CreateWindowEx&lt;/code&gt;&lt;/a&gt; создаёт скрытое окно, если только приложение явно не укажет стиль &lt;code&gt;WS_VISIBLE&lt;/code&gt;. Обычно приложение создаёт окна скрытыми, а затем показывает их - после настройки окна, чтобы пользователь не видел процесс создания окна. Если при вызове функции &lt;code&gt;CreateWindowEx&lt;/code&gt; указан стиль &lt;code&gt;WS_VISIBLE&lt;/code&gt;, то система отправляет окну &lt;a href="http://msdn.microsoft.com/en-us/library/ms632645(VS.85).aspx" title="MSDN: WM_SHOWWINDOW Message"&gt;сообщение &lt;code&gt;WM_SHOWWINDOW&lt;/code&gt;&lt;/a&gt; после его создания, но до отображения на экране.&lt;br /&gt;
&lt;br /&gt;
Приложение может определить, видимо ли окно, используя &lt;a href="http://msdn.microsoft.com/en-us/library/ms633530(VS.85).aspx" title="MSDN: IsWindowVisible Function"&gt;функцию &lt;code&gt;IsWindowVisible&lt;/code&gt;&lt;/a&gt;. Приложение может изменить видимость окон вызовом функций &lt;a href="http://msdn.microsoft.com/en-us/library/ms633548(VS.85).aspx" title="MSDN: ShowWindow Function"&gt;&lt;code&gt;ShowWindow&lt;/code&gt;&lt;/a&gt;, &lt;a href="http://msdn.microsoft.com/en-us/library/ms633545(VS.85).aspx" title="MSDN: SetWindowPos Function"&gt;&lt;code&gt;SetWindowPos&lt;/code&gt;&lt;/a&gt;, &lt;a href="http://msdn.microsoft.com/en-us/library/ms632681(VS.85).aspx" title="MSDN: DeferWindowPos Function"&gt;&lt;code&gt;DeferWindowPos&lt;/code&gt;&lt;/a&gt;, &lt;a href="http://msdn.microsoft.com/en-us/library/ms633544(VS.85).aspx" title="MSDN: SetWindowPlacement Function"&gt;&lt;code&gt;SetWindowPlacement&lt;/code&gt;&lt;/a&gt; или &lt;a href="http://msdn.microsoft.com/en-us/library/ms633591(VS.85).aspx" title="MSDN: SetWindowLong Function"&gt;&lt;code&gt;SetWindowLong&lt;/code&gt;&lt;/a&gt;. Любая из этих функций может скрыть или показать окно - удалением или добавлением &lt;a href="http://msdn.microsoft.com/en-us/library/ms632600(VS.85).aspx" title="MSDN: Window Styles"&gt;стиля &lt;code&gt;WS_VISIBLE&lt;/code&gt;&lt;/a&gt;. Они также отправят окну &lt;a href="http://msdn.microsoft.com/en-us/library/ms632645(VS.85).aspx" title="MSDN: WM_SHOWWINDOW Function"&gt;сообщение &lt;code&gt;WM_SHOWWINDOW&lt;/code&gt;&lt;/a&gt; непосредственно перед его показом или скрытием.&lt;br /&gt;
&lt;br /&gt;
Когда окно-владелец (owner) минимизируется, система автоматически скрывает ассоциированные с ним owned  окна. Аналогично, когда owner окно восстанавливается, система автоматически показывает ассоциированные owned окна. В обоих случаях система посылает owned окнам &lt;a href="http://msdn.microsoft.com/en-us/library/ms632645(VS.85).aspx" title="MSDN: WM_SHOWWINDOW Message"&gt;сообщения &lt;code&gt;WM_SHOWWINDOW&lt;/code&gt;&lt;/a&gt;. Иногда приложению может понадобится скрыть owned окна не минимизируя владельца. В этом случае приложение может использовать &lt;a href="http://msdn.microsoft.com/en-us/library/ms633547(VS.85).aspx" title="MSDN: ShowOwnedPopups Function"&gt;функцию &lt;code&gt;ShowOwnedPopups&lt;/code&gt;&lt;/a&gt;. Эта функция устанавливает или удаляет &lt;a href="http://msdn.microsoft.com/en-us/library/ms632600(VS.85).aspx" title="MSDN: Window Styles"&gt;стиль &lt;code&gt;WS_VISIBLE&lt;/code&gt;&lt;/a&gt; для всех owned окон, а также рассылает им сообщения &lt;code&gt;WM_SHOWWINDOW&lt;/code&gt;. Учтите, что скрытие окна-владельца не влияет на видимость ассоциированных с ним owned окон.&lt;br /&gt;
&lt;br /&gt;
Когда родительское окно видимо, то его child окна также видимы. Если родительское окно скрывается, то дочерние окна также скрываются. Минимизация родительского окна не влияет на видимость дочерних окон; т.е. child окна исчезают с экрана вместе с родителем, но &lt;a href="http://msdn.microsoft.com/en-us/library/ms632600(VS.85).aspx" title="MSDN: Window Styles"&gt;стиль &lt;code&gt;WS_VISIBLE&lt;/code&gt;&lt;/a&gt; для них не удаляется.&lt;br /&gt;
&lt;br /&gt;
Даже если у окна есть &lt;a href="http://msdn.microsoft.com/en-us/library/ms632600(VS.85).aspx" title="MSDN: Window Styles"&gt;стиль &lt;code&gt;WS_VISIBLE&lt;/code&gt;&lt;/a&gt;, пользователь может не видеть окна на экране; окно может быть перекрыто другими окнами или быть расположено за границами экрана. Кроме того, видимое child окно может обрезаться, как того диктуют отношения с родительским окном. И если родительское окно не видимо, то child окно также не будет видимо. Если родительское окно будет расположено за границами экрана, то его child окна также будут расположены за границами экрана, потому что child окно перемещается вместе с родительским окном, ограничен им и располагается в относительных координатах.&lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;&lt;a id="min_max"&gt;&lt;/a&gt;Минимизированные, максимизированные и развёрнутые окна&lt;/h4&gt;
&lt;em&gt;Максимизированное окно&lt;/em&gt; (maximized window) - это окно, у которого установлен &lt;a href="http://msdn.microsoft.com/en-us/library/ms632600(VS.85).aspx" title="MSDN: Window Styles"&gt;стиль &lt;code&gt;WS_MAXIMIZE&lt;/code&gt;&lt;/a&gt;. По умолчанию система увеличивает максимизированное окно так, чтобы оно заполняло экран или, в случае child окна, клиентскую область родительского окна. Хотя вы также можете установить размеры и положение окна так, чтобы они совпали с положением максимизированного окна, эти состояния отличаются. Система отключает возможность перемещения окна и изменения его размера для максимизированных окон. Кроме того, кнопка максимизации изменяется на кнопку восстановления размера.&lt;br /&gt;
&lt;br /&gt;
&lt;em&gt;Минимизированное окно&lt;/em&gt; (minimized window) - это окно, у которого установлен &lt;a href="http://msdn.microsoft.com/en-us/library/ms632600(VS.85).aspx" title="MSDN: Window Styles"&gt;стиль &lt;code&gt;WS_MINIMIZE&lt;/code&gt;&lt;/a&gt;. По умолчанию система уменьшает окно до размеров его кнопки на панели задач и перемещает окно на панель задач. &lt;br /&gt;
&lt;br /&gt;
&lt;em&gt;Развёрнутое окно&lt;/em&gt; (restored window) - это окно, которое было восстановлено в исходные размер и положение после минимизации или максимизации.&lt;br /&gt;
&lt;br /&gt;
Если приложение укажет стили &lt;code&gt;WS_MAXIMIZE&lt;/code&gt; или &lt;code&gt;WS_MINIMIZE&lt;/code&gt; в функции &lt;a href="http://msdn.microsoft.com/en-us/library/ms632680(VS.85).aspx" title="MSDN: CreateWindowEx Function"&gt;&lt;code&gt;CreateWindowEx&lt;/code&gt;&lt;/a&gt;, окно создастся изначально максимизированным или минимизированным. После создания окна приложение может использовать &lt;a href="http://msdn.microsoft.com/en-us/library/ms632678(VS.85).aspx" title="MSDN: CloseWindow Function"&gt;функцию &lt;code&gt;CloseWindow&lt;/code&gt;&lt;/a&gt; для минимизации окна. &lt;a href="http://msdn.microsoft.com/en-us/library/ms632671(VS.85).aspx" title="MSDN: ArrangeIconicWindows Function"&gt;Функция &lt;code&gt;ArrangeIconicWindows&lt;/code&gt;&lt;/a&gt; упорядочивает свёрнутые окна на рабочем столе или в родительском окне. &lt;a href="http://msdn.microsoft.com/en-us/library/ms633535(VS.85).aspx" id="MSDN: OpenIcon Function"&gt;Функция &lt;code&gt;OpenIcon&lt;/code&gt;&lt;/a&gt; восстанавливает минимизированное окно.&lt;br /&gt;
&lt;br /&gt;
Дополнительно: &lt;a href="http://www.transl-gunsmoker.ru/2010/02/160x31.html" title="Почему все свёрнутые окна имеют размер 160x31?"&gt;Почему все свёрнутые окна имеют размер 160x31?&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;a href="http://msdn.microsoft.com/en-us/library/ms633548(VS.85).aspx" title="MSDN: ShowWindow Function"&gt;Функция &lt;code&gt;ShowWindow&lt;/code&gt;&lt;/a&gt; может минимизировать, максимизировать и восстанавливать окна. Она также может менять видимость окон и задавать активное окно. &lt;a href="http://msdn.microsoft.com/en-us/library/ms633544(VS.85).aspx" title="MSDN: SetWindowPlacement Function"&gt;Функция &lt;code&gt;SetWindowPlacement&lt;/code&gt;&lt;/a&gt; содержит ту же функциональность, что и &lt;code&gt;ShowWindow&lt;/code&gt;, но она также может менять размещение окна по умолчанию в положениях минимизации, максимизации и восстановления.&lt;br /&gt;
&lt;br /&gt;
Функции &lt;a href="http://msdn.microsoft.com/en-us/library/ms633531(VS.85).aspx" title="MSDN: IsZoomed Function"&gt;&lt;code&gt;IsZoomed&lt;/code&gt;&lt;/a&gt; и &lt;a href="http://msdn.microsoft.com/en-us/library/ms633527(VS.85).aspx" title="MSDN: IsIconic Function"&gt;&lt;code&gt;IsIconic&lt;/code&gt;&lt;/a&gt; определяют, является ли заданное окно максимизированным или минимизированным соответственно. &lt;a href="http://msdn.microsoft.com/en-us/library/ms633518(VS.85).aspx" title="MSDN: GetWindowPlacement Function"&gt;Функция &lt;code&gt;GetWindowPlacement&lt;/code&gt;&lt;/a&gt; может получить положения минимизации, максимизации и восстановления для окна, а также определить состояние отображения окна.&lt;br /&gt;
&lt;br /&gt;
Когда система получает команду на максимизацию или восстановление минимизированного окна, она отправляет окну &lt;a href="http://msdn.microsoft.com/en-us/library/ms632640(VS.85).aspx" title="MSDN: WM_QUERYOPEN Message"&gt;сообщение&lt;code&gt;WM_QUERYOPEN&lt;/code&gt;&lt;/a&gt;. Если оконная процедура вернёт &lt;code&gt;0&lt;/code&gt;, то система проигнориует команду максимизации или восстановления.&lt;br /&gt;
&lt;br /&gt;
Система автоматически устанавливает размеры и положение максимизированного окна. Чтобы изменить системные умолчания, приложение может либо использовать &lt;a href="http://msdn.microsoft.com/en-us/library/ms633544(VS.85).aspx" title="MSDN: SetWindowPlacement Function"&gt;функцию &lt;code&gt;SetWindowPlacement&lt;/code&gt;&lt;/a&gt;, либо обрабатывать &lt;a href="http://msdn.microsoft.com/en-us/library/ms632626(VS.85).aspx" title="MSDN: WM_GETMINMAXINFO Message"&gt;сообщение &lt;code&gt;WM_GETMINMAXINFO&lt;/code&gt;&lt;/a&gt;, которое отправляется окну непосредственно перед максимизацией. Сообщение &lt;code&gt;WM_GETMINMAXINFO&lt;/code&gt; содержит указатель на &lt;a href="http://msdn.microsoft.com/en-us/library/ms632605(VS.85).aspx" title="MSDN: MINMAXINFO Structure"&gt;запись &lt;code&gt;MINMAXINFO&lt;/code&gt;&lt;/a&gt;, содержащую значения размера и положения окна, присвоенные системой. Приложение может изменить эти значения.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;&lt;a id="size_position"&gt;&lt;/a&gt;Размер и положение окна&lt;/h3&gt;
Размер и положение окна выражаются как ограничивающий прямоугольник (bounding rectangle), задавая его координаты относительно экрана (для окон верхнего уровня) или родительского окна (для child окон). Приложение указывает размер и положение окна при его создании, но может изменить их в любое время. Подробнее - см. &lt;a href="http://msdn.microsoft.com/en-us/library/dd162714(VS.85).aspx" title="MSDN: Filled Shapes"&gt;Filled Shapes&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
В этой секции также рассматриваются:
&lt;ul&gt;
&lt;li&gt;&lt;a href="#default"&gt;Размеры и положение по умолчанию&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#tracking"&gt;Отслеживание размера&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#system_commands"&gt;Системные команды&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#functions"&gt;Функции для размера и положения&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#messages"&gt;Сообщения для размера и положения&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;br /&gt;
&lt;h4&gt;&lt;a id="default"&gt;&lt;/a&gt;Размеры и положение по умолчанию&lt;/h4&gt;
Приложение может разрешить системе задать начальные положение и размер окна верхнего уровня, указав &lt;code&gt;CW_USEDEFAULT&lt;/code&gt; в &lt;a href="http://msdn.microsoft.com/en-us/library/ms632680(VS.85).aspx" title="MSDN: CreateWindowEx Function"&gt;функции &lt;code&gt;CreateWindowEx&lt;/code&gt;&lt;/a&gt;. Если приложение установит координаты окна в значение &lt;code&gt;CW_USEDEFAULT&lt;/code&gt;, то если приложение не создало других окон верхнего уровня, то система будет размещать окно относительно экрана; в противном случае система будет размещать окно относительно последнего созданного окна верхнего уровня. Если приложение также укажет &lt;code&gt;CW_USEDEFAULT&lt;/code&gt; для ширины и высоты окна, то размеры окна будут определяться системой. Если приложение до этого создавало окна верхнего уровня, то система будет определять размер окна по последнему созданному окну верхнего уровня. Указание &lt;code&gt;CW_USEDEFAULT&lt;/code&gt; для child или pop-up окна создаст окно с минимальным размером.&lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;&lt;a id="tracking"&gt;&lt;/a&gt;Отслеживание размера&lt;/h4&gt;
Система хранит максимальный и минимальный размеры окон со стилем &lt;a href="http://msdn.microsoft.com/en-us/library/ms632600(VS.85).aspx" title="Window Styles"&gt;&lt;code&gt;WS_THICKFRAME&lt;/code&gt;&lt;/a&gt;; окно с этим стилем имеет рамку с возможностью изменения размера. &lt;em&gt;Минимальный размер&lt;/em&gt; (minimum tracking size) - это наименьший размер окна, который пользователь сможет задать, перетаскивая рамку окна. Аналогично &lt;em&gt;максимальный размер&lt;/em&gt; (maximum tracking size) - это наибольший размер окна, который сможет получить пользователь при изменении размеров окна.&lt;br /&gt;
&lt;br /&gt;
Минимальные и максимальные размеры окна устанавливаются в системные значения по умолчанию при создании окна. Приложение может узнать эти значения и заместить их на свои при обработке &lt;a href="http://msdn.microsoft.com/en-us/library/ms632626(VS.85).aspx" title="MSDN: WM_GETMINMAXINFO Message"&gt;сообщения &lt;code&gt;WM_GETMINMAXINFO&lt;/code&gt;&lt;/a&gt;. Подробнее - см. &lt;a href="#messages" title="Сообщения для размера и положения"&gt;ниже&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;&lt;a id="system_commands"&gt;&lt;/a&gt;Системные команды&lt;/h4&gt;
Приложение, которое имеет оконное меню ("системное меню"), может изменить размеры окна отправкой системный команд. Системные команды отправляются, когда пользователь выбирает команды из оконного меню. Приложение может эмулировать действия пользователя отправкой &lt;a href="http://msdn.microsoft.com/en-us/library/ms646360(VS.85).aspx" title="MSDN: WM_SYSCOMMAND Message"&gt;сообщения &lt;code&gt;WM_SYSCOMMAND&lt;/code&gt;&lt;/a&gt; окну. Следующие системные команды влияют на размер и положение окна:&lt;br /&gt;
&lt;br /&gt;
&lt;table border=1 cellpadding=4&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Команда&lt;/th&gt;&lt;th&gt;Описание&lt;/th&gt;&lt;/tr&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;SC_CLOSE&lt;/td&gt;&lt;td&gt;Закрывает окно. Эта команда отправляет &lt;a href="http://msdn.microsoft.com/en-us/library/ms632617(VS.85).aspx" title="MSDN: WM_CLOSE Message"&gt;сообщение &lt;code&gt;WM_CLOSE&lt;/code&gt;&lt;/a&gt; окну. Дальнейшие действия зависят от окна (обработчика сообщения &lt;code&gt;WM_CLOSE&lt;/code&gt;).&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;SC_MAXIMIZE&lt;/td&gt;&lt;td&gt;Максимизирует окно.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;SC_MINIMIZE&lt;/td&gt;&lt;td&gt;Минимизирует окно.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;SC_MOVE&lt;/td&gt;&lt;td&gt;Перемещает окно.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;SC_RESTORE&lt;/td&gt;&lt;td&gt;Восстанавливает минимизированное или максимизированное окно на его исходные положение и размер.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;SC_SIZE&lt;/td&gt;&lt;td&gt;Начинает процедуру изменения размера. Для изменения размера окна можно использовать мышь или стрелки на клавиатуре.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;&lt;a id="functions"&gt;&lt;/a&gt;Функции для размера и положения&lt;/h4&gt;
После создания окна приложение может установить размер или положение окна вызовом одной из следующих функций: &lt;a href="http://msdn.microsoft.com/en-us/library/ms633544(VS.85).aspx" title="MSDN: SetWindowPlacement Function"&gt;&lt;code&gt;SetWindowPlacement&lt;/code&gt;&lt;/a&gt;, &lt;a href="http://msdn.microsoft.com/en-us/library/ms633534(VS.85).aspx" title="MSDN: MoveWindow Function"&gt;&lt;code&gt;MoveWindow&lt;/code&gt;&lt;/a&gt;, &lt;a href="http://msdn.microsoft.com/en-us/library/ms633545(VS.85).aspx" title="MSDN: SetWindowPos Function"&gt;&lt;code&gt;SetWindowPos&lt;/code&gt;&lt;/a&gt; и &lt;a href="http://msdn.microsoft.com/en-us/library/ms632681(VS.85).aspx" title="MSDN: DeferWindowPos Function"&gt;&lt;code&gt;DeferWindowPos&lt;/code&gt;&lt;/a&gt;. &lt;code&gt;SetWindowPlacement&lt;/code&gt; может установить минимизированный размер, максимизированный размер, обычный размер и положение окна, а также состояние видимости. Функции &lt;code&gt;MoveWindow&lt;/code&gt; и &lt;code&gt;SetWindowPos&lt;/code&gt; похожи; они обе устанавливают размер или положение одного окна в приложении. Но функция &lt;code&gt;SetWindowPos&lt;/code&gt; допускает изменение видимости окна; а &lt;code&gt;MoveWindow&lt;/code&gt; - нет. Используя функции &lt;a href="http://msdn.microsoft.com/en-us/library/ms632672(VS.85).aspx" title="MSDN: BeginDeferWindowPos Function"&gt;&lt;code&gt;BeginDeferWindowPos&lt;/code&gt;&lt;/a&gt;, &lt;code&gt;DeferWindowPos&lt;/code&gt; и &lt;a href="http://msdn.microsoft.com/en-us/library/ms633440(VS.85).aspx" title="MSND: EndDeferWindowPos Function"&gt;&lt;code&gt;EndDeferWindowPos&lt;/code&gt;&lt;/a&gt;, приложение может одновременно изменить атрибуты нескольких окон сразу - включая положение, размеры, Z-порядок и видимость.&lt;br /&gt;
&lt;br /&gt;
Приложение может получить ограничивающий прямоугольник вызовом &lt;a href="http://msdn.microsoft.com/en-us/library/ms633519(VS.85).aspx" title="MSDN: GetWindowRect Function"&gt;функции &lt;code&gt;GetWindowRect&lt;/code&gt;&lt;/a&gt;. &lt;code&gt;GetWindowRect&lt;/code&gt; заполняет &lt;a href="http://msdn.microsoft.com/en-us/library/dd162897(VS.85).aspx" title="MSDN: RECT Structure"&gt;запись &lt;code&gt;TRect&lt;/code&gt;&lt;/a&gt; координатами левого-верхнего и правого-нижнего углов окна. Координаты вычисляются относительно левого-верхнего угла экрана - даже для child окон (позиционирование которых осуществляется относительно родительского окна). Функции &lt;a href="http://msdn.microsoft.com/en-us/library/dd162952(VS.85).aspx" title="MSDN: ScreenToClient Function"&gt;&lt;code&gt;ScreenToClient&lt;/code&gt;&lt;/a&gt; или &lt;a href="http://msdn.microsoft.com/en-us/library/dd145046(VS.85).aspx" title="MSDN: MapWindowPoints Function"&gt;&lt;code&gt;MapWindowPoints&lt;/code&gt;&lt;/a&gt; могут быть использованы для перевода экранных координат в относительные координаты для child окон.&lt;br /&gt;
&lt;br /&gt;
&lt;a href="http://msdn.microsoft.com/en-us/library/ms633503(VS.85).aspx" title="MSDN: GetClientRect Function"&gt;Функция &lt;code&gt;GetClientRect&lt;/code&gt;&lt;/a&gt; возвращает координаты клиентской области окна. &lt;code&gt;GetClientRect&lt;/code&gt; заполняет &lt;code&gt;TRect&lt;/code&gt; координатами левого-верхнего и правого-нижнего углов клиентской области окна, но координаты считаются относительно самой клиентской области окна. Т.е. левый-верхний угол всегда имеет координаты (0, 0), а координаты правого-нижнего угла - это ширина и высота клиентской области окна.&lt;br /&gt;
&lt;br /&gt;
&lt;a href="http://msdn.microsoft.com/en-us/library/ms632674(VS.85).aspx" title="MSDN: CascadeWindows Function"&gt;Функция &lt;code&gt;CascadeWindows&lt;/code&gt;&lt;/a&gt; раскладывает каскадом (cascade) окна на рабочем столе или child окна в родительском окне. &lt;a href="http://msdn.microsoft.com/en-us/library/ms633554(VS.85).aspx" title="MSDN: TileWindows Function"&gt;Функция &lt;code&gt;TileWindows&lt;/code&gt;&lt;/a&gt; раскладывает окна черепицей (tiles).&lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;&lt;a id="messages"&gt;&lt;/a&gt;Сообщения для размера и положения&lt;/h4&gt;
Окну, чьё положение должно измениться, система отправляет &lt;a href="http://msdn.microsoft.com/en-us/library/ms632626(VS.85).aspx" title="MSDN: WM_GETMINMAXINFO Message"&gt;сообщение &lt;code&gt;WM_GETMINMAXINFO&lt;/code&gt;&lt;/a&gt;. К примеру, это сообщение будет отправлено, если пользователь щёлкнул по командам &lt;code&gt;Переместить&lt;/code&gt; или &lt;code&gt;Изменить размер&lt;/code&gt; из оконного меню или щёлкает по рамке окна или заголовку; также это сообщение будет отправлено, если приложение вызовет &lt;a href="http://msdn.microsoft.com/en-us/library/ms633545(VS.85).aspx" title="MSDN: SetWindowPos Function"&gt;функцию &lt;code&gt;SetWindowPos&lt;/code&gt;&lt;/a&gt; для перемещения или изменения размера окна. &lt;code&gt;Сообщение WM_GETMINMAXINFO&lt;/code&gt; передаёт указатель на &lt;a href="http://msdn.microsoft.com/en-us/library/ms632605(VS.85).aspx" title="MSDN: MINMAXINFO Structure"&gt;запись &lt;code&gt;TMinMaxInfo&lt;/code&gt;&lt;/a&gt;, которая содержит размер и положение максимизированного окна по умолчанию, равно как и минимальные и максимальные размеры окна. Приложение может изменить эти значения, обрабатывая сообщение &lt;code&gt;WM_GETMINMAXINFO&lt;/code&gt;. Чтобы получать сообщения &lt;code&gt;WM_GETMINMAXINFO&lt;/code&gt;, окно должно иметь стиль &lt;code&gt;WS_THICKFRAME&lt;/code&gt; или &lt;code&gt;WS_CAPTION&lt;/code&gt;. Окно со стилем &lt;code&gt;WS_THICKFRAME&lt;/code&gt; получит это сообщение во время создания окна, а также при изменениях размера и положения.&lt;br /&gt;
&lt;br /&gt;
Окну, чьи размеры, положение, Z-порядок или видимость должны измениться, система отправит сообщение &lt;a href="http://msdn.microsoft.com/en-us/library/ms632653(VS.85).aspx" title="MSDN: WM_WINDOWPOSCHANGING Message"&gt;&lt;code&gt;WM_WINDOWPOSCHANGING&lt;/code&gt;&lt;/a&gt;. Это сообщение содержит указатель на запись &lt;a href="http://msdn.microsoft.com/en-us/library/ms632612(VS.85).aspx" title="MSDN: WINDOWPOS Structure"&gt;&lt;code&gt;TWindowsPos&lt;/code&gt;&lt;/a&gt;, которая указывает новые положения, размер, Z-порядок и видимость окна. Изменяя поля записи &lt;code&gt;TWindowsPos&lt;/code&gt;, приложение может изменить способ отображения окна.&lt;br /&gt;
&lt;br /&gt;
После того, как изменились размер, положение, Z-порядок или видимость окна, система отправляет окну &lt;a href="http://msdn.microsoft.com/en-us/library/ms632652(VS.85).aspx" title="MSDN: WM_WINDOWPOSCHANGED Message"&gt;сообщение &lt;code&gt;WM_WINDOWPOSCHANGED&lt;/code&gt;&lt;/a&gt;. Это сообщение содержит указатель на &lt;a href="http://msdn.microsoft.com/en-us/library/ms632612(VS.85).aspx" title="MSDN: WINDOWPOS Structure"&gt;запись &lt;code&gt;TWindowPos&lt;/code&gt;&lt;/a&gt;, которая информирует окно о его новых атрибутах. Изменение этой записи не приведёт к изменению атрибутов окна. Окно, которому нужно обрабатывать сообщения &lt;a href="http://msdn.microsoft.com/en-us/library/ms632646(VS.85).aspx" title="MSDN: WM_SIZE Message"&gt;&lt;code&gt;WM_SIZE&lt;/code&gt;&lt;/a&gt; и &lt;a href="http://msdn.microsoft.com/en-us/library/ms632631(VS.85).aspx" title="MSDN: WM_MOVE Message"&gt;&lt;code&gt;WM_MOVE&lt;/code&gt;&lt;/a&gt;, должно передавать сообщение &lt;code&gt;WM_WINDOWPOSCHANGED&lt;/code&gt; в &lt;a href="http://msdn.microsoft.com/en-us/library/ms633572(VS.85).aspx" title="MSDN: DefWindowProc Function"&gt;функцию &lt;code&gt;DefWindowProc&lt;/code&gt;&lt;/a&gt;; иначе система не будет рассылать сообщения &lt;code&gt;WM_SIZE&lt;/code&gt; и &lt;code&gt;WM_MOVE&lt;/code&gt;.&lt;br /&gt;
&lt;br /&gt;
Когда окно создаётся или изменяет размер, система также отправляет окну сообщение &lt;a href="http://msdn.microsoft.com/en-us/library/ms632634(VS.85).aspx" title="MSDN: WM_NCCALCSIZE Message"&gt;&lt;code&gt;WM_NCCALCSIZE&lt;/code&gt;&lt;/a&gt;. Это сообщение используется системой для вычисления размера и относительного положения клиентской области окна (относительно левого-верхнего угла самого окна). Как правило, окно просто передаёт это сообщение обработчику по умолчанию; однако это сообщение может быть использовано приложениями, которые хотят изменить размеры клиентской области окна. Для более подробной информации - см. &lt;a href="http://msdn.microsoft.com/en-us/library/dd162759(VS.85).aspx" title="MSDN: Painting and Drawing"&gt;Painting and Drawing&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;&lt;a id="animation"&gt;&lt;/a&gt;Анимация окон&lt;/h3&gt;
&lt;br /&gt;
Вы можете создавать специальные эффекты при показе или скрытии окон, используя &lt;a href="http://msdn.microsoft.com/en-us/library/ms632669(VS.85).aspx" title="MSDN: AnimateWindow Function"&gt;функцию &lt;code&gt;AnimateWindow&lt;/code&gt;&lt;/a&gt;. При использовании этой функции вы можете указать эффекты roll, slide, collapse, expand или fade.&lt;br /&gt;
&lt;br /&gt;
По умолчанию система использует анимацию &lt;em&gt;roll&lt;/em&gt;. При этой анимации окно "раскатывается" от одного края до противоположного. Вы можете указать в флагах направление анимации: горизонтально, вертикально или по диагонали.&lt;br /&gt;
&lt;br /&gt;
С флагом &lt;code&gt;AW_SLIDE&lt;/code&gt; система использует анимацию &lt;em&gt;slide&lt;/em&gt;. При этом окно "выезжает". Анимация похожа на roll анимацию. roll анимация использует сглаженный край окна, имитируя рулон, а slide использует чёткие края, имитируя плоское окно. Аналогично, вы можете указать в флагах направление анимации: горизонтально, вертикально или по диагонали.&lt;br /&gt;
&lt;br /&gt;
Флаг &lt;code&gt;AW_BLEND&lt;/code&gt; задаёт анимацию &lt;em&gt;alpha-blended fade&lt;/em&gt;. При этом окно постепенно "проявляется" или исчезает. У этой анимации нет направления.&lt;br /&gt;
&lt;br /&gt;
Вы также можете использовать флаг &lt;code&gt;AW_CENTER&lt;/code&gt; для анимации разворота окна из центра окна. Анимация похожа на roll анимацию, но roll анимация показывает окно от границы к границе окна, а center анимация - от центра окна до его границ. У этой анимации нет направления.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;&lt;a id="layout_mirroring"&gt;&lt;/a&gt;Компоновка и отражение&lt;/h3&gt;
Компоновка окна (window layout) определяет способ размещения в окне или контексте устройства (DC - Device Context) текста и объектов GDI. Некоторые языки (вроде английского, французского, русского и немецкого) требуют компоновку left-to-right (LTR) - "слева направо". Другие языки (арабский, иврит) требуют компоновки right-to-left (RTL) - "справа налево". Компоновка окна применяется к тексту, но она также влияет и на другие элементы окна, включая растры, значки, расположение кнопок, многоуровневых деревьев, то, будет ли увеличиваться или уменьшаться горизонтальная координата при сдвиге вправо и т.п. К примеру, после того, как приложение установит компоновку RTL, начало координатной сетки в окне будет расположено в правом-верхнем углу окна (или контекста устройства) - в то время как обычно точка отсчёта располагается в левом-верхнем угле; а горизонтальная координата будет увеличиваться при движении влево, а не вправо, как это было с LTR. Однако не все объекты подчиняются компоновке окна. К примеру, компоновки диалоговых окон, окон-сообщений (message box) и контекстов устройств, не ассоциированных с окном (метафайлы, принтеры и другие) - обрабатываются отдельно. Некоторые такие особенности также упомянуты ниже.&lt;br /&gt;
&lt;br /&gt;
Оконные функции позволяют вам указать или изменить компоновку окна. Заметьте, что изменение компоновки на RTL (этот процесс также называется зеркалированием или отражением окна - mirroring) не поддерживается окнами, которые установили &lt;a href="http://msdn.microsoft.com/en-us/library/ms633574(VS.85).aspx#class_styles" title="MSDN: About Window Classes: Class styles"&gt;стиль &lt;code&gt;CS_OWNDC&lt;/code&gt;&lt;/a&gt;, а также контекстами устройств в режиме &lt;code&gt;GM_ADVANCED&lt;/code&gt;.&lt;br /&gt;
&lt;br /&gt;
По умолчанию окно и контексты устройств имеют компоновку left-to-right (LTR). Чтобы изменить компоновку окна на RTL - вызовите &lt;a href="http://msdn.microsoft.com/en-us/library/ms632680(VS.85).aspx" title="MSDN: CreateWindowEx Function"&gt;функцию &lt;code&gt;CreateWindowEx&lt;/code&gt;&lt;/a&gt; с флагом &lt;code&gt;WS_EX_LAYOUTRTL&lt;/code&gt;. Также, по умолчанию, child окна (т.е. окна, создаваемые со &lt;a href="http://msdn.microsoft.com/en-us/library/ms632600(VS.85).aspx" title="MSDN: Window Styles"&gt;стилем &lt;code&gt;WS_CHILD&lt;/code&gt;&lt;/a&gt;) будут иметь ту же компоновку, что и их родитель. Чтобы отключить наследование зеркалирования для child окон и задавать компоновку явно - добавьте стиль &lt;code&gt;WS_EX_NOINHERITLAYOUT&lt;/code&gt; в вызове &lt;code&gt;CreateWindowEx&lt;/code&gt;. Заметьте, что компоновка не наследуется owned окнами (создаваемыми без стиля &lt;code&gt;WS_CHILD&lt;/code&gt;) или окнами, чей параметр &lt;code&gt;hWnd&lt;/code&gt; в функции &lt;code&gt;CreateWindowEx&lt;/code&gt; был установлен в 0. Чтобы отключить наследование зеркалирования в отдельном окне, обработайте &lt;a href="http://msdn.microsoft.com/en-us/library/ms632635(VS.85).aspx" title="MSDN: WM_NCCREATE Message"&gt;сообщение &lt;code&gt;WM_NCCREATE&lt;/code&gt;&lt;/a&gt;, где используйте функции &lt;a href="http://msdn.microsoft.com/en-us/library/ms633584(VS.85).aspx" title="MSDN: GetWindowLong Function"&gt;&lt;code&gt;GetWindowLong&lt;/code&gt;&lt;/a&gt; и &lt;a href="http://msdn.microsoft.com/en-us/library/ms633591(VS.85).aspx" title="MSDN: SetWindowLong"&gt;&lt;code&gt;SetWindowLong&lt;/code&gt;&lt;/a&gt; для замены флага &lt;code&gt;WS_EX_LAYOUTRTL&lt;/code&gt;.&lt;br /&gt;
&lt;br /&gt;
Вы также можете изменить умолчание для всего процесса вызовом &lt;a href="http://msdn.microsoft.com/en-us/library/ms633542(VS.85).aspx" title="MSDN: SetProcessDefaultLayout Function"&gt;функции &lt;code&gt;SetProcessDefaultLayout&lt;/code&gt;&lt;/a&gt;. Все окна, создаваемые после вызова &lt;code&gt;SetProcessDefaultLayout(LAYOUT_RTL)&lt;/code&gt;, будут создаваться зеркалированными (без указания флага), но уже существующие окна не изменятся. Чтобы отключить зеркалирование по умолчанию - используйте &lt;code&gt;SetProcessDefaultLayout(0)&lt;/code&gt;.&lt;br /&gt;
&lt;br /&gt;
Заметьте, что &lt;a href="http://msdn.microsoft.com/en-us/library/ms633542(VS.85).aspx" title="MSDN: SetProcessDefaultLayout Function"&gt;функция &lt;code&gt;SetProcessDefaultLayout&lt;/code&gt;&lt;/a&gt; отразит контексты устройств только в отзеркалированных окнах. Чтобы отразить произвольный DC - вызовите &lt;a href="http://msdn.microsoft.com/en-us/library/dd162979(VS.85).aspx" title="SetLayout Function"&gt;функцию &lt;code&gt;SetLayout&lt;/code&gt;&lt;/a&gt;. См. также ниже обсуждение &lt;a href="#mirroring_dc" title="Отражение контекстов устройств, не ассоциированных с окном"&gt;отражённых контекстов устройств, не связанных с окнами&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
Растры (bitmap) и значки (icon) в отражённом окне также по умолчанию зеркалируются. Однако это не всегда бывает необходимо. К примеру, бизнес-лого или аналоговые часы не должны зеркалироваться. Чтобы отключить зеркалирование растра, вызовите &lt;a href="http://msdn.microsoft.com/en-us/library/dd162979(VS.85).aspx" title="SetLayout Function"&gt;функцию &lt;code&gt;SetLayout&lt;/code&gt;&lt;/a&gt; с установленным флагом &lt;code&gt;LAYOUT_BITMAPORIENTATIONPRESERVED&lt;/code&gt; в &lt;code&gt;dwLayout&lt;/code&gt;. Чтобы отключить зеркалирование в DC, вызовите &lt;code&gt;SetLayout(DC, 0)&lt;/code&gt;.&lt;br /&gt;
&lt;br /&gt;
Чтобы узнать текущую компоновку по умолчанию, вызовите &lt;a href="http://msdn.microsoft.com/en-us/library/ms633511(VS.85).aspx" title="MSDN: GetProcessDefaultLayout Function"&gt;функцию &lt;code&gt;GetProcessDefaultLayout&lt;/code&gt;&lt;/a&gt;. При успешном вызове &lt;code&gt;pdwDefaultLayout&lt;/code&gt; будет содержать &lt;code&gt;LAYOUT_RTL&lt;/code&gt; или 0. Чтобы узнать компоновку контекста устройства, вызовите &lt;a href="http://msdn.microsoft.com/en-us/library/dd144896(VS.85).aspx" title="MSDN: GetLayout Function"&gt;функцию &lt;code&gt;GetLayout&lt;/code&gt;&lt;/a&gt;. При успешном вызове она вернёт набор флагов, управляющих компоновкой, которые вы можете проверить на &lt;code&gt;LAYOUT_RTL&lt;/code&gt; и &lt;code&gt;LAYOUT_BITMAPORIENTATIONPRESERVED&lt;/code&gt;.&lt;br /&gt;
&lt;br /&gt;
Изменить компоновку после создания окна можно функцией &lt;a href="http://msdn.microsoft.com/en-us/library/ms633591(VS.85).aspx" title="MSDN: SetWindowLong Function"&gt;&lt;code&gt;SetWindowLong&lt;/code&gt;&lt;/a&gt;. К примеру, это может понадобится при смене языка UI в run-time. Однако при этом вы также должны обновить (перерисовать) содержимое окна.&lt;br /&gt;
&lt;br /&gt;
При отражении окна вы должны думать в терминах "ближе" и "дальше", а не "левее" и "правее". Иначе у вас могут быть проблемы. Частой ошибкой является сценарий конвертирования координат между экранными и клиентскими. К примеру, приложение может использовать такой код для расположения элемента управления в окне:
&lt;pre class="brush:delphi"&gt;// КОД НИЖЕ - ОШИБОЧЕН

// Получаем координаты окна в экранных координатах 
GetWindowRect(hControl, rControlRect);  

// Проецируем координаты экрана в клиентские координаты диалога
ScreenToClient(hDialog, rControlRect.left); 
ScreenToClient(hDialog, rControlRect.right);&lt;/pre&gt;
Этот код вызовет проблемы в отзеркалированных окнах, потому что в них "лево" становится "право" и наоборот. Чтобы избежать этой проблемы, замените вызовы &lt;a href="http://msdn.microsoft.com/en-us/library/dd162952(VS.85).aspx" title="MSDN: ScreenToClient Function"&gt;функции &lt;code&gt;ScreenToClient&lt;/code&gt;&lt;/a&gt; на вызовы &lt;a href="http://msdn.microsoft.com/en-us/library/dd145046(VS.85).aspx" title="MSDN: MapWindowPoints Function"&gt;&lt;code&gt;MapWindowPoints&lt;/code&gt;&lt;/a&gt;:
&lt;pre class="brush:delphi"&gt;// Корректный код
GetWindowRect(hControl, ControlRect);
MapWindowPoints(0, hDialog, rControlRect, 2);&lt;/pre&gt;
Этот код будет работать корректно, потому что &lt;a href="http://msdn.microsoft.com/en-us/library/dd145046(VS.85).aspx" title="MSDN: MapWindowPoints Function"&gt;функция &lt;code&gt;MapWindowPoints&lt;/code&gt;&lt;/a&gt; умеет работать с прямоугольниками (&lt;a href="http://msdn.microsoft.com/en-us/library/dd162952(VS.85).aspx" title="MSDN: ScreenToClient Function"&gt;&lt;code&gt;ScreenToClient&lt;/code&gt;&lt;/a&gt; и &lt;a href="http://msdn.microsoft.com/en-us/library/dd183434(VS.85).aspx" title="MSDN: ClientToScreen Function"&gt;&lt;code&gt;ClientToScreen&lt;/code&gt;&lt;/a&gt; работают только с точками), поэтому она может учесть зеркалированность окон и поменять местами левые и правые грани прямоугольника при необходимости.&lt;br /&gt;
&lt;br /&gt;
Ещё одна практика, которая может вызвать проблемы в отзеркалированных окнах - позиционирование элементов управления, используя экранные координаты вместо клиентских. К примеру, код ниже использует разницу в экранных коодинатах для размещения элемента управления в диалоговом окне:
&lt;pre class="brush:delphi"&gt;// КОД НИЖЕ - ОШИБОЧЕН
var 
  rdDialog: TRect;
  rcControl: TRect;
  hControl: HWND;
begin
  GetWindowRect(hDlg, rcDialog);              // Получение прямоугольника в экранных координатах
  GetWindowRect(hControl, rcControl);
  MoveWindow(hControl,
             rcControl.left - rcDialog.left,  // Получение позиции x в клиентских координатах
             rcControl.top - rcDialog.top,
             nWidth,
             nHeight,
             False);&lt;/pre&gt;
Этот код будет работать для диалогового окна с компоновкой left-to-right (LTR) и режимом проецирования (mapping mode) элемента управления &lt;code&gt;MM_TEXT&lt;/code&gt;, потому что новая позиция по X в клиентских координатах соответствует разнице в левых краях элемента управления и диалога в экранных координатах. Но для отзеркалированного диалога вам нужно использовать &lt;a href="http://msdn.microsoft.com/en-us/library/dd145046(VS.85).aspx" title="MSDN: MapWindowPoints Function"&gt;&lt;code&gt;MapWindowPoints&lt;/code&gt;&lt;/a&gt;, как показано ниже:
&lt;pre class="brush:delphi"&gt;// Корректный код
var
  rcDialog: TRect;
  rcControl: TRect;
  hControl: HWND;
begin
  ..
  GetWindowRect(hControl, rcControl);

  // MapWindowPoints работает корректно в обоих режимах (прямом и зеркальном)
  MapWindowPoints(0, hDlg, rcControl, 2);

  // Теперь rcControl - в клиентских координатах
  MoveWindow(hControl, rcControl.left, rcControl.top, nWidth, nHeight, False);&lt;/pre&gt;
&lt;br /&gt;
&lt;h4&gt;&lt;a id="mirroring_boxes"&gt;&lt;/a&gt;Отражение диалоговых окон и окон-сообщений&lt;/h4&gt;
Диалоговые окна и окна-сообщения (message box) не наследуют компоновку окна, поэтому вам нужно устанавливать компоновку явно. Чтобы отразить окно-сообщени, используйте флаг &lt;code&gt;MB_RTLREADING&lt;/code&gt; при вызове функций &lt;a href="http://msdn.microsoft.com/en-us/library/ms645505(VS.85).aspx" title="MSDN: MessageBox Function"&gt;&lt;code&gt;MessageBox&lt;/code&gt;&lt;/a&gt; или &lt;a href="http://msdn.microsoft.com/en-us/library/ms645507(VS.85).aspx" title="MSDN: MessageBoxEx Function"&gt;&lt;code&gt;MessageBoxEx&lt;/code&gt;&lt;/a&gt;. Чтобы изменить компоновку диалога - укажите стиль &lt;code&gt;WS_EX_LAYOUTRTL&lt;/code&gt; в шаблоне диалога. Вкладки свойств (property sheets) являются частным случаем диалогов. Каждая вкладка трактуется как отдельный диалог, поэтому вам нужно включать стиль &lt;code&gt;WS_EX_LAYOUTRTL&lt;/code&gt; для каждой вкладки отдельно.&lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;&lt;a id="mirroring_dc"&gt;&lt;/a&gt;Отражение контекстов устройств, не ассоциированных с окном&lt;/h4&gt;
Контексты устройств (DC), которые не ассоциированы с окном, не наследуют компоновку, поэтому вам нужно указывать её явно. Для изменения компоновки контекста устройства используйте &lt;a href="http://msdn.microsoft.com/en-us/library/dd162979(VS.85).aspx" title="MSDN: SetLayout Function"&gt;функцию &lt;code&gt;SetLayout&lt;/code&gt;&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;a href="http://msdn.microsoft.com/en-us/library/dd162979(VS.85).aspx" title="SetLayout Function"&gt;Функция &lt;code&gt;SetLayout&lt;/code&gt;&lt;/a&gt; редко используется с окнами. Как правило, окна получают ассоциированный DC только при обработке &lt;a href="http://msdn.microsoft.com/en-us/library/dd145213(VS.85).aspx" title="MSDN: WM_PAINT Message"&gt;сообщения &lt;code&gt;WM_PAINT&lt;/code&gt;&lt;/a&gt;. Иногда приложение может создавать DC для окна вызовом &lt;a href="http://msdn.microsoft.com/en-us/library/dd144871(VS.85).aspx" title="MSDN: GetDC Function"&gt;&lt;code&gt;GetDC&lt;/code&gt;&lt;/a&gt;. В любом случае, компоновка этих DC устанавливается в соответствии с компоновкой целевого окна.&lt;br /&gt;
&lt;br /&gt;
Вызов &lt;a href="http://msdn.microsoft.com/en-us/library/dd162979(VS.85).aspx" title="MSDN: SetLayout Function"&gt;функции &lt;code&gt;SetLayout&lt;/code&gt;&lt;/a&gt; не влияет на значения, возвращаемые функциями &lt;a href="http://msdn.microsoft.com/en-us/library/dd144949(VS.85).aspx" title="MSDN: GetWindowOrgEx Function"&gt;&lt;code&gt;GetWindowOrgEx&lt;/code&gt;&lt;/a&gt;, &lt;a href="http://msdn.microsoft.com/en-us/library/dd144948(VS.85).aspx" title="MSDN: GetWindowExtEx Function"&gt;&lt;code&gt;GetWindowExtEx&lt;/code&gt;&lt;/a&gt;, &lt;a href="http://msdn.microsoft.com/en-us/library/dd144946(VS.85).aspx" title="MSDN: GetViewportOrgEx Function"&gt;&lt;code&gt;GetViewportOrgEx&lt;/code&gt;&lt;/a&gt; и &lt;a href="http://msdn.microsoft.com/en-us/library/dd144945(VS.85).aspx" title="MSDN: GetViewportExtEx Function"&gt;&lt;code&gt;GetViewportExtEx&lt;/code&gt;&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
При RTL компоновке &lt;a href="http://msdn.microsoft.com/en-us/library/dd144897(VS.85).aspx" title="MSDN: GetMapMode Function"&gt;функция &lt;code&gt;GetMapMode&lt;/code&gt;&lt;/a&gt; вернёт &lt;code&gt;MM_ANISOTROPIC&lt;/code&gt; вместо &lt;code&gt;MM_TEXT&lt;/code&gt;. Вызов &lt;a href="http://msdn.microsoft.com/en-us/library/dd162980(VS.85).aspx" title="MSDN: SetMapMode Function"&gt;&lt;code&gt;SetMapMode&lt;/code&gt;&lt;/a&gt; с &lt;code&gt;MM_TEXT&lt;/code&gt; будет работать корректно; будет изменено только возвращаемое значение &lt;code&gt;GetMapMode&lt;/code&gt;. Аналогично, вызов &lt;code&gt;SetLayout(hdc, LAYOUT_RTL)&lt;/code&gt; при режиме &lt;code&gt;MM_TEXT&lt;/code&gt; приведёт к смене режима на &lt;code&gt;MM_ANISOTROPIC&lt;/code&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;&lt;a id="destruction"&gt;&lt;/a&gt;Уничтожение окна&lt;/h3&gt;
В целом, приложение должно уничтожать все окна, которые оно создало. Оно может сделать это вызывая &lt;a href="http://msdn.microsoft.com/en-us/library/ms632682(VS.85).aspx" title="MSDN: DestroyWindow Function"&gt;функцию &lt;code&gt;DestroyWindow&lt;/code&gt;&lt;/a&gt;. Когда окно уничтожается, система скрывает окно (если оно было видимо), а затем удаляет все внутренние данные, ассоциированные с окном. Описатель окна (handle) становится недопустимым и не должен более использоваться приложением.&lt;br /&gt;
&lt;br /&gt;
Обычно приложения удаляет окна вскоре после их создания. К примеру, приложение удаляет диалоговое окно со всеми дочерними окнами как только оно получит достаточно информации от пользователя. В конечном итоге приложение уничтожит и главное окно (непосредственно перед выходом).&lt;br /&gt;
&lt;br /&gt;
Перед уничтожением окна приложение должно удалить любые данные, ассоциированные с окном (если они есть), а также освободить системные ресурсы, выделенные для окна. Если приложение не освободит ресурсы, то они будут освобождены системой при завершении приложения.&lt;br /&gt;
&lt;br /&gt;
Уничтожение окна не влияет на оконный класс этого окна. Приложение может создавать окна данного оконного класса и после удаления окна этого класса, равно как и продолжить использование других окон этого класса. Уничтожение окна также уничтожит все его descendant окна (т.е. все child окна, child окна child окон и так далее). &lt;a href="http://msdn.microsoft.com/en-us/library/ms632682(VS.85).aspx" title="MSDN: DestroyWindow Function"&gt;Функция &lt;code&gt;DestroyWindow&lt;/code&gt;&lt;/a&gt; отправляет &lt;a href="http://msdn.microsoft.com/en-us/library/ms632620(VS.85).aspx" title="MSDN: WM_DESTROY Message"&gt;сообщение &lt;code&gt;WM_DESTROY&lt;/code&gt;&lt;/a&gt; сначала самому окну, затем всем его child окнам, а затем, аналогично, и descendant окнам.&lt;br /&gt;
&lt;br /&gt;
Окно с оконным меню также получит &lt;a href="http://msdn.microsoft.com/en-us/library/ms632617(VS.85).aspx" title="MSDN: WM_CLOSE Message"&gt;сообщение &lt;code&gt;WM_CLOSE&lt;/code&gt;&lt;/a&gt;, когда пользователь щёлкнет по &lt;code&gt;Закрыть&lt;/code&gt;. Приложение может запросить у пользователя подтверждение закрытия окна в обработчике этого сообщения. Если пользователь согласен с закрытием окна, то приложение может вызвать &lt;a href="http://msdn.microsoft.com/en-us/library/ms632682(VS.85).aspx" title="MSDN: DestroyWindow Function"&gt;функцию &lt;code&gt;DestroyWindow&lt;/code&gt;&lt;/a&gt; для уничтожения окна.&lt;br /&gt;
&lt;br /&gt;
Если уничтожаемое окно является активным, то активным окном становится другое окно. Оно же получает фокус ввода. Окно, которое станет активным, определяется по порядку окон в списке ALT+ESC.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688132520265708820-3984829993570054445?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=84rrGPp4cH4:zWTP_zrAQPc: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=84rrGPp4cH4:zWTP_zrAQPc:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=84rrGPp4cH4:zWTP_zrAQPc:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=84rrGPp4cH4:zWTP_zrAQPc:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=84rrGPp4cH4:zWTP_zrAQPc:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=84rrGPp4cH4:zWTP_zrAQPc: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=84rrGPp4cH4:zWTP_zrAQPc:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=84rrGPp4cH4:zWTP_zrAQPc:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=84rrGPp4cH4:zWTP_zrAQPc:-BTjWOF_DHI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=84rrGPp4cH4:zWTP_zrAQPc:-BTjWOF_DHI" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/GunsmokersTranslations/~4/84rrGPp4cH4" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.transl-gunsmoker.ru/feeds/3984829993570054445/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://www.transl-gunsmoker.ru/2012/04/windows.html#comment-form" title="Комментарии: 0" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/3984829993570054445?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/3984829993570054445?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/GunsmokersTranslations/~3/84rrGPp4cH4/windows.html" title="Возможности окон Windows" /><author><name>Александр Алексеев</name><uri>https://profiles.google.com/113168002104297556003</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh3.googleusercontent.com/-aUMttZQBsuI/AAAAAAAAAAI/AAAAAAAAC3Y/QuZ7K9t_WzE/s512-c/photo.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://www.transl-gunsmoker.ru/2012/04/windows.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CE8ASX8zcCp7ImA9WhVXFUU.&quot;"><id>tag:blogger.com,1999:blog-1688132520265708820.post-7366484187489338513</id><published>2012-03-20T23:17:00.003+04:00</published><updated>2012-04-16T17:27:28.188+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-04-16T17:27:28.188+04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="железо" /><title>Добро пожаловать в эру пост-PC</title><content type="html">Это перевод &lt;a href="http://www.codinghorror.com/blog/2012/03/welcome-to-the-post-pc-era.html" title="Welcome to the Post PC Era"&gt;Welcome to the Post PC Era&lt;/a&gt;. Автор: Jeff Atwood.&lt;br /&gt;
&lt;br /&gt;
В чём была &lt;a href="http://windows.microsoft.com/ru-RU/windows/history" title="История Windows"&gt;оригинальная миссия Microsoft&lt;/a&gt;?
&lt;br /&gt;
&lt;blockquote&gt;
В 1975 году Гейтс и Аллен создают компанию с названием Microsoft. Как и большинство вновь создаваемых предприятий, Microsoft начинает свою историю с небольших масштабов, но имеет глобальную цель — компьютер на каждый рабочий стол и в каждый дом.&lt;/blockquote&gt;
&lt;a name='more'&gt;&lt;/a&gt;
Экзистанциальный кризис, который переживает сейчас Microsoft, заключается в том, что &lt;i&gt;их миссия была выполнена несколько лет назад&lt;/i&gt; - по меньшей мере, в развитом мире. &lt;b&gt;Когда последний раз вы видели письменный стол или дом без компьютера? 2001? 2005?&lt;/b&gt; Мы давно уже прошли ту точку, где оказались выполнены и даже перевыполнены планы  Microsoft. Персональные компьютеры (PC) теперь вездесущны. Если вы проснётесь однажды утром и обнаружите, что вы завоевали мир...  что будет дальше?&lt;br /&gt;
&lt;br /&gt;
Пост-PC эра, конечно же.&lt;br /&gt;
&lt;br /&gt;
Microsoft, кажется, так и не оправилась от потрясения от выполнения их исходной цели 1975 года. Или, возможно, они просто думают, что цель не совсем достигнута и что для PC всегда будут новые миры, которые нужно завоёвывать. Но вот Стив Джобс &lt;a href="http://en.wikiquote.org/wiki/Steve_Jobs" title="Стив Джобс: цитаты"&gt;увидел эпоху надвигающейся пост-PC эры еще в 1996 году&lt;/a&gt;:
&lt;br /&gt;
&lt;blockquote&gt;
Индустрия персональных компьютеров мертва. В ней практически прекратились инновации. Microsoft доминирует, но не создаёт инновации. Всё кончено. Apple проиграла. На рынке настольных компьютеров наступают тёмные времена, и, думаю, это будет продолжаться ещё лет десять, ну или до конца этого десятилетия точно.&lt;br /&gt;
&lt;br /&gt;
Если бы Apple управлял бы я, то я бы выдоил все соки из Macintosh – и просто перешёл бы к следующей Великой Вещи. Войны PC закончены. Всё. Microsoft выиграла их давным-давно.&lt;/blockquote&gt;
Более того, Джобс сделал кое-что по этому поводу. Apple является, пожалуй, самым большим (и с точки зрения финансов в настоящее время - &lt;i&gt;буквально&lt;/i&gt; самым большим) врагом вычислительной техники общего назначения со своими iPhone и iPad. Сегодня даже их собственная система общего назначения OS X для Mac играет второстепенную роль, по отношению к Джаггернауту, обслуживающему iPhone и iPad: iOS.&lt;br /&gt;
&lt;br /&gt;
&lt;a href="http://www.asymco.com/2012/02/16/ios-devices-in-2011-vs-macs-sold-it-in-28-years/" title="Apple sold more iOS devices in 2011 than all the Macs it sold in 28 years"&gt;И вот почему&lt;/a&gt;:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;
&lt;a href="http://4.bp.blogspot.com/-4GXCd20GvFY/T2jLGvvr3wI/AAAAAAAADkY/S5kANdSftro/s1600/6a0120a85dcdae970b0163030a719a970d-800wi.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://4.bp.blogspot.com/-4GXCd20GvFY/T2jLGvvr3wI/AAAAAAAADkY/S5kANdSftro/s1600/6a0120a85dcdae970b0163030a719a970d-800wi.png" /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
Вся суть тут в наклоне кривой. Сложные машины общего назначения - аутсайдеры, а простые специализированные устройства лидируют.&lt;br /&gt;
&lt;br /&gt;
Тут я в противоречии с самим собой. Как бы много я не любил компьютеры вида "могу сделать что угодно"…
&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;Я не знаю, зачем большинству людей в мире вообще может понадобится настоящий универсальный компьютер, который может делать что угодно и запускать любые программы. Большинство людей заботят базовые потребности в виде просмотра интернета, почты, общения и, быть может, нескольких аналогичных вещей.&lt;/li&gt;
&lt;li&gt;Я считаю, что идея "каждой домохозяйке" просто несовместима с универсальными компьютерами в виде PC, Mac и Unix, что делает их фундаментально не готовыми к эре пост-PC. Обновления. Панели инструментов. Сервис-паки. Настройки. Анти-вирусы. Файловые системы. Панель управления. Не это ли вы начинаете ненавидеть, когда вам звонит ваша мама с просьбой "помочь с компьютером"? И всё это глубоко встроено в культуру и дизайн любого современного универсального компьютера. За способность делать что угодно нужно платить ценой усложнения.&lt;/li&gt;
&lt;li&gt;Крохотные PC (которые умещаются у вас в кармане) стали иметь мощности настольных компьютеров, скажем, пятилетней давности. И этих возможностей ещё тогда было более чем достаточно для относительно неэффективной универсальной операционной системы.&lt;/li&gt;
&lt;/ul&gt;
Но главным звонком для меня стала инновация, созданная новым iPad. &lt;b&gt;Инновация, которую универсальные компьютеры ждали тридцать лет&lt;/b&gt;: настоящий экран высокого разрешения по доступной цене и размеру. В 2007 году я &lt;a href="http://www.codinghorror.com/blog/2007/06/where-are-the-high-resolution-displays.html" title="Where Are The High Resolution Displays?"&gt;узнавал, где же экраны с высокой плотностью пикселей&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;a href="http://www.theverge.com/2012/3/14/2870533/ipad-review" title="iPad review"&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;
&lt;a href="http://2.bp.blogspot.com/-jpOY9jtP5YM/T2jQAEnjZzI/AAAAAAAADkk/NzaCGlpfeD0/s1600/6a0120a85dcdae970b016763ff8fbf970b-800wi.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://2.bp.blogspot.com/-jpOY9jtP5YM/T2jQAEnjZzI/AAAAAAAADkk/NzaCGlpfeD0/s1600/6a0120a85dcdae970b016763ff8fbf970b-800wi.jpg" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/a&gt;
&lt;br /&gt;
Вот почему я не просто купил iPad 3 (извините, - &lt;i&gt;Новый iPad&lt;/i&gt;). &lt;b&gt;Я купил два iPad 3.&lt;/b&gt; И, возможно, куплю ещё.&lt;br /&gt;
&lt;br /&gt;
Обзоры iPad 3, которые жалуются на то, что "всё, что они сделали - улучшили экран" - это невежество, граничащее с глупостью. Планшеты, почти по определению, - это экран в руках. Для планшета нет ничего более фундаментального и значимого, чем качество экрана. Это мои первые iPad-ы, и экран в них безупречен, как я и надеялся. Разрешение и чёткость поражают, глаза смотрят на него с радостью, и я надеюсь, что в один прекрасный день мы могли бы достичь почти печатного качества в электронной технике. Новый экран iPad - это всё, что я всегда хотел иметь для моего настольного PC и ноутбуков за последние 5 лет, но никогда так и не мог получить.&lt;br /&gt;
&lt;br /&gt;
Эй, вам не нужно верить мне на слово. Послушайте, что &lt;a title="3rd Generation iPad: Entering A High-Resolution, Post-PC World..." href="http://billhillsblog.blogspot.com/2012/03/3rd-generation-ipad-entering-high.html"&gt;говорит о новом iPad&lt;/a&gt; пионер и изобретатель ClearType, Bill Hills:
&lt;blockquote&gt;Третье поколение iPad имеет разрешение экрана в 264ppi. И при этом они работают 10 часов (9 часов - с включенными беспроводными интерфейсами). Несомненно, это разрешение ошеломляет. Увидеть такое на мейнстримовом потребительском устройстве вроде iPad, а не на экзотическом мониторе за $13'000 - это воистину удивительно. Я ждал подобного более десяти лет.&lt;br /&gt;
&lt;br /&gt;
Это событие должно установить планку для разрешений экранов на новый уровень, который придётся брать другим производителям PC и устройств.&lt;/blockquote&gt;
А эксперты калибровки экранов в DisplayMate готовы &lt;a href="http://www.displaymate.com/iPad_ShootOut_1.htm" title="new iPad Display Technology Shoot-Out"&gt;привести измерения и метрики&lt;/a&gt;, чтобы подкрепить эти слова:
&lt;blockquote&gt;… качество экрана нового iPad, точность цветопередачи и оттенков серого не только лучше, чем у любого другого планшета или смартфона, но они даже лучше, чем у многих HDTV, ноутбуков и мониторов. Фактически, после небольшой калибровки новый iPad может служить студийным контрольным монитором.&lt;/blockquote&gt;
Конечно, нужно признать, что сначала это будет происходить для небольших экранов в 4" и 10" из-за чисто экономических причин. Чтобы вырасти, потребуется время. Я содрогаюсь в попытках представить, сколько может стоить монитор на 24" или 27" с технологией экрана как у iPad 3. Но до iPhone и iPad никто, насколько я могу судить, &lt;i&gt;даже не пытался&lt;/i&gt; улучшать качество компьютерных экранов – даже хотя все проведённые &lt;a title="HCI - Человеко-компьютерное взаимодействие" href="http://ru.wikipedia.org/wiki/%D0%A7%D0%B5%D0%BB%D0%BE%D0%B2%D0%B5%D0%BA%D0%BE-%D0%BA%D0%BE%D0%BC%D0%BF%D1%8C%D1%8E%D1%82%D0%B5%D1%80%D0%BD%D0%BE%D0%B5_%D0%B2%D0%B7%D0%B0%D0%B8%D0%BC%D0%BE%D0%B4%D0%B5%D0%B9%D1%81%D1%82%D0%B2%D0%B8%D0%B5"&gt;исследования HCI&lt;/a&gt; говорят нам, что высокие разрешения являются основой для фундаментальных улучшений.&lt;br /&gt;
&lt;br /&gt;
Ну и если эти простые компьютерные устройства пост-PC эры с единственной функцией будет не просто "достаточными" компьютерами для простых людей, но при этом ещё и &lt;b&gt;привносить фундаментальные инновации во всю компьютерную область&lt;/b&gt;… ну, всё что я могу сказать - скорей бы наступила эра пост-PC.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688132520265708820-7366484187489338513?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=-5GkNKdUNnk:0AGSO2u7U1g: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=-5GkNKdUNnk:0AGSO2u7U1g:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=-5GkNKdUNnk:0AGSO2u7U1g:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=-5GkNKdUNnk:0AGSO2u7U1g:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=-5GkNKdUNnk:0AGSO2u7U1g:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=-5GkNKdUNnk:0AGSO2u7U1g: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=-5GkNKdUNnk:0AGSO2u7U1g:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=-5GkNKdUNnk:0AGSO2u7U1g:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=-5GkNKdUNnk:0AGSO2u7U1g:-BTjWOF_DHI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=-5GkNKdUNnk:0AGSO2u7U1g:-BTjWOF_DHI" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/GunsmokersTranslations/~4/-5GkNKdUNnk" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.transl-gunsmoker.ru/feeds/7366484187489338513/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://www.transl-gunsmoker.ru/2012/03/pc.html#comment-form" title="Комментарии: 7" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/7366484187489338513?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/7366484187489338513?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/GunsmokersTranslations/~3/-5GkNKdUNnk/pc.html" title="Добро пожаловать в эру пост-PC" /><author><name>Александр Алексеев</name><uri>https://profiles.google.com/113168002104297556003</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh3.googleusercontent.com/-aUMttZQBsuI/AAAAAAAAAAI/AAAAAAAAC3Y/QuZ7K9t_WzE/s512-c/photo.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://4.bp.blogspot.com/-4GXCd20GvFY/T2jLGvvr3wI/AAAAAAAADkY/S5kANdSftro/s72-c/6a0120a85dcdae970b0163030a719a970d-800wi.png" height="72" width="72" /><thr:total>7</thr:total><feedburner:origLink>http://www.transl-gunsmoker.ru/2012/03/pc.html</feedburner:origLink></entry><entry gd:etag="W/&quot;Dk4EQ3w5eCp7ImA9WhVQEks.&quot;"><id>tag:blogger.com,1999:blog-1688132520265708820.post-7279905968512780618</id><published>2012-03-16T22:35:00.001+04:00</published><updated>2012-04-01T11:21:42.220+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-04-01T11:21:42.220+04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="кодинг" /><category scheme="http://www.blogger.com/atom/ns#" term="окна" /><title>Об окнах Windows</title><content type="html">Это перевод &lt;a href="http://msdn.microsoft.com/en-us/library/ms632597(VS.85).aspx" title="About Windows"&gt;About Windows&lt;/a&gt;. Автор: MSDN.&lt;br /&gt;
&lt;br /&gt;
Эта статья описывает программные элементы, которые приложения могут использовать для создания и управления окнами; управления отношениями между окнами; изменения размера, перемещения и отображения окон.&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
Содержание:
&lt;ul&gt;
&lt;li&gt;&lt;a href="#desktop"&gt;Окно рабочего стола&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#application"&gt;Окна приложений&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="#client_area"&gt;Клиентская область&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#nonclient_area"&gt;Неклиентская область&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#control_dlgboxes"&gt;Элементы управления и диалоговые окна&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#attributes"&gt;Атрибуты окон&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#class_name"&gt;Имя класса&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#window_name"&gt;Имя окна&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#window_style"&gt;Стили окна&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#extended_window_style"&gt;Дополнительные стили&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#position"&gt;Положение&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#Size"&gt;Размер&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#parent_owner_handle"&gt;Описатель родительского окна &lt;b&gt;или&lt;/b&gt; окна-владельца&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#menu_handle_child_id"&gt;Описатель меню &lt;b&gt;или&lt;/b&gt; идентификатор дочернего окна&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#app_instance_handle"&gt;Описатель экземпляра&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#creation_data"&gt;Пользовательские данные&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#window_handle"&gt;Оконный описатель&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#delphi"&gt;Примечания переводчика (специфика Delphi)&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#VCL"&gt;Окна в VCL&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#tapplication"&gt;Окно Application&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#modal"&gt;Модальные окна&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;br /&gt;
&lt;h3&gt;&lt;a id="desktop"&gt;&lt;/a&gt;Окно рабочего стола&lt;/h3&gt;
Когда вы запускаете систему, она автоматически создаёт окно рабочего стола (desktop window). &lt;em&gt;Окно рабочего стола&lt;/em&gt; - это системное окно, которое отвечает за отрисовку фона экрана и служит базой для всех остальных окон, показываемых приложениями. И поэтому, &lt;a href="http://www.transl-gunsmoker.ru/2009/01/blog-post_3072.html" title="Что такого особенного в окне рабочего стола?"&gt;окно рабочего стола является особым окном&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
Окно рабочего стола использует растр (bitmap) для рисования фона экрана. Этот рисунок называется &lt;em&gt;обоями рабочего стола&lt;/em&gt; (desktop wallpaper). По умолчанию окно рабочего стола использует растр из .bmp файла, указанного в настройках системы.&lt;br /&gt;
&lt;br /&gt;
&lt;a href="http://msdn.microsoft.com/en-us/library/ms633504(VS.85).aspx" title="MSDN: GetDesktopWindow Function"&gt;Функция &lt;code&gt;GetDesktopWindow&lt;/code&gt;&lt;/a&gt; возвращает описатель окна рабочего стола.&lt;br /&gt;
&lt;br /&gt;
Приложения настройки системы (такие как апплет Панели управления) могут изменять обои рабочего стола, используя &lt;a href="http://msdn.microsoft.com/en-us/library/ms724947(VS.85).aspx" title="MSDN: SystemParametersInfo Function"&gt;функцию &lt;code&gt;SystemParametersInfo&lt;/code&gt;&lt;/a&gt; с &lt;em&gt;wAction&lt;/em&gt; = &lt;code&gt;SPI_SETDESKWALLPAPER&lt;/code&gt; и &lt;em&gt;lpvParam&lt;/em&gt; = имени файла с обоями. Функция &lt;code&gt;SystemParametersInfo&lt;/code&gt; загрузит растр из указанного файла и будет использовать его для отрисовки фона экрана, и, кроме того, сохранит имя файла в настройках.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;&lt;a id="application"&gt;&lt;/a&gt;Окна приложений&lt;/h3&gt;
Каждое графическое приложение Windows имеет как минимум одно окно, называемое &lt;em&gt;главным окном&lt;/em&gt; (main window), которое обеспечивает первичный интерфейс между пользователем и приложением. Большинство приложений создаёт дополнительные окна (явно или неявно), чтобы выполнять задачи приложения. Каждое окно играет свою роль в отображении информации и приёма ввода от пользователя.&lt;br /&gt;
&lt;br /&gt;
Когда вы запускаете приложение, система также ассоциирует с приложением кнопку на панели задач (taskbar button). &lt;em&gt;Кнопка на панели задач&lt;/em&gt; содержит значок программы и заголовок. Когда приложение активно, эта кнопка показывается как нажатая, либо выделяется цветом.&lt;br /&gt;
&lt;br /&gt;
Окно приложения может включать в себя элементы вроде заголовка (title bar), оконного меню (window menu, ранее известного как "системное меню"), меню (menu bar), кнопок минимизации и максимизации, кнопки восстановления (restore button), кнопки закрытия (close button), бордюра (border), клиентской области (client area), горизонтальной и вертикальной полосок прокрутки (scroll bar). Главное окно приложений часто включает в себя все эти элементы сразу. Рисунок ниже иллюстрирует эти элементы в типичном главном окне программы:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;
&lt;a href="http://2.bp.blogspot.com/-LR9iRydjEa8/T1iQYftKeuI/AAAAAAAADf8/nwAzhLLPWxI/s1600/ms632597.cswin_02%2528en-us%252CVS.85%2529.png" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="272" width="376" src="http://2.bp.blogspot.com/-LR9iRydjEa8/T1iQYftKeuI/AAAAAAAADf8/nwAzhLLPWxI/s400/ms632597.cswin_02%2528en-us%252CVS.85%2529.png" /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;&lt;a id="client_area"&gt;&lt;/a&gt;Клиентская область&lt;/h4&gt;
&lt;em&gt;Клиентская область&lt;/em&gt; (client area) является той частью окна, где приложение показывает основную информацию (текст или графику). Например, текстовый редактор отображает документ в клиентской области главного окна. Чтобы управлять клиентской областью окна (принимать ввод пользователя и отображать в ней информацию), приложение должно указать функцию обратного вызова, называемую оконной процедурой (window procedure). См. &lt;a href="http://msdn.microsoft.com/en-us/library/ms632593(VS.85).aspx" title="MSDN: Window Procedures"&gt;Window Procedures&lt;/a&gt; для дальнейшего обсуждения.&lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;&lt;a id="nonclient_area"&gt;&lt;/a&gt;Неклиентская область&lt;/h4&gt;
Заголовок, меню, оконное меню, кнопки минимизации, максимизации, закрытия и восстановления, бордюр и полосы прокрутки собирательно называются &lt;em&gt;неклиентской областью&lt;/em&gt; окна (nonclient area). Система автоматически управляет большинством аспектов неклиентской области; приложение же управляет поведением клиентской области окна.&lt;br /&gt;
&lt;br /&gt;
&lt;em&gt;Заголовок окна&lt;/em&gt; (title bar) показывает значок окна (указывается приложением) и строку текста; обычно текст указывает название приложения и цель окна. Приложение указывает значок и текст при создании окна. Кроме того заголовок также позволяет перемещать окно по экрану, используя мышь или иное указующее устройство.&lt;br /&gt;
&lt;br /&gt;
Большинство приложений добавляют окнам &lt;em&gt;меню&lt;/em&gt; (menu bar), которое включает в себя команды, поддерживаемые приложением. Элементы в полоске меню соответствуют категориям команд меню. Щелчок по элементу меню обычно приводит к его "раскрытию": показу всплывающего меню с элементами, соответствующими заданной категории. Щёлкая по элементу-команде, пользователь просит приложение выполнить эту задачу.&lt;br /&gt;
&lt;br /&gt;
&lt;em&gt;Оконное меню&lt;/em&gt; (window menu) создаётся и управляется системой (и поэтому оно ранее называлось системным меню). Оно содержит стандартный набор команд по управлению окном - вроде изменения размера и положения окна, его закрытия. Для получения дальнейшей информации - см. &lt;a href="http://msdn.microsoft.com/en-us/library/ms646977(VS.85).aspx" title="MSDN: Menus"&gt;Menus&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
Кнопки в верхнем-правом углу окна влияют на размер и положение окна. Когда пользователь щёлкает по &lt;em&gt;кнопке максимизации&lt;/em&gt; (maximize button), система увеличивает окно до размера текущего монитора, так что оно займёт весь рабочий стол минус панель задач (в одномониторной конфигурации). Одновременно с этим система заменит кнопку максимизации кнопкой восстановления. Когда вы щёлкаете по &lt;em&gt;кнопке восстановления&lt;/em&gt; (restore button), система восстанавливает окно на его исходной позиции и с исходным размером. Когда вы щёлкаете по &lt;em&gt;кнопке минимизации&lt;/em&gt; (minimize button), система уменьшает окно до размера её кнопки на панели задач, размещает окно поверх его кнопки на панели задач и показывает кнопку на панели задач в поднятом (нормальном) состоянии (прим. пер.: &lt;a href="http://www.transl-gunsmoker.ru/2009/10/blog-post.html" title="Куда прятались окна до того, как была изобретена панель задач?"&gt;это описание - упрощение&lt;/a&gt;). Чтобы восстановить приложение с его исходными положением и размерами - щёлкните по кнопке в панели задач. Когда вы щёлкаете по &lt;em&gt;кнопке закрытия&lt;/em&gt; (close button), приложение выходит.&lt;br /&gt;
&lt;br /&gt;
&lt;em&gt;Бордюр&lt;/em&gt; (border) - это область по периметру окна, которая позволяет пользователю изменять размер окна, используя мышь или иное указующее устройство.&lt;br /&gt;
&lt;br /&gt;
&lt;em&gt;Полосы прокрутки&lt;/em&gt; (scroll bar) переводят ввод с клавиатуры и мыши в смещения, которые используются приложением для сдвига содержимого клиентской области. К примеру, приложение текстовый редактор может показывать большой документ с вертикальной полосой прокрутки, чтобы пользователь мог использовать её для перемещения по документу.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;&lt;a id="control_dlgboxes"&gt;&lt;/a&gt;Элементы управления и диалоговые окна&lt;/h3&gt;
Приложение может создавать несколько типов окон в дополнение к его главному окну, включая элементы управления (controls) и диалоговые окна (dialog box).&lt;br /&gt;
&lt;br /&gt;
&lt;em&gt;Элемент управления&lt;/em&gt; (control) - это окно, которое приложение использует для получения информации от пользователя. Приложения также используют элементы управления, чтобы управлять определённой возможностью приложения. К примеру, элементами управления являются кнопки, списки, поля ввода, дополнительные полосы прокрутки, панели, панели инструментов и т.п. Для дальнейшей информации -см. &lt;a href="http://msdn.microsoft.com/en-us/library/bb773173(VS.85).aspx" title="MSDN: Windows Controls"&gt;Windows Controls&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
Элементы управления не могут использовать сами по себе и всегда используются с другим окном - обычно, диалоговым окном. &lt;em&gt;Диалоговое окно&lt;/em&gt; (dialog box) - это окно, которое содержит один или более элементов управления. Приложение использует диалоговые окна, чтобы запросить у пользователя информацию для выполнения команды. К примеру, приложение, которое имеет команду открытия файла, может показывать диалоговое окно, которое включает в себя элементы управления для указания имени и пути файла. Диалоговые окна обычно не используют те же компоненты окна, что и главные окна приложения. Большинство  диалоговых окон имеют заголовок, системное меню, кнопки минимизации/максимизации и закрытия, бордюр (часто - не позволяющий менять размеры окна) и клиентскую область, но они обычно не имеют меню и полос прокрутки. Для получения дополнительной информации - см. &lt;a href="http://msdn.microsoft.com/en-us/library/ms632588(VS.85).aspx" title="MSDN: Dialog Boxes"&gt;Dialog Boxes&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;em&gt;Окно-сообщение&lt;/em&gt; (message box) - это специальное диалоговое окно, которое показывает сообщение, предупреждение или сообщение об ошибке пользователю. К примеру, окно-сообщение может использоваться, чтобы сообщить пользователю о невозможности выполнения запрошенной им операции. Для дальнейшей информации - см. &lt;a href="http://msdn.microsoft.com/en-us/library/ms644994(VS.85).aspx#message_boxes#message_boxes" title="MSDN: Message Boxes"&gt;Message Boxes&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;&lt;a id="attributes"&gt;&lt;/a&gt;Оконные атрибуты&lt;/h3&gt;
Приложение должно указывать следующую информацию при создании окна (за исключением &lt;a href="#window_handle"&gt;оконного описателя&lt;/a&gt;, который создаёт сама функция создания окон):
&lt;ul&gt;
&lt;li&gt;&lt;a href="#class_name"&gt;Имя класса&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#window_name"&gt;Имя окна&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#window_style"&gt;Стиль окна&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#extended_window_style"&gt;Дополнительный стиль окна&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#position"&gt;Положение&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#Size"&gt;Размер&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#parent_owner_handle"&gt;Описатель родительского окна &lt;b&gt;или&lt;/b&gt; окна-владельца&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#menu_handle_child_id"&gt;Описатель меню &lt;b&gt;или&lt;/b&gt; идентификатор дочернего окна&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#app_instance_handle"&gt;Описатель экземпляра&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#creation_data"&gt;Пользовательские данные&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#window_handle"&gt;Оконный описатель&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
Эти оконные атрибуты описываются ниже.&lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;&lt;a id="class_name"&gt;&lt;/a&gt;Имя класса&lt;/h4&gt;
Каждое окно принадлежит оконному классу. Приложение должно зарегистрировать оконный класс до создания окна этого класса. &lt;em&gt;Оконный класс&lt;/em&gt; (window class) определяет большинство аспектов поведения и отображения окна. Главным компонентом оконного класса является &lt;em&gt;оконная процедура&lt;/em&gt; (window procedure) - функция обратного вызова, которая принимает весь ввод пользователя и все запросы, отправленные окну. Система предоставляет ей ввод и запросы в виде &lt;em&gt;оконных сообщений&lt;/em&gt; (window message). Для дальнейшей информации - см. &lt;a href="http://msdn.microsoft.com/en-us/library/ms632596(VS.85).aspx" title="MSDN: Window Classes"&gt;Window Classes&lt;/a&gt;, &lt;a href="http://msdn.microsoft.com/en-us/library/ms632593(VS.85).aspx" title="MSDN: Window Procedures"&gt;Window Procedures&lt;/a&gt; и &lt;a href="http://msdn.microsoft.com/en-us/library/ms632590(VS.85).aspx" title="MSDN: Messages and Message Queues"&gt;Messages and Message Queues&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;&lt;a id="window_name"&gt;&lt;/a&gt;Имя окна&lt;/h4&gt;
&lt;em&gt;Имя окна&lt;/em&gt; (window name) - это текстовая строка, которая идентифицирует окно для пользователя. Главное окно, диалоговое окно или окно-сообщение обычно показывают это имя в заголовке окна. Элемент управления также может показывать имя окна, в зависимости от своего оконного класса. К примеру, кнопки, поля ввода и статический текст показывают имя окна в прямоугольнике элемента управления.  С другой стороны, элементы вроде списков и списков выбора не показывают имена окон.&lt;br /&gt;
&lt;br /&gt;
Чтобы изменить имя окна после создания окна, используйте &lt;a href="http://msdn.microsoft.com/en-us/library/ms633546(VS.85).aspx" title="MSDN: SetWindowText Function"&gt;функцию &lt;code&gt;SetWindowText&lt;/code&gt;&lt;/a&gt;. Используйте функции &lt;a href="http://msdn.microsoft.com/en-us/library/ms633521(VS.85).aspx" title="MSDN: GetWindowTextLength"&gt;&lt;code&gt;GetWindowTextLength&lt;/code&gt;&lt;/a&gt; и &lt;a href="http://msdn.microsoft.com/en-us/library/ms633520(VS.85).aspx" title="MSDN: GetWindowText Function"&gt;&lt;code&gt;GetWindowText&lt;/code&gt;&lt;/a&gt; для получения имени окна.&lt;br /&gt;
&lt;br /&gt;
См. также: &lt;a title="Тайная жизнь GetWindowText" href="http://www.transl-gunsmoker.ru/2008/11/getwindowtext.html"&gt;тайная жизнь &lt;code&gt;GetWindowText&lt;/code&gt;&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;&lt;a id="window_style"&gt;&lt;/a&gt;Стиль окна&lt;/h4&gt;
Каждое окно имеет один или несколько оконных стилей. &lt;em&gt;Оконный стиль&lt;/em&gt; (window style) - это числовая константа, которая определяет аспект поведения или отображения, не указанный в оконном классе. Приложение обычно устанавливает стили во время создания окна. Но оно также может изменить некоторые стили после создания окна, используя &lt;a href="http://msdn.microsoft.com/en-us/library/ms633591(VS.85).aspx" title="MSDN: SetWindowLong Function"&gt;функцию &lt;code&gt;SetWindowLong&lt;/code&gt;&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
Стили интерпретируются системой и, в некотором роде, оконной процедурой класса окна.&lt;br /&gt;
&lt;br /&gt;
Некоторые оконные стили применимы к любым окнам, но большинство стилей специфичны для оконных классов. Общие стили представлены константами, которые начинаются с префикса &lt;code&gt;WS_&lt;/code&gt;; вы можете комбинировать стили, используя оператор &lt;code&gt;or&lt;/code&gt;. Стили окна, специфичные для оконного класса, определяют поведение и отображение окон, принадлежащих этому классу. К примеру, класс &lt;code&gt;SCROLLBAR&lt;/code&gt; создаёт элемент управления "полоса прокрутки". При этом стили &lt;a href="http://msdn.microsoft.com/en-us/library/bb787533(VS.85).aspx" title="MSDN: Scroll Bar Control Styles"&gt;&lt;code&gt;SBS_HORZ&lt;/code&gt;&lt;/a&gt; и &lt;code&gt;SBS_VERT&lt;/code&gt; указывают создаётся ли вертикальная или горизонтальная полоса прокрутки.&lt;br /&gt;
&lt;br /&gt;
Вот описание стилей для некоторых стандартных системных оконных классов:
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://msdn.microsoft.com/en-us/library/ms632600(VS.85).aspx" title="MSDN: Window Styles"&gt;Window Styles&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://msdn.microsoft.com/en-us/library/bb775951(VS.85).aspx" title="MSDN: Button Styles"&gt;Button Styles&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://msdn.microsoft.com/en-us/library/bb775796(VS.85).aspx" title="MSDN: Combo Box Styles"&gt;Combo Box Styles&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://msdn.microsoft.com/en-us/library/bb775464(VS.85).aspx" title="MSDN: Edit Control Styles"&gt;Edit Control Styles&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://msdn.microsoft.com/en-us/library/bb775149(VS.85).aspx" title="MSDN: List Box Styles"&gt;List Box Styles&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://msdn.microsoft.com/en-us/library/bb774367(VS.85).aspx" title="MSDN: Rich Edit Control Styles"&gt;Rich Edit Control Styles&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://msdn.microsoft.com/en-us/library/bb787533(VS.85).aspx" title="MSDN: Scroll Bar Control Styles"&gt;Scroll Bar Control Styles&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://msdn.microsoft.com/en-us/library/bb760773(VS.85).aspx" title="MSDN: Static Control Styles"&gt;Static Control Styles&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;br /&gt;
&lt;h4&gt;&lt;a id="extended_window_style"&gt;&lt;/a&gt;Расширенный стиль окна&lt;/h4&gt;
Каждое окно опционально может иметь один или несколько дополнительных (расширенных) оконных стилей. &lt;em&gt;Расширенный оконный стиль&lt;/em&gt; (extended window style) - это числовая константа, которая определяет аспект окна, не указанный в классе окна или простом стиле окна. Приложение обычно устанавливает расширенные стили во время создания окна. Но оно также может изменить некоторые стили после создания окна, используя &lt;a href="http://msdn.microsoft.com/en-us/library/ms633591(VS.85).aspx" title="MSDN: SetWindowLong Function"&gt;функцию &lt;code&gt;SetWindowLong&lt;/code&gt;&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
Для дальнейшей информации - см. &lt;a href="http://msdn.microsoft.com/en-us/library/ms632680(VS.85).aspx" title="MSDN: CreateWindowEx Function"&gt;&lt;code&gt;CreateWindowEx&lt;/code&gt;&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;&lt;a id="position"&gt;&lt;/a&gt;Положение&lt;/h4&gt;
Положение окна определяется как координаты его левого-верхнего угла, измеряемые в пикселях экрана. Эти координаты (иногда называемые оконными координатами (window coordinates)) всегда относительны левого-верхнего угла экрана или (для child окон) - левого-верхнего угла клиентской области родительского окна. К примеру, окно верхнего уровня с координатами (10,10) размещается на 10 пикселей правее левого-верхнего угла экрана и на 10 пикселей ниже его. С другой стороны, child окно с координатами (10,10) размещается на 10 пикселей правее и на 10 пикселей ниже левого-верхнего угла клиентской области родительского окна.&lt;br /&gt;
&lt;br /&gt;
&lt;a href="http://msdn.microsoft.com/en-us/library/ms633558(VS.85).aspx" title="MSDN: WindowFromPoint"&gt;Функция &lt;code&gt;WindowFromPoint&lt;/code&gt;&lt;/a&gt; ищет окно, занимающее указанную точку на экране, и возвращает его описатель. Аналогичным образом, &lt;a href="http://msdn.microsoft.com/en-us/library/ms632676(VS.85).aspx" title="MSDN: ChildWindowFromPoint Function"&gt;функция &lt;code&gt;ChildWindowFromPoint&lt;/code&gt;&lt;/a&gt; и &lt;a href="http://msdn.microsoft.com/en-us/library/ms632677(VS.85).aspx" title="MSDN: ChildWindowFromPointEx Function"&gt;функция &lt;code&gt;ChildWindowFromPointEx&lt;/code&gt;&lt;/a&gt; возвращают описатель дочернего окна, занимающего указанную точку в родительском окне. Хотя &lt;code&gt;ChildWindowFromPointEx&lt;/code&gt; может игнорировать невидимые, отключенные и прозрачные окна - &lt;code&gt;ChildWindowFromPoint&lt;/code&gt; перечисляет их все.&lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;&lt;a id="Size"&gt;&lt;/a&gt;Размер&lt;/h4&gt;
Размер окна (высота и ширина) измеряется в пикселях. Окно может иметь нулевую высоту или нулевую ширину. Если приложение задаст нулевые высоту или ширину, то система установит размер в минимально допустимый. Чтобы определить минимально возможный размер окон, приложение может вызвать &lt;a href="http://msdn.microsoft.com/en-us/library/ms724385(VS.85).aspx" title="MSDN: GetSystemMetrics Function"&gt;функцию &lt;code&gt;GetSystemMetrics&lt;/code&gt;&lt;/a&gt; с флагами &lt;code&gt;SM_CXMIN&lt;/code&gt; и &lt;code&gt;SM_CYMIN&lt;/code&gt;.&lt;br /&gt;
&lt;br /&gt;
Приложению может потребоваться создать окно с клиентской областью определённого размера (обычно приложение задаёт размеры окна, а клиентская область получается по окну за вычетом размера рамки, заголовка и т.п.). &lt;a href="http://msdn.microsoft.com/en-us/library/ms632665(VS.85).aspx" title="MSDN: AdjustWindowRect Function"&gt;Функция &lt;code&gt;AdjustWindowRect&lt;/code&gt;&lt;/a&gt; и &lt;a href="http://msdn.microsoft.com/en-us/library/ms632667(VS.85).aspx" title="MSDN: AdjustWindowRectEx Function"&gt;функция &lt;code&gt;AdjustWindowRectEx&lt;/code&gt;&lt;/a&gt; могут быть использованы для вычисления нужного размера окна, основываясь на желаемом размере клиентской области окна. Приложение может передать вычисленные значения размера в &lt;a href="http://msdn.microsoft.com/en-us/library/ms632680(VS.85).aspx" title="MSDN: CreateWindowEx Function"&gt;функцию &lt;code&gt;CreateWindowEx&lt;/code&gt;&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
Приложение может изменить размер окна на чрезмерно большой; однако ему не следует изменять размер окна на размер, больший экрана. До изменения размеров окна, приложение может узнать размеры экрана, используя &lt;a href="http://msdn.microsoft.com/en-us/library/ms724385(VS.85).aspx" title="MSDN: GetSystemMetrics Function"&gt;функцию &lt;code&gt;GetSystemMetrics&lt;/code&gt;&lt;/a&gt; с флагами &lt;code&gt;SM_CXSCREEN&lt;/code&gt; и &lt;code&gt;SM_CYSCREEN&lt;/code&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;&lt;a id="parent_owner_handle"&gt;&lt;/a&gt;Описатель родительского окна &lt;b&gt;или&lt;/b&gt; окна-владельца&lt;/h4&gt;
Окно может иметь родительское окно. Окно, которое имеет родительское окно, называется &lt;em&gt;дочерним окном&lt;/em&gt; (child window). &lt;em&gt;Родительское окно&lt;/em&gt; (parent window) предоставляет своим дочерним окнам систему координат для относительного позиционирования. Если у окна есть окно-родитель, то это влияет на его отображение; к примеру, дочернее окно обрезается так, чтобы оно не выходило за пределы родительского окна и (опционально) за пределы клиентской области родительского окна. Окно, у которого нет родителя, либо же родительским окном является окно рабочего стола, называется &lt;em&gt;окном верхнего уровня&lt;/em&gt; (top-level window). Приложение может использовать &lt;a href="http://msdn.microsoft.com/en-us/library/ms633497(VS.85).aspx" title="MSDN: EnumWindows Function"&gt;функцию &lt;code&gt;EnumWindows&lt;/code&gt;&lt;/a&gt;, чтобы получить описатель каждого окна верхнего уровня в системе.&lt;br /&gt;
&lt;br /&gt;
Кроме того, окно может владеть другим окном или иметь окно-владельца (owner window). Владетельное окно всегда появляется поверх своего владельца, оно также сворачивается, когда сворачивается его владелец, и уничтожается, когда уничтожается его владелец. Для дальнейшей информации - см. &lt;a href="http://www.transl-gunsmoker.ru/2012/04/windows.html#owned_windows" title="Owned окна"&gt;Owned окна&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;&lt;a id="menu_handle_child_id"&gt;&lt;/a&gt;Описатель меню &lt;b&gt;или&lt;/b&gt; идентификатор дочернего окна&lt;/h4&gt;
Child окно может иметь &lt;em&gt;идентификатор дочернего окна&lt;/em&gt; (child-window identifier) - это уникальное число, присваиваемое приложением. Идентификатор дочернего окна особенно полезен для приложений, создающих несколько дочерних окон. Когда приложение создаёт дочернее окно, оно может указать идентификатор для этого окна. После создания окна приложение может поменять идентификатор &lt;a href="http://msdn.microsoft.com/en-us/library/ms633591(VS.85).aspx" title="MSDN: SetWindowLong Function"&gt;функцией &lt;code&gt;SetWindowLong&lt;/code&gt;&lt;/a&gt; или получить его &lt;a href="http://msdn.microsoft.com/en-us/library/ms633584(VS.85).aspx" title="MSDN: GetWindowLong Function"&gt;функцией &lt;code&gt;GetWindowLong&lt;/code&gt;&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
Каждое окно, кроме child-окон, может иметь меню. Приложение может добавить меню в окно, указав описатель меню либо при регистрации оконного класса, либо непосредственно при создании окна.&lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;&lt;a id="app_instance_handle"&gt;&lt;/a&gt;Описатель экземпляра&lt;/h4&gt;
Каждый исполняемый модуль (exe или DLL) имеет описатель, ассоциированный с ним. &lt;a title="Для чего используется HINSTANCE, передаваемый в CreateWindow и RegisterClass?" href="http://www.transl-gunsmoker.ru/2010/04/hinstance-createwindow-registerclass.html"&gt;Система использует этот идентификатор, чтобы отличать оконные классы, создаваемые разными модулями&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;&lt;a id="creation_data"&gt;&lt;/a&gt;Пользовательские данные&lt;/h4&gt;
Каждое окно может иметь ассоциированные с ним пользовательские данные, не используемые системой. Когда создаётся окно, система передаёт указатель на пользовательские данные в оконную процедуру создаваемого окна. Оконная процедура может использовать эти данные для инициализации окна и (опционально) для сохранения данных с окном.&lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;&lt;a id="window_handle"&gt;&lt;/a&gt;Описатель окна&lt;/h4&gt;
После создания окна функция создания возвращает &lt;em&gt;оконный описатель&lt;/em&gt; (window handle), который уникально идентифицирует окно в системе. Оконный описатель имеет тип &lt;code&gt;HWND&lt;/code&gt;. Приложение может использовать этот описатель в других функциях работы с окнами.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;&lt;a id="delphi"&gt;Примечания переводчика&lt;/a&gt;&lt;/h3&gt;
Приложения Delphi имеют некоторые особенности по сравнению с вышеизложенным материалом.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Предупреждение:&lt;/b&gt; я не являюсь экспертом в интерфейсе пользователя, поэтому текст ниже может содержать ошибки. &lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;&lt;a id="VCL"&gt;Окна в VCL&lt;/a&gt;&lt;/h4&gt;
В Delphi вся работа с окнами заключается в класс-оболочку &lt;a href="http://docwiki.embarcadero.com/VCL/en/Controls.TWinControl" title="Controls.TWinControl"&gt;&lt;code&gt;TWinControl&lt;/code&gt;&lt;/a&gt;. Формы, кнопки, списки - всё это классы, наследуемые от &lt;code&gt;TWinControl&lt;/code&gt;. К примеру: &lt;code&gt;TForm1 -&gt; TForm -&gt; TCustomForm -&gt; TScrollingWinControl -&gt; TWinControl&lt;/code&gt; или &lt;code&gt;TButton -&gt; TCustomButton -&gt; TButtonControl -&gt; TWinControl&lt;/code&gt;.&lt;br /&gt;
&lt;br /&gt;
У каждого объекта типа &lt;code&gt;TWinControl&lt;/code&gt; есть описатель - &lt;a href="http://docwiki.embarcadero.com/VCL/en/Controls.TWinControl.Handle" title="Controls.TWinControl.Handle"&gt;свойство &lt;code&gt;Handle&lt;/code&gt; типа &lt;code&gt;HWND&lt;/code&gt;&lt;/a&gt;. Этот описатель создаётся по запросу - при первом обращении к свойству &lt;code&gt;Handle&lt;/code&gt;. Эту проверку выполняет метод &lt;code&gt;HandleNeeded&lt;/code&gt;. Создание описателя выполняет &lt;a href="http://docwiki.embarcadero.com/VCL/en/Controls.TWinControl.CreateHandle" title="Controls.TWinControl.CreateHandle"&gt;метод &lt;code&gt;CreateHandle&lt;/code&gt;&lt;/a&gt;, который является обёрткой к  &lt;a href="http://docwiki.embarcadero.com/VCL/en/Controls.TWinControl.CreateWnd" title="Controls.TWinControl.CreateWnd"&gt;методу &lt;code&gt;CreateWnd&lt;/code&gt;&lt;/a&gt;. Фактически, метод &lt;code&gt;CreateHandle&lt;/code&gt; просто вызывает &lt;code&gt;CreateWnd&lt;/code&gt;, выполняя пост-настройку объекта после создания окна (настройка якорей и ассоциация объекта &lt;code&gt;TWinControl&lt;/code&gt; с окном).&lt;br /&gt;
&lt;br /&gt;
Само создание окна выполняется так: сперва метод &lt;code&gt;CreateWnd&lt;/code&gt; строит параметры окна, используя &lt;a href="http://docwiki.embarcadero.com/VCL/en/Controls.TWinControl.CreateParams" title="Controls.TWinControl.CreateParams"&gt;метод &lt;code&gt;CreateParams&lt;/code&gt;&lt;/a&gt;, который заполняет &lt;a href="http://docwiki.embarcadero.com/VCL/en/Controls.TCreateParams" title="Controls.TCreateParams"&gt;запись типа &lt;code&gt;TCreateParams&lt;/code&gt;&lt;/a&gt;. Эти параметры проверяются на допустимость, в них записывается оконная процедура и вызывается &lt;a href="http://docwiki.embarcadero.com/VCL/en/Controls.TWinControl.CreateWindowHandle" title="Controls.TWinControl.CreateWindowHandle"&gt;метод &lt;code&gt;CreateWindowHandle&lt;/code&gt;&lt;/a&gt;, который, собственно, и создаёт окно (и является простой обёрткой к функции &lt;code&gt;CreateWindowEx&lt;/code&gt;).&lt;br /&gt;
&lt;br /&gt;
Если вы заинтересованы в изменении создаваемого окна, то вы можете заместить метод &lt;code&gt;CreateParams&lt;/code&gt; (если вам нужно просто поменять параметры создания окна), &lt;code&gt;CreateWindowHandle&lt;/code&gt; (если вы хотите задавать параметры явно вручную), либо &lt;code&gt;CreateWnd&lt;/code&gt; (если вы хотите сделать пост-обработку создания окна).&lt;br /&gt;
&lt;br /&gt;
Оконной процедурой (точкой входа) в &lt;code&gt;TWinControl&lt;/code&gt; является метод &lt;code&gt;MainWndProc&lt;/code&gt;. Этот метод не виртуален, но всё, что он делает - вызывает настоящую оконную процедуру &lt;a href="http://docwiki.embarcadero.com/VCL/en/Controls.TControl.WindowProc" title="Controls.TControl.WindowProc"&gt;&lt;code&gt;WindowProc&lt;/code&gt;&lt;/a&gt;, обернув вызов в &lt;code&gt;try&lt;/code&gt;/&lt;code&gt;except&lt;/code&gt; и вызывая &lt;a href="http://docwiki.embarcadero.com/VCL/en/Forms.TApplication.HandleException" title="Forms.TApplication.HandleException"&gt;&lt;code&gt;Application.HandleException&lt;/code&gt;&lt;/a&gt; при ошибке. Именно это - то самое место, в котором вы получаете сообщение об ошибке при исключении в обработчике сообщения (например, нажатия на кнопку) вместо вылета программы. Сама же оконная процедура &lt;code&gt;WindowProc&lt;/code&gt; является и вовсе свойством, которое можно присвоить в любое значение (для установки своей оконной процедуры). По умолчанию, &lt;code&gt;WindowProc&lt;/code&gt; указывает на (виртуальный) &lt;a href="http://docwiki.embarcadero.com/VCL/en/Controls.TControl.WndProc" title="Controls.TControl.WndProc"&gt;метод &lt;code&gt;WndProc&lt;/code&gt;&lt;/a&gt; того же объекта. Как правило, если вы хотите дополнить или заменить оконную процедуру, то вы должны заметить метод &lt;code&gt;WndProc&lt;/code&gt; в классе-наследнике (если вы хотите выполнить замещение в "вашем" классе), либо изменить свойство &lt;code&gt;WindowProc&lt;/code&gt; (если вы хотите изменить поведение внешнего объекта).&lt;br /&gt;
&lt;br /&gt;
Метод &lt;code&gt;WndProc&lt;/code&gt; является конечной реализацией оконной процедуры. Собственно, это даже не "изобретение" &lt;code&gt;TWinControl&lt;/code&gt; - этот метод появился у предка &lt;code&gt;TWinControl&lt;/code&gt;: &lt;a href="http://docwiki.embarcadero.com/VCL/en/Controls.TControl" title="Controls.TControl"&gt;&lt;code&gt;TControl&lt;/code&gt;&lt;/a&gt;. &lt;code&gt;TControl&lt;/code&gt; является базовым классом для всех элементов управления, необязательно оконных. Оконные элементу управления наследуются от &lt;code&gt;TWinControl&lt;/code&gt;.&lt;br /&gt;
&lt;br /&gt;
Таким образом, цепочка вызовов при обработке сообщений выглядит так: &lt;code&gt;MainWndProc -&gt; WindowProc -&gt; WndProc&lt;/code&gt;. &lt;code&gt;WndProc&lt;/code&gt; может сама обработать сообщение, а может оставить его на обработку по умолчанию. В последнем случае сообщение дополнительно проходит такой путь: &lt;code&gt;WndProc -&gt; &lt;a href="http://docwiki.embarcadero.com/VCL/en/System.TObject.Dispatch" title="System.TObject.Dispatch"&gt;Dispatch&lt;/a&gt; -&gt; &lt;a href="http://docwiki.embarcadero.com/VCL/en/System.TObject.DefaultHandler" title="System.TObject.DefaultHandler"&gt;DefaultHandler&lt;/a&gt; -&gt; &lt;a href="http://docwiki.embarcadero.com/VCL/en/Controls.TWinControl.DefWndProc" title="Controls.TWinControl.DefWndProc"&gt;DefWndProc&lt;/a&gt;&lt;/code&gt;. Метод &lt;code&gt;Dispatch&lt;/code&gt; сначала пытается направить сообщение &lt;a href="http://www.transl-gunsmoker.ru/2011/07/hack-9-dynamic-method-table-structure.html" title="Хак №9: структура таблицы динамических методов"&gt;message-методам&lt;/a&gt;. И только при их отсутствии - вызывает &lt;code&gt;DefaultHandler&lt;/code&gt;. &lt;code&gt;DefWndProc&lt;/code&gt; вызывается только для оконных контролов при наличии описателя. Таким образом, если вы заинтересованы в необработанных сообщениях - то вам нужно использовать &lt;code&gt;DefWndProc&lt;/code&gt; для окон и &lt;code&gt;DefaultHandler&lt;/code&gt; для неоконных контролов.&lt;br /&gt;
&lt;br /&gt;
Заметьте, что &lt;code&gt;Dispatch&lt;/code&gt;, &lt;code&gt;DefaultHandler&lt;/code&gt; и message-методы вводятся аж в &lt;a href="http://docwiki.embarcadero.com/VCL/en/System.TObject" title="System.TObject"&gt;&lt;code&gt;TObject&lt;/code&gt;&lt;/a&gt;, что означает доступность диспетчеризации сообщений для &lt;b&gt;любого&lt;/b&gt; объекта Delphi.&lt;br /&gt;
&lt;br /&gt;
Почти все вышеупомянутые методы являются виртуальными - т.е. доступными для замещения и модификации в классах-наследниках, что позволяет нам менять стандартное поведение в широких пределах.&lt;br /&gt;
&lt;br /&gt;
&lt;a href="http://docwiki.embarcadero.com/VCL/en/Classes.TComponent" title="Classes.TComponent"&gt;&lt;code&gt;TComponent&lt;/code&gt;&lt;/a&gt; (который является предком &lt;code&gt;TControl&lt;/code&gt;, а, следовательно и &lt;code&gt;TWinControl&lt;/code&gt;) вводит понятие "владельца" компонента (Owner). Заметьте, что это не то же самое понятие, что окно-владелец в терминах системы, как обсуждается выше. Окно-владелец и окно-родитель (в терминах системы) указываются одинаковым образом - как поле &lt;code&gt;WndParent&lt;/code&gt; в записи &lt;code&gt;TCreateParams&lt;/code&gt;, параметр &lt;code&gt;hWndParent&lt;/code&gt; в &lt;a title="MSDN: CreateWindowEx" href="http://msdn.microsoft.com/en-us/library/windows/desktop/ms632680(v=vs.85).aspx"&gt;функции &lt;code&gt;CreateWindowEx&lt;/code&gt;&lt;/a&gt; и аналогичных местах. То, как трактуется это окно (как родитель или владелец) зависит от типа создаваемого окна (дочернее или верхнего уровня). Поведение этих окон описано выше.&lt;br /&gt;
&lt;br /&gt;
Delphi же вводит понятие владельца в ином смысле. &lt;a href="http://docwiki.embarcadero.com/VCL/en/Classes.TComponent.Owner" title="Classes.TComponent.Owner"&gt;Владелец (в терминах Delphi)&lt;/a&gt; - это компонент, который отвечает за удаление владеемого компонента. Его можно указать при создании компонента, либо сменить/задать уже после создания (что бывает редко). При удалении компонента-владельца удаляются и все компоненты, которыми владеет владелец. Вы можете не указывать владельца, если хотите удалять компонент сами, вручную. Как правило, форма является владельцем всех компонентов, размещённых на ней.&lt;br /&gt;
&lt;br /&gt;
Вам не следует путать владельца в терминах системы и владельца в терминах Delphi. Владелец в терминах системы отвечает за взаимодействие окон. Владелец в терминах Delphi не связан с окнами, он контролирует время жизни объекта. Фактически, он применим также и к неоконным элементам управления и даже невизуальным компонентам.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Примечание:&lt;/b&gt; Delphi также позволяет вам изменить родительское окно уже после создания объекта. Это позволяет вам сделать "перескакивание" дочернего контрола с одного контейнера (формы, панели) на другой.&lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;&lt;a id="tapplication"&gt;Окно Application&lt;/a&gt;&lt;/h4&gt;
Следующий тонкий момент связан со специальным окном Application в Delphi. Как вам должно быть известно, в Delphi есть глобальный объект &lt;a href="http://docwiki.embarcadero.com/VCL/en/Forms.TApplication" title="Forms.TApplication"&gt;&lt;code&gt;Application&lt;/code&gt;&lt;/a&gt;, представляющий собой "приложение". В нём содержатся глобальные параметры и некоторые служебные методы-помощники. Сейчас нас интересует тот факт, что в &lt;code&gt;Application&lt;/code&gt; есть окно. Это - служебное окно. Его описатель доступен через &lt;a href="http://docwiki.embarcadero.com/VCL/en/Forms.TApplication.Handle" title="Forms.TApplication.Handle"&gt;свойство &lt;code&gt;Application.Handle&lt;/code&gt;&lt;/a&gt;. Это окно является обычным окном. Оно видимо, находится на экране (в центре), но... имеет нулевой размер! Так что реально вы его увидеть не можете, не можете и щёлкнуть по нему.&lt;br /&gt;
&lt;br /&gt; 
&lt;b&gt;Примечание:&lt;/b&gt; окно &lt;code&gt;Application&lt;/code&gt; создаётся только в программе, но не в DLL.&lt;br /&gt; 
&lt;br /&gt; 
Хорошо, а чем ещё интересно это окно? Вспомните &lt;a title="А что это за пустые кнопки на панели задач, которые исчезают, когда я щёлкаю по ним?" href="http://www.transl-gunsmoker.ru/2008/12/blog-post_08.html"&gt;правила, по которым окна появляются на панели задач&lt;/a&gt;. Идеология системы: одно окно - одна кнопка. Каждое окно имеет кнопку (некоторые окна не имеют кнопок как служебные), окно не может иметь две кнопки. Если на панели задач есть кнопка, то она имеет взаимно-однозначное соответствие с окном (текст, меню, иконка). Это достаточно простая и понятная модель.&lt;br /&gt; 
&lt;br /&gt;
В Delphi принята несколько иная идеология. Кнопка в панели задач представляет собой "приложение", а не отдельное окно. У неё свой заголовок, своя иконка, она сворачивает все окна приложения - и так далее. Поскольку в системе окна работают не так, то Delphi эмулирует такое поведение. Кнопка в панели задач - это, на самом деле, окно Application, а реальные окна программы там не появляются. Все окна приложения имеют владельца - окно Application. Сворачивание приложения на самом деле заключается в скрытии окон (реально сворачивается только окно Application). При этом, конечно же, куча сил уходит на синхронизацию различных состояний между участниками подобной системы. Отсюда идёт множество вопросов вроде "как мне свернуть/размернуть приложение в трей", вопросов про правильную связку окон и им подобных.&lt;br /&gt;
&lt;br /&gt;
Когда вышла Windows Vista, в которой появились новые возможности вроде "живого предпросмотра", анимации сворачивания/разворачивания окон и других Aero-эффектов, обнаружились пробелы в модели эмуляции поведения Delphi. &lt;a title="Создание приложений для Windows Vista в Delphi - часть 1" href="http://www.transl-gunsmoker.ru/2009/03/windows-vista-delphi-1.html"&gt;Delphi приложения в новых условиях вели себя не лучшим образом&lt;/a&gt;. Реализованная в Delphi эмуляция не смогла покрыть возможности новой системы. Delphi приложения усиленно притворялись нормальными, но это им не удалось.&lt;br /&gt;
&lt;br /&gt;
Поэтому, вместо того, чтобы допиливать эмуляцию до нового уровня (и снова "проиграть" при выходе новой системы Windows с новыми возможностями), разработчики Delphi просто решили играть по правилам системы. Начиная с Delphi 2007 окна ведут себя так, как положено, не пытаясь эмулировать иную концепцию взаимодействия окон. Теперь на панели задач показывается настоящее главное окно (и именно поэтому &lt;a href="http://docwiki.embarcadero.com/VCL/en/Forms.TApplication.Title" title="Forms.TApplication.Title"&gt;&lt;code&gt;Application.Title&lt;/code&gt;&lt;/a&gt; и &lt;a href="http://docwiki.embarcadero.com/VCL/en/Forms.TApplication.Icon" title="Forms.TApplication.Icon"&gt;&lt;code&gt;Application.Icon&lt;/code&gt;&lt;/a&gt; не будут иметь эффекта). Вы можете переключаться между старым и новым поведением приложения переключая &lt;a href="http://docwiki.embarcadero.com/VCL/en/Forms.TApplication.MainFormOnTaskBar" title="Forms.TApplication.MainFormOnTaskBar"&gt;свойство &lt;code&gt;MainFormOnTaskBar&lt;/code&gt;&lt;/a&gt;. Предполагается, что вы измените его один раз - сразу после вызова &lt;code&gt;Application.Initialize&lt;/code&gt; в .dpr-файле проекта и не будете трогать в дальнейшем.&lt;br /&gt;
&lt;br /&gt;
Я бы рекомендовал по возможности использовать новое поведение на Delphi 2007+. Гораздо лучше, когда ваше приложение играет по правилам системы, и вам будет проще разбираться с вопросами взаимодействия окон (если у вас не будет дополнительного скрытого окна в цепочке).&lt;br /&gt;
&lt;br /&gt;
Заметьте, что новые правила поведения окон отличаются от старых. К примеру, если раньше вы создавали немодальное окно - оно появлялось наравне со старым. Вы могли переключаться между ними и выносить любое из них на передний план. Это потому, что оба окна (и главное и немодальное) были равноценны - они имели окно-владельца Application. Они сворачивались вместе с ним, но между собой они были равны. Теперь же, если вы создадите окно, то ему придётся включить главное окно как своего владельца. Это даст вам сворачивание вместе с владельцем, единую кнопку на панели задач (т.е. старое поведение вида "единое приложение"), но это также будет означать, что вы не сможете вынести на передний план главное окно - вторичное окно всегда будет поверх окна-владельца (как указано в &lt;a href="#parent_owner_handle"&gt;информации выше&lt;/a&gt;). Вам нужно научится играть по новым правилам или от чего-то отказаться.&lt;br /&gt; 
&lt;br /&gt;
К примеру, вы можете перегрузить &lt;code&gt;CreateParams&lt;/code&gt; и очистить поле &lt;code&gt;WndParent&lt;/code&gt;. Тогда вы получите два равноценных окна, не связанных друг с другом. Вы можете вынести любое из них на передний план, но при этом второе окно будет иметь иконку на панели задач и сворачивание одного окна не приведёт к сворачиванию другого. Вы можете бороться с этим дальше, но... нужно ли? Короче говоря, либо вы связываете окна (и миритесь с ограничениями в виде Z-порядка), либо не связываете (и миритесь с ограничениями в виде... не связанности окон).&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Примечание:&lt;/b&gt; окно &lt;code&gt;Application&lt;/code&gt; всё ещё создаётся - даже при &lt;code&gt;MainFormOnTaskBar = True&lt;/code&gt;. Но только теперь оно используется лишь для служебных целей - реакции на события системы и т.п., но не для управления окнами приложения, как это было ранее. &lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;&lt;a id="modal"&gt;Модальные окна&lt;/a&gt;&lt;/h4&gt;
Ещё одним моментом, где Delphi эмулирует нетипичное поведение в системе, являются модальные окна. В дизайне системы модальность предназначена для диалоговых окон, которые создаются специальными функциями по шаблону из ресурса. Причём модальность диалога указывается специальным флагом - &lt;code&gt;DS_MODALFRAME&lt;/code&gt;. Иными словами, это означает, что одно и то же окно не может показываться в разных режимах - модальном и не модальном. &lt;br /&gt;
&lt;br /&gt;
Разумеется, такая модель не подходит для Delphi, где вы можете показывать окно различными способами - вызывая &lt;a href="http://docwiki.embarcadero.com/VCL/en/Forms.TCustomForm.Show" title="Forms.TCustomForm.Show"&gt;&lt;code&gt;Show&lt;/code&gt;&lt;/a&gt; или &lt;a href="http://docwiki.embarcadero.com/VCL/en/Forms.TCustomForm.ShowModal" title="Forms.TCustomForm.ShowModal"&gt;&lt;code&gt;ShowModal&lt;/code&gt;&lt;/a&gt;. Поэтому Delphi не использует системный механизм модальных окон. Вместо этого она эмулирует поведение модальных окон. Когда вы показываете "модальное" окно, VCL отключает все прочие окна в программе, а после окончания показа окна - восстанавливает их доступность. Отключение окна блокирует взаимодействие с пользователем - вы не можете переключиться в окно и вводить в него информацию. Как правило, отключение окна приводит к изменению его вида: окно-контрол показывается "затенённым". Однако окна-формы не изменяют свой вид. Поэтому отключенное окно визуально не отличается от обычного окна. Так что у вас создаётся впечатление, что окно не отключено, а просто сейчас показывается другое (модальное) окно.&lt;br /&gt;
&lt;br /&gt;
Заметьте, что это отличается от модальности в смысле системы. К примеру, приложению приходится рассылать служебные сообщения (вроде &lt;code&gt;WM_CANCELMODE&lt;/code&gt;) вручную и самому делать вложенный модальный цикл. Второе отличие - модальное окно в смысле системы отключает только своего владельца. Модальное же окно Delphi отключает все окна в приложении (т.е. оно "модально" в более сильном смысле). Именно в этом отличии заключается суть флага &lt;code&gt;MB_TASKMODAL&lt;/code&gt; &lt;a title="MSDN: MessageBox Function" href="http://msdn.microsoft.com/en-us/library/windows/desktop/ms645505(v=vs.85).aspx"&gt;функции &lt;code&gt;MessageBox&lt;/code&gt;&lt;/a&gt;: если вы укажете флаг, то вместо поведения по умолчанию (отключения только окна-владельца), система отключит все окна приложения (как это делает Delphi).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688132520265708820-7279905968512780618?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=aZtqI9SBtLk:zbu4A-2HBMY: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=aZtqI9SBtLk:zbu4A-2HBMY:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=aZtqI9SBtLk:zbu4A-2HBMY:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=aZtqI9SBtLk:zbu4A-2HBMY:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=aZtqI9SBtLk:zbu4A-2HBMY:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=aZtqI9SBtLk:zbu4A-2HBMY: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=aZtqI9SBtLk:zbu4A-2HBMY:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=aZtqI9SBtLk:zbu4A-2HBMY:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=aZtqI9SBtLk:zbu4A-2HBMY:-BTjWOF_DHI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=aZtqI9SBtLk:zbu4A-2HBMY:-BTjWOF_DHI" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/GunsmokersTranslations/~4/aZtqI9SBtLk" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.transl-gunsmoker.ru/feeds/7279905968512780618/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://www.transl-gunsmoker.ru/2012/03/windows.html#comment-form" title="Комментарии: 2" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/7279905968512780618?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/7279905968512780618?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/GunsmokersTranslations/~3/aZtqI9SBtLk/windows.html" title="Об окнах Windows" /><author><name>Александр Алексеев</name><uri>https://profiles.google.com/113168002104297556003</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh3.googleusercontent.com/-aUMttZQBsuI/AAAAAAAAAAI/AAAAAAAAC3Y/QuZ7K9t_WzE/s512-c/photo.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://2.bp.blogspot.com/-LR9iRydjEa8/T1iQYftKeuI/AAAAAAAADf8/nwAzhLLPWxI/s72-c/ms632597.cswin_02%2528en-us%252CVS.85%2529.png" height="72" width="72" /><thr:total>2</thr:total><feedburner:origLink>http://www.transl-gunsmoker.ru/2012/03/windows.html</feedburner:origLink></entry><entry gd:etag="W/&quot;AkYBQns6fyp7ImA9WhRaF04.&quot;"><id>tag:blogger.com,1999:blog-1688132520265708820.post-539405443253219257</id><published>2012-02-19T15:35:00.000+04:00</published><updated>2012-02-20T15:42:33.517+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-02-20T15:42:33.517+04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Delphi" /><category scheme="http://www.blogger.com/atom/ns#" term="кодинг" /><title>Используется ли файл?</title><content type="html">Это перевод &lt;a href="http://blog.delphi-jedi.net/2010/11/14/is-file-in-use/" title="Is File In Use"&gt;Is File In Use&lt;/a&gt;. Автор: Christian Wimmer.&lt;br /&gt;
&lt;br /&gt;
На форумах DelphiPraxis задали вопрос, который обычно всплывает несколько раз в год. Однако в этот раз я мог сказать, что появился API, который решает эту проблему. Вопрос был о том, как определить, кто держит файл. Главной проблемой стало то, что я никак не мог вспомнить название API, так что Assarbad пришлось постараться и поискать его.&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
И это имя - &lt;a href="http://msdn.microsoft.com/en-us/library/windows/desktop/bb775874(v=vs.85).aspx" title="MSDN: IFileIsInUse interface"&gt;&lt;code&gt;IFileIsInUse&lt;/code&gt;&lt;/a&gt;, интерфейс. Он объявлен в &lt;code&gt;Shobjidl.h&lt;/code&gt;, &lt;code&gt;Shobjidl.idl&lt;/code&gt; и, сейчас уже, в &lt;code&gt;JwaShlObj.pas&lt;/code&gt;. Вот вырезка кода:
&lt;pre class="brush:delphi"&gt;{$IFDEF WINVISTA_UP}
const
  IID_IFileIsInUse: TGUID = (
    D1:$64a1cbf0; D2:$3a1a; D3:$4461; D4:($91,$58,$37,$69,$69,$69,$39,$50));

type
  {$ALIGN 4}
  tagFILE_USAGE_TYPE = (
    FUT_PLAYING = 0,
    FUT_EDITING = 1,
    FUT_GENERIC = 2
  );
  FILE_USAGE_TYPE = tagFILE_USAGE_TYPE;
  TFileUsageType = FILE_USAGE_TYPE;

const
  OF_CAP_CANSWITCHTO     = $0001;
  OF_CAP_CANCLOSE        = $0002;

type
  IFileIsInUse = interface(IUnknown)
    ['{64a1cbf0-3a1a-4461-9158-376969693950}']
    function GetAppName(out ppszName: LPWSTR) : HRESULT; stdcall;
    function GetUsage(out pfut : FILE_USAGE_TYPE) : HRESULT; stdcall;
    function GetCapabilities(out pdwCapFlags : DWORD) : HRESULT; stdcall;
    function GetSwitchToHWND(out phwnd : HWND) : HRESULT; stdcall;
    function CloseFile() : HRESULT; stdcall;
  end;

{$ENDIF WINVISTA_UP}&lt;/pre&gt;
Интерфейс &lt;a title="Вы можете читать контракт и с другой стороны" href="http://www.transl-gunsmoker.ru/2008/12/blog-post_4027.html"&gt;может использоваться двояко&lt;/a&gt; - и клиентом и быть реализованным сервером. Клиент обычно проверяет, заблокирован ли файл, и получает ссылку на интерфейс для вызова его методов. Сервер же может держать блокировку на файле и реализовывать этот интерфейс, чтобы предоставить его услуги клиенту. Эта статья коснётся только стороны клиента.&lt;br /&gt;
&lt;br /&gt;
Вы увидите, что этот API будет работать только если обе стороны выполняют свою работу. Процесс, блокирующий файл, также должен реализовать этот интерфейс и зарегистрировать его, чтобы клиент мог получать информацию. Нет никакой прямой связи между блокировкой файла и именем процесса. Если процесс держит блокировку на файл, но не реализует интерфейс, то вы снова оказываетесь сами за себя. &lt;br /&gt;
&lt;br /&gt;
В конечном итоге, это просто помощник Оболочки для красивого диалога удаления файлов Windows:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;
&lt;a href="http://2.bp.blogspot.com/-Woz17UgovcA/T0IjuVGkvvI/AAAAAAAADWM/apa-e88Jf5o/s1600/FileInUse-2%2B%25281%2529.png" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="236" width="400" src="http://2.bp.blogspot.com/-Woz17UgovcA/T0IjuVGkvvI/AAAAAAAADWM/apa-e88Jf5o/s400/FileInUse-2%2B%25281%2529.png" /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
Итак, напомню вид самого интерфейса:
&lt;pre class="brush:delphi"&gt;IFileIsInUse = interface(IUnknown)
  function GetAppName(out ppszName: LPWSTR) : HRESULT; stdcall;
  function GetUsage(out pfut : FILE_USAGE_TYPE) : HRESULT; stdcall;
  function GetCapabilities(out pdwCapFlags : DWORD) : HRESULT; stdcall;
  function GetSwitchToHWND(out phwnd : HWND) : HRESULT; stdcall;
  function CloseFile() : HRESULT; stdcall;
end;&lt;/pre&gt;
Методы интерфейса достаточно просто понять:
&lt;ul&gt;
&lt;li&gt;&lt;a title="MSDN: IFileIsInUse::GetAppName method" href="http://msdn.microsoft.com/en-us/library/windows/desktop/bb775867(v=vs.85).aspx"&gt;&lt;code&gt;GetAppName&lt;/code&gt;&lt;/a&gt; получает имя процесса, который держит файл. Это будет произвольное имя, выбранное самим приложением. Всегда используйте &lt;code&gt;PWideChar&lt;/code&gt; для получения имени и не забывайте освободить память с помощью CoTaskMemFree (&lt;a title="В чём разница между SHGetMalloc, SHAlloc, CoGetMalloc и CoTaskMemAlloc?" href="http://www.transl-gunsmoker.ru/2009/05/shgetmalloc-shalloc-cogetmalloc.html"&gt;или через &lt;code&gt;IMalloc&lt;/code&gt;&lt;/a&gt;). Не забывайте проверять результат вызова. Если вам нравятся исключения, то вы можете изменить прототип метода на &lt;code&gt;safecall&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&lt;/li&gt;

&lt;li&gt;&lt;a title="MSDN: IFileIsInUse::GetUsage method" href="http://msdn.microsoft.com/en-us/library/windows/desktop/bb775873(v=vs.85).aspx"&gt;&lt;code&gt;GetUsage&lt;/code&gt;&lt;/a&gt; возвращает причину блокировки файла. Это может быть одна из констант в перечислении &lt;code&gt;TFileUsage&lt;/code&gt;. Это может быть проигрывание видео, музыки или редактирование файла. Либо же это может быть общая причина &lt;code&gt;FUT_GENERIC&lt;/code&gt;. Возможно, в будущем будет добавлено больше кодов.&lt;br /&gt;&amp;nbsp;&lt;/li&gt;

&lt;li&gt;&lt;code&gt;GetCapabilities&lt;/code&gt; возвращает набор флагов. Они указывают, можно ли попросить закрыть файл вызовом &lt;code&gt;CloseFile&lt;/code&gt; (&lt;code&gt;OF_CAP_CANCLOSE&lt;/code&gt;) или же мы можем активировать приложение, блокирующее файл (&lt;code&gt;OF_CAP_CANSWITCHTO&lt;/code&gt;). Лучше всего уведомить пользователя о переключении окон, иначе он может сильно расстроиться из-за внезапно пропавшего окна вашего приложения. Само переключение окон можно сделать вызовом &lt;a title="MSDN: SetForegroundWindow function" href="http://msdn.microsoft.com/en-us/library/windows/desktop/ms633539(v=vs.85).aspx"&gt;&lt;code&gt;SetForegroundWindow&lt;/code&gt;&lt;/a&gt;. Но &lt;a href="http://www.transl-gunsmoker.ru/2010/10/blog-post_25.html" title="Вынос окна на передний план - это как любовь: вы не можете её украсть, вам её должны подарить"&gt;для успешности выполнения этой операции ваше приложение должно иметь фокус&lt;/a&gt;.&lt;br /&gt;&amp;nbsp;&lt;/li&gt;

&lt;li&gt;&lt;a title="MSDN: IFileIsInUse::GetSwitchToHWND method" href="http://msdn.microsoft.com/en-us/library/windows/desktop/bb775870(v=vs.85).aspx"&gt;&lt;code&gt;GetSwitchToHWND&lt;/code&gt;&lt;/a&gt; возвращает описатель окна процесса, блокирующего файл. Используйте этот описатель только для переключения на окно. Вы понятия не имеете, что за окно вам могут тут вернуть. Не нужно пытаться его закрыть или делать что-то ещё - это просто будет плохим поведением. И всегда проверяйте на ошибки вызова. Иначе вы не узнаете, вернули ли вам корректный описатель.&lt;br /&gt;
&lt;br /&gt;
Конечно же, вы можете вызывать этот метод только если вызов &lt;code&gt;GetCapabilities&lt;/code&gt; вернул бит &lt;code&gt;OF_CAP_CANSWITCHTO&lt;/code&gt;.&lt;br /&gt;&amp;nbsp;&lt;/li&gt;

&lt;li&gt;&lt;a title="MSDN: IFileIsInUse::CloseFile method" href="http://msdn.microsoft.com/en-us/library/windows/desktop/bb775865(v=vs.85).aspx"&gt;&lt;code&gt;CloseFile&lt;/code&gt;&lt;/a&gt; просит сервер закрыть файл. Вы можете вызывать этот метод только если вызов &lt;code&gt;GetCapabilities&lt;/code&gt; вернул бит &lt;code&gt;OF_CAP_CANCLOSE&lt;/code&gt;. Ну, это действительно приятная возможность. Но не доверяйте ей слепо! Всегда перепроверьте блокировку файла после вызова прежде чем двигаться дальше.&lt;/li&gt;
&lt;/ul&gt;
Чтобы получить интерфейс для файла вам нужно сначала иметь заблокированный файл. Вы можете скачать и скомпилировать &lt;a title="MSDN: File Is In Use Sample" href="http://msdn.microsoft.com/en-us/library/ee330722(VS.85).aspx"&gt;пример из MSDN с именем IsFileInUse&lt;/a&gt; или вы можете просто открыть файл в приложении, которое реализует такие интерфейсы (например, MS Office).&lt;br /&gt;
&lt;br /&gt;
Следующий шаг - узнать, где размещаются заблокированные файлы. Это место - running object table, или ROT для краткости (её можно получить через ActiveX.&lt;a href="http://msdn.microsoft.com/en-us/library/windows/desktop/ms684004(v=vs.85).aspx" title="MSDN: GetRunningObjectTable function"&gt;GetRunningObjectTable&lt;/a&gt;()). Это глобальная* (для машины) таблица, которая хранит запущенные (running) COM объекты. Вы можете реализовать свой собственный интерфейс и поместить его в эту таблицу. Поскольку интерфейсы могут быть произвольными, к ним прикрепляются &lt;a title="Википедия: Моникер" href="http://ru.wikipedia.org/wiki/%D0%9C%D0%BE%D0%BD%D0%B8%D0%BA%D0%B5%D1%80"&gt;моникеры&lt;/a&gt; (&lt;a title="MSDN: IMoniker interface" href="http://msdn.microsoft.com/en-us/library/windows/desktop/ms679705(v=vs.85).aspx"&gt;IMoniker&lt;/a&gt;), которые уникально их описывают. Существует несколько типов моникеров вроде класса (class), элемента (item) и файла (file) - и нас интересует только последний тип.&lt;br /&gt;
&lt;br /&gt; 
Так что вся работа будет заключаться в переборе моникеров в ROT. Обычно их там не много (у меня было всего 5). Каждый моникер проверяется на тип (&lt;a title="MSDN: IMoniker::IsSystemMoniker method" href="http://msdn.microsoft.com/en-us/library/windows/desktop/ms690482(v=vs.85).aspx"&gt;&lt;code&gt;IsSystemMoniker&lt;/code&gt;&lt;/a&gt;) и то, является ли он файловым моникером (&lt;a href="http://msdn.microsoft.com/en-us/library/windows/desktop/ms694326(v=vs.85).aspx" title="MSDN: MKSYS enumeration"&gt;&lt;code&gt;MKSYS_FILEMONIKER&lt;/code&gt;&lt;/a&gt;), а затем путь к файлу сравнивается с нашим файлом. Если честно, то я не могу сказать, зачем пример сперва сравнивает префикс, а потом сам моникер - извините.&lt;br /&gt;
&lt;br /&gt; 
Сам объект получается по моникеру через вызов &lt;a title="MSDN: IRunningObjectTable::GetObject method" href="http://msdn.microsoft.com/en-us/library/windows/desktop/ms683841(v=vs.85).aspx"&gt;&lt;code&gt;GetObject&lt;/code&gt;&lt;/a&gt; у ROT. Этот вызов может завершиться с ошибкой &lt;code&gt;E_ACCESS_DENIED&lt;/code&gt;, так что проверяйте на ошибки. В итоге мы получаем интерфейс &lt;code&gt;IFileIsInUse&lt;/code&gt;. Поскольку файл мог быть зарегистрирован без реализации этого интерфейса, то нам снова нужна проверка на ошибки.&lt;br /&gt;
&lt;br /&gt;
Я не писал весь этот код сам. Фактически, я взял за основу уже упоминавшийся мною пример из MSDN. 
&lt;pre class="brush:delphi"&gt;function GetFileInUseInfo(const FileName : WideString) : IFileIsInUse;
var
  ROT : IRunningObjectTable;
  mFile, enumIndex, Prefix : IMoniker;
  enumMoniker : IEnumMoniker;
  MonikerType : LongInt;
  unkInt  : IInterface;
begin
  result := nil;

  OleCheck(GetRunningObjectTable(0, ROT));
  OleCheck(CreateFileMoniker(PWideChar(FileName), mFile));

  OleCheck(ROT.EnumRunning(enumMoniker));

  while (enumMoniker.Next(1, enumIndex, nil) = S_OK) do
  begin
    OleCheck(enumIndex.IsSystemMoniker(MonikerType));
    if MonikerType = MKSYS_FILEMONIKER then
    begin
      if Succeeded(mFile.CommonPrefixWith(enumIndex, Prefix)) and
         (mFile.IsEqual(Prefix) = S_OK) then
      begin
       if Succeeded(ROT.GetObject(enumIndex, unkInt)) then
        begin
          if Succeeded(unkInt.QueryInterface(IID_IFileIsInUse, result)) then
          begin
            result := unkInt as IFileIsInUse;
            exit;
          end;
        end;
      end;
    end;
  end;
end;&lt;/pre&gt;
&lt;br /&gt;
&lt;h1&gt;Заключение&lt;/h1&gt;
В целом этот API имеет такие недостатки:
&lt;ul&gt;
&lt;li&gt;Этот API рассчитывает на то, что программа, заблокировавшая файл, реализует и зарегистрирует специальный интерфейс. Если же приложение не озаботится этой задачей, то вы не сможете использовать этот метод.&lt;/li&gt;
&lt;li&gt;Если вы заблокируете файл на разделяемой сетевой папке, а затем попробуете обратиться к нему по локальному имени, то не сможете определить процесс, заблокировавший файл. Причина кроется в самой Windows. Windows является провайдером файлов внешнему миру (даже если это всего лишь петля обратно на локальную машину), так что Проводник Windows покажет просто "Система" в качестве блокиратора. Также, в ROT вы увидите только сетевой (UNC) путь вместо локального.&lt;/li&gt;
&lt;li&gt;(*) Примечание безопасности: ROT не является по настоящему глобальной. Фактически, в системе есть несколько ROT. ROT делятся индивидуально по пользователям, а затем по mandatory integrity control (MIC) или integrity levels (IL) - высокому (high), среднему (medium) и, вероятно, низкому (low) (я не проверял). Если вы запущены как обычный пользователь, ваши процессы будут иметь средний уровень, а процессы администратора - высокий. Программа со средним уровнем сможет зарегистрировать себя только в ROT для среднего уровня. Поэтому, если она хочет создать ключи реестра для COM (LOCAL_MACHINE\Classes\AppID\guid), то ей нужно запуститься под администратором хотя бы раз. Таким образом, она сможет стать видимой для всех ROT при желании. К сожалению, это ещё не всё. На многопользовательской системе каждый объект в моникере имеет дескриптор безопасности, который говорит COM, кто имеет к нему доступ. Если Алиса хочет получить доступ к ROT, созданной Бобом, то Боб должен явно разрешить Алисе доступ к ней. Как обычно в вопросах безопасности, по умолчанию доступ разрешается только системе, администраторам и создателю - эти настройки копируются из глобальных настроек безопасности COM и они могут быть изменены либо в реестре для AppID при запуске процесса, либо для каждого регистрируемого объекта сервером. JWSCL даёт доступ к обоим вариантам в файле JwsclComSecurity.pas (&gt;= 0.9.4). И таким образом сервер может изменить настройки, чтобы, скажем, дать всем прошедшим проверку пользователям доступ к объекту.&lt;/li&gt;
&lt;/ul&gt;
&lt;br /&gt;
&lt;h1&gt;Пример&lt;/h1&gt;
Вы можете скачать файл-пример напрямую с Subversion:&lt;br /&gt;
&lt;a href="https://jedi-apilib.svn.sourceforge.net/svnroot/jedi-apilib/jwapi/trunk/Examples/FileIsInUse/Client/FileIsInUseClientExample.dpr" title="FileIsInUseClientExample.dpr"&gt;https://jedi-apilib.svn.sourceforge.net/svnroot/jedi-apilib/jwapi/trunk/Examples/FileIsInUse/Client/FileIsInUseClientExample.dpr&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
См. также: &lt;a title="Как мне найти программу, которая держит этот файл?" href="http://www.transl-gunsmoker.ru/2012/02/blog-post_18.html"&gt;Как мне найти программу, которая держит этот файл?&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688132520265708820-539405443253219257?l=www.transl-gunsmoker.ru' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=b-tmZLxXB94:qqOHa2ycum4:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=b-tmZLxXB94:qqOHa2ycum4:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=b-tmZLxXB94:qqOHa2ycum4:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=b-tmZLxXB94:qqOHa2ycum4:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=b-tmZLxXB94:qqOHa2ycum4:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=b-tmZLxXB94:qqOHa2ycum4:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=b-tmZLxXB94:qqOHa2ycum4:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=b-tmZLxXB94:qqOHa2ycum4:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=b-tmZLxXB94:qqOHa2ycum4:-BTjWOF_DHI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=b-tmZLxXB94:qqOHa2ycum4:-BTjWOF_DHI" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/GunsmokersTranslations/~4/b-tmZLxXB94" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.transl-gunsmoker.ru/feeds/539405443253219257/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://www.transl-gunsmoker.ru/2012/02/blog-post_19.html#comment-form" title="Комментарии: 0" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/539405443253219257?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/539405443253219257?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/GunsmokersTranslations/~3/b-tmZLxXB94/blog-post_19.html" title="Используется ли файл?" /><author><name>Александр Алексеев</name><uri>https://profiles.google.com/113168002104297556003</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh3.googleusercontent.com/-aUMttZQBsuI/AAAAAAAAAAI/AAAAAAAAC3Y/QuZ7K9t_WzE/s512-c/photo.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://2.bp.blogspot.com/-Woz17UgovcA/T0IjuVGkvvI/AAAAAAAADWM/apa-e88Jf5o/s72-c/FileInUse-2%2B%25281%2529.png" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://www.transl-gunsmoker.ru/2012/02/blog-post_19.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DU4BRXcyfyp7ImA9WhRaF04.&quot;"><id>tag:blogger.com,1999:blog-1688132520265708820.post-5311522479826646968</id><published>2012-02-18T05:33:00.000+04:00</published><updated>2012-02-20T15:39:14.997+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-02-20T15:39:14.997+04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="кодинг" /><title>Как мне найти программу, которая держит этот файл?</title><content type="html">Это перевод &lt;a href="http://blogs.msdn.com/b/oldnewthing/archive/2012/02/17/10268840.aspx" title="How do I find out which process has a file open?"&gt;How do I find out which process has a file open?&lt;/a&gt; Автор: Реймонд Чен.&lt;br /&gt;
&lt;br /&gt;
Исторически, нет никакого специального способа найти процесс, который держит файл. Файловый объект имеет обычный счётчик ссылок объекта ядра и когда счётчик опускается до нуля - файл закрывается. Но в системе никто не отслеживает процессы, открывшие данный описатель, и сколько именно раз они его открыли (и это упрощённое изложение даже игнорирует тот факт, что счётчик может быть увеличен вовсе не процессом, а, скажем, драйвером режима ядра; или, быть может, изначально счётчик был увеличен процессом, который теперь уже закрыт, но &lt;a title="MSDN: ObReferenceObjectByHandle routine" href="http://msdn.microsoft.com/ru-ru/library/ff558679(en-us,VS.85).aspx"&gt;файл ещё держится драйвером ядра&lt;/a&gt;).&lt;br /&gt;
&lt;br /&gt;
Это состояние вещей согласуется с концепцией не хранить информацию, которая вам не нужна. Файловую систему не заботит, кто там держит её файлы. Её задача - закрыть файл, когда уйдёт последняя ссылка.&lt;br /&gt;
&lt;br /&gt;
Аналогичную ситуацию вы видите в COM. Всё, что вас заботит - когда же счётчик опустится до нуля (потому что в этот момент вам нужно удалить объект). Если позже вы обнаружите в своём процессе утечку, то у вас нет никакого волшебного запроса "Покажи мне всех, то вызывал _AddRef для моего объекта" - просто потому, что вы никогда и не вели учёт вызывающих _AddRef. Нет у вас и возможности сделать так: "Вот объект, который я хочу удалить. Покажи мне всех использующих его, так что я смогу их удалить".&lt;br /&gt;
&lt;br /&gt;
По крайней мере таким был классический сценарий.&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
А теперь познакомьтесь с &lt;a title="MSDN: Restart Manager" href="http://msdn.microsoft.com/ru-ru/library/cc948910(en-us,VS.85).aspx"&gt;Restart Manager&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
Официальная цель Restart Manager - оказать помощь в закрытии и перезапуске приложений, которые вы хотите обновить. Чтобы сделать это, вам нужно отслеживать, какие процессы держат ссылки и на какие файлы. И вот она та самая база данных, что нам нужна (почему это ядро хранит список процессов, открывших файл? Потому что это принцип, обратный к принципу не хранить вещи, которые вам не нужны: теперь ядру нужна эта информация!)&lt;br /&gt;
&lt;br /&gt;
Вот простая программа, которая принимает в командной строке имя файла и показывает список процессов, открывших этот файл.
&lt;pre class="brush:delphi"&gt;program Project78;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  Winapi.Windows,
  System.SysUtils;

{$A+,Z4}

const
  PROCESS_QUERY_LIMITED_INFORMATION = $1000;

  RstrtMgr = 'Rstrtmgr.dll';

  RM_SESSION_KEY_LEN = SizeOf(TGUID);          // RM_SESSION_KEY_LEN - size in bytes of binary session key
  CCH_RM_SESSION_KEY = RM_SESSION_KEY_LEN * 2; // CCH_RM_SESSION_KEY - character count of text-encoded session key
  CCH_RM_MAX_APP_NAME = 255;                   // CCH_RM_MAX_APP_NAME - maximum character count of application friendly name
  CCH_RM_MAX_SVC_NAME = 63;                    // CCH_RM_MAX_SVC_NAME - maximum character count of service short name
  RM_INVALID_TS_SESSION = -1;                  // Uninitialized value for TS Session ID
  RM_INVALID_PROCESS = -1;                     // Uninitialized value for Process ID

type
  TAppName = array[0..CCH_RM_MAX_APP_NAME] of WideChar;
  TServiceName = array[0..CCH_RM_MAX_SVC_NAME] of WideChar;
  TSessionKey = array[0..CCH_RM_SESSION_KEY] of WideChar;

  _RM_APP_TYPE = (
    RmUnknownApp = 0,   // Application type cannot be classified in known categories
    RmMainWindow = 1,   // Application is a windows application that displays a top-level window
    RmOtherWindow = 2,  // Application is a windows app but does not display a top-level window
    RmService = 3,      // Application is an NT service
    RmExplorer = 4,     // Application is Explorer
    RmConsole = 5,      // Application is Console application
    RmCritical = 1000   // Application is critical system process where a reboot is required to restart
  );
  RM_APP_TYPE = _RM_APP_TYPE;
  TRMAppType = RM_APP_TYPE;

  _RM_SHUTDOWN_TYPE = (
    RmForceShutdown = $1,          // Force app shutdown
    RmShutdownOnlyRegistered = $10 // Only shudown apps if all apps registered for restart
  );
  RM_SHUTDOWN_TYPE = _RM_SHUTDOWN_TYPE;
  TRMShutdownType = RM_SHUTDOWN_TYPE;

  _RM_APP_STATUS = (
    RmStatusUnknown = $0,          // Application in unknown state or state not important
    RmStatusRunning = $1,          // Application is currently running
    RmStatusStopped = $2,          // Application stopped by Restart Manager
    RmStatusStoppedOther = $4,     // Application detected stopped by outside action
    RmStatusRestarted = $8,        // Application restarted by Restart Manager
    RmStatusErrorOnStop = $10,     // An error occurred when stopping this application
    RmStatusErrorOnRestart = $20,  // An error occurred when restarting this application
    RmStatusShutdownMasked = $40,  // Shutdown action masked by filer
    RmStatusRestartMasked = $80    // Restart action masked by filter
  );
  RM_APP_STATUS = _RM_APP_STATUS;
  TRMAppStatus = RM_APP_STATUS;

  _RM_REBOOT_REASON = (
    RmRebootReasonNone = $0,               // Reboot not required
    RmRebootReasonPermissionDenied = $1,   // Current user does not have permission to shut down one or more detected processes
    RmRebootReasonSessionMismatch = $2,    // One or more processes are running in another TS session.
    RmRebootReasonCriticalProcess = $4,    // A critical process has been detected
    RmRebootReasonCriticalService = $8,    // A critical service has been detected
    RmRebootReasonDetectedSelf = $10       // The current process has been detected
  );
  RM_REBOOT_REASON = _RM_REBOOT_REASON;
  TRMRebootReason = RM_REBOOT_REASON;

  _RM_UNIQUE_PROCESS = record
    dwProcessId: DWORD;               // PID
    ProcessStartTime: TFileTime;      // Process creation time
  end;
  RM_UNIQUE_PROCESS = _RM_UNIQUE_PROCESS;
  PRM_UNIQUE_PROCESS = ^_RM_UNIQUE_PROCESS;
  TRMUniqueProcess = RM_UNIQUE_PROCESS;
  PRMUniqueProcess = PRM_UNIQUE_PROCESS;

  _RM_PROCESS_INFO = record
    Process: TRMUniqueProcess;         // Unique process identification
    strAppName: TAppName;              // Application friendly name
    strServiceShortName: TServiceName; // Service short name, if applicable
    ApplicationType: TRMAppType;       // Application type
    AppStatus: ULONG;                  // Bit mask of application status
    TSSessionId: DWORD;                // Terminal Service session ID of process (-1 if n/a)
    bRestartable: BOOL;                // Is application restartable?
  end;
  RM_PROCESS_INFO = _RM_PROCESS_INFO;
  PRM_PROCESS_INFO = ^_RM_PROCESS_INFO;
  TRMProcessInfo = RM_PROCESS_INFO;
  PRMProcessInfo = PRM_PROCESS_INFO;

function QueryFullProcessImageName(hProcess: THandle; dwFlags: DWORD; lpExeName: PChar; var lpdwSize: Integer): BOOL; stdcall; external kernel32 name {$IFDEF UNICODE}'QueryFullProcessImageNameW'{$ELSE}'QueryFullProcessImageNameA'{$ENDIF};

function RmStartSession(out pSessionHandle: DWORD; dwSessionFlags: DWORD; out strSessionKey: TSessionKey): DWORD; stdcall; external RstrtMgr;
function RmEndSession(dwSessionHandle: DWORD): DWORD; stdcall; external RstrtMgr;
function RmRegisterResources(dwSessionHandle: DWORD; nFiles: UINT; rgsFileNames: PPWideChar; nApplications: UINT; rgApplications: PRMUniqueProcess; nServices: UINT; rgsServiceNames: PPWideChar): DWORD; stdcall; external RstrtMgr;
function RmGetList(dwSessionHandle: DWORD; out pnProcInfoNeeded: UINT; var pnProcInfo: UINT; out rgAffectedApps: TRMProcessInfo; out lpdwRebootReasons: DWORD): DWORD; stdcall; external RstrtMgr;

procedure Run;

  function StrFromAppType(const AAppType: TRMAppType): String;
  begin
    case AAppType of
      RmMainWindow:  Result := 'Application is a windows application that displays a top-level window';
      RmOtherWindow: Result := 'Application is a windows app but does not display a top-level window';
      RmService:     Result := 'Application is an NT service';
      RmExplorer:    Result := 'Application is Explorer';
      RmConsole:     Result := 'Application is Console application';
      RmCritical:    Result := 'Application is critical system process where a reboot is required to restart';
    else
                     Result := 'Application type cannot be classified in known categories';
    end;
  end;

const
  Num = 10;
var
  dwSession: DWORD;
  szSessionKey: TSessionKey;
  pszFile: PPWideChar;
  P: PWideChar;
  FileName: WideString;
  dwReason: DWORD;
  i: Integer;
  nProcInfoNeeded: UINT;
  nProcInfo: UINT;
  rgpi: array[0..Num - 1] of TRMProcessInfo;
  hProcess: THandle;
  ftCreate, ftExit, ftKernel, ftUser: TFileTime;
  sz: String;
  cch: Integer;
begin
  FileName := ParamStr(1);

  FillChar(SessionKey, SizeOf(SessionKey), 0);
  SetLastError(RmStartSession(dwSession, 0, szSessionKey));
  Win32Check(GetLastError = ERROR_SUCCESS);
  try
    P := PWideChar(FileName);
    pszFile := @P;
    SetLastError(RmRegisterResources(dwSession, 1, pszFile, 0, nil, 0, nil));
    Win32Check(GetLastError = ERROR_SUCCESS);

    nProcInfo := Num;
    SetLastError(RmGetList(dwSession, nProcInfoNeeded, nProcInfo, rgpi[0], dwReason));
    Win32Check(GetLastError = ERROR_SUCCESS);

    for i := 0 to nProcInfo - 1 do
    begin
      WriteLn(Format('%d.ApplicationType = %d (%s)', [i, Ord(rgpi[i].ApplicationType), StrFromAppType(rgpi[i].ApplicationType)]));
      WriteLn(Format('%d.strAppName = %s', [i, rgpi[i].strAppName]));
      WriteLn(Format('%d.Process.dwProcessId = %d', [i, rgpi[i].Process.dwProcessId]));

      hProcess := OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, False, rgpi[i].Process.dwProcessId);
      if hProcess &lt;&gt; 0 then
      try
        if GetProcessTimes(hProcess, ftCreate, ftExit, ftKernel, ftUser) and
           (CompareFileTime(rgpi[i].Process.ProcessStartTime, ftCreate) = 0) then
        begin
          cch := MAX_PATH;
          SetLength(sz, cch);
          if QueryFullProcessImageName(hProcess, 0, PChar(sz), cch) and
             (cch &lt;= MAX_PATH) then
          begin
            SetLength(sz, cch);
            WriteLn(Format('%d.Process.Name = %s', [i, sz]));
          end;
        end;
      finally
        CloseHandle(hProcess);
      end;
      WriteLn;
    end;
  finally
    RmEndSession(dwSession);
  end;
end;

begin
  try
    if ParamCount = 0 then
      Exit;
    Run;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.&lt;/pre&gt;
Итак, первой строкой в этом коде... нет, постойте - ещё до вызова функции &lt;a title="MSDN: Rm­Start­Session function" href="http://msdn.microsoft.com/en-us/library/windows/desktop/aa373668(v=vs.85).aspx"&gt;&lt;code&gt;Rm­Start­Session&lt;/code&gt;&lt;/a&gt; у нас есть строка
&lt;pre class="brush:delphi"&gt;FillChar(SessionKey, SizeOf(SessionKey), 0);&lt;/pre&gt; 
Одна эта строка кода решает аж два бага!&lt;br /&gt;
&lt;br /&gt;
Первый из них - баг в документации. Документация по функции &lt;code&gt;Rm­Start­Session&lt;/code&gt; не указывает, насколько большим должен быть буфер для ключа сессии. Правильный ответ - CCH_RM_SESSION_KEY + 1.&lt;br /&gt;
&lt;br /&gt;
Второй баг - в коде. Функция &lt;code&gt;Rm­Start­Session&lt;/code&gt; не завершает ключ терминатором, даже хотя прототип функции описан как возвращающий нуль-терминированную строку. Чтобы обойти эту проблему, мы очищаем буфер перед использованием, заполняя его нулями, так что то, что будет записано в ключ сессии, автоматически получит корректный терминатор (а именно - один из тех нулей, что мы записали).&lt;br /&gt;
&lt;br /&gt;
Прим. пер.: при переводе с C на Delphi прототип функции был существенно изменён. Так что теперь ключ сессии передаётся как фиксированный массив.&lt;br /&gt;
&lt;br /&gt;
Окей, эти проблемы ушли с дороги. Теперь, базовый алгоритм:
&lt;ol&gt;
&lt;li&gt;Создать сессию Restart Manager.&lt;/li&gt;
&lt;li&gt;Добавить интересующий нас файл в сессию.&lt;/li&gt;
&lt;li&gt;Запросить список процессов, влияющих на ресурс.&lt;/li&gt;
&lt;li&gt;Напечатать немного информации по каждому процессу.&lt;/li&gt;
&lt;li&gt;Закрыть сессию.&lt;/li&gt;
&lt;/ol&gt;
Я уже упомянул, что вы создаёте сессию вызовом &lt;code&gt;Rm­Start­Session&lt;/code&gt;. Следующим шагом мы добавляем в сессию единственный файл вызовом &lt;a title="MSDN: Rm­Register­Resources function" href="http://msdn.microsoft.com/en-us/library/windows/desktop/aa373663(v=vs.85).aspx"&gt;&lt;code&gt;Rm­Register­Resources&lt;/code&gt;&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
Теперь начинается веселье. Получение списка процессов обычно происходит в два этапа. Сначала вы запрашиваете число доступных процессов (передавая 0 в &lt;code&gt;nProcInfo&lt;/code&gt;), затем выделяете память и вызываете функцию второй раз для получения данных. Но поскольку это просто пример, я вшил в программу фиксированное число процессов. Если файл открыт более 10 процессами, то я просто сдамся (вы можете проверить это, запустив программу и указав файл вроде kernel32.dll).&lt;br /&gt;
&lt;br /&gt;
Прим.пер.: и если какой-то сильно умный читатель вздумает &lt;a href="http://www.gunsmoker.ru/2010/05/90.html" title="90% кода в интернете - говно"&gt;скопировать этот код и использовать "как есть"&lt;/a&gt;...&lt;br /&gt;
&lt;br /&gt;
Вторая хитрая часть заключается в поиске процесса по &lt;a title="MSDN: RM_PROCESS_INFO structure" href="http://msdn.microsoft.com/en-us/library/windows/desktop/aa373674(v=vs.85).aspx"&gt;&lt;code&gt;TRMProcessInfo&lt;/code&gt;&lt;/a&gt;. Поскольку ID процессов могут использоваться заново (recycle), то структура &lt;code&gt;TRMProcessInfo&lt;/code&gt; идентифицирует процесс комбинацией ID и времени его запуска. Такая комбинация является уникальной в рамках одной машины, потому что два процесса не могут иметь одинаковые ID в одно и то же время. Поэтому мы открываем процесс по его ID, а затем проверяем, что это именно тот процесс, что нам нужен (а если нет - то &lt;a title="RE: How do I recover the window handle passed to ShellExecute? by Anonymous" href="http://blogs.msdn.com/b/oldnewthing/archive/2010/08/26/10054386.aspx#10054750"&gt;ID ссылается на процесс, который уже завершил работу&lt;/a&gt; с того момента, когда мы запрашивали список). Если же все данные совпали, то мы выводим путь к .exe файлу процесса.&lt;br /&gt;
&lt;br /&gt;
Вот и всё, что нужно, чтобы перечислить все процессы, открывшие какой-то конкретный файл. &lt;br /&gt;
&lt;br /&gt;
Конечно же, более выразительным интерфейсом для управления используемыми файлами является &lt;a title="MSDN: IFileIsInUse interface" href="http://msdn.microsoft.com/en-us/library/windows/desktop/bb775874(v=vs.85).aspx"&gt;&lt;code&gt;IFileIsInUse&lt;/code&gt;&lt;/a&gt;, который &lt;a title="Некоторые папки двигать нельзя - и вам придётся научиться с этим жить" href="http://www.transl-gunsmoker.ru/2012/02/blog-post.html"&gt;я упоминал не так давно&lt;/a&gt;. Этот интерфейс скажет вам не только какие приложения открыли файл (и в более дружелюбном формате, чем просто путь к .exe), но вы также сможете переключиться на это приложение и даже попросить его закрыть файл (если оно поддерживает эту возможность). Сама Windows 7 сначала пытается использовать &lt;code&gt;IFileIsInUse&lt;/code&gt;, и лишь если он не сумел освободить файл - обращается к Restart Manager.&lt;br /&gt;
&lt;br /&gt;
Читать далее: &lt;a title="Используется ли файл?" href="http://www.transl-gunsmoker.ru/2012/02/blog-post_19.html"&gt;использование &lt;code&gt;IFileIsInUse&lt;/code&gt;&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688132520265708820-5311522479826646968?l=www.transl-gunsmoker.ru' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=g3yyaGW447o:J9e2AXkvEQI:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=g3yyaGW447o:J9e2AXkvEQI:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=g3yyaGW447o:J9e2AXkvEQI:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=g3yyaGW447o:J9e2AXkvEQI:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=g3yyaGW447o:J9e2AXkvEQI:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=g3yyaGW447o:J9e2AXkvEQI:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=g3yyaGW447o:J9e2AXkvEQI:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=g3yyaGW447o:J9e2AXkvEQI:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=g3yyaGW447o:J9e2AXkvEQI:-BTjWOF_DHI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=g3yyaGW447o:J9e2AXkvEQI:-BTjWOF_DHI" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/GunsmokersTranslations/~4/g3yyaGW447o" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.transl-gunsmoker.ru/feeds/5311522479826646968/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://www.transl-gunsmoker.ru/2012/02/blog-post_18.html#comment-form" title="Комментарии: 0" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/5311522479826646968?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/5311522479826646968?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/GunsmokersTranslations/~3/g3yyaGW447o/blog-post_18.html" title="Как мне найти программу, которая держит этот файл?" /><author><name>Александр Алексеев</name><uri>https://profiles.google.com/113168002104297556003</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh3.googleusercontent.com/-aUMttZQBsuI/AAAAAAAAAAI/AAAAAAAAC3Y/QuZ7K9t_WzE/s512-c/photo.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://www.transl-gunsmoker.ru/2012/02/blog-post_18.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CE8BQng5cCp7ImA9WhRaFU8.&quot;"><id>tag:blogger.com,1999:blog-1688132520265708820.post-3299934105999946592</id><published>2012-02-17T03:52:00.000+04:00</published><updated>2012-02-18T03:54:13.628+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-02-18T03:54:13.628+04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="кодинг" /><title>Некоторые папки двигать нельзя - и вам придётся научиться с этим жить</title><content type="html">Это перевод &lt;a href="http://blogs.msdn.com/b/oldnewthing/archive/2010/08/06/10046812.aspx" title="Some known folders cannot be moved, but others can, and you'll just have to accept that"&gt;Some known folders cannot be moved, but others can, and you'll just have to accept that&lt;/a&gt;. Автор: Реймонд Чен.&lt;br /&gt;
&lt;br /&gt;
Некоторые из "известных папок" оболочки (Shell known folders) могут быть помечены как &lt;a title="MSDN: KF_CATEGORY enumeration" href="http://msdn.microsoft.com/en-us/library/bb762512(VS.85).aspx"&gt;&lt;code&gt;KF_CATEGORY_FIXED&lt;/code&gt;&lt;/a&gt;, что делает их неперемещаемыми. И наоборот, если папка файловой системы не является "неподвижной", то она может быть перемещена.&lt;br /&gt;
&lt;br /&gt;
Эта дихотомия представляется простой и недостойной отдельного обсуждения - за исключением того, что у некоторых клиентов иногда возникают проблемы включения этого понятия в своё мировоззрение.&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;
&lt;blockquote&gt;У меня есть код, который вызывает &lt;a title="MSDN: SHSetFolderPath function" href="http://msdn.microsoft.com/en-us/library/windows/desktop/bb762247(v=vs.85).aspx"&gt;&lt;code&gt;SHSet­Folder­Path&lt;/code&gt;&lt;/a&gt;, и он работает для большинства папок, но для некоторых значений CSIDL вроде &lt;code&gt;CSIDL_COMMON_APPDATA&lt;/code&gt; код завершается с ошибкой &lt;code&gt;E_INVALIDARG&lt;/code&gt;. При этом не имеет значения, вызываю ли я его с повышением привилегий или нет. Что я делаю не так?&lt;/blockquote&gt;
Разница в том, что &lt;code&gt;CSIDL_COMMON_APPDATA&lt;/code&gt; (известная в &lt;i&gt;Новом Мировом Порядке&lt;/i&gt; как &lt;code&gt;FOLDERID_ProgramData&lt;/code&gt;) помечена как &lt;code&gt;KF_CATEGORY_FIXED&lt;/code&gt;, так что её нельзя переместить.
&lt;blockquote&gt;А есть ли способ игнорировать флаг &lt;code&gt;KF_CATEGORY_FIXED&lt;/code&gt; и переместить её?&lt;/blockquote&gt;
Нет. Она неперемещаема. Извините. Вам просто остаётся принять тот факт, что эта папка не поменяет своё расположение по вашему желанию.&lt;br /&gt;
&lt;br /&gt;
И буквально на следующий день мы получили такой вопрос от совершенно другого клиента:
&lt;blockquote&gt;У нас есть программа, которая следит за "известной папкой", но наш код вылетает, если пользователь меняет расположение папки в то время, пока программа за ней следит. Есть ли какой-нибудь способ сделать так, чтобы пользователь не мог двигать папку?&lt;/blockquote&gt;
Если папка может быть перемещена, то вам нужно принять тот факт, что она может быть перемещена. Вы не можете изменить порядок вещей только потому, что это сделает легче жизнь лично вам.&lt;br /&gt;
&lt;br /&gt;
Я нахожу любопытным, что мы получили два запроса подряд, которые просят о полностью противоположных вещах: "Я хочу сделать эту папку перемещаемой!" и "Я хочу сделать эту папку неперемещаемой!". Я могу только догадываться о &lt;a title="Что если две программы сделают это?" href="http://www.transl-gunsmoker.ru/2010/06/blog-post_11.html"&gt;том хаосе, который вызовут эти программы, будучи запущенными одновременно на одной машине&lt;/a&gt;!&lt;br /&gt;
&lt;br /&gt;
Что может сделать программа - зарегистрировать &lt;a title="MSDN: IFile­Is­In­Use interface" href="http://msdn.microsoft.com/en-us/library/windows/desktop/bb775874(v=vs.85).aspx"&gt;&lt;code&gt;IFile­Is­In­Use&lt;/code&gt;&lt;/a&gt; для каталога, так что оболочка вызовет её, когда кто-то захочет переместить папку. И тогда, по крайней мере, программа будет знать, когда будут происходить &lt;i&gt;страшные вещи&lt;/i&gt;, и подготовиться к ним. Мне сказали, что &lt;a href="http://msdn.microsoft.com/en-us/library/ee330722(VS.85).aspx" title="MSDN: File Is In Use Sample"&gt;простой пример использования &lt;code&gt;IFile­Is­In­Use&lt;/code&gt;&lt;/a&gt; можно найти в SDK Windows 7 SDK в каталоге winui\Shell\AppPlatform\FileIsInUse. И ещё есть &lt;a title="Your File Is In Use… Demystified" href="http://wayback.archive.org/web/jsp/Interstitial.jsp?seconds=5&amp;date=1177850876000&amp;url=http%3A%2F%2Fshellrevealed.com%2Fblogs%2Fshellblog%2Farchive%2F2007%2F03%2F29%2FYour-File-Is-In-Use_2620_-Demystified.aspx&amp;target=http%3A%2F%2Fweb.archive.org%2Fweb%2F20070429124756%2Fhttp%3A%2F%2Fshellrevealed.com%2Fblogs%2Fshellblog%2Farchive%2F2007%2F03%2F29%2FYour-File-Is-In-Use_2620_-Demystified.aspx"&gt;старая статья&lt;/a&gt; по этой теме в сейчас-уже-не-работающем блоге Shell Revealed.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688132520265708820-3299934105999946592?l=www.transl-gunsmoker.ru' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=5-74wKDK0B0:ZeYGKeD_qnk:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=5-74wKDK0B0:ZeYGKeD_qnk:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=5-74wKDK0B0:ZeYGKeD_qnk:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=5-74wKDK0B0:ZeYGKeD_qnk:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=5-74wKDK0B0:ZeYGKeD_qnk:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=5-74wKDK0B0:ZeYGKeD_qnk:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=5-74wKDK0B0:ZeYGKeD_qnk:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=5-74wKDK0B0:ZeYGKeD_qnk:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=5-74wKDK0B0:ZeYGKeD_qnk:-BTjWOF_DHI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=5-74wKDK0B0:ZeYGKeD_qnk:-BTjWOF_DHI" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/GunsmokersTranslations/~4/5-74wKDK0B0" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.transl-gunsmoker.ru/feeds/3299934105999946592/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://www.transl-gunsmoker.ru/2012/02/blog-post.html#comment-form" title="Комментарии: 1" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/3299934105999946592?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/3299934105999946592?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/GunsmokersTranslations/~3/5-74wKDK0B0/blog-post.html" title="Некоторые папки двигать нельзя - и вам придётся научиться с этим жить" /><author><name>Александр Алексеев</name><uri>https://profiles.google.com/113168002104297556003</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh3.googleusercontent.com/-aUMttZQBsuI/AAAAAAAAAAI/AAAAAAAAC3Y/QuZ7K9t_WzE/s512-c/photo.jpg" /></author><thr:total>1</thr:total><feedburner:origLink>http://www.transl-gunsmoker.ru/2012/02/blog-post.html</feedburner:origLink></entry><entry gd:etag="W/&quot;Ak4GSHwyeip7ImA9WhdUEEo.&quot;"><id>tag:blogger.com,1999:blog-1688132520265708820.post-757251907260489577</id><published>2011-09-08T02:59:00.000+04:00</published><updated>2011-09-27T03:15:29.292+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-09-27T03:15:29.292+04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="История" /><category scheme="http://www.blogger.com/atom/ns#" term="Win32" /><category scheme="http://www.blogger.com/atom/ns#" term="серия" /><title>Почему функция SetWindowsHookEx принимает параметр HINSTANCE?</title><content type="html">Это перевод &lt;a href="http://blogs.msdn.com/b/oldnewthing/archive/2006/08/11/695514.aspx" title="Why does SetWindowsHookEx take an HINSTANCE parameter?"&gt;Why does SetWindowsHookEx take an HINSTANCE parameter?&lt;/a&gt; Автор: Реймонд Чен.&lt;br /&gt;
&lt;br /&gt;
&lt;a href="http://blogs.msdn.com/b/oldnewthing/archive/2005/04/25/411741.aspx#412529" title="RE: What is the HINSTANCE passed to SetWindowsHookEx used for? by Anonymous"&gt;Анонимный комментатор спросил&lt;/a&gt;, почему функция &lt;code&gt;SetWindowsHookEx&lt;/code&gt; принимает параметр &lt;code&gt;HINSTANCE&lt;/code&gt;, если она всё равно сконвертирует это значение в имя файла.&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
Потому что ловушки работали иначе в 16-битных Windows.&lt;br /&gt;
&lt;br /&gt;
В мире 16-битных Windows не было такой вещи как внедрение ловушки. Все 16-битные приложения Windows работали в едином адресном пространстве, так что не было никакой необходимости внедрять ловушку - она и так была доступна всем процессам. Соответственно, не было никакой необходимости конвертировать описатель в имя файла.&lt;br /&gt;
&lt;br /&gt;
Но что там с описателем? Он был нужен, чтобы увеличить счётчик загрузок модуля, чтобы модуль не выгрузился бы в то время, пока установлена и работает ловушка. Когда ловушка удалялась, то счётчик ссылок уменьшался.&lt;br /&gt;
&lt;br /&gt;
И даже в 32-битных Windows оконному менеджеру всё ещё нужен описатель. Зачем? Чтобы сконвертировать адрес функции в DLL в RVA (прим.пер.: относительное смещение) - чтобы эту функцию можно было потом найти в DLL, когда она будет загружена в другой процесс (прим.пер.: и по другому адресу). Так что если бы вы передавали имя фала вместо описателя, то оконному менеджеру всё равно пришлось бы вызывать &lt;code&gt;GetModuleHandle&lt;/code&gt; для получения описателя. Поскольку большинство программ хранит описатели, а не имена, то выбор параметра-описателя был естественным выбором (не говоря уже о сохранении совместимости с 16-битными Windows - а это очень важный критерий, когда ты пытаешься убедить людей писать программы под Win32).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688132520265708820-757251907260489577?l=www.transl-gunsmoker.ru' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=wHc6933CS4M:vXOSRAmI7TA:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=wHc6933CS4M:vXOSRAmI7TA:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=wHc6933CS4M:vXOSRAmI7TA:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=wHc6933CS4M:vXOSRAmI7TA:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=wHc6933CS4M:vXOSRAmI7TA:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=wHc6933CS4M:vXOSRAmI7TA:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=wHc6933CS4M:vXOSRAmI7TA:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=wHc6933CS4M:vXOSRAmI7TA:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=wHc6933CS4M:vXOSRAmI7TA:-BTjWOF_DHI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=wHc6933CS4M:vXOSRAmI7TA:-BTjWOF_DHI" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/GunsmokersTranslations/~4/wHc6933CS4M" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.transl-gunsmoker.ru/feeds/757251907260489577/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://www.transl-gunsmoker.ru/2011/09/why-does-setwindowshookex-take.html#comment-form" title="Комментарии: 0" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/757251907260489577?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/757251907260489577?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/GunsmokersTranslations/~3/wHc6933CS4M/why-does-setwindowshookex-take.html" title="Почему функция SetWindowsHookEx принимает параметр HINSTANCE?" /><author><name>Александр Алексеев</name><uri>https://profiles.google.com/113168002104297556003</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh3.googleusercontent.com/-aUMttZQBsuI/AAAAAAAAAAI/AAAAAAAAC3Y/QuZ7K9t_WzE/s512-c/photo.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://www.transl-gunsmoker.ru/2011/09/why-does-setwindowshookex-take.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DUQBQ3Y6fip7ImA9WhdUEEo.&quot;"><id>tag:blogger.com,1999:blog-1688132520265708820.post-8509687284284189844</id><published>2011-09-07T02:55:00.000+04:00</published><updated>2011-09-27T02:49:12.816+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-09-27T02:49:12.816+04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="История" /><category scheme="http://www.blogger.com/atom/ns#" term="серия" /><category scheme="http://www.blogger.com/atom/ns#" term="Win16" /><title>Как злоупотребляли оконными ловушками в Win16</title><content type="html">Это перевод &lt;a href="http://blogs.msdn.com/b/oldnewthing/archive/2006/08/10/694362.aspx" title="One way people abused hooks in 16-bit Windows"&gt;One way people abused hooks in 16-bit Windows&lt;/a&gt;. Автор: Реймонд Чен.&lt;br /&gt;
&lt;br /&gt;
В &lt;a title="Как были реализованы оконные ловушки в 16-битных Windows?" href="http://www.transl-gunsmoker.ru/2011/09/how-were-window-hooks-implemented-in-16.html"&gt;прошлый раз&lt;/a&gt; мы увидели, как в 16-битных Windows реализуются оконные ловушки. Даже хотя значения &lt;code&gt;HHOOK&lt;/code&gt; являются не прозрачными типами данных ("чёрным ящиком"), с которыми нужно работать как с описателями, многие программы "знали слишком много, чтобы стать опасными" - и воспользовались тем фактом, что значения &lt;code&gt;HHOOK&lt;/code&gt; были просто указателями на предыдущую процедуру ловушки.&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
Самый частый способ использования этой информации во зло заключается в снятии ловушки неверным способом. Вместо вызова функции &lt;code&gt;UnhookWindowsHook&lt;/code&gt; снова вызывалась &lt;code&gt;SetWindowsHook&lt;/code&gt;! Более конкретно: текущая ловушка удалялась установкой на вершину цепочки предыдущей ловушки:
&lt;pre class="brush:delphi"&gt;var
  g_hhkPrev: HHOOK;

// Установка ловушки
g_hhkPrev := SetWindowsHook(WH_KEYBOARD, MyHookProc);
...

// Безумие! Удаление ловушки установкой предыдущей ловушки "обратно"
SetWindowsHook(WH_KEYBOARD, g_hhkPrev);&lt;/pre&gt;
Несмотря на это вопиюще неверное поведение, код работал; это вроде как две неверные вещи при сложении дали почти правильное. Если никто не трогал цепочку ловушек между установкой и таким "снятием" ловушки, то переустановка старой ловушки в вершину списка восстанавливала список ловушек в то же состояние, в которое он приходит при правильном удалении ловушки.&lt;br /&gt;
&lt;br /&gt;
Но если между этими двумя вызовами кто-то другой установит их собственную ловушку, то установка старой ловушки заново приведёт к "удалению" не только &lt;code&gt;MyHookProc&lt;/code&gt;, но и &lt;b&gt;всех ловушек, которые были установлены после неё&lt;/b&gt; (это в точности та же проблема, которая получается, если &lt;a title="Homework assignment about window subclassing" href="http://blogs.msdn.com/b/oldnewthing/archive/2003/11/10/55647.aspx"&gt;вы неосторожно удаляете саб-классированные оконные процедуры&lt;/a&gt;).&lt;br /&gt;
&lt;br /&gt;
Я до сих пор понятия не имею, почему кому-то взбрело в голову удалять ловушку именно таким способом вместо правильного - они ничего не сэкономили, а просто заменили одну строчку на другую:
&lt;pre class="brush:delphi"&gt;UnhookWindowsHook(WH_KEYBOARD, MyHookProc);&lt;/pre&gt;
Windows 3.1 ввела модель с &lt;code&gt;SetWindowsHookEx&lt;/code&gt;/&lt;code&gt;CallNextHookEx&lt;/code&gt;, которая не использовала технику внешнего связанного списка, а хранила список сама, внутренне. Это защитило цепочку ловушек от программ, которые портили её неверным управлением списком, но это также означало, что теперь все эти сумасшедшие программы, которые пытались удалить ловушку повторной установкой старой ловушки, теперь будут просто портить внутренний список ловушек. Нам пришлось написать специальный код, который определял такие случаи и заменял их на корректные вызовы.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688132520265708820-8509687284284189844?l=www.transl-gunsmoker.ru' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=NDA3RtiRuuc:NGYRCi0dRLo:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=NDA3RtiRuuc:NGYRCi0dRLo:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=NDA3RtiRuuc:NGYRCi0dRLo:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=NDA3RtiRuuc:NGYRCi0dRLo:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=NDA3RtiRuuc:NGYRCi0dRLo:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=NDA3RtiRuuc:NGYRCi0dRLo:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=NDA3RtiRuuc:NGYRCi0dRLo:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=NDA3RtiRuuc:NGYRCi0dRLo:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=NDA3RtiRuuc:NGYRCi0dRLo:-BTjWOF_DHI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=NDA3RtiRuuc:NGYRCi0dRLo:-BTjWOF_DHI" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/GunsmokersTranslations/~4/NDA3RtiRuuc" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.transl-gunsmoker.ru/feeds/8509687284284189844/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://www.transl-gunsmoker.ru/2011/09/one-way-people-abused-hooks-in-16-bit.html#comment-form" title="Комментарии: 0" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/8509687284284189844?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/8509687284284189844?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/GunsmokersTranslations/~3/NDA3RtiRuuc/one-way-people-abused-hooks-in-16-bit.html" title="Как злоупотребляли оконными ловушками в Win16" /><author><name>Александр Алексеев</name><uri>https://profiles.google.com/113168002104297556003</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh3.googleusercontent.com/-aUMttZQBsuI/AAAAAAAAAAI/AAAAAAAAC3Y/QuZ7K9t_WzE/s512-c/photo.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://www.transl-gunsmoker.ru/2011/09/one-way-people-abused-hooks-in-16-bit.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DUIBQn8zfCp7ImA9WhdUEEo.&quot;"><id>tag:blogger.com,1999:blog-1688132520265708820.post-3284805136637551190</id><published>2011-09-06T02:55:00.000+04:00</published><updated>2011-09-27T02:52:33.184+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-09-27T02:52:33.184+04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="История" /><category scheme="http://www.blogger.com/atom/ns#" term="серия" /><category scheme="http://www.blogger.com/atom/ns#" term="Win16" /><title>Как были реализованы оконные ловушки в 16-битных Windows?</title><content type="html">Это перевод &lt;a href="http://blogs.msdn.com/b/oldnewthing/archive/2006/08/09/693280.aspx" title="How were window hooks implemented in 16-bit Windows?"&gt;How were window hooks implemented in 16-bit Windows?&lt;/a&gt; Автор: Реймонд Чен.&lt;br /&gt;
&lt;br /&gt;
Механизм отслеживания оконных ловушек был реализован существенно иначе в 16-битных Windows.&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
При этом использовались функции &lt;code&gt;SetWindowsHook&lt;/code&gt;, &lt;code&gt;UnhookWindowsHook&lt;/code&gt; и &lt;code&gt;DefHookProc&lt;/code&gt;. Первые две функции существуют даже сегодня, но третья заменена простым макросом:
&lt;pre class="brush:cpp"&gt;// 16-битный прототип
DWORD WINAPI DefHookProc(int nCode, WPARAM wParam,
                         LPARAM lParam, HHOOK FAR *phk);

// 32-битный макрос
#define DefHookProc(nCode, wParam, lParam, phhk)\
        CallNextHookEx(*phhk, nCode, wParam, lParam)&lt;/pre&gt;
Предупреждение: весь код реконструируется по памяти. Его дух не тронут, но точные детали могут быть указаны не верно.&lt;br /&gt;
&lt;br /&gt;
Чтобы установить ловушку в 16-битных Windows, вы сначала вызываете &lt;code&gt;SetWindowsHook&lt;/code&gt;:
&lt;pre class="brush:delphi"&gt;var
  g_hhkPrev: HHOOK;
...
  g_hhkPrev := SetWindowsHook(WH_WHATEVER, MyHookProc);&lt;/pre&gt;
Возвращаемое значение от &lt;code&gt;SetWindowsHook&lt;/code&gt; должно быть сохранено в глобальной переменной, которой мы дали несколько провокационное имя &lt;code&gt;g_hhkPrev&lt;/code&gt;. Процедура ловушки выглядела вот так:
&lt;pre class="brush:delphi"&gt;// В Win16 процедура ловушки возвращала DWORD, а не LRESULT
function MyHookProc(nCode: Integer; wParam: WinTypes.WPARAM; lParam: WinTypes.LPARAM): DWord;
begin
  if nCode &gt;= 0 then
  begin
    ...
  end;
  Result := DefHookProc(nCode, wParam, lParam, g_hhkPrev);
end;&lt;/pre&gt;
А когда вы заканчивали работу, то вы удаляли ловушку вызовом &lt;code&gt;UnhookWindowsHook&lt;/code&gt;:
&lt;pre class="brush:delphi"&gt;UnhookWindowsHook(WH_WHATEVER, MyhookProc);
g_hhkPrev := 0;&lt;/pre&gt;
Внутренне, цепочка функций ловушек управлялась как связанный список, но вместо использования для хранения списка каких-то внутренних структур, связанный список управлялся через сами переменные &lt;code&gt;HHOOK&lt;/code&gt;.&lt;br /&gt;
&lt;br /&gt;
Внутренняя реализация &lt;code&gt;SetWindowsHook&lt;/code&gt; была довольно простой:
&lt;pre class="brush:delphi"&gt;// Этот массив инициализирован процедурами вида "ничего не делай"
var
  g_rgHook: array[0..NUMHOOKS - 1] of HOOKPROC;

function SetWindowsHook(nType: Integer; pfnHookProc: HOOKPROC): HHOOK; 
var
  hhkPrev: HHOOK;
begin
  hhkPrev := HHOOK(g_rgHook[nType]);
  g_rgHook[nType] := pfnHookProc;
  Result := hhkPrev;
end;&lt;/pre&gt;
Установка ловушки просто записывала вашу процедуру ловушки в заголовок списка (цепочки ловушек), и возвращала предыдущее начало списка. Вызов ловушки заключался просто в вызове первой ловушки (головы списка):
&lt;pre class="brush:delphi"&gt;function CallHook(nType, nCode: Integer; wParam: WinTypes.WPARAM; lParam: WinTypes.LPARAM): DWORD;
begin
  Result := g_rgHook[nType](nCode, wParam, lParam);
end;&lt;/pre&gt;
Каждая процедура ловушки сначала выполняла свою работу, а затем передавала управление следующему элементу в цепочке, вызывая &lt;code&gt;DefHookProc&lt;/code&gt; и передавая ему &lt;code&gt;HHOOK&lt;/code&gt;:
&lt;pre class="brush:delphi"&gt;function DefHookProc(nCode: Integer; wParam: WinTypes.WPARAM; lParam: WinTypes.LPARAM; var phk: HHOOK): DWord;
var
  pfnNext: HOOKPROC;
begin
  pfnNext := HOOKPROC(phk);
  if nCode &gt;=0 then
  begin 
    Result := pfnNext(nCode, wParam, lParam);
    Exit;
  end;
  ... ещё код ...
end;&lt;/pre&gt;
Как вы можете видеть, это чрезвычайно примитивно: вызов ловушки вызывает первую процедуру в списке, которая затем вызывает &lt;code&gt;DefHookProc&lt;/code&gt;, которая знает, что &lt;code&gt;HHOOK&lt;/code&gt; - это просто &lt;code&gt;HOOKPROC&lt;/code&gt;, и она просто перенаправляет вызов вниз по списку, прямо вызывая эту самую функцию.&lt;br /&gt;
&lt;br /&gt;
А волшебство здесь заключается в последнем шаге, когда вы снимаете ловушку. Вспомните, что правило вызова ловушек таково, что отрицательные коды должны передаваться в &lt;code&gt;DefHookProc&lt;/code&gt; (или, по современному: &lt;code&gt;CallNextHookEx&lt;/code&gt;) напрямую. Это соглашение позволяет подсистеме ловушек использовать отрицательные коды для ведения учёта. В данном случае мы использовали -1 как код "снять эту ловушку":
&lt;pre class="brush:delphi"&gt;function UnhookWindowsHook(nType: Integer; pfnHookProc: HOOKPROC): BOOL;
begin
  Result := DefHookProc(-1, 0, LPARAM(pfnHookProc), HHOOK(g_rgHook[nType]));
end;&lt;/pre&gt;
А затем начинается магия:
&lt;pre class="brush:delphi"&gt;function DefHookProc(nCode: Integer; wParam: WinTypes.WPARAM; lParam: WinTypes.LPARAM; var phk: HHOOK): DWord;
var
  pfnNext: HOOKPROC;
begin
  pfnNext := HOOKPROC(phk);
  if nCode &gt;=0 then
  begin 
    Result := pfnNext(nCode, wParam, lParam);
    Exit;
  end;
  case nCode of 
    -1: // пытаемся снять ловушку
    begin
      if pfnNext = HOOKPROC(lParam) then // нашли ловушку
      begin
        phk := HHOOK(pfnNext(-2, 0, 0));
        Result := Ord(True);
      end
      else
        // иначе - продолжаем искать
        Result := pfnNext(nCode, wParam, lParam);
    end;
    -2: // сообщить о следующей процедуре ловушке
      Result := DWORD(@phk);
  else
    Result := 0;
  end;
end;&lt;/pre&gt;
Вот так - вся система оконных ловушек занимает всего несколько строк. Вам надо отдать должное Win16 за её компактный размер.&lt;br /&gt;
&lt;br /&gt;
Прим.пер.: вырезан разбор примера работы.&lt;br /&gt;
&lt;br /&gt;
Каждый раз, когда я говорю об этом, я восхищаюсь, как компактно была написана 16-битная Windows. Всего в двух десятках строк мы создали систему управления связанным списком функций, включающую в себя диспетчеризацию, а также поддержку удаления произвольного элемента из середины списка - и всё это без единого выделения памяти.&lt;br /&gt;
&lt;br /&gt;
В &lt;a title="Как злоупотребляли оконными ловушками в Win16" href="http://www.transl-gunsmoker.ru/2011/09/one-way-people-abused-hooks-in-16-bit.html"&gt;следующий раз&lt;/a&gt; мы посмотрим на то, как люди злоупотребляли этой простой системой.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688132520265708820-3284805136637551190?l=www.transl-gunsmoker.ru' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=83Wl_1bc-Qs:mB6kCA-Endk:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=83Wl_1bc-Qs:mB6kCA-Endk:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=83Wl_1bc-Qs:mB6kCA-Endk:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=83Wl_1bc-Qs:mB6kCA-Endk:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=83Wl_1bc-Qs:mB6kCA-Endk:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=83Wl_1bc-Qs:mB6kCA-Endk:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=83Wl_1bc-Qs:mB6kCA-Endk:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=83Wl_1bc-Qs:mB6kCA-Endk:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=83Wl_1bc-Qs:mB6kCA-Endk:-BTjWOF_DHI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=83Wl_1bc-Qs:mB6kCA-Endk:-BTjWOF_DHI" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/GunsmokersTranslations/~4/83Wl_1bc-Qs" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.transl-gunsmoker.ru/feeds/3284805136637551190/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://www.transl-gunsmoker.ru/2011/09/how-were-window-hooks-implemented-in-16.html#comment-form" title="Комментарии: 0" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/3284805136637551190?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/3284805136637551190?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/GunsmokersTranslations/~3/83Wl_1bc-Qs/how-were-window-hooks-implemented-in-16.html" title="Как были реализованы оконные ловушки в 16-битных Windows?" /><author><name>Александр Алексеев</name><uri>https://profiles.google.com/113168002104297556003</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh3.googleusercontent.com/-aUMttZQBsuI/AAAAAAAAAAI/AAAAAAAAC3Y/QuZ7K9t_WzE/s512-c/photo.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://www.transl-gunsmoker.ru/2011/09/how-were-window-hooks-implemented-in-16.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DEEERXo6fip7ImA9WhdVGU0.&quot;"><id>tag:blogger.com,1999:blog-1688132520265708820.post-7872144256622663884</id><published>2011-09-05T02:54:00.000+04:00</published><updated>2011-09-25T03:23:24.416+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-09-25T03:23:24.416+04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="клавиатуры" /><title>Кандидат на звание самого непонятного сочетания клавиш: Shift+F8</title><content type="html">Это перевод &lt;a href="http://blogs.msdn.com/b/oldnewthing/archive/2006/08/08/692129.aspx" title="Candidate for most obscure keyboard shortcut: Shift+F8"&gt;Candidate for most obscure keyboard shortcut: Shift+F8&lt;/a&gt;. Автор: Реймонд Чен.&lt;br /&gt;
&lt;br /&gt;
Так получилось, что одной из самых непонятных сочетаний клавиш является Shift+F8, которая используется в listbox для несмежного расширенного выбора. Господи, сколько же слов.&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
&lt;a title="Список используемых в Windows XP сочетаний клавиш" href="http://support.microsoft.com/default.aspx?scid=301583"&gt;Статья KB Q301583&lt;/a&gt; не очень помогает в понимании, перечисляя это сочетание в разделе "Сочетания клавиш для диалоговых окон", даже хотя это не диалоговая комбинация клавиш. Это сочетание клавиш listbox-а.&lt;br /&gt;
&lt;br /&gt;
Если в listbox включен режим расширенного выделения, то вы можете использовать комбинацию Shift+F8, чтобы создать несмежное расширенное выделение с клавиатуры (с помощью мыши вы можете сделать это просто делая Ctrl+щелчок). Нажмите Shift+F8 первый раз, чтобы войти в режим расширенного выделения, затем используйте стрелочки для перемещения по списку и нажмите Ctrl+Space или Shift+Space для выделения (или снятия выделения). Когда вы закончите - нажмите Shift+F8 ещё раз (или просто переместите фокус к следующему элементу управления).&lt;br /&gt;
&lt;br /&gt;
И да, конкретно этот клавиатурный интерфейс просто кошмарен. Более естественный механизм использовал бы Ctrl+Стрелка для перемещения фокуса без изменения выбора, и Ctrl+Пробел для выбора (или отмены выбора) элемента. К счастью, элемент управления listview использовал именно этот подход вместо подражания сумасшедшему сочетанию клавиш Shift+F8.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688132520265708820-7872144256622663884?l=www.transl-gunsmoker.ru' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=ffsMYf88BfM:YGWqz_oxEQ8:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=ffsMYf88BfM:YGWqz_oxEQ8:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=ffsMYf88BfM:YGWqz_oxEQ8:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=ffsMYf88BfM:YGWqz_oxEQ8:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=ffsMYf88BfM:YGWqz_oxEQ8:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=ffsMYf88BfM:YGWqz_oxEQ8:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=ffsMYf88BfM:YGWqz_oxEQ8:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=ffsMYf88BfM:YGWqz_oxEQ8:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=ffsMYf88BfM:YGWqz_oxEQ8:-BTjWOF_DHI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=ffsMYf88BfM:YGWqz_oxEQ8:-BTjWOF_DHI" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/GunsmokersTranslations/~4/ffsMYf88BfM" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.transl-gunsmoker.ru/feeds/7872144256622663884/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://www.transl-gunsmoker.ru/2011/09/candidate-for-most-obscure-keyboard.html#comment-form" title="Комментарии: 0" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/7872144256622663884?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/7872144256622663884?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/GunsmokersTranslations/~3/ffsMYf88BfM/candidate-for-most-obscure-keyboard.html" title="Кандидат на звание самого непонятного сочетания клавиш: Shift+F8" /><author><name>Александр Алексеев</name><uri>https://profiles.google.com/113168002104297556003</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh3.googleusercontent.com/-aUMttZQBsuI/AAAAAAAAAAI/AAAAAAAAC3Y/QuZ7K9t_WzE/s512-c/photo.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://www.transl-gunsmoker.ru/2011/09/candidate-for-most-obscure-keyboard.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DEAERXkyfyp7ImA9WhdaEEs.&quot;"><id>tag:blogger.com,1999:blog-1688132520265708820.post-8928091661563735431</id><published>2011-09-04T22:10:00.000+04:00</published><updated>2011-10-20T03:25:04.797+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-10-20T03:25:04.797+04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Win32" /><category scheme="http://www.blogger.com/atom/ns#" term="серия" /><title>Как менее наивный компилятор вызывает импортируемую функцию</title><content type="html">Это перевод &lt;a href="http://blogs.msdn.com/b/oldnewthing/archive/2006/07/24/676669.aspx" title="How a less naive compiler calls an imported function"&gt;How a less naive compiler calls an imported function&lt;/a&gt;. Автор: Реймонд Чен.&lt;br /&gt;
&lt;br /&gt;
Если функция объявлена со спецификатором &lt;code&gt;dllimport&lt;/code&gt;, то это указывает компилятору Visual Studio C/C++, что эта функция импортируется из другого (исполняемого) модуля, а не является обычной функцией в этом же исполняемом модуле. Имея на руках эту информацию, компилятор генерирует немного другой код, поскольку теперь он осведомлён об особенностях импортируемых функций.&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
Во-первых, теперь больше нет необходимости в функции-заглушке, потому что компилятор может сгенерировать инструкцию call [__imp__FunctionName]. Кроме того, компилятор знает, что этот адрес (адрес импортируемой функции) никогда не меняется, и, соответственно, он может оптимизировать многократное использование этого адреса, например:
&lt;pre class="brush:asm"&gt;    mov   ebx, [__imp__FunctionName]
    push  1
    call  ebx ; FunctionName(1)
    push  2
    call  ebx ; FunctionName(2)&lt;/pre&gt;
(Примечание к сумасшедшим людям: подобная оптимизация означает, что у вас могут возникнуть проблемы, если вы исправляете таблицу импорта модуля после того, как код в модуле начал работу - потому что указатель на функцию может быть сохранён в регистр до того, как вы начали править импорт. Рассмотрите случай с примером выше, когда вы изменили запись в таблице __imp__FunctionName после выполнения инструкции mov ebx, [__imp__FunctionName]: ваша функция-перехватчик не будет вызвана, потому что старый указатель на функцию сохранён в регистре ebx).&lt;br /&gt;
&lt;br /&gt;
Аналогично, если ваша программа попытается взять адрес импортируемой функции, которая была объявлена со спецификатором dllimport, то компилятор распознает эту операцию и преобразует её в загрузку адреса из таблицы адресов импортируемых функций.&lt;br /&gt;
&lt;br /&gt;
В результате этого дополнительного знания, сообщаемого компилятору, функции-заглушки больше не нужны; компилятор знает, что ему надо идти прямо к таблице адресов импортированных функций.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688132520265708820-8928091661563735431?l=www.transl-gunsmoker.ru' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=_sd7mkdZpWs:IcXXMx1d9ro:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=_sd7mkdZpWs:IcXXMx1d9ro:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=_sd7mkdZpWs:IcXXMx1d9ro:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=_sd7mkdZpWs:IcXXMx1d9ro:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=_sd7mkdZpWs:IcXXMx1d9ro:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=_sd7mkdZpWs:IcXXMx1d9ro:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=_sd7mkdZpWs:IcXXMx1d9ro:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=_sd7mkdZpWs:IcXXMx1d9ro:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=_sd7mkdZpWs:IcXXMx1d9ro:-BTjWOF_DHI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=_sd7mkdZpWs:IcXXMx1d9ro:-BTjWOF_DHI" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/GunsmokersTranslations/~4/_sd7mkdZpWs" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.transl-gunsmoker.ru/feeds/8928091661563735431/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://www.transl-gunsmoker.ru/2011/09/how-less-naive-compiler-calls-imported.html#comment-form" title="Комментарии: 2" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/8928091661563735431?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/8928091661563735431?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/GunsmokersTranslations/~3/_sd7mkdZpWs/how-less-naive-compiler-calls-imported.html" title="Как менее наивный компилятор вызывает импортируемую функцию" /><author><name>Александр Алексеев</name><uri>https://profiles.google.com/113168002104297556003</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh3.googleusercontent.com/-aUMttZQBsuI/AAAAAAAAAAI/AAAAAAAAC3Y/QuZ7K9t_WzE/s512-c/photo.jpg" /></author><thr:total>2</thr:total><feedburner:origLink>http://www.transl-gunsmoker.ru/2011/09/how-less-naive-compiler-calls-imported.html</feedburner:origLink></entry><entry gd:etag="W/&quot;A0cNQns_eyp7ImA9WhRaFU8.&quot;"><id>tag:blogger.com,1999:blog-1688132520265708820.post-735344658191301764</id><published>2011-09-03T22:10:00.000+04:00</published><updated>2012-02-18T05:38:13.543+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-02-18T05:38:13.543+04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Win32" /><category scheme="http://www.blogger.com/atom/ns#" term="серия" /><title>Вызов импортируемой функции, наивный способ</title><content type="html">Это перевод &lt;a href="http://blogs.msdn.com/b/oldnewthing/archive/2006/07/21/673830.aspx" title="Calling an imported function, the naive way"&gt;Calling an imported function, the naive way&lt;/a&gt;. Автор: Реймонд Чен.&lt;br /&gt;
&lt;br /&gt;
Библиотека импорта (import library) разрешает (resolve) символы импортируемых функций, но к ней не обращаются до этапа компоновки. Давайте посмотрим на наивную реализацию, когда компилятор слепо не осведомлён о существовании импортируемых функций.&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
В 16-битном мире это не вызывало никаких проблем. Компилятор генерировал дальний (far) вызов функции и оставлял внешнюю запись в объектном файле, указывающую, что вот этот адрес функции требует заполнения компоновщиком. На стадии компоновки линкер осознаёт, что этот внешний символ соответствует импортируемой функции, так что он &lt;a href="http://www.transl-gunsmoker.ru/2011/08/how-were-dll-functions-imported-in-16.html" title="Как импортировались DLL функции в 16-битных Windows?"&gt;собирает вместе все места её вызова в цепочку&lt;/a&gt; и создаёт запись импорта функции в таблице импорта модуля. В run-time эти записи исправляются загрузчиком ОС и все счастливы.&lt;br /&gt;
&lt;br /&gt;
Давайте посмотрим, как справится с этой же ситуацией наивный 32-битный компилятор. Компилятор сгенерирует обычную инструкцию вызова функции, перекладывая разрешение адреса на компоновщик. Линкер увидит, что этот внешний символ, на самом деле, является импортируемой функций, и, ой, прямой вызов нужно переделать в косвенный. Но компоновщик не может переписать код, сгенерированный компилятором. Что же делать компоновщику?&lt;br /&gt;
&lt;br /&gt;
Решение заключается во вставке ещё одно уровня косвенности (предупреждение: информация ниже не верна буквально, но она "достаточно правдоподобна". Мы копнём детали в следующих постах).&lt;br /&gt;
&lt;br /&gt;
Для каждой экспортируемой функции создаётся два символа. Первый из них - запись в таблице импортируемых функций. Назовём его __imp__FunctionName. Конечно же, наивный компилятор не знает ни про какой префикс __imp__. Он просто генерирует код для инструкции call FunctionName, ожидая что компоновщик подставит нужный адрес.&lt;br /&gt;
&lt;br /&gt;
Тут на сцену выходит второй символ. Он имеет имя FunctionName и является однострочной функцией, состоящей из одной-единственной инструкции: jmp [__imp__FunctionName] (генерируется компоновщиком). Эта крохотная заглушка предназначена для удовлетворения внешних ссылок на функцию FunctionName, и в свою очередь она генерирует ссылку на __imp__FunctionName, которая разрешается записью в таблице импортируемых функций.&lt;br /&gt;
&lt;br /&gt;
Когда модуль загружается - его зависимости импорта будут разрешены, и реальный адрес функции будет записан в __imp__FunctionName. Затем, когда код вызовет импортируемую функцию, то сгенерированный компилятором код вызовет функцию FunctionName, которая является заглушкой, которая и вызовет целевую функцию через косвенный вызов.&lt;br /&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;
&lt;a href="http://3.bp.blogspot.com/-_1g_6FMRM6o/TnsxZkkTlFI/AAAAAAAAC4g/CQIe-1tnjHo/s1600/x86_call.png" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="400" width="314" src="http://3.bp.blogspot.com/-_1g_6FMRM6o/TnsxZkkTlFI/AAAAAAAAC4g/CQIe-1tnjHo/s400/x86_call.png" /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
Заметьте, что с наивным компилятором, если ваш код попытается взять адрес импортируемой функции, то он получит адрес заглушки, поскольку наивный компилятор оперирует с адресом функции FunctionName, не зная о том, что она импортируется из другого модуля и для неё, на самом деле, создаётся заглушка.&lt;br /&gt;
&lt;br /&gt;
В &lt;a title="Как менее наивный компилятор вызывает импортируемую функцию" href="http://www.transl-gunsmoker.ru/2011/09/how-less-naive-compiler-calls-imported.html"&gt;следующий раз&lt;/a&gt; мы увидим, что с этой ситуацией сможет сделать менее наивный компилятор.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688132520265708820-735344658191301764?l=www.transl-gunsmoker.ru' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=2ymklcqcx08:SUIVWzYeMhY:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=2ymklcqcx08:SUIVWzYeMhY:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=2ymklcqcx08:SUIVWzYeMhY:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=2ymklcqcx08:SUIVWzYeMhY:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=2ymklcqcx08:SUIVWzYeMhY:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=2ymklcqcx08:SUIVWzYeMhY:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=2ymklcqcx08:SUIVWzYeMhY:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=2ymklcqcx08:SUIVWzYeMhY:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=2ymklcqcx08:SUIVWzYeMhY:-BTjWOF_DHI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=2ymklcqcx08:SUIVWzYeMhY:-BTjWOF_DHI" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/GunsmokersTranslations/~4/2ymklcqcx08" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.transl-gunsmoker.ru/feeds/735344658191301764/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://www.transl-gunsmoker.ru/2011/09/calling-imported-function-naive-way.html#comment-form" title="Комментарии: 5" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/735344658191301764?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/735344658191301764?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/GunsmokersTranslations/~3/2ymklcqcx08/calling-imported-function-naive-way.html" title="Вызов импортируемой функции, наивный способ" /><author><name>Александр Алексеев</name><uri>https://profiles.google.com/113168002104297556003</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh3.googleusercontent.com/-aUMttZQBsuI/AAAAAAAAAAI/AAAAAAAAC3Y/QuZ7K9t_WzE/s512-c/photo.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://3.bp.blogspot.com/-_1g_6FMRM6o/TnsxZkkTlFI/AAAAAAAAC4g/CQIe-1tnjHo/s72-c/x86_call.png" height="72" width="72" /><thr:total>5</thr:total><feedburner:origLink>http://www.transl-gunsmoker.ru/2011/09/calling-imported-function-naive-way.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DUMGQX04fSp7ImA9WhVTEEQ.&quot;"><id>tag:blogger.com,1999:blog-1688132520265708820.post-871306012853550258</id><published>2011-09-02T22:09:00.000+04:00</published><updated>2012-02-24T19:30:20.335+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-02-24T19:30:20.335+04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Win32" /><category scheme="http://www.blogger.com/atom/ns#" term="серия" /><title>Смена дизайна импорта в 32-х битных Windows</title><content type="html">Это перевод &lt;a href="http://blogs.msdn.com/b/oldnewthing/archive/2006/07/20/672695.aspx" title="Rethinking the way DLL exports are resolved for 32-bit Windows"&gt;Rethinking the way DLL exports are resolved for 32-bit Windows&lt;/a&gt;. Автор: Реймонд Чен.&lt;br /&gt;
&lt;br /&gt;
За последние дни мы узнали, как работает &lt;a title="Как экспортировались DLL функции в 16-битных Windows?" href="http://www.transl-gunsmoker.ru/2011/08/how-were-dll-functions-exported-in-16.html"&gt;экспорт&lt;/a&gt; и &lt;a title="Как импортировались DLL функции в 16-битных Windows?" href="http://www.transl-gunsmoker.ru/2011/08/how-were-dll-functions-imported-in-16.html"&gt;импорт&lt;/a&gt; функций в 16-битных Windows, а также что &lt;a href="http://www.transl-gunsmoker.ru/2011/08/how-are-dll-functions-exported-in-32.html" title="Как экспортируются DLL функции в 32-битных Windows?"&gt;экспорт функций в Win32 очень похож на экспорт функций в 16-битных Windows&lt;/a&gt;. Но 16-битный импорт был полностью переписан в мире 32-битных Windows.&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
Вспомните, что в 16-битных Windows правки для импортируемой функции размещались по всему сегменту цепочкой. Это прекрасно работало в Win16, потому что там было глобальное адресное пространство: сегменты кода разделялись глобально, так что будучи единожды загруженным, сегмент мог быть использован любым процессом. Но 32-битные Windows использовали изолированные адресные пространства. Если правки были расположены по всему сегменту, то загрузка сегмента кода с диска (вернее - страницы сегмента кода) означала его модификацию, что не давало разделять один сегмент несколькими процессами (механизм copy-on-write). Даже если бы таблица правок хранилась бы во внешнем сегменте, вам всё равно пришлось бы править сегмент кода, чтобы указать цели для переходов (если вы достаточно умны, то вы могли бы разделять страницы между процессами, если все правки были бы одинаковы для всех этих процессов; но учёт изменений для этой модели был бы полным кошмаром).&lt;br /&gt;
&lt;br /&gt;
Но помимо простой неэффективности, идея применения правок импорта к сегменту кода просто невозможна. К примеру, Alpha AXP имеет инструкцию "call direct" (прямой вызов), но она ограничена функциями, которые находятся лишь на расстоянии не более 128 Кб от места вызова. Если вы хотите вызвать функцию, которая находится дальше от вас, то вы должны загрузить адрес назначения во временный регистр и вызвать функцию через этот регистр. И, &lt;a href="http://www.transl-gunsmoker.ru/2008/11/64.html" title="Почему гранулярность выделения адресного пространства равна 64 Кб?"&gt;как мы видели ранее&lt;/a&gt;, загрузка 32-битного значения в регистр на Alpha AXP является двухступенчатой операцией, которая зависит от того, установлен или очищен 15-й бит того значения, которое вы хотите загрузить. Поскольку это импортируемая функция, то на этапе компиляции или компоновки вы понятия не имеете будет ли адрес целевой функции иметь 15-й бит или нет.&lt;br /&gt;
&lt;br /&gt;
И Alpha AXP была далеко не единственной архитектурой, которая ограничивала расстояние для прямых вызовов. К примеру, Intel ia64 (Itanium) мог делать прямые вызовы функций только в пределах 4 Мб, а популярные сегодня архитектуры AMD x86-64 и Intel EM64T ограничивают вас 2 Гб. Да, это не звучит как ограничение пока вы не вспомните, что это - 64-разрядные процессоры с 16 эксабайтами адресного пространства. И снова мы видим, что &lt;a title="Архитектура x86 - очень странная" href="http://www.transl-gunsmoker.ru/2009/07/x86.html"&gt;архитектура x86 является странной&lt;/a&gt; (прим.пер.: в том смысле, что на ней нет ограничения на прямые вызовы в отличие от многих других архитектур).&lt;br /&gt;
&lt;br /&gt;
Оба момента выше сделали правки импорта в сегменте кода нежелательными (и невозможными). Вместо этого правки импорта пришлось применять к данным. Вместо того, чтобы применять правку к каждому месту, где вызывается импортируемая функция, применяется всего одна правка в таблице указателей на функции. Это означает, что вызовы импортируемых функций на самом деле являются косвенными вызовами через указатель. К примеру, на x86 это означает, что вместо &lt;code&gt;call ImportedFunction&lt;/code&gt; генерируется код, который говорит &lt;code&gt;call [__imp__ImportedFunction]&lt;/code&gt;, где __imp__ImportedFunction - это имя переменной (места в таблице), которая содержит указатель на эту импортируемую функцию.&lt;br /&gt;
&lt;br /&gt;
Это означает, что правки импортируемых функций заключаются в простом поиске целевых адресов и их записи в таблицу импортируемых адресов (прим.пер.: хранимую в сегменте данных). Код не изменяется; он просто читает указатель на функцию в run-time и вызывает функцию через указатель.&lt;br /&gt;
&lt;br /&gt;
С этой информацией мы теперь вооружены для более глубокого погружения в кое-какие последствия этого дизайна - что мы и сделаем в &lt;a title="Вызов импортируемой функции, наивный способ" href="http://www.transl-gunsmoker.ru/2011/09/calling-imported-function-naive-way.html"&gt;следующий раз&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688132520265708820-871306012853550258?l=www.transl-gunsmoker.ru' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=cEfLWysfvaI:xeGz1LsBrng:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=cEfLWysfvaI:xeGz1LsBrng:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=cEfLWysfvaI:xeGz1LsBrng:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=cEfLWysfvaI:xeGz1LsBrng:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=cEfLWysfvaI:xeGz1LsBrng:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=cEfLWysfvaI:xeGz1LsBrng:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=cEfLWysfvaI:xeGz1LsBrng:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=cEfLWysfvaI:xeGz1LsBrng:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=cEfLWysfvaI:xeGz1LsBrng:-BTjWOF_DHI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=cEfLWysfvaI:xeGz1LsBrng:-BTjWOF_DHI" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/GunsmokersTranslations/~4/cEfLWysfvaI" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.transl-gunsmoker.ru/feeds/871306012853550258/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://www.transl-gunsmoker.ru/2011/09/rethinking-way-dll-exports-are-resolved.html#comment-form" title="Комментарии: 0" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/871306012853550258?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/871306012853550258?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/GunsmokersTranslations/~3/cEfLWysfvaI/rethinking-way-dll-exports-are-resolved.html" title="Смена дизайна импорта в 32-х битных Windows" /><author><name>Александр Алексеев</name><uri>https://profiles.google.com/113168002104297556003</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh3.googleusercontent.com/-aUMttZQBsuI/AAAAAAAAAAI/AAAAAAAAC3Y/QuZ7K9t_WzE/s512-c/photo.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://www.transl-gunsmoker.ru/2011/09/rethinking-way-dll-exports-are-resolved.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DUUAQHwzeCp7ImA9WhVTEEQ.&quot;"><id>tag:blogger.com,1999:blog-1688132520265708820.post-2702370324084433719</id><published>2011-09-01T22:09:00.000+04:00</published><updated>2012-02-24T19:27:21.280+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-02-24T19:27:21.280+04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Win32" /><category scheme="http://www.blogger.com/atom/ns#" term="серия" /><title>Экспортируемые функции, являющиеся forwarder-ами</title><content type="html">Это перевод &lt;a href="http://blogs.msdn.com/b/oldnewthing/archive/2006/07/19/671238.aspx" title="Exported functions that are really forwarders"&gt;Exported functions that are really forwarders&lt;/a&gt;. Автор: Реймонд Чен.&lt;br /&gt;
&lt;br /&gt;
В &lt;a title="Как экспортируются DLL функции в 32-битных Windows?" href="http://www.transl-gunsmoker.ru/2011/08/how-are-dll-functions-exported-in-32.html"&gt;прошлый раз&lt;/a&gt; мы видели как экспортируемые функции в Win32 ведут себя похоже на экспорт функций в 16-битных Windows - только сместился акцент использования с импорта по номеру на импорт по имени. Этот акцент никак не выражен в формате исполняемого файла; и 16-битные и 32-битные DLL могут экспортировать функции по номеру и имени (и обоим одновременно).&lt;br /&gt;
&lt;br /&gt;
Но в Win32 появляется новый тип экспортируемых функций, известный как forwarder. &lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
Forwarder выглядит как обычная экспортируемая функция, за исключением того что запись в таблице экспорта по номерам говорит "Ох, на самом деле, я не функция в этой DLL. На самом деле - я функция вон в той DLL". К примеру, если вы сделаете &lt;pre&gt;link /dump /exports kernel32.dll&lt;/pre&gt;
То вы увидите что-то такое:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;151   EnterCriticalSection (forwarded to NTDLL.RtlEnterCriticalSection)&lt;/pre&gt;
&lt;br /&gt;
Это означает, что если программа связана с &lt;code&gt;KERNEL32.EnterCriticalSection&lt;/code&gt;, то загрузчик ОС молчаливо перенаправит функцию на &lt;code&gt;NTDLL.RtlEnterCriticalSection&lt;/code&gt;. Forwarder-ы являются удобным способом сохранить внешний интерфейс, изменив функциональность выносом в отдельные DLL. Старая DLL продолжает экспортировать функцию, но сама функция была перемещена в новую DLL.&lt;br /&gt;
&lt;br /&gt;
Трюк с forwarder - это лучше, чем иметь пустую функцию-заглушку в старой DLL, которая просто вызывает функцию в новой DLL: потому что функция заглушка создаёт зависимость между новой и старой DLL (в конце концов, старую DLL нужно связать с новой DLL, чтобы была возможность вызвать функцию!). А при использовании forwarder новая DLL не будет загружаться, если только кто-то не попросит перенаправленную функцию из старой DLL. В результате вы не платите за загрузку новой DLL пока кто-то её явно не попросит.&lt;br /&gt;
&lt;br /&gt;
Okей, как мы увидели, с введением forwarder Win32 отошла от 16-битных Windows, но как только мы посмотрим на импорт, то увидим, что тут изменилось вообще всё. Мы посмотрим на эту историю в &lt;a href="http://www.transl-gunsmoker.ru/2011/09/rethinking-way-dll-exports-are-resolved.html" title="Смена дизайна импорта в 32-х битных Windows"&gt;следующий раз&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688132520265708820-2702370324084433719?l=www.transl-gunsmoker.ru' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=z48Etnt8mbs:-2aqWjW8yP4:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=z48Etnt8mbs:-2aqWjW8yP4:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=z48Etnt8mbs:-2aqWjW8yP4:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=z48Etnt8mbs:-2aqWjW8yP4:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=z48Etnt8mbs:-2aqWjW8yP4:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=z48Etnt8mbs:-2aqWjW8yP4:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=z48Etnt8mbs:-2aqWjW8yP4:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=z48Etnt8mbs:-2aqWjW8yP4:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=z48Etnt8mbs:-2aqWjW8yP4:-BTjWOF_DHI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=z48Etnt8mbs:-2aqWjW8yP4:-BTjWOF_DHI" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/GunsmokersTranslations/~4/z48Etnt8mbs" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.transl-gunsmoker.ru/feeds/2702370324084433719/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://www.transl-gunsmoker.ru/2011/09/exported-functions-that-are-really.html#comment-form" title="Комментарии: 0" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/2702370324084433719?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/2702370324084433719?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/GunsmokersTranslations/~3/z48Etnt8mbs/exported-functions-that-are-really.html" title="Экспортируемые функции, являющиеся forwarder-ами" /><author><name>Александр Алексеев</name><uri>https://profiles.google.com/113168002104297556003</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh3.googleusercontent.com/-aUMttZQBsuI/AAAAAAAAAAI/AAAAAAAAC3Y/QuZ7K9t_WzE/s512-c/photo.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://www.transl-gunsmoker.ru/2011/09/exported-functions-that-are-really.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DUYDQXsyfSp7ImA9WhVTEEQ.&quot;"><id>tag:blogger.com,1999:blog-1688132520265708820.post-5477983680087185034</id><published>2011-08-31T22:08:00.000+04:00</published><updated>2012-02-24T19:26:10.595+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-02-24T19:26:10.595+04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Win32" /><category scheme="http://www.blogger.com/atom/ns#" term="серия" /><title>Как экспортируются DLL функции в 32-битных Windows?</title><content type="html">Это перевод &lt;a href="http://blogs.msdn.com/b/oldnewthing/archive/2006/07/18/669668.aspx" title="How are DLL functions exported in 32-bit Windows?"&gt;How are DLL functions exported in 32-bit Windows?&lt;/a&gt; Автор: Реймонд Чен.&lt;br /&gt;
&lt;br /&gt;
Дизайнерам 32-битных Windows не нужно было беспокоиться о втискивании в 256 Кб памяти. Поскольку модули в Win32 построены на загрузке по запросу, то всё, что вам нужно сделать - спроецировать весь образ в память, а затем коснуться тех частей, которые вам нужны. Поэтому нет никакого различия между резидентной и не резидентной таблицами, так что имена экспортируемых функций просто записываются в исполняемый образ вместе с указателем (вернее относительным виртуальным адресом) на имя, хранимое в таблице экспорта.&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
В отличие от &lt;a title="Как экспортировались DLL функции в 16-битных Windows?" href="http://www.transl-gunsmoker.ru/2011/08/how-were-dll-functions-exported-in-16.html"&gt;главной 16-битной таблицы экспорта&lt;/a&gt;, 32-битная таблица не является разрежённой. Иными словами, если ваша DLL экспортирует две функции с номерами 10 и 1000 - у вас будет таблица экспорта на 991 записей, состоящая из двух функций и кучи нулей. Поэтому вам не следует назначать номера функциям с большими пробелами в нумерации - иначе вы будете зря тратить пространство в таблице экспорта.&lt;br /&gt;
&lt;br /&gt;
Как я заметил выше, теперь есть только одна таблица экспортируемых имён, поскольку вам не нужно делать отличия между резидентными и не резидентными именами. Таблица экспортируемых имён работает в той же манере, что и в 16-битных Windows, проецируя имена на номера. В отличие от 16-битных таблиц, где порядок не имеет значения, таблица экспортируемых имён в 32-битных Windows должна быть отсортирована, чтобы система могла воспользоваться (эффективным) двоичным поиском для ускорения поиска имён.&lt;br /&gt;
&lt;br /&gt;
Как и в 16-битных Windows, каждому имени присваивается номер. Если программист не указывает номер, то его назначает компоновщик, и, как и в 16-битных Windows, автоматически присваиваемые значения могут отличаться в сборках. Однако тут есть существенное отличие в моделях использования: вспомните, что в 16-битных Windows импорт по имени не поощрялся (по соображениям производительности), и в результате обычно каждой экспортируемой функции явно присваивался номер, что и было предпочтительным способом связывания функций. С другой стороны, именованный импорт в Win32 является нормой, а явное присваивание номеров функций ушло в прошлое. Это означает, что &lt;b&gt;номера в таблице экспорта не фиксированы&lt;/b&gt;. К примеру, посмотрите на номера, присваиваемые функции &lt;code&gt;LocalAlloc&lt;/code&gt; из kernel32 за эти годы:&lt;br /&gt;
&lt;br /&gt;
&lt;TABLE&gt;&lt;TR&gt;&lt;TD&gt;Windows NT 3.1&lt;/TD&gt;&lt;TD&gt;314&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;Windows NT 3.5&lt;/TD&gt;&lt;TD&gt;372&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;Windows 95&lt;/TD&gt;&lt;TD&gt;501&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;Windows NT 4.0&lt;/TD&gt;&lt;TD&gt;407&lt;/TD&gt;&lt;/TR&gt;&lt;/TABLE&gt; 
&lt;br /&gt;
Но некоторые люди имеют привычку "взламывать" (reverse-engineer) библиотеки импорта - вероятно потому, что им лень качать Platform SDK чтобы получить &lt;b&gt;официальные&lt;/b&gt; библиотеки импорта. Проблема с генерацией библиотеки импорта заключается в том, что вы не можете сказать, был ли номер, скажем, функции &lt;code&gt;LoadLibrary&lt;/code&gt; присвоен явно программистом (и, значит, будет постоянен) или же был назначен автоматически компоновщиком (и в этом случае он &lt;b&gt;будет&lt;/b&gt; меняться). Утилиты генерации библиотек импорта могут просто перестраховаться и всегда использовать именованный импорт, но по какой-то причине они часто предпочитают импорт по номерам (вероятно, это остаток из времён 16-битных Windows, где такой способ импорта был предпочитаемым).&lt;br /&gt;
&lt;br /&gt;
Этот неудачный выбор части утилит по генерации библиотек импорта жить опасной жизнью создал проблемы совместимости команде DirectX (я не знаю, почему именно DirectX оказался под ударом больше остальных. Возможно, потому что у разработчиков игр нет времени учить детали Win32; они просто хотят писать их игры). Как только кто-то использовал такую утилиту, он оказался привязанным к функциям DirectX вроде &lt;code&gt;DirectDrawCreate&lt;/code&gt; по номеру, а не по имени. А когда вышла следующая версия DirectX и этому имени был присвоен другой номер компоновщиком, их программы стали вылетать. Команде DirectX пришлось поднять из архива старые DLL, тщательно выписать все номера функций (присвоенные случайным образом компоновщиком) и в точности повторить это присвоение вручную, так чтобы эти номера более не менялись бы в будущем.&lt;br /&gt;
&lt;br /&gt;
Есть и другие причины, почему вы не можете сгенерировать библиотеку импорта по DLL; я подниму эти причины в следующий раз, когда буду обсуждать импорт из DLL.&lt;br /&gt;
&lt;br /&gt;
В &lt;a  title="Экспортируемые функции, являющиеся forwarder-ами" href="http://www.transl-gunsmoker.ru/2011/09/exported-functions-that-are-really.html"&gt;следующий раз&lt;/a&gt; - переходники.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688132520265708820-5477983680087185034?l=www.transl-gunsmoker.ru' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=q46XlZKNi6o:TH_06LUKk-c:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=q46XlZKNi6o:TH_06LUKk-c:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=q46XlZKNi6o:TH_06LUKk-c:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=q46XlZKNi6o:TH_06LUKk-c:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=q46XlZKNi6o:TH_06LUKk-c:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=q46XlZKNi6o:TH_06LUKk-c:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=q46XlZKNi6o:TH_06LUKk-c:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=q46XlZKNi6o:TH_06LUKk-c:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=q46XlZKNi6o:TH_06LUKk-c:-BTjWOF_DHI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=q46XlZKNi6o:TH_06LUKk-c:-BTjWOF_DHI" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/GunsmokersTranslations/~4/q46XlZKNi6o" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.transl-gunsmoker.ru/feeds/5477983680087185034/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://www.transl-gunsmoker.ru/2011/08/how-are-dll-functions-exported-in-32.html#comment-form" title="Комментарии: 0" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/5477983680087185034?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/5477983680087185034?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/GunsmokersTranslations/~3/q46XlZKNi6o/how-are-dll-functions-exported-in-32.html" title="Как экспортируются DLL функции в 32-битных Windows?" /><author><name>Александр Алексеев</name><uri>https://profiles.google.com/113168002104297556003</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh3.googleusercontent.com/-aUMttZQBsuI/AAAAAAAAAAI/AAAAAAAAC3Y/QuZ7K9t_WzE/s512-c/photo.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://www.transl-gunsmoker.ru/2011/08/how-are-dll-functions-exported-in-32.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DUcGQXg9fyp7ImA9WhVTEEQ.&quot;"><id>tag:blogger.com,1999:blog-1688132520265708820.post-3568786456412055911</id><published>2011-08-30T22:08:00.000+04:00</published><updated>2012-02-24T19:23:40.667+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-02-24T19:23:40.667+04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="серия" /><category scheme="http://www.blogger.com/atom/ns#" term="Win16" /><title>Как импортировались DLL функции в 16-битных Windows?</title><content type="html">Это перевод &lt;a href="http://blogs.msdn.com/b/oldnewthing/archive/2006/07/17/668284.aspx" title="How were DLL functions imported in 16-bit Windows?"&gt;How were DLL functions imported in 16-bit Windows?&lt;/a&gt; Автор: Реймонд Чен.&lt;br /&gt;
&lt;br /&gt;
В &lt;a title="Как экспортировались DLL функции в 16-битных Windows?" href="http://www.transl-gunsmoker.ru/2011/08/how-were-dll-functions-exported-in-16.html"&gt;прошлый раз&lt;/a&gt; я рассказал о том, как экспортировались функции в 16-битных Windows. Сегодня мы посмотрим на их импорт.&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
Каждый раз, когда сегмент загружается с диска в память, сначала читается raw-содержимое сегмента с диска, а затем применяются relocation-правки. Правка для импортируемой функции состоит из имени целевой DLL, целевой функции (либо её имени, либо номера), и позиции первого места в сегменте, где нужно произвести правку. Все импортируемые адреса являются far-адресами, поскольку они ссылаются на другой сегмент (почему? если бы они были в том же сегменте, то это означало бы, что они расположены в той же DLL, так что это получился бы импорт функции из самого себя!). На 16-битных Windows дальний указатель занимает 4 байта (двухбайтовый селектор и двухбайтовое смещение), а поскольку точный адрес функции не известен в момент компиляции DLL, то эти 4 байта являются просто заглушками - они помещаются в файл с тем, чтобы быть заполненными настоящими значениями в run-time, когда будет получен адрес импортируемой функции. И эти байты заглушек служат двойной цели.&lt;br /&gt;
&lt;br /&gt;
Все вызовы сегмента одной и той же импортируемой функции связываются в список; запись в relocation таблице ссылается только на первый элемент этого списка. А что насчёт остальных вызовов? На них указывают заглушки! К примеру, предположим, что у нас есть сегмент, который требует две правки для функции &lt;code&gt;GetPrivateProfileInt&lt;/code&gt;, которая является функцией ядра с номером 127. Тогда в relocation таблице будет создана одна запись, которая говорит: "этот сегмент требует функцию номер 127 из KERNEL; начало по смещению 01D1". Копия сегмента на диске может выглядеть так:
&lt;TABLE&gt;&lt;TR&gt;&lt;TD&gt;...&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;&lt;TT&gt;01D0&lt;/TT&gt;&lt;/TD&gt;&lt;TD&gt;&lt;TT&gt;9A&lt;/TT&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;&lt;TT&gt;01D1&lt;/TT&gt;&lt;/TD&gt;&lt;TD&gt;&lt;TT&gt;FE&lt;/TT&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;&lt;TT&gt;01D2&lt;/TT&gt;&lt;/TD&gt;&lt;TD&gt;&lt;TT&gt;01&lt;/TT&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;&lt;TT&gt;01D3&lt;/TT&gt;&lt;/TD&gt;&lt;TD&gt;&lt;TT&gt;00&lt;/TT&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;&lt;TT&gt;01D0&lt;/TT&gt;&lt;/TD&gt;&lt;TD&gt;&lt;TT&gt;00&lt;/TT&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;...&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;&lt;TT&gt;01FD&lt;/TT&gt;&lt;/TD&gt;&lt;TD&gt;&lt;TT&gt;9A&lt;/TT&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;&lt;TT&gt;01FE&lt;/TT&gt;&lt;/TD&gt;&lt;TD&gt;&lt;TT&gt;FF&lt;/TT&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;&lt;TT&gt;01FF&lt;/TT&gt;&lt;/TD&gt;&lt;TD&gt;&lt;TT&gt;FF&lt;/TT&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;&lt;TT&gt;0200&lt;/TT&gt;&lt;/TD&gt;&lt;TD&gt;&lt;TT&gt;00&lt;/TT&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;&lt;TT&gt;0201&lt;/TT&gt;&lt;/TD&gt;&lt;TD&gt;&lt;TT&gt;00&lt;/TT&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;...&lt;/TD&gt;&lt;/TR&gt;&lt;/TABLE&gt;
Чтобы применить правку, мы вызываем &lt;code&gt;GetProcAddress&lt;/code&gt; для получения настоящего адреса функции 127. Затем мы идём к месту первой правки (это место со смещением $01D1), записываем в него адрес, а затем смотрим на значение, которое мы только что перезаписали (конечно, мы должны его сохранить перед записью). Это значение равно $01FE, поэтому мы идём к месту по смещению $01FE и снова записываем адрес. Перезаписанное значение равно $FFFF, что означает конец цепочки правок.&lt;br /&gt;
&lt;br /&gt;
Но что если вызов &lt;code&gt;GetProcAddress&lt;/code&gt; будет неудачным? (к примеру - в KERNEL нет никакой функции 127). Тогда вместо записи адреса целевой функции, загрузчик запишет адрес специальной функции-заглушки, которая вызывает фатальную ошибку "Call to Undefined Dynalink".&lt;br /&gt;
&lt;br /&gt;
Okей, это было краткое введение в импорт и экспорт функций в 16-битных Windows. В &lt;a title="Как экспортируются DLL функции в 32-битных Windows?"  href="http://www.transl-gunsmoker.ru/2011/08/how-are-dll-functions-exported-in-32.html"&gt;следующий раз&lt;/a&gt; мы посмотрим на переход на 32-битные Windows и решения дизайна, которые сформировали новую модель экспорта-импорта.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688132520265708820-3568786456412055911?l=www.transl-gunsmoker.ru' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=TjRoF4u2lh8:Ca6BxspHBdw:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=TjRoF4u2lh8:Ca6BxspHBdw:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=TjRoF4u2lh8:Ca6BxspHBdw:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=TjRoF4u2lh8:Ca6BxspHBdw:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=TjRoF4u2lh8:Ca6BxspHBdw:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=TjRoF4u2lh8:Ca6BxspHBdw:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=TjRoF4u2lh8:Ca6BxspHBdw:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=TjRoF4u2lh8:Ca6BxspHBdw:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=TjRoF4u2lh8:Ca6BxspHBdw:-BTjWOF_DHI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=TjRoF4u2lh8:Ca6BxspHBdw:-BTjWOF_DHI" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/GunsmokersTranslations/~4/TjRoF4u2lh8" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.transl-gunsmoker.ru/feeds/3568786456412055911/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://www.transl-gunsmoker.ru/2011/08/how-were-dll-functions-imported-in-16.html#comment-form" title="Комментарии: 0" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/3568786456412055911?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/3568786456412055911?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/GunsmokersTranslations/~3/TjRoF4u2lh8/how-were-dll-functions-imported-in-16.html" title="Как импортировались DLL функции в 16-битных Windows?" /><author><name>Александр Алексеев</name><uri>https://profiles.google.com/113168002104297556003</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh3.googleusercontent.com/-aUMttZQBsuI/AAAAAAAAAAI/AAAAAAAAC3Y/QuZ7K9t_WzE/s512-c/photo.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://www.transl-gunsmoker.ru/2011/08/how-were-dll-functions-imported-in-16.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DE4ERHkyfyp7ImA9WhVTEEQ.&quot;"><id>tag:blogger.com,1999:blog-1688132520265708820.post-7449359055015592386</id><published>2011-08-29T22:07:00.000+04:00</published><updated>2012-02-24T19:21:45.797+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-02-24T19:21:45.797+04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="серия" /><category scheme="http://www.blogger.com/atom/ns#" term="Win16" /><title>Как экспортировались DLL функции в 16-битных Windows?</title><content type="html">Это перевод &lt;a href="http://blogs.msdn.com/b/oldnewthing/archive/2006/07/14/665669.aspx" title="How were DLL functions exported in 16-bit Windows?"&gt;How were DLL functions exported in 16-bit Windows?&lt;/a&gt; Автор: Реймонд Чен.&lt;br /&gt;
&lt;br /&gt;
Весь смысл динамических библиотек (DLL) заключается в динамическом связывании. В то время как статические библиотеки встраиваются в конечный продукт, модуль, который использует динамическую библиотеку, просто говорит: "мне, пожалуйста, функцию X из модуля Y, спасибо". Эта техника имеет преимущества и недостатки. Одно из преимуществ - эффективное использование места на диске и в памяти, поскольку существует только одна копия модуля, вместо отдельных копий для каждого модуля. Второе преимущество - обновление DLL может быть выполнено без перекомпиляции всех программ, её использующих. С другой стороны, возможность менять функциональность также является и одним из недостатков DLL, поскольку одна программа может изменить DLL и вызвать этим каскадные эффекты в других клиентах DLL.&lt;br /&gt;
&lt;br /&gt;
В любом случае, давайте начнём с того, как 16-битные Windows управляли импортом и экспортом. После этого мы посмотрим что было изменено при миграции на 32-битные Windows, а затем мы посмотрим на импорт с точки зрения компилятора.&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
16-битная DLL имела не одну, а целых три таблицы экспорта (на самом деле всё было немного сложнее, чем я описываю здесь, но я собираюсь опустить некоторые занудные детали, чтобы ваши головы не взорвались от переизбытка информации). Самая важная таблица представляла собой разрежённый массив функций, проиндексированных с 1("ordinal"). Это и есть таблица функций, которая является мастер-списком всех экспортируемых функций. Если вы запрашивали функцию по номеру (ordinal), этот номер искался именно в этой таблице. Физически таблица устроена не так просто - из-за разрежённости, но логически она выглядит так:&lt;br /&gt;
&lt;br /&gt;
&lt;TABLE RULES=cols&gt;&lt;TR&gt;&lt;TH&gt;Номер&amp;nbsp;&lt;/TH&gt;&lt;TH&gt;Адрес&lt;/TH&gt;&lt;TH&gt;прочее&lt;/TH&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;1&lt;/TD&gt;&lt;TD&gt;02:0014&lt;/TD&gt;&lt;TD&gt;...&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;2&lt;/TD&gt;&lt;TD&gt;04:0000&lt;/TD&gt;&lt;TD&gt;...&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;5&lt;/TD&gt;&lt;TD&gt;02:02C8&lt;/TD&gt;&lt;TD&gt;...&lt;/TD&gt;&lt;/TR&gt;&lt;/TABLE&gt; 
&lt;br /&gt;
Первая колонка в таблице представляет собой номер функции, а вторая описывает размещение функции (заметьте, что в этой DLL нет функций 3 и 4).&lt;br /&gt;
&lt;br /&gt;
Интересные вещи начинаются, когда вы хотите получить функцию по её имени. Таблица экспортируемых имён представляет собой список имён функций с ассоциированными номерами. К примеру, секция экспортируемых имён для 16-битного оконного менеджера выглядела так:&lt;br /&gt;
&lt;br /&gt;
&lt;TABLE&gt;&lt;TR&gt;&lt;TD COLSPAN=2&gt;...&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;&lt;CODE&gt;ClipCursor&lt;/CODE&gt;&lt;/TD&gt;&lt;TD&gt;16&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;&lt;CODE&gt;GetCursorPos&lt;/CODE&gt;&lt;/TD&gt;&lt;TD&gt;17&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;&lt;CODE&gt;SetCapture&lt;/CODE&gt;&lt;/TD&gt;&lt;TD&gt;18&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD COLSPAN=2&gt;...&lt;/TD&gt;&lt;/TR&gt;&lt;/TABLE&gt; 
&lt;br /&gt;
Если кто-то просил адрес функции "ClipCursor", то сначала проверялась таблица экспортируемых имён и из неё извлекался номер 16, после чего этот номер использовался для обращения к мастер-таблице, упомянутой выше. Хотя вы не видите этого здесь, но не существовало никакого требования, чтобы имена в таблице экспортируемых имён шли в каком-то определённом порядке или чтобы каждая функция с номером имела бы экспортируемое имя.&lt;br /&gt;
&lt;br /&gt;
Подождите-ка, я сказал "таблица экспортируемых имён"? Ох, извините, это было сильное упрощение. На самом деле было две таблицы экспортируемых имён: резидентная таблица имён и не резидентная таблица имён. Как следует из их названий, имена в резидентной таблице оставались в памяти всё время, пока DLL была загружена, а имена из не резидентной таблицы загружались в память только когда кто-то вызывал &lt;a title="MSDN: GetProcAddress Function" href="http://msdn.microsoft.com/en-us/library/ms683212(VS.85).aspx"&gt;&lt;code&gt;GetProcAddress&lt;/code&gt;&lt;/a&gt; (или её эквивалент). Это различие появилось из-за очень жёстких ограничений на память, с которыми Windows приходилось работать в те дни. К примеру, оконный менеджер имел около шестисот экспортируемых функций; если бы все эти функции были резидентными, то они занимали бы более 10 Кб памяти. Это означает, что вы тратите 4 процента вашей памяти (машины с 256 Кб), чтобы хранить вещи, которые вам не нужны 99% процентов времени.&lt;br /&gt;
&lt;br /&gt;
Большой размер таблицы экспортируемых имён означал, что только функции, часто передаваемые в &lt;code&gt;GetProcAddress&lt;/code&gt;, заслуживают появления в резидентной таблице. Для большинства DLL таких функций не было вообще, так что таблица резидентных имён была обычно пустой (для пущей безопасности удалены голово-взрывные детали).&lt;br /&gt;
&lt;br /&gt;
Поскольку получение функции по имени было таким дорогим (загрузка не резидентной таблицы с диска), то все функции ОС экспортировались по именам и номеру. Импорт функции по номеру был предпочтителен - это позволяло полностью избегать таблицы имён.&lt;br /&gt;
&lt;br /&gt;
Заметьте, что каждая функция с именем имела соответствующий номер. Если вы не присваивали номер функции явно, то это делал за вас компоновщик (линкер), однако автоматически сгенерированное значение не обязано было сохраняться одинаковым при каждой сборке. Обычно эта ситуация не возникала на практике, потому что все старались явно присваивать номера функциям по причинам, указанным выше - чтобы избежать огромных накладных расходов импорта функции по имени.&lt;br /&gt;
&lt;br /&gt;
Это был краткий обзор экспорта функций в 16-битных Windows. В &lt;a title="Как импортировались DLL функции в 16-битных Windows?" href="http://www.transl-gunsmoker.ru/2011/08/how-were-dll-functions-imported-in-16.html"&gt;следующий раз&lt;/a&gt; мы посмотрим на то, как они импортировались.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688132520265708820-7449359055015592386?l=www.transl-gunsmoker.ru' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=HBcj1E6vZzY:pKf81ynTNY0:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=HBcj1E6vZzY:pKf81ynTNY0:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=HBcj1E6vZzY:pKf81ynTNY0:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=HBcj1E6vZzY:pKf81ynTNY0:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=HBcj1E6vZzY:pKf81ynTNY0:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=HBcj1E6vZzY:pKf81ynTNY0:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=HBcj1E6vZzY:pKf81ynTNY0:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=HBcj1E6vZzY:pKf81ynTNY0:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=HBcj1E6vZzY:pKf81ynTNY0:-BTjWOF_DHI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=HBcj1E6vZzY:pKf81ynTNY0:-BTjWOF_DHI" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/GunsmokersTranslations/~4/HBcj1E6vZzY" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.transl-gunsmoker.ru/feeds/7449359055015592386/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://www.transl-gunsmoker.ru/2011/08/how-were-dll-functions-exported-in-16.html#comment-form" title="Комментарии: 0" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/7449359055015592386?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/7449359055015592386?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/GunsmokersTranslations/~3/HBcj1E6vZzY/how-were-dll-functions-exported-in-16.html" title="Как экспортировались DLL функции в 16-битных Windows?" /><author><name>Александр Алексеев</name><uri>https://profiles.google.com/113168002104297556003</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh3.googleusercontent.com/-aUMttZQBsuI/AAAAAAAAAAI/AAAAAAAAC3Y/QuZ7K9t_WzE/s512-c/photo.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://www.transl-gunsmoker.ru/2011/08/how-were-dll-functions-exported-in-16.html</feedburner:origLink></entry><entry gd:etag="W/&quot;D0QFR3c4eSp7ImA9WhdWGEw.&quot;"><id>tag:blogger.com,1999:blog-1688132520265708820.post-6457972541294089131</id><published>2011-08-28T22:07:00.000+04:00</published><updated>2011-09-12T12:15:16.931+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-09-12T12:15:16.931+04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="прочее" /><title>Pidl и моникеры делают примерно одно и то же, только по-разному</title><content type="html">Это перевод &lt;a href="http://blogs.msdn.com/b/oldnewthing/archive/2006/07/12/663365.aspx" title="Pidls and monikers do roughly the same thing, just backwards"&gt;Pidls and monikers do roughly the same thing, just backwards&lt;/a&gt;. Автор: Реймонд Чен.&lt;br /&gt;
&lt;br /&gt;
Работая с Оболочкой (Shell) Windows, вам наверняка в какой-то момент пришлось работать с указателем на список ID элементов (pointer to item ID list), известным как "pidl" (рифмуется с "middle"). С другой стороны, работая с OLE, вы могли работать с &lt;a title="Википедия: Моникер" href="http://ru.wikipedia.org/wiki/%D0%9C%D0%BE%D0%BD%D0%B8%D0%BA%D0%B5%D1%80"&gt;моникерами&lt;/a&gt; (moniker). В каком-то смысле они делают одно и то же. Они позволяют вам ссылаться на какой-то объект внутри пространства имён (с которым вы как-то работаете), они имеют иерархическую структуру, вы можете их сериализовать и т.п.&lt;br /&gt;
&lt;br /&gt;
Но почему тогда команда Оболочки Windows изобрела pidl-ы, если моникеры делали ровно это же?&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
Фундаментальное отличие между pidl и моникерами - не в том, что они делают, а скорее в их "акценте". Моникер - это COM объект. Это означает, что они существуют в форме COM интерфейса (&lt;a title="MSDN: IMoniker Interface" href="http://msdn.microsoft.com/en-us/library/ms679705(VS.85).aspx"&gt;&lt;code&gt;IMoniker&lt;/code&gt;&lt;/a&gt;), что означает, что код, стоящий за объектом, должен быть загружен и готов к работе. Вы можете сконвертировать моникер в данные, но это не то, как обычно проводят свою жизнь моникеры. Если у вас есть массив моникеров, то весь код под капотом должен быть проинициализирован.&lt;br /&gt;
&lt;br /&gt;
Pidl, с другой стороны, проводит большую часть своей жизни будучи засериализован в данные. COM объект появляется только когда вы производите связывание (bind). Рассмотрите, к примеру, случай, когда вы перечисляете содержимое папки. Эта операция генерирует pidl для каждого элемента в папке, но создание и хранение этих pidl не требует выполнения кода каждого элемента. Папка с тысячей элементов создаст тысячу кусков данных, а не тысячу COM объектов. Рассмотрим, к примеру, папку с тысячей таблиц Excel. Если вы открываете эту папку, то вам бы не хотелось запускать Excel для каждой из тысячи таблиц в папке (для обслуживания элементов). На этом этапе вы пока просто говорите &lt;b&gt;про&lt;/b&gt; файлы. И лишь когда пользователь щёлкает на файле, вы хотели бы запускать Excel.&lt;br /&gt;
&lt;br /&gt;
Поскольку Оболочка большую часть времени проводит в "разговоре &lt;b&gt;о&lt;/b&gt; вещах", а не "разговорах &lt;b&gt;с&lt;/b&gt; вещами", то объект, который большую часть времени "мёртв", является более предпочтительным. Вы можете сказать, что разница между pidl и моникерами является вопросом жизни и смерти (прим.пер.: это юмор, если кто не понял).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688132520265708820-6457972541294089131?l=www.transl-gunsmoker.ru' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=vRC8DxLBkfw:kVueaLLFl_I:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=vRC8DxLBkfw:kVueaLLFl_I:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=vRC8DxLBkfw:kVueaLLFl_I:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=vRC8DxLBkfw:kVueaLLFl_I:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=vRC8DxLBkfw:kVueaLLFl_I:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=vRC8DxLBkfw:kVueaLLFl_I:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=vRC8DxLBkfw:kVueaLLFl_I:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=vRC8DxLBkfw:kVueaLLFl_I:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=vRC8DxLBkfw:kVueaLLFl_I:-BTjWOF_DHI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=vRC8DxLBkfw:kVueaLLFl_I:-BTjWOF_DHI" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/GunsmokersTranslations/~4/vRC8DxLBkfw" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.transl-gunsmoker.ru/feeds/6457972541294089131/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://www.transl-gunsmoker.ru/2011/08/pidls-and-monikers-do-roughly-same.html#comment-form" title="Комментарии: 0" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/6457972541294089131?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/6457972541294089131?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/GunsmokersTranslations/~3/vRC8DxLBkfw/pidls-and-monikers-do-roughly-same.html" title="Pidl и моникеры делают примерно одно и то же, только по-разному" /><author><name>Александр Алексеев</name><uri>https://profiles.google.com/113168002104297556003</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh3.googleusercontent.com/-aUMttZQBsuI/AAAAAAAAAAI/AAAAAAAAC3Y/QuZ7K9t_WzE/s512-c/photo.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://www.transl-gunsmoker.ru/2011/08/pidls-and-monikers-do-roughly-same.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DkcNR3c4cSp7ImA9WhdWGEw.&quot;"><id>tag:blogger.com,1999:blog-1688132520265708820.post-6249901763765167057</id><published>2011-08-27T22:06:00.000+04:00</published><updated>2011-09-12T11:54:56.939+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-09-12T11:54:56.939+04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="прочее" /><title>Не всякий человек с не-Windows разделом является гиком</title><content type="html">Это перевод &lt;a href="http://blogs.msdn.com/b/oldnewthing/archive/2006/07/11/662325.aspx" title="Not everybody with a non-Windows partition type is a geek"&gt;Not everybody with a non-Windows partition type is a geek&lt;/a&gt;. Автор: Реймонд Чен.&lt;br /&gt;
&lt;br /&gt;
В обсуждении поста "Почему установщик Windows записывает новый загрузочный сектор?" (прим.пер. имеется в виду &lt;a href="http://blogs.msdn.com/b/oldnewthing/archive/2005/12/20/505887.aspx" title="Why does Windows setup lay down a new boot sector?"&gt;оригинал&lt;/a&gt;) некоторые комментаторы предложили такое поведение: если на машине обнаруживается не-Windows раздел, то предполагать, что машина принадлежит &lt;a title="Википедия: Гик" href="http://ru.wikipedia.org/wiki/%D0%93%D0%B8%D0%BA_(%D1%81%D0%BB%D0%B5%D0%BD%D0%B3)"&gt;техно-гику&lt;/a&gt;. Таким образом, типичный пользователь компьютера может быть спасён от лицезрения технического диалога с &lt;a href="http://www.transl-gunsmoker.ru/2009/09/blog-post_10.html" title="Чтобы продемонстрировать вам превосходство нашего интеллекта, мы сейчас зададим вам вопрос, на который вы не сможете ответить"&gt;гик-вопросом, на которой они не смогут ответить&lt;/a&gt;.&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
Проблема в этом плане в том, что не всякий человек с не-Windows разделом на жёстком диске обязательно является техно-гиком. Многие OEM-машины (особенно ноутбуки - прим.пер.) поставляются с диском, разбитым на два раздела. На первом стоит Windows, а второй - не-Windows раздел, который имеет небольшой размер и содержит утилиты диагностики и восстановления системы (иногда - дистрибутивный образ мастер-копии системы). Наличие этого небольшого раздела не слишком известно, поскольку он работает только в редких случаях загрузки с "system recovery CD" от поставщика машины.&lt;br /&gt;
&lt;br /&gt;
Следствие из этого состоит в том, что если бы установщик Windows принял бы этот подход "человек с не-Windows разделом должен быть гиком", то в итоге мы бы пометили гиками ужасно много людей (многие из которых гиками не являются).&lt;br /&gt;
&lt;br /&gt;
Вы также можете сказать: "ну, только гики устанавливают операционные системы. Обычные люди покупают компьютер с предустановленной системой. Тот факт, что они вообще запустили установщик Windows, доказывает, что они - гики. Поэтому, установщик Windows должен быть оптимизирован для использования гиками". Действительно, предпосылки этого аргумента (что только гики устанавливают Windows) верны - но только если вы достигли устойчивого состояния. В течение нескольких месяцев сразу после выпуска новой версии Windows, Windows устанавливают все подряд - как гики, так и простые люди (кроме того, наплыв установок Windows приходится на каждое Рождество). Рецензенты журналов, пишущие обзоры новой операционной системы, будут устанавливать Windows для пробы. Так что впечатление от установки - это первое, что они увидят. Так что установке лучше бы быть гладкой и безболезненной.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688132520265708820-6249901763765167057?l=www.transl-gunsmoker.ru' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=YdXD2id5Bg8:i-24xKO80zo:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=YdXD2id5Bg8:i-24xKO80zo:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=YdXD2id5Bg8:i-24xKO80zo:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=YdXD2id5Bg8:i-24xKO80zo:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=YdXD2id5Bg8:i-24xKO80zo:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=YdXD2id5Bg8:i-24xKO80zo:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=YdXD2id5Bg8:i-24xKO80zo:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=YdXD2id5Bg8:i-24xKO80zo:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=YdXD2id5Bg8:i-24xKO80zo:-BTjWOF_DHI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=YdXD2id5Bg8:i-24xKO80zo:-BTjWOF_DHI" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/GunsmokersTranslations/~4/YdXD2id5Bg8" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.transl-gunsmoker.ru/feeds/6249901763765167057/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://www.transl-gunsmoker.ru/2011/08/not-everybody-with-non-windows.html#comment-form" title="Комментарии: 0" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/6249901763765167057?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/6249901763765167057?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/GunsmokersTranslations/~3/YdXD2id5Bg8/not-everybody-with-non-windows.html" title="Не всякий человек с не-Windows разделом является гиком" /><author><name>Александр Алексеев</name><uri>https://profiles.google.com/113168002104297556003</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh3.googleusercontent.com/-aUMttZQBsuI/AAAAAAAAAAI/AAAAAAAAC3Y/QuZ7K9t_WzE/s512-c/photo.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://www.transl-gunsmoker.ru/2011/08/not-everybody-with-non-windows.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CE8GQXs4fSp7ImA9WhdWGEw.&quot;"><id>tag:blogger.com,1999:blog-1688132520265708820.post-8552443543260493264</id><published>2011-08-26T22:06:00.000+04:00</published><updated>2011-09-12T11:33:40.535+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-09-12T11:33:40.535+04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="обработка ошибок" /><category scheme="http://www.blogger.com/atom/ns#" term="кодинг" /><title>Прежде чем учиться опознавать неправильное, вам нужно узнать, что есть правильное</title><content type="html">Это перевод &lt;a href="http://blogs.msdn.com/b/oldnewthing/archive/2006/07/10/661389.aspx" title="Before you can learn to recognize what's wrong, you must learn to recognize what's right"&gt;Before you can learn to recognize what's wrong, you must learn to recognize what's right&lt;/a&gt;. Автор: Реймонд Чен.&lt;br /&gt;
&lt;br /&gt;
Иногда, когда я отлаживаю проблему, я могу проигнорировать какой-то поток, а кто-то спросит: "а что делает этот поток? Почему ты его игнорируешь?"&lt;br /&gt;
&lt;br /&gt;
Мой ответ: "Я понятия не имею, что он делает, но что бы он ни делал, это - нормально".&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
&lt;a title="MSDN Blogs: If broken it is, fix it you should" href="http://blogs.msdn.com/b/tess/"&gt;Tess&lt;/a&gt; публикует отличную серию постов, посвящённых отладке CLR, но один из самых важных уроков - это &lt;a title="Things to ignore when debugging an ASP.NET hang" href="http://blogs.msdn.com/b/tess/archive/2005/12/20/505862.aspx"&gt;где вы учитесь игнорировать вещи, когда отлаживаете зависание в ASP.NET&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
Зависания и блокировки могут быть сложны в отладке, потому что у вас нет исключения, которое кричит: "эй, смотри на меня, вот она проблема, я - это баг!". Вместо этого программа просто замирает и вам нужно понять почему. При этом вам нужно искать что-то неординарное, необычное, но чтобы вы могли опознать это что-то - вам нужно знать, как обычно выглядят вещи в вашем процессе.&lt;br /&gt;
&lt;br /&gt;
Ну так сделайте это. Запустите программу, пусть она работает, а вы остановите её в отладчике и просто посмотрите на неё. Отметьте то, что вы увидите. Это - вещи, которые происходят в вашей программе, когда всё идёт своим чередом. Так выглядит ваша программа, когда она работает в нормальном режиме. Теперь, когда вы знаете как выглядят нормальные вещи в вашей программе, вы можете опознать не нормальные.&lt;br /&gt;
&lt;br /&gt;
Заметьте, что вам даже не нужно понимать, что все эти нормальные вещи значат или делают. К примеру, когда я подключаю отладчик к процессу, я часто вижу потоки, ожидающие RPC или спящие в пуле потоков ядра. Я понятия не имею, что они делают, но раз уж они тут и раньше были, то я их игнорирую.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688132520265708820-8552443543260493264?l=www.transl-gunsmoker.ru' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=zxEoDkKqE7I:ELo2W2SVQ3o:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=zxEoDkKqE7I:ELo2W2SVQ3o:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=zxEoDkKqE7I:ELo2W2SVQ3o:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=zxEoDkKqE7I:ELo2W2SVQ3o:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=zxEoDkKqE7I:ELo2W2SVQ3o:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=zxEoDkKqE7I:ELo2W2SVQ3o:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=zxEoDkKqE7I:ELo2W2SVQ3o:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=zxEoDkKqE7I:ELo2W2SVQ3o:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=zxEoDkKqE7I:ELo2W2SVQ3o:-BTjWOF_DHI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=zxEoDkKqE7I:ELo2W2SVQ3o:-BTjWOF_DHI" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/GunsmokersTranslations/~4/zxEoDkKqE7I" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.transl-gunsmoker.ru/feeds/8552443543260493264/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://www.transl-gunsmoker.ru/2011/08/before-you-can-learn-to-recognize-whats.html#comment-form" title="Комментарии: 0" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/8552443543260493264?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/8552443543260493264?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/GunsmokersTranslations/~3/zxEoDkKqE7I/before-you-can-learn-to-recognize-whats.html" title="Прежде чем учиться опознавать неправильное, вам нужно узнать, что есть правильное" /><author><name>Александр Алексеев</name><uri>https://profiles.google.com/113168002104297556003</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh3.googleusercontent.com/-aUMttZQBsuI/AAAAAAAAAAI/AAAAAAAAC3Y/QuZ7K9t_WzE/s512-c/photo.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://www.transl-gunsmoker.ru/2011/08/before-you-can-learn-to-recognize-whats.html</feedburner:origLink></entry><entry gd:etag="W/&quot;C0AHRXoyfCp7ImA9WhdWGEw.&quot;"><id>tag:blogger.com,1999:blog-1688132520265708820.post-4720696836164099851</id><published>2011-08-25T22:05:00.000+04:00</published><updated>2011-09-12T11:15:34.494+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-09-12T11:15:34.494+04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="История" /><category scheme="http://www.blogger.com/atom/ns#" term="окна" /><title>Нет, а почему именно 160x31?</title><content type="html">Это перевод &lt;a href="http://blogs.msdn.com/b/oldnewthing/archive/2006/07/07/659013.aspx" title="No, really, why is it 160x31?"&gt;No, really, why is it 160x31?&lt;/a&gt; Автор: Реймонд Чен.&lt;br /&gt;
&lt;br /&gt;
Когда я ранее пояснял, &lt;a title="Почему все свёрнутые окна имеют размер 160x31?" href="http://www.transl-gunsmoker.ru/2010/02/160x31.html"&gt;почему свёрнутые (минимизированные) окна имеют размер 160x31&lt;/a&gt;, я не пояснил, почему размером выбрано именно ровно 160 и 31.&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
Ширина минимизированного окна определяется полем &lt;code&gt;iWidth&lt;/code&gt; в &lt;a title="MSDN: MINIMIZEDMETRICS Structure" href="http://msdn.microsoft.com/en-us/library/ms724500(VS.85).aspx"&gt;записи &lt;code&gt;TMinimizedMetrics&lt;/code&gt;&lt;/a&gt;. Вы можете получить и изменить эту запись с помощью &lt;a title="MSDN: SystemParametersInfo Function" href="http://msdn.microsoft.com/en-us/library/ms724947(VS.85).aspx"&gt;функции &lt;code&gt;SystemParametersInfo&lt;/code&gt;&lt;/a&gt; (вас будут интересовать флаги &lt;code&gt;SPI_GETMINIMIZEDMETRICS&lt;/code&gt; и &lt;code&gt;SPI_SETMINIMIZEDMETRICS&lt;/code&gt;). Некоторые люди упоминают использование значение &lt;code&gt;MinWidth&lt;/code&gt; в реестре, но &lt;a href="http://www.transl-gunsmoker.ru/2010/03/systemparametersinfo.html" title="Использование SystemParametersInfo для получения настроек интерфейса пользователя"&gt;они не правы&lt;/a&gt;. К примеру, заметьте, что исправление &lt;code&gt;MinWidth&lt;/code&gt; требует выхода из системы, а использование &lt;code&gt;SPI_SETMINIMIZEDMETRICS&lt;/code&gt; действует немедленно. Это потому что &lt;code&gt;SPI_SETMINIMIZEDMETRICS&lt;/code&gt; обновляет внутреннее состояние системы, а правки реестра просто меняют значение в БД, на которую никто не смотрит (после запуска).&lt;br /&gt;
&lt;br /&gt;
Что насчёт высоты? А это просто высота строки-заголовка (caption bar), которую вы можете изменить на вкладке "Оформление" апплета "Экран" панели управления (программно вы можете использовать эту же полезную функцию &lt;code&gt;SystemParametersInfo&lt;/code&gt;, но в этот раз используя поле &lt;code&gt;iCaptionHeight&lt;/code&gt; &lt;a href="http://msdn.microsoft.com/en-us/library/ff729175(VS.85).aspx" title="MSDN: NONCLIENTMETRICS Structure"&gt;записи &lt;code&gt;TNonClientMetrics&lt;/code&gt;&lt;/a&gt;).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688132520265708820-4720696836164099851?l=www.transl-gunsmoker.ru' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=aIaExmMygo8:PauMbLoT8d0:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=aIaExmMygo8:PauMbLoT8d0:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=aIaExmMygo8:PauMbLoT8d0:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=aIaExmMygo8:PauMbLoT8d0:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=aIaExmMygo8:PauMbLoT8d0:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=aIaExmMygo8:PauMbLoT8d0:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=aIaExmMygo8:PauMbLoT8d0:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=aIaExmMygo8:PauMbLoT8d0:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=aIaExmMygo8:PauMbLoT8d0:-BTjWOF_DHI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=aIaExmMygo8:PauMbLoT8d0:-BTjWOF_DHI" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/GunsmokersTranslations/~4/aIaExmMygo8" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.transl-gunsmoker.ru/feeds/4720696836164099851/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://www.transl-gunsmoker.ru/2011/08/no-really-why-is-it-160x31.html#comment-form" title="Комментарии: 0" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/4720696836164099851?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/4720696836164099851?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/GunsmokersTranslations/~3/aIaExmMygo8/no-really-why-is-it-160x31.html" title="Нет, а почему именно 160x31?" /><author><name>Александр Алексеев</name><uri>https://profiles.google.com/113168002104297556003</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh3.googleusercontent.com/-aUMttZQBsuI/AAAAAAAAAAI/AAAAAAAAC3Y/QuZ7K9t_WzE/s512-c/photo.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://www.transl-gunsmoker.ru/2011/08/no-really-why-is-it-160x31.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DUIDQ3YycSp7ImA9WhdWGE0.&quot;"><id>tag:blogger.com,1999:blog-1688132520265708820.post-6564820483730097774</id><published>2011-08-24T22:04:00.000+04:00</published><updated>2011-09-12T10:06:12.899+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-09-12T10:06:12.899+04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="кодинг" /><category scheme="http://www.blogger.com/atom/ns#" term="Win32" /><title>Максимальный размер переменных окружения - это 32 или 64 Кб?</title><content type="html">Это перевод &lt;a href="http://blogs.msdn.com/b/oldnewthing/archive/2006/07/06/657868.aspx" title="Is the maximum size of the environment 32K or 64K?"&gt;Is the maximum size of the environment 32K or 64K?&lt;/a&gt; Автор: Реймонд Чен.&lt;br /&gt;
&lt;br /&gt;
Кажется, есть некоторая путаница в максимальном размере переменных окружения: 32 Кб или 64 Кб. Какое значение правильное?&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
Оба.&lt;br /&gt;
&lt;br /&gt;
Ограничение - 32'767 Unicode символов, что есть 65'534 байт. Назовите это 32 Кб или 64 Кб - как вам угодно. Но только убедитесь, что вы включили единицу измерения в ваши утверждения, если она не следует из контекста.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688132520265708820-6564820483730097774?l=www.transl-gunsmoker.ru' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=rqIRtqIfOWU:hEc3RQjTiCo:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=rqIRtqIfOWU:hEc3RQjTiCo:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=rqIRtqIfOWU:hEc3RQjTiCo:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=rqIRtqIfOWU:hEc3RQjTiCo:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=rqIRtqIfOWU:hEc3RQjTiCo:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=rqIRtqIfOWU:hEc3RQjTiCo:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=rqIRtqIfOWU:hEc3RQjTiCo:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=rqIRtqIfOWU:hEc3RQjTiCo:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=rqIRtqIfOWU:hEc3RQjTiCo:-BTjWOF_DHI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=rqIRtqIfOWU:hEc3RQjTiCo:-BTjWOF_DHI" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/GunsmokersTranslations/~4/rqIRtqIfOWU" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.transl-gunsmoker.ru/feeds/6564820483730097774/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://www.transl-gunsmoker.ru/2011/08/is-maximum-size-of-environment-32k-or.html#comment-form" title="Комментарии: 0" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/6564820483730097774?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/6564820483730097774?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/GunsmokersTranslations/~3/rqIRtqIfOWU/is-maximum-size-of-environment-32k-or.html" title="Максимальный размер переменных окружения - это 32 или 64 Кб?" /><author><name>Александр Алексеев</name><uri>https://profiles.google.com/113168002104297556003</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh3.googleusercontent.com/-aUMttZQBsuI/AAAAAAAAAAI/AAAAAAAAC3Y/QuZ7K9t_WzE/s512-c/photo.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://www.transl-gunsmoker.ru/2011/08/is-maximum-size-of-environment-32k-or.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CEcGSHk5eSp7ImA9WhdWFk8.&quot;"><id>tag:blogger.com,1999:blog-1688132520265708820.post-1270620744194217875</id><published>2011-08-23T22:04:00.000+04:00</published><updated>2011-09-10T06:33:49.721+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-09-10T06:33:49.721+04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="безопасность" /><title>Безопасность: не забывайте обнулять вещи, на которые вам наплевать</title><content type="html">Это перевод &lt;a href="http://blogs.msdn.com/b/oldnewthing/archive/2006/07/03/655251.aspx" title="Security: Don't forget to initialize the stuff you don't care about"&gt;Security: Don't forget to initialize the stuff you don't care about&lt;/a&gt;. Автор: Реймонд Чен.&lt;br /&gt;
&lt;br /&gt;
Затерянным в волнении уязвимостей повышения привилегий является простое раскрытие информации через отсутствующую инициализацию мусора (прим.пер.: понятия не имею, зачем оригинальное предложение так построено).&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
Сегодня каждый знаком с использованием &lt;a title="MSDN: SecureZeroMemory Function" href="http://msdn.microsoft.com/en-us/library/aa366877(VS.85).aspx"&gt;функции &lt;code&gt;SecureZeroMemory&lt;/code&gt;&lt;/a&gt; для гарантии того, что буфера, содержащие чувствительную (важную) информацию, очищены после работы с ними (*), но вы также должны очищать буфера перед их записью в другое место. Рассмотрим, к примеру, такой формат данных:
&lt;pre class="brush:delphi"&gt;type
  TFileHeader = packed record
    dwMagic: DWORD;
    dwVersion: DWORD;
    wszComment: array[0..256-1] of WideChar;
    cbData: DWORD;
    // далее следует cbData байт данных
  end;&lt;/pre&gt;
Код, который заполняет такие файлы, мог бы выглядеть так:
&lt;pre class="brush:delphi"&gt;function SaveToFile(hFile: THandle; pszComment: PWideChar; cbData: DWORD; pbData: PByte): Boolean;
var
  cbWritten: DWORD;
  fh: TFileHeader;
begin
  fh.dwMagic := FILE_MAGICNUMBER;
  fh.dwVersion := FILE_CURRENTVERSION;
  fh.cbData := cbData;
  Result := SUCCEEDED(StringCchCopyW(fh.wszComment, 256, pszComment)) and
                  WriteFile(hFile, @fh, SizeOf(fh), cbWritten, nil) and
                  (cbWritten = SizeOf(fh)) and
                  WriteFile(hFile, pbData, cbData, cbWritten, nil) and
                  (cbWritten = cbData);
end;&lt;/pre&gt;
Увидели баг безопасности?&lt;br /&gt;
&lt;br /&gt;
Если комментарий к файлу будет менее 255 символов, то байты после терминирующего нуля будут содержать не инициализированный мусор со стека. Мусор со стека может содержать интересную информацию, которую вы бы не хотели выкладывать в файл. Конечно, он не будет содержать информацию, которую вы уже идентифицировали как чувствительную (вроде паролей) - потому что вы уже подчистили её; но мусор может содержать информацию, хотя и менее критическую, но всё же привлекательную для злоумышленника. К примеру, у вас может утечь имя аккаунта.&lt;br /&gt;
&lt;br /&gt;
Мне рассказали, что одна сетевая программа известной компании содержала аналогичный баг. Они использовали очень продвинутый алгоритм "смены пароля", детали которого сейчас не важны. Дизайном было предусмотрено, что по сети передавались только сложно зашифрованные данные. Таким образом, если кто-то просматривал сетевые пакеты, то он не мог увидеть ничего интересного. Вот только у них был баг в их клиентской программе: когда они отправляли зашифрованный пароль на сервер, они забыли обнулить не используемые байты в пакете "смена пароля". А в этих данных была... ага, верно: не зашифрованная копия пароля.&lt;br /&gt;
&lt;br /&gt;
Примечание переводчика:&lt;br /&gt;
(*) &lt;code&gt;SecureZeroMemory&lt;/code&gt; практически не известна в Delphi, потому что предназначена для обхода оптимизатора C++ (видимо, по причине того, что &lt;a title="MSDN: ZeroMemory Function" href="http://msdn.microsoft.com/en-us/library/aa366920(VS.85).aspx"&gt;&lt;code&gt;ZeroMemory&lt;/code&gt;&lt;/a&gt; в C++ является макросом, а не функцией; поэтому её "вызов" последним действием на переменную может быть выброшен оптимизатором). Вместо неё в Delphi используется стандартная системная &lt;code&gt;ZeroMemory&lt;/code&gt; (которая в Delphi является обычной функцией; фактически - inline-переходником к &lt;a title="System.FillChar" href="http://docwiki.embarcadero.com/VCL/en/System.FillChar"&gt;&lt;code&gt;FillChar&lt;/code&gt;&lt;/a&gt;) или чисто Delphi-ёвая &lt;code&gt;FillChar&lt;/code&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688132520265708820-1270620744194217875?l=www.transl-gunsmoker.ru' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=twnktCXNSks:irNuEEcp3nQ:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=twnktCXNSks:irNuEEcp3nQ:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=twnktCXNSks:irNuEEcp3nQ:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=twnktCXNSks:irNuEEcp3nQ:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=twnktCXNSks:irNuEEcp3nQ:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=twnktCXNSks:irNuEEcp3nQ:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=twnktCXNSks:irNuEEcp3nQ:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=twnktCXNSks:irNuEEcp3nQ:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=twnktCXNSks:irNuEEcp3nQ:-BTjWOF_DHI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=twnktCXNSks:irNuEEcp3nQ:-BTjWOF_DHI" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/GunsmokersTranslations/~4/twnktCXNSks" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.transl-gunsmoker.ru/feeds/1270620744194217875/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://www.transl-gunsmoker.ru/2011/08/security-dont-forget-to-initialize.html#comment-form" title="Комментарии: 0" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/1270620744194217875?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/1270620744194217875?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/GunsmokersTranslations/~3/twnktCXNSks/security-dont-forget-to-initialize.html" title="Безопасность: не забывайте обнулять вещи, на которые вам наплевать" /><author><name>Александр Алексеев</name><uri>https://profiles.google.com/113168002104297556003</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh3.googleusercontent.com/-aUMttZQBsuI/AAAAAAAAAAI/AAAAAAAAC3Y/QuZ7K9t_WzE/s512-c/photo.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://www.transl-gunsmoker.ru/2011/08/security-dont-forget-to-initialize.html</feedburner:origLink></entry><entry gd:etag="W/&quot;A0UFSX85fip7ImA9WhdWFkw.&quot;"><id>tag:blogger.com,1999:blog-1688132520265708820.post-6019062231079066604</id><published>2011-08-22T22:03:00.000+04:00</published><updated>2011-09-10T05:46:58.126+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-09-10T05:46:58.126+04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="кодинг" /><title>Событие с авто-сбросом - это просто глупый семафор</title><content type="html">Это перевод &lt;a href="http://blogs.msdn.com/b/oldnewthing/archive/2006/06/22/642849.aspx" title="An auto-reset event is just a stupid semaphore"&gt;An auto-reset event is just a stupid semaphore&lt;/a&gt;. Автор: Реймонд Чен.&lt;br /&gt;
&lt;br /&gt;
Когда вы создаёте событие с помощью &lt;a title="MSDN: CreateEvent Function" href="http://msdn.microsoft.com/en-us/library/ms682396(VS.85).aspx"&gt;функции &lt;code&gt;CreateEvent&lt;/code&gt;&lt;/a&gt;, вы указываете, хотите ли вы создать событие с авто-сбросом (auto-reset event) или с ручным сбросом (manual-reset event).&lt;br /&gt;
&lt;br /&gt;
Событие с ручным сбросом прозрачно для понимания: если событие сброшено, то вы ждёте сигнального состояния; если событие установлено (в сигнальном состоянии), то ожидания не происходит (ожидание удовлетворяется сразу). Не играет значения, сколько человек ждут события; они все обрабатываются одинаково, а состояние события не зависит от числа его ожидающих.&lt;br /&gt;
&lt;br /&gt;
Событие с авто-сбросом - более запутывающая штука.&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
Вероятно, проще всего думать о событии с авто-сбросом как о семафоре с максимумом счётчика равным единице. Если событие сброшено, то вы ждёте сигнального состояния. Если событие устанавливается, то один из ожидающих будет отпущен, а остальные продолжат ждать (и из &lt;a href="http://www.transl-gunsmoker.ru/2010/02/pulseevent.html" title="PulseEvent имеет фундаментальный изъян"&gt;нашего обсуждения &lt;code&gt;PulseEvent&lt;/code&gt;&lt;/a&gt; вы уже знаете, что то, кто именно будет освобождён, - не определено).&lt;br /&gt;
&lt;br /&gt;
"Ага!" в случае событий с авто-сбросом заключается в случае, когда вы устанавливаете уже установленное событие. Поскольку у события есть только два состояния (сброшенное и сигнальное), то установка события, которое и так находится в сигнальном состоянии, не даст никакого эффекта, ничего не изменит. Если вы используете событие, чтобы контролировать ресурс в модели producer/consumer, то "установка уже установленного события" приведёт к потере токена. Рассмотрим такой шаблон:&lt;br /&gt;
&lt;br /&gt;
&lt;TABLE RULES=cols BORDER=1 BORDERCOLOR=black CELLPADDING=3 STYLE="border-collapse: collapse"&gt;&lt;TR&gt;&lt;TH&gt;Producer&lt;/TH&gt;&lt;TH&gt;Consumer&lt;/TH&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;        &lt;/TD&gt;&lt;TD&gt;Ожидание&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;Задать работу&lt;/TD&gt;&lt;TD&gt;&lt;/TR&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;&lt;CODE&gt;SetEvent&lt;/CODE&gt;&lt;/TD&gt;&lt;TD&gt;&lt;/TR&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;        &lt;/TD&gt;&lt;TD&gt;Проснуться и сбросить событие&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;        &lt;/TD&gt;&lt;TD&gt;Работа&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;Задать работу&lt;/TD&gt;&lt;TD&gt;&lt;/TR&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;        &lt;/TD&gt;&lt;TD&gt;Wait&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;&lt;CODE&gt;SetEvent&lt;/CODE&gt;&lt;/TD&gt;&lt;TD&gt;&lt;/TR&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;        &lt;/TD&gt;&lt;TD&gt;Проснуться и сбросить событие&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;        &lt;/TD&gt;&lt;TD&gt;Работа&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;...     &lt;/TD&gt;&lt;TD&gt;...&lt;/TD&gt;&lt;/TR&gt;&lt;/TABLE&gt; 
&lt;br /&gt;
Но что если тайминг немного не совпадёт? Что если потребитель (consumer) будет медленно делать работу (либо же producer будет слишком быстро её генерировать)?&lt;br /&gt;
&lt;br /&gt;
&lt;TABLE RULES=cols BORDER=1 BORDERCOLOR=black CELLPADDING=3 STYLE="border-collapse: collapse"&gt;&lt;TR&gt;&lt;TH&gt;Producer&lt;/TH&gt;&lt;TH&gt;Consumer&lt;/TH&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;        &lt;/TD&gt;&lt;TD&gt;Ожидание&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;Задать работу&lt;/TD&gt;&lt;TD&gt;&lt;/TR&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;&lt;CODE&gt;SetEvent&lt;/CODE&gt;&lt;/TD&gt;&lt;TD&gt;&lt;/TR&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;        &lt;/TD&gt;&lt;TD&gt;Проснуться и сбросить событие&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;Задать работу&lt;/TD&gt;&lt;TD&gt;&lt;/TR&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;&lt;CODE&gt;SetEvent&lt;/CODE&gt;&lt;/TD&gt;&lt;TD&gt;&lt;/TR&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;        &lt;/TD&gt;&lt;TD&gt;Работа&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;Задать работу&lt;/TD&gt;&lt;TD&gt;&lt;/TR&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;&lt;CODE&gt;SetEvent&lt;/CODE&gt; (без эффекта)&lt;/TD&gt;&lt;TD&gt;&lt;/TR&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;        &lt;/TD&gt;&lt;TD&gt;Ожидание удовлетворено сразу&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;        &lt;/TD&gt;&lt;TD&gt;Сброс события&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;        &lt;/TD&gt;&lt;TD&gt;Работа&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;        &lt;/TD&gt;&lt;TD&gt;Ожидание&lt;/TD&gt;&lt;/TR&gt;&lt;/TABLE&gt;
&lt;br /&gt;
Заметьте, что producer создал три задания на работу, но consumer обработал только два. Третий &lt;code&gt;SetEvent&lt;/code&gt; просто ничего не сделал, поскольку событие было уже установлено (вы увидите ту же проблему, если попытаетесь увеличить счётчик семафора за его максимум). Если вы хотите, чтобы оба числа соответствовали друг другу, то вам нужно использовать семафор с максимальным значением счётчика выше чем максимум элементов в очереди (прим.пер.: либо же consumer при выходе из ожидания может обрабатывать не один элемент, а все в очереди).&lt;br /&gt;
&lt;br /&gt;
Мораль истории: знайте ваши инструменты, знайте их ограничения и используйте правильный инструмент для правильной задачи.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688132520265708820-6019062231079066604?l=www.transl-gunsmoker.ru' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=XB013wWlDZM:8g3dsEerWUw:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=XB013wWlDZM:8g3dsEerWUw:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=XB013wWlDZM:8g3dsEerWUw:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=XB013wWlDZM:8g3dsEerWUw:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=XB013wWlDZM:8g3dsEerWUw:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=XB013wWlDZM:8g3dsEerWUw:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=XB013wWlDZM:8g3dsEerWUw:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=XB013wWlDZM:8g3dsEerWUw:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=XB013wWlDZM:8g3dsEerWUw:-BTjWOF_DHI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=XB013wWlDZM:8g3dsEerWUw:-BTjWOF_DHI" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/GunsmokersTranslations/~4/XB013wWlDZM" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.transl-gunsmoker.ru/feeds/6019062231079066604/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://www.transl-gunsmoker.ru/2011/08/auto-reset-event-is-just-stupid.html#comment-form" title="Комментарии: 0" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/6019062231079066604?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/6019062231079066604?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/GunsmokersTranslations/~3/XB013wWlDZM/auto-reset-event-is-just-stupid.html" title="Событие с авто-сбросом - это просто глупый семафор" /><author><name>Александр Алексеев</name><uri>https://profiles.google.com/113168002104297556003</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh3.googleusercontent.com/-aUMttZQBsuI/AAAAAAAAAAI/AAAAAAAAC3Y/QuZ7K9t_WzE/s512-c/photo.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://www.transl-gunsmoker.ru/2011/08/auto-reset-event-is-just-stupid.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CE4DSH89cSp7ImA9WhdWFkw.&quot;"><id>tag:blogger.com,1999:blog-1688132520265708820.post-9158012274920786985</id><published>2011-08-21T22:02:00.000+04:00</published><updated>2011-09-10T04:02:59.169+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-09-10T04:02:59.169+04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="безопасность" /><title>Одноэкземплярная программа является своим собственным отказом в обслуживании</title><content type="html">Это перевод &lt;a href="http://blogs.msdn.com/b/oldnewthing/archive/2006/06/20/639479.aspx" title="A single-instance program is its own denial of service"&gt;A single-instance program is its own denial of service&lt;/a&gt;. Автор: Реймонд Чен.&lt;br /&gt;
&lt;br /&gt;
Есть много способов, которыми программа может достичь поведения запуска в едином экземпляре; я не буду разбирать их. Но как только люди встают на путь программы с одним запущенным экземпляром, они тут же спотыкаются о другую проблему при анализе безопасности: отказ в обслуживании (denial of service attack).
&lt;blockquote&gt;Мы используем мьютекс с фиксированным именем, чтобы определить, не запущена ли уже копия программы. Но это также означает, что злоумышленник может создать мьютекс с таким именем до запуска программы, блокируя, таким образом, запуск нашей программы! Как я могу предотвратить этот тип отказа в обслуживании?&lt;/blockquote&gt;
&lt;a name='more'&gt;&lt;/a&gt;
Если атакующий запущен в том же контексте безопасности, что и ваша программа (ну, или &lt;i&gt;будет&lt;/i&gt; запущена ваша программа), то вы ничего не можете сделать с этой ситуацией. Какое бы "секретное рукопожатие" вы ни придумали бы для определения запуска второго экземпляра - атакующий всегда может просто повторить его. Поскольку он запущен в том же контексте безопасности - он может ровно столько же, сколько и ваша программа.&lt;br /&gt;
&lt;br /&gt;
Но посмотрите на этот вопрос шире: одноэкземплярная программа является "своим собственным отказом в обслуживании"! В конце концов, первый экземпляр программы блокирует последующие запуски программы. Ваши требования к программе изначально являются уязвимостью в безопасности. Соответственно, вы не можете защитить себя от отказа в обслуживании, потому что вы &lt;b&gt;хотите&lt;/b&gt; иметь отказ в обслуживании.&lt;br /&gt;
&lt;br /&gt;
Но что вы можете сделать - понять и сузить область уязвимости. Очевидно, вы не можете защитить себя от атакующего, работающего на том же уровне привилегий, что и вы. Но вы можете защитить себя от атакующих, работающих под другими (не привилегированными) учётными записями. Для этого вам нужно включить защищаемые объекты в процесс вашего программного "рукопожатия". Тогда простые пользователи (не администраторы) не смогут заблокировать запуск вашей программы для других пользователей. Худшей вещью, которой допускается делать не администратору - делать свою жизнь жалкой, но не жизнь других пользователей.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688132520265708820-9158012274920786985?l=www.transl-gunsmoker.ru' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=wYm5AmwLqXg:Yj7BN1ygOJw:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=wYm5AmwLqXg:Yj7BN1ygOJw:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=wYm5AmwLqXg:Yj7BN1ygOJw:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=wYm5AmwLqXg:Yj7BN1ygOJw:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=wYm5AmwLqXg:Yj7BN1ygOJw:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=wYm5AmwLqXg:Yj7BN1ygOJw:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=wYm5AmwLqXg:Yj7BN1ygOJw:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=wYm5AmwLqXg:Yj7BN1ygOJw:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GunsmokersTranslations?a=wYm5AmwLqXg:Yj7BN1ygOJw:-BTjWOF_DHI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GunsmokersTranslations?i=wYm5AmwLqXg:Yj7BN1ygOJw:-BTjWOF_DHI" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/GunsmokersTranslations/~4/wYm5AmwLqXg" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.transl-gunsmoker.ru/feeds/9158012274920786985/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://www.transl-gunsmoker.ru/2011/08/single-instance-program-is-its-own.html#comment-form" title="Комментарии: 0" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/9158012274920786985?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1688132520265708820/posts/default/9158012274920786985?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/GunsmokersTranslations/~3/wYm5AmwLqXg/single-instance-program-is-its-own.html" title="Одноэкземплярная программа является своим собственным отказом в обслуживании" /><author><name>Александр Алексеев</name><uri>https://profiles.google.com/113168002104297556003</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh3.googleusercontent.com/-aUMttZQBsuI/AAAAAAAAAAI/AAAAAAAAC3Y/QuZ7K9t_WzE/s512-c/photo.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://www.transl-gunsmoker.ru/2011/08/single-instance-program-is-its-own.html</feedburner:origLink></entry></feed>

