<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:blogger='http://schemas.google.com/blogger/2008' xmlns:georss='http://www.georss.org/georss' xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-1688132520265708820</id><updated>2025-03-25T06:04:18.134+03:00</updated><category term="кодинг"/><category term="История"/><category term="серия"/><category term="не делай так"/><category term="прочее"/><category term="Win32"/><category term="окна"/><category term="Совместимость"/><category term="забавно"/><category term="железо"/><category term="UI"/><category term="Delphi"/><category term="Win16"/><category term="потоки"/><category term="unicode"/><category term="ты можешь это сделать"/><category term="The Old New Thing"/><category term="encoding/codepages"/><category term="хак"/><category term="безопасность"/><category term="обработка ошибок"/><category term="locale/cultures"/><category term="дизайн/проектирование"/><category term="Int&#39;l Programming"/><category term="collation/casing"/><category term="RTTI"/><category term="реестр"/><category term="Win64"/><category term="linguistic"/><category term="Vista"/><category term="fonts"/><category term="дата/время"/><category term="клавиатуры"/><category term="справка"/><category term="LIP/MUI"/><category term="MSLU"/><category term="Potpourri"/><category term="аудио"/><category term="фильмы"/><category term="языки"/><title type='text'>Блог GunSmoker-а (переводы)</title><subtitle type='html'>...when altering one&#39;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='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688132520265708820/posts/default?redirect=false'/><link rel='alternate' type='text/html' href='http://www.transl-gunsmoker.ru/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><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'/><author><name>GunSmoker</name><uri>http://www.blogger.com/profile/15611696588191431330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhrYMBDZyhmwpngoHu4MbxoNS55hTzfYX9MRyMyiL7lk8FV4zzROyizfpqf_E85yuu8-BtGtcAZg_miWOVH_8I9hdkdBcDd5UkBhBcWo1jkcyacq_Lgcmt9gECy0zV0zNo/s220/avatar.png'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>916</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-1688132520265708820.post-2108634923690843331</id><published>2024-07-25T14:09:00.001+03:00</published><updated>2024-07-25T14:09:25.028+03:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="кодинг"/><title type='text'>Принцип имён в API: если объект не указан явно, то действие относится к исходному объекту</title><content type='html'>Это перевод &lt;a href=&quot;https://devblogs.microsoft.com/oldnewthing/20240724-00/?p=110037&quot; title=&quot;API naming principle: If there is no direct object, then the direct object is the source object&quot;&gt;API naming principle: If there is no direct object, then the direct object is the source object&lt;/a&gt;. Автор: Реймонд Чен.&lt;br /&gt;
&lt;br /&gt;
Обычно имена методов начинаются с глагола: &lt;code&gt;widget.&lt;span style=&quot;border: solid 1px currentcolor;&quot;&gt;&lt;b&gt;Toggle&lt;/b&gt;&lt;/span&gt;&lt;/code&gt;, &lt;code&gt;widget.&lt;span style=&quot;border: solid 1px currentcolor;&quot;&gt;&lt;b&gt;Set&lt;/b&gt;&lt;/span&gt;Color&lt;/code&gt;, &lt;code&gt;widget.&lt;span style=&quot;border: solid 1px currentcolor;&quot;&gt;&lt;b&gt;Get&lt;/b&gt;&lt;/span&gt;AssociatedDoodad&lt;/code&gt;.&lt;br /&gt;
&lt;br /&gt;
Часто за глаголом следует объект: &lt;code&gt;widget.Set&lt;span style=&quot;border: solid 1px currentcolor;&quot;&gt;&lt;b&gt;Color&lt;/b&gt;&lt;/span&gt;&lt;/code&gt;, &lt;code&gt;widget.Get&lt;span style=&quot;border: solid 1px currentcolor;&quot;&gt;&lt;b&gt;AssociatedDoodad&lt;/b&gt;&lt;/span&gt;&lt;/code&gt;. Такой объект, указываемый явно — это то, над чем глагол работает, или что он создает. В примере выше: &lt;code&gt;SetColor&lt;/code&gt; устанавливает (set) цвет (color), а &lt;code&gt;GetAssociatedDoodad&lt;/code&gt; возвращает (get) ассоциированный doodad (associated doodad).&lt;br /&gt;
&lt;br /&gt;
Иногда за глаголом вообще не следует название объекта. Например, метод &lt;code&gt;widget.Toggle&lt;/code&gt; выше. В этом случае объект указывается неявно: это исходный объект. В приведенном выше примере &lt;code&gt;widget.Toggle&lt;/code&gt; переключает (toggle) виджет (widget).&lt;br /&gt;
&lt;br /&gt;
Всё это может показаться очевидным, но этот принцип легко упустить из виду.&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
Например, одна команда разработчиков предложила создать API с объектом &lt;code&gt;WidgetNotification&lt;/code&gt; и методом &lt;code&gt;widgetNotification.Delete&lt;/code&gt;. Если трактовать это предложение, как было указано выше, то можно подумать, что предложенный метод удалит сам объект уведомления виджета, но они хотели, чтобы этот метод удалял бы прослушиватель (listener) уведомлений. По правилам выше, методы для создания и удаления прослушивателей должны быть названы как-то вроде &lt;code&gt;widgetNotification.Create&lt;span style=&quot;border: solid 1px currentcolor;&quot;&gt;&lt;b&gt;Listener&lt;/b&gt;&lt;/span&gt;&lt;/code&gt; и &lt;code&gt;widgetNotification.Delete&lt;span style=&quot;border: solid 1px currentcolor;&quot;&gt;&lt;b&gt;Listener&lt;/b&gt;&lt;/span&gt;&lt;/code&gt;.&lt;br /&gt;
&lt;br /&gt;
В качестве другого примера: имя метода &lt;code&gt;ApplicationDataManager.CreateForPackageFamily&lt;/code&gt; не говорит о том, что именно он создаёт, поэтому можно решить, что он создаёт &lt;code&gt;ApplicationDataManager&lt;/code&gt;. Но это не так. На самом деле, он создаёт объект &lt;code&gt;ApplicationData&lt;/code&gt;. Более правильное название для такого метода могло бы быть &lt;code&gt;ApplicationDataManager.Create&lt;span style=&quot;border: solid 1px currentcolor;&quot;&gt;&lt;b&gt;Data&lt;/b&gt;&lt;/span&gt;ForPackageFamily&lt;/code&gt;.&lt;br /&gt;
&lt;br /&gt;
Исключением из этого правила являются фабрики объектов. Целью объектов-фабрик является создание других объектов, поэтому предполагается, что метод &lt;code&gt;WidgetFactory.Create&lt;/code&gt; создаст widget. Но я не стану жаловаться, если бы вы назвали свой метод &lt;code&gt;WidgetFactory.CreateWidget&lt;/code&gt;, просто чтобы внести ясность.</content><link rel='replies' type='application/atom+xml' href='http://www.transl-gunsmoker.ru/feeds/2108634923690843331/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.transl-gunsmoker.ru/2024/07/api.html#comment-form' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688132520265708820/posts/default/2108634923690843331'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688132520265708820/posts/default/2108634923690843331'/><link rel='alternate' type='text/html' href='http://www.transl-gunsmoker.ru/2024/07/api.html' title='Принцип имён в API: если объект не указан явно, то действие относится к исходному объекту'/><author><name>GunSmoker</name><uri>http://www.blogger.com/profile/15611696588191431330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhrYMBDZyhmwpngoHu4MbxoNS55hTzfYX9MRyMyiL7lk8FV4zzROyizfpqf_E85yuu8-BtGtcAZg_miWOVH_8I9hdkdBcDd5UkBhBcWo1jkcyacq_Lgcmt9gECy0zV0zNo/s220/avatar.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688132520265708820.post-3385942992745968541</id><published>2023-12-04T21:10:00.002+03:00</published><updated>2023-12-04T21:10:36.364+03:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Win64"/><category scheme="http://www.blogger.com/atom/ns#" term="кодинг"/><title type='text'>Что такое указатель статической цепочки в контексте соглашения о вызовах ABI?</title><content type='html'>Это перевод &lt;a href=&quot;https://devblogs.microsoft.com/oldnewthing/20231204-00/?p=109095&quot; title=&quot;What is a static chain pointer in the context of calling convention ABI?&quot;&gt;What is a static chain pointer in the context of calling convention ABI?&lt;/a&gt; Автор: Реймонд Чен.&lt;br /&gt;
&lt;br /&gt;
Глубоко в руководстве Application Binary Interface из System V для архитектуры AMD64 есть сноска на странице 24, в которой говорится: &quot;&lt;code&gt;%r10&lt;/code&gt; используется для передачи указателя статической цепочки функции&quot;. Что такое &quot;указатель статической цепочки&quot;?&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
Некоторые языки (например, Паскаль/Delphi) поддерживают &lt;b&gt;вложенные&lt;/b&gt; функции. Вложенной функции может быть разрешен доступ к переменным родительской функции. Например:
&lt;pre class=&quot;brush:delphi&quot;&gt;function Outer(n: Integer): Integer;
var
  i: Integer;

  procedure Inner(m: Integer);
  begin
    i := i + m;
  end;

// Тут начинаяется функция Outer
begin
  i := 0;
  Inner(n);
  Result := i;
end;&lt;/pre&gt;
Функция &lt;code&gt;Outer&lt;/code&gt; не делает ничего полезного, но она не делает это ничего интересным образом.&lt;br /&gt;
&lt;br /&gt;
Она начинается с объявления локальной переменной &lt;code&gt;i&lt;/code&gt;, а затем определяет локальную функцию &lt;code&gt;Inner&lt;/code&gt;, которая добавляет свой параметр &lt;code&gt;m&lt;/code&gt; к внешней переменной &lt;code&gt;i&lt;/code&gt;. Затем идёт код самой функции &lt;code&gt;Outer&lt;/code&gt;, который начинается с инициализации &lt;code&gt;i&lt;/code&gt; нулём, вызывает &lt;code&gt;Inner(n)&lt;/code&gt; (которая добавляет &lt;code&gt;n&lt;/code&gt; к &lt;code&gt;i&lt;/code&gt;), а затем возвращает изменённое значение &lt;code&gt;i&lt;/code&gt;.&lt;br /&gt;
&lt;br /&gt;
По сути, функция &lt;code&gt;Outer&lt;/code&gt; просто возвращает свой аргумент, но она делает это с помощью внутренней функции.&lt;br /&gt;
&lt;br /&gt;
Это работает так: функция &lt;code&gt;Inner&lt;/code&gt; получает скрытый параметр, который сообщает ей, где находятся локальные переменные процедуры &lt;code&gt;Outer&lt;/code&gt;.&lt;br /&gt;
&lt;br /&gt;
На практике передается указатель на кадр стека внешней функции.&lt;br /&gt;
&lt;br /&gt;
А если внешняя функция сама по себе является вложенной, вы можете использовать родительский фрейм для доступа к локальным переменным &quot;дедушки&quot;:
&lt;pre class=&quot;brush:delphi&quot;&gt;function Outer(n: Integer): Integer;
var
  i: Integer;

  procedure Inner(m: Integer);
  
    procedure MoreInner;    
    begin              
      i := i + m;
    end;
    
  begin
    MoreInner;
  end;

// Тут начинаяется функция Outer
begin
  i := 0;
  Inner(n);
  Result := i;
end;&lt;/pre&gt;
В этом примере функция &lt;code&gt;MoreInner&lt;/code&gt; получает скрытый указатель на кадр стека функции &lt;code&gt;Inner&lt;/code&gt;, что позволяет ей получить доступ к параметру &lt;code&gt;m&lt;/code&gt; из &lt;code&gt;Inner&lt;/code&gt;. Но &lt;code&gt;Inner&lt;/code&gt; сама по себе является вложенной процедурой - и поэтому она тоже получила указатель на кадр стека родителя (в данном случае - &lt;code&gt;Outer&lt;/code&gt;). Следовательно, &lt;code&gt;MoreInner&lt;/code&gt; может использовать этот указатель для доступа к локальной переменной &lt;code&gt;i&lt;/code&gt; функции &lt;code&gt;Outer&lt;/code&gt;.&lt;br /&gt;
&lt;br /&gt;
Вот как это выглядит:&lt;br /&gt;
&lt;br /&gt;
&lt;table class=&quot;cp3&quot; style=&quot;border-collapse: collapse;&quot; title=&quot;Кадр процедуры MoreInner содержит только указатель цепочки, указывающий на кадр процедуры Inner. Кадр процедуры Inner содержит переменную m и имеет указатель цепочки, указывающий на кадр процедуры Outer. Фрейм процедуры Outer содержит переменные n и i.&quot; border=&quot;0&quot; cellspacing=&quot;0&quot; cellpadding=&quot;3&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;MoreInner&lt;/td&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;td&gt;Inner&lt;/td&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;td&gt;Outer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;border: solid 1px currentcolor;&quot;&gt;указатель цепочки&lt;/td&gt;
&lt;td&gt;➝&lt;/td&gt;
&lt;td style=&quot;border: solid 1px currentcolor;&quot;&gt;указатель цепочки&lt;/td&gt;
&lt;td&gt;➝&lt;/td&gt;
&lt;td style=&quot;border: solid 1px currentcolor;&quot;&gt;&lt;code&gt;n&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;border: solid 1px currentcolor;&quot;&gt;&lt;code&gt;m&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;border: solid 1px currentcolor;&quot;&gt;&lt;code&gt;i&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;br /&gt;
Это называется &lt;i&gt;статической&lt;/i&gt; цепочкой, поскольку структура цепочки основана на лексической, а не динамической области видимости. Вы можете увидеть разницу в этом примере:
&lt;pre class=&quot;brush:delphi&quot;&gt;function Outer(n: Integer): Integer;
var
  i: Integer;

  procedure Update(j: Integer);
  begin
    i := i + j;
  end;

  procedure Inner(m: Integer);

    procedure MoreInner;
    begin
      Update(m);
    end;

  // Тело функции Inner
  begin
    MoreInner;
  end;

// Тут начинается функция Outer
begin
  i := 0;
  Inner(n);
  Result := i;
end;&lt;/pre&gt;
Эта версия еще более бесполезна, чем предыдущая: &lt;code&gt;MoreInner&lt;/code&gt; не обновляет &lt;code&gt;i&lt;/code&gt; напрямую, а вместо этого просит &lt;code&gt;Update&lt;/code&gt; (функцию-&quot;дядю&quot;) сделать это.&lt;br /&gt;
&lt;br /&gt;
В тот момент, когда функция &lt;code&gt;MoreInner&lt;/code&gt; вызывает &lt;code&gt;Update&lt;/code&gt;, она не передает свой собственный кадр стека в качестве указателя статической цепочки. Вместо этого она передает кадр стека &lt;code&gt;Outer&lt;/code&gt;, поскольку родительским элементом &lt;code&gt;Update&lt;/code&gt; является &lt;code&gt;Outer&lt;/code&gt;.&lt;br /&gt;
&lt;br /&gt;
Статическая цепочка не соответствует динамическому стеку вызовов: стек вызовов говорит, что вызывающий объект функции &lt;code&gt;Update&lt;/code&gt; — функция &lt;code&gt;MoreInner&lt;/code&gt;, но статическая цепочка говорит, что родительским объектом &lt;code&gt;Update&lt;/code&gt; является функция &lt;code&gt;Outer&lt;/code&gt;:&lt;br /&gt;
&lt;br /&gt;
&lt;table class=&quot;cp3&quot; style=&quot;border-collapse: collapse; text-align: center;&quot; title=&quot;Связанный список фреймов для Update, MoreInner, Inner и Outer. Указатели цепочки в каждом случае не совпадают с указателями стековых фреймов. Указатель цепочки Update указывает на Outer, минуя MoreInner и Inner. Указатель цепочки MoreInner указывает на фрейм для Inner, чей указатель цепочки указывает на фрейм для Outer.&quot; border=&quot;0&quot; cellspacing=&quot;0&quot; cellpadding=&quot;3&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Update&lt;/td&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;td&gt;MoreInner&lt;/td&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;td&gt;Inner&lt;/td&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;td&gt;Outer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;border: solid 1px currentcolor;&quot;&gt;указатель фрейма&lt;/td&gt;
&lt;td&gt;➝&lt;/td&gt;
&lt;td style=&quot;border: solid 1px currentcolor;&quot;&gt;указатель фрейма&lt;/td&gt;
&lt;td&gt;➝&lt;/td&gt;
&lt;td style=&quot;border: solid 1px currentcolor;&quot;&gt;указатель фрейма&lt;/td&gt;
&lt;td&gt;➝&lt;/td&gt;
&lt;td style=&quot;border: solid 1px currentcolor;&quot;&gt;указатель фрейма&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;border: solid 1px currentcolor;&quot;&gt;указатель цепочки&lt;/td&gt;
&lt;td id=&quot;p20231204_rarr5&quot; colspan=&quot;5&quot;&gt;&lt;svg width=&quot;300&quot; height=&quot;20&quot;&gt;&lt;use href=&quot;#rarr5&quot;&gt;&lt;/use&gt;&lt;/svg&gt;&lt;/td&gt;
&lt;td style=&quot;border: solid 1px currentcolor;&quot;&gt;&lt;code&gt;n&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;border: solid 1px currentcolor;&quot;&gt;указатель цепочки&lt;/td&gt;
&lt;td&gt;➝&lt;/td&gt;
&lt;td style=&quot;border: solid 1px currentcolor;&quot;&gt;указатель цепочки&lt;/td&gt;
&lt;td style=&quot;position: relative;&quot;&gt;&lt;span style=&quot;position: relative; top: -.5em;&quot;&gt;⭧&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: solid 1px currentcolor;&quot;&gt;&lt;code&gt;i&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;border: solid 1px currentcolor;&quot;&gt;&lt;code&gt;m&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;br /&gt;
Авторы руководства Application Binary Interface предполагают, что вы знакомы с тем, как реализованы вложенные функции, и просто отмечают, что соглашение о вызове вложенных функций заключается в передаче статической цепочки в регистре &lt;code&gt;%r10&lt;/code&gt;.</content><link rel='replies' type='application/atom+xml' href='http://www.transl-gunsmoker.ru/feeds/3385942992745968541/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.transl-gunsmoker.ru/2023/12/static-chain-pointer.html#comment-form' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688132520265708820/posts/default/3385942992745968541'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688132520265708820/posts/default/3385942992745968541'/><link rel='alternate' type='text/html' href='http://www.transl-gunsmoker.ru/2023/12/static-chain-pointer.html' title='Что такое указатель статической цепочки в контексте соглашения о вызовах ABI?'/><author><name>GunSmoker</name><uri>http://www.blogger.com/profile/15611696588191431330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhrYMBDZyhmwpngoHu4MbxoNS55hTzfYX9MRyMyiL7lk8FV4zzROyizfpqf_E85yuu8-BtGtcAZg_miWOVH_8I9hdkdBcDd5UkBhBcWo1jkcyacq_Lgcmt9gECy0zV0zNo/s220/avatar.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688132520265708820.post-3637822773390867288</id><published>2023-02-21T13:28:00.000+03:00</published><updated>2023-02-21T13:28:08.978+03:00</updated><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="ты можешь это сделать"/><title type='text'>Случай с загадочной ошибкой &quot;out of bounds&quot; из CreateUri и memmove</title><content type='html'>Это перевод &lt;a href=&quot;https://devblogs.microsoft.com/oldnewthing/20230220-00/?p=107848&quot; title=&quot;The case of the mysterious out of bounds error from CreateUri and memmove&quot;&gt;The case of the mysterious &quot;out of bounds&quot; error from &lt;code&gt;CreateUri&lt;/code&gt; and &lt;code&gt;memmove&lt;/code&gt;&lt;/a&gt;. Автор: Реймонд Чен.&lt;br /&gt;
&lt;br /&gt;
Один клиент пытался понять, почему его программа вылетала с ошибкой &lt;code&gt;E_BOUNDS&lt;/code&gt; (&quot;out of bounds&quot;), возбуждаемой из вызова метода &lt;code&gt;CreateUri&lt;/code&gt;:&lt;br /&gt;
&lt;pre&gt;combase!RoOriginateErrorW+0x50
wincorlib!Platform::Details::ReCreateFromException+0x40
contoso!`__abi_translateCurrentException&#39;::`1&#39;::catch$0+0x10
contoso!memmove+0x217f4
contoso!Windows::Foundation::IUriRuntimeClassFactory::CreateUri+0x44
contoso!Contoso::DashboardView::DashboardView_obj1_Bindings::Update_ViewModel_Layout_Groups+0x50
contoso!Contoso::DashboardView::DashboardView_obj1_Bindings::Update_ViewModel_Layout+0xe4
contoso!Contoso::DashboardView::DashboardView_obj1_Bindings::PropertyChanged+0x1134
contoso!XamlBindingInfo::XamlBindingTrackingBase::PropertyChanged+0x30&lt;/pre&gt;
Судя по стеку, процедура копирования памяти &lt;code&gt;memmove&lt;/code&gt; вызывала высокоуровневое исключение RTL C++/CX &lt;code&gt;E_BOUNDS&lt;/code&gt; — что не имеет никакого смысла. Ещё более загадочно то, что &lt;code&gt;memmove&lt;/code&gt; была вызвана из метода &lt;code&gt;CreateUri&lt;/code&gt; интерфейса &lt;code&gt;IUriRuntimeClassFactory&lt;/code&gt;, но код клиента &lt;code&gt;DashboardView&lt;/code&gt; вообще не работает с URI. Похоже, что этот стек вызовов - это просто какая-то чепуха.&lt;br /&gt;
&lt;br /&gt;
Что ж, попробуем раскрутить эту чушь.&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
Что касается таинственного &lt;code&gt;memmove&lt;/code&gt;: обратите внимание на смещение 0x217f4. Маловероятно, что размер примитивной функции копирования памяти превышает 100 Кб. Тогда что тут происходит? Ну, это просто &lt;i&gt;какой-то код&lt;/i&gt;, который, вероятно, был перемещен в редко используемую кодовую страницу подальше от остального кода. Ну и так случилось, что ближайший к нему символ — &lt;code&gt;memmove&lt;/code&gt;:
&lt;pre class=&quot;brush:asm&quot;&gt;    xor     ecx,ecx
    call    contoso!__abi_translateCurrentException
    int     3       ; memmove+0x217f4&lt;/pre&gt;
Да, так и есть: это код повторного возбуждения исключения. Поскольку исключения случаются редко, то оптимизатор может переместить весь связанный с исключениями код в отдельные страницы, чтобы они не занимали драгоценное место в часто используемых страницах кода.&lt;br /&gt;
&lt;br /&gt;
Хорошо, тогда почему же метод &lt;code&gt;CreateUri&lt;/code&gt; выбрасывает исключение &quot;out of bounds&quot;?&lt;br /&gt;
&lt;br /&gt;
Подождите, а вы уверены, что это действительно метод &lt;code&gt;CreateUri&lt;/code&gt;?&lt;br /&gt;
&lt;br /&gt;
Я посмотрел на записи выше по стеку и спросил себя: &quot;почему код привязки данных (data binding) вызывает &lt;code&gt;CreateUri&lt;/code&gt;?&quot;.&lt;br /&gt;
&lt;br /&gt;
Код привязки данных автоматически создается компилятором XAML; он не присутствует в исходном коде программы. Вместо того, чтобы пытаться понять, как собрать и построить их проект (чтобы я мог извлечь автоматически сгенерированный файл), возможно, у меня получится разобраться по стеку и исходному коду.&lt;br /&gt;
&lt;br /&gt;
Одно из основных предположений о коде в целом состоит в том, что люди, которые пишут код — не садисты. Это означает, что имена функций обычно описывают то, что они делают, имена переменных обычно описывают то, что они представляют, и так далее. Поэтому, когда я вижу класс с именем &lt;code&gt;DashboardView_obj1_Bindings&lt;/code&gt;, я предполагаю, что этот класс предназначен для работы с привязками некоторого объекта внутри &lt;code&gt;DashboardView&lt;/code&gt;, и поскольку у него есть метод с именем &lt;code&gt;Update_ViewModel_Layout_Groups&lt;/code&gt;, он, вероятно, как-то связан с обновлением привязки чего-то с именем, включающим в себя слова ViewModel, Layout и Groups.&lt;br /&gt;
&lt;br /&gt;
Я просмотрел &lt;code&gt;DashboardView.xaml&lt;/code&gt; в исходном коде программы и, действительно, нашёл там слово &lt;code&gt;ViewModel&lt;/code&gt; в элементах, которые, как оказалось, связаны с привязкой данных:
&lt;pre class=&quot;brush:xml&quot;&gt;&amp;lt;ContentControl
    Grid.Row=&quot;0&quot;
    x:Name=&quot;TogglesGroup&quot;
    IsTabStop=&quot;False&quot;
    Width=&quot;360&quot;
    Content=&quot;{x:Bind ViewModel.Layout.Groups[0], Mode=OneWay}&quot;
    ContentTemplateSelector=&quot;{StaticResource DashboardGroupTemplateSelector}&quot;/&amp;gt;&lt;/pre&gt;
Это было не первое использование &lt;code&gt;x:Bind&lt;/code&gt; в разметке XAML, что не соответствует &lt;code&gt;obj1&lt;/code&gt;, но другие части совпадают (Layout и Groups), поэтому я списал это на &quot;возможно, компилятор XAML создаёт привязки в порядке, отличном от порядка их появления в разметке».&lt;br /&gt;
&lt;br /&gt;
Как эта привязка могла вызвать исключение &quot;out of bounds&quot;? Ну, тут записана операция с индексом 0, так что, возможно, коллекция Groups была пуста?&lt;br /&gt;
&lt;br /&gt;
Я посмотрел на метод &lt;code&gt;Update_ViewModel_Layout_Groups&lt;/code&gt;, чтобы убедиться, что эта теория соответствует действительности:
&lt;pre class=&quot;brush:asm&quot;&gt;; Update_ViewModel_Layout_Groups:
    test    rdx,rdx
    je      ...
    mov     qword ptr [rsp+8],rbx
    mov     qword ptr [rsp+18h],rbp

    push    rsi
    push    rdi
    push    r14
    sub     rsp,20h
    mov     rbp,rdx
    mov     rsi,rcx
    test    r8d,0C0000001h
    je      ...

    xor     edx,edx
    mov     rcx,rbp
    call    contoso!Windows::Foundation::IUriRuntimeClassFactory::CreateUri&lt;/pre&gt;
Функция начинается с раннего завершения (без формирования стекового фрейма), если первый параметр равен нулю (это код метода C++, поэтому &lt;code&gt;RCX&lt;/code&gt; содержит Self, а &lt;code&gt;RDX&lt;/code&gt; содержит первый &quot;настоящий&quot; параметр метода). Я понятия не имею, как работает связывание, поэтому предположим, что это часть его логики.&lt;br /&gt;
&lt;br /&gt;
А если параметр не равен нулю, то мы создаем правильный фрейм стека, проверяем некоторые биты в третьем параметре и, если они установлены, мы вызываем... &lt;i&gt;простите, что?&lt;/i&gt; &lt;code&gt;CreateUri&lt;/code&gt; с &lt;code&gt;nil&lt;/code&gt;? Это же бессмысленно. XAML не запрашивает URI, зачем ему пытаться создать URI из пустой строки?&lt;br /&gt;
&lt;br /&gt;
Опытный разработчик сообразит, что клиент (снова) был обманут оптимизатором.&lt;br /&gt;
&lt;br /&gt;
Параметр Self для вызова &lt;code&gt;CreateUri&lt;/code&gt; должен быть &lt;code&gt;IUriRuntimeClassFactory&lt;/code&gt;, но это не то, что мы передаем: мы передаем первый формальный параметр (который не является &lt;code&gt;IUriRuntimeClassFactory&lt;/code&gt;). Это значит, что перед нами вовсе не вызов &lt;code&gt;CreateUri&lt;/code&gt;.&lt;br /&gt;
&lt;br /&gt;
На самом деле, это — вызов метода &lt;code&gt;GetAt&lt;/code&gt; интерфейса &lt;code&gt;IVector&lt;/code&gt;, а параметр метода равен нулю, потому что нам нужен объект с нулевым индексом. Методы &lt;code&gt;GetAt&lt;/code&gt; и &lt;code&gt;CreateUri&lt;/code&gt; были свёрнуты оптимизатором (что означает, что они занимают одно и то же место в машинном коде), потому что они &lt;b&gt;побайтно идентичны&lt;/b&gt;! Они оба реализуют логику &quot;вызови метод с одним параметром по индексу 6 из VMT объекта&quot;. Для &lt;code&gt;IUriRuntimeClassFactory&lt;/code&gt; этим методом будет &lt;code&gt;CreateUri&lt;/code&gt; со строковым параметром, а для &lt;code&gt;IVector&lt;/code&gt; этот метод — &lt;code&gt;GetAt&lt;/code&gt;, а параметр — индекс.&lt;br /&gt;
&lt;br /&gt;
Благодаря этому объяснению клиент понял, что у него действительно была нерешенная проблема, которая гласила: &quot;Если наш файл настроек поврежден, то у нас не будет групп&quot;, а это исключение просто являлось альтернативным проявлением этой проблемы.</content><link rel='replies' type='application/atom+xml' href='http://www.transl-gunsmoker.ru/feeds/3637822773390867288/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.transl-gunsmoker.ru/2023/02/mysterious-case-out-of-bounds-CreateUri-memmove.html#comment-form' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688132520265708820/posts/default/3637822773390867288'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688132520265708820/posts/default/3637822773390867288'/><link rel='alternate' type='text/html' href='http://www.transl-gunsmoker.ru/2023/02/mysterious-case-out-of-bounds-CreateUri-memmove.html' title='Случай с загадочной ошибкой &quot;out of bounds&quot; из CreateUri и memmove'/><author><name>GunSmoker</name><uri>http://www.blogger.com/profile/15611696588191431330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhrYMBDZyhmwpngoHu4MbxoNS55hTzfYX9MRyMyiL7lk8FV4zzROyizfpqf_E85yuu8-BtGtcAZg_miWOVH_8I9hdkdBcDd5UkBhBcWo1jkcyacq_Lgcmt9gECy0zV0zNo/s220/avatar.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688132520265708820.post-7383483378852093483</id><published>2022-11-10T13:10:00.001+03:00</published><updated>2022-11-10T13:42:21.696+03:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Win32"/><category scheme="http://www.blogger.com/atom/ns#" term="Совместимость"/><category scheme="http://www.blogger.com/atom/ns#" term="кодинг"/><title type='text'>Почему x86-64 функции Windows НЕ начинаются с бессмысленной инструкции MOV EDI, EDI?</title><content type='html'>Это перевод &lt;a href=&quot;https://devblogs.microsoft.com/oldnewthing/20221109-00/?p=107373&quot; title=&quot;Why don’t Windows functions begin with a pointless MOV EDI,EDI instruction on x86-64?&quot;&gt;Why don’t Windows functions begin with a pointless MOV EDI,EDI instruction on x86-64?&lt;/a&gt; Автор: Реймонд Чен.&lt;br /&gt;
&lt;br /&gt;
Когда-то мы обсуждали, &lt;a title=&quot;Почему все функции Windows начинаются с бессмысленной инструкции MOV EDI, EDI?&quot; href=&quot;https://www.transl-gunsmoker.ru/2022/11/why-Windows-functions-begin-with-MOV-EDI-EDI.html&quot;&gt;почему все функции Windows начинаются с бессмысленной инструкции &lt;code&gt;MOV EDI, EDI&lt;/code&gt;&lt;/a&gt;. Ответ заключался в том, что эта инструкция использовалась как двухбайтовый &lt;code&gt;NOP&lt;/code&gt;, что позволяло безопасно заменить её на инструкцию перехода &lt;code&gt;JMP $-5&lt;/code&gt;, что позволяло применять различные исправления к работающей системе (такой метод не подходит для тех исправлений, которые изменяют структуры данных или включают взаимодействие между процессами).&lt;br /&gt;
&lt;br /&gt;
Но вы могли заметить, что в 64-битной Windows эти бессмысленные инструкции исчезли. Значит ли это, что метод мёртв?&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
Нет, этот способ всё ещё применяется. Но в 64-битной Windows точка для внедрения реализована иначе.&lt;br /&gt;
&lt;br /&gt;
Идея состоит в том, что нам не нужно вставлять в каждую функцию бессмысленную двухбайтовую инструкцию &lt;code&gt;NOP&lt;/code&gt;, если первая (настоящая) инструкция функции уже является двухбайтовой инструкцией (или длиннее). В этом случае эта инструкция сама по себе может служить точкой для внедрения.&lt;br /&gt;
&lt;br /&gt;
Случай, когда первая инструкция функции занимает два байта или больше, является наиболее распространенным. В x86-64 осталось всего несколько однобайтовых инструкций. Вот те, с которыми вы можете столкнуться в коде, сгенерированном компилятором пользовательского режима:
&lt;table style=&quot;border-collapse: collapse;&quot; border=&quot;1&quot; cellspacing=&quot;0&quot; cellpadding=&quot;3&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;PUSH R&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;LEAVE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;CWDE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;INT 3&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;POP R&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;RET&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;CDQ&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;NOP&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
где &lt;code&gt;R&lt;/code&gt; — 64-битная версия одного из восьми именованных (не пронумерованных) регистров.&lt;br /&gt;
&lt;br /&gt;
Некоторые из этих инструкций не могут стоять в начале функции:&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;LEAVE&lt;/code&gt; не имеет смысла, потому что она изменяет регистр, который мы (как вызываемый) должны сохранять.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CWDE&lt;/code&gt; и &lt;code&gt;CDQ&lt;/code&gt; не имеют смысла, потому что они используют &lt;code&gt;RAX&lt;/code&gt; в качестве входного регистра, но этот регистр не определен при входе в функцию.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;NOP&lt;/code&gt; можно просто опустить.&lt;/li&gt;
&lt;li&gt;Начало функции с &lt;code&gt;POP&lt;/code&gt; запрещено Win32 ABI: адрес возврата должен оставаться в стеке.&lt;/li&gt;
&lt;/ul&gt;
Остаётся всего пара инструкций, которые можно обойти:&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;PUSH&lt;/code&gt;: если функция вносит в стек какие-либо регистры &lt;code&gt;R8&lt;/code&gt; или выше, компилятор может поставить эти инструкции в начало функции, поскольку внос в стек регистра с верхним номером является двухбайтовой инструкцией. Либо же компилятор может перекодировать инструкцию с избыточным префиксом &lt;code&gt;REX 0x48&lt;/code&gt;. Наконец, в качестве альтернативы компилятор может сохранить регистр в домашнем пространстве (home space), для чего используется многобайтовая инструкция &lt;code&gt;MOV [RSP+n], R&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;RET&lt;/code&gt;: это происходит, если функция пуста и не возвращает значения. Тогда компилятор может изменить её на 3-байтовый &lt;code&gt;RET 0&lt;/code&gt; или 2-байтовый &lt;a href=&quot;https://repzret.org/p/repzret/&quot; title=&quot;repz ret&quot;&gt;&lt;code&gt;REPZ RET&lt;/code&gt;&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
Последняя оставшаяся инструкция — &lt;code&gt;INT 3&lt;/code&gt; (программная точка останова).&lt;br /&gt;
&lt;br /&gt;
Один из вариантов — использовать альтернативную двухбайтовую кодировку &lt;code&gt;CD 03&lt;/code&gt; (&lt;code&gt;INT nn&lt;/code&gt;, где &lt;code&gt;nn&lt;/code&gt; = 3). Однако код, который установил &lt;code&gt;INT 3&lt;/code&gt;, может полагаться на то, что это именно однобайтовая инструкция, потому что он намеревается исправить её с помощью однобайтового &lt;code&gt;NOP&lt;/code&gt;, или он намеревается обработать исключение &quot;точка останова&quot;, перешагнув код инструкции путём увеличения указателя инструкции на 1.&lt;br /&gt;
&lt;br /&gt;
Вместо этого компилятор перестраховывается и начинает функцию с двухбайтового &lt;code&gt;NOP&lt;/code&gt;, который закодирован как &lt;code&gt;XCHG AX, AX&lt;/code&gt; - и, действительно, отладчик Microsoft именно так его и дизассемблирует.&lt;br /&gt;
&lt;br /&gt;
Бессмысленная инструкция &lt;code&gt;MOV EDI, EDI&lt;/code&gt; исчезла. И в большинстве случаев компилятор может жонглировать командами так, что вы даже не заметите, что он устроил так, чтобы первая инструкция вашей функции была многобайтовой инструкцией. Единственный случай, когда компилятор терпит неудачу, это если первой инструкцией вашей функции является &lt;code&gt;INT 3&lt;/code&gt;, но тогда компилятор вставляет бессмысленную инструкцию &lt;code&gt;XCHG AX, AX&lt;/code&gt;, также известную как двухбайтная команда &lt;code&gt;NOP&lt;/code&gt;.</content><link rel='replies' type='application/atom+xml' href='http://www.transl-gunsmoker.ru/feeds/7383483378852093483/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.transl-gunsmoker.ru/2022/11/why-dont-Windows-functions-begin-with-MOV-EDI-EDI.html#comment-form' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688132520265708820/posts/default/7383483378852093483'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688132520265708820/posts/default/7383483378852093483'/><link rel='alternate' type='text/html' href='http://www.transl-gunsmoker.ru/2022/11/why-dont-Windows-functions-begin-with-MOV-EDI-EDI.html' title='Почему x86-64 функции Windows НЕ начинаются с бессмысленной инструкции MOV EDI, EDI?'/><author><name>GunSmoker</name><uri>http://www.blogger.com/profile/15611696588191431330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhrYMBDZyhmwpngoHu4MbxoNS55hTzfYX9MRyMyiL7lk8FV4zzROyizfpqf_E85yuu8-BtGtcAZg_miWOVH_8I9hdkdBcDd5UkBhBcWo1jkcyacq_Lgcmt9gECy0zV0zNo/s220/avatar.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688132520265708820.post-4490045019058651609</id><published>2022-11-10T12:29:00.001+03:00</published><updated>2022-11-10T13:42:01.241+03:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Win32"/><category scheme="http://www.blogger.com/atom/ns#" term="Совместимость"/><category scheme="http://www.blogger.com/atom/ns#" term="кодинг"/><title type='text'>Почему все функции Windows начинаются с бессмысленной инструкции MOV EDI, EDI?</title><content type='html'>Это перевод &lt;a href=&quot;https://devblogs.microsoft.com/oldnewthing/20110921-00/?p=9583&quot; title=&quot;Why do Windows functions all begin with a pointless MOV EDI, EDI instruction?&quot;&gt;Why do Windows functions all begin with a pointless MOV EDI, EDI instruction?&lt;/a&gt; Автор: Реймонд Чен.&lt;br /&gt;
&lt;br /&gt;
Если вы посмотрите на ассемблерный код функций внутри DLL Windows, вы обнаружите, что они начинаются с бессмысленной инструкции &lt;code&gt;MOV EDI, EDI&lt;/code&gt;. Эта инструкция копирует регистр в самого себя и не обновляет флаги, что совершенно не имеет смысла. Тогда зачем она там?&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhqBimmcTe4dw5jot1BjfildkJjCnguqbMJcvm010z-QeomA-iqjByIEyCnOcygfXpA210h5kY3E86fODm3jZUgAUs2TJGsWPQHAQl4nb5KNMVFzAlINSxOzP8zh586v0ZnxtEpfDTi7UpFLbMSmtZCQxaFojjNnvAK25u6WTX6w-hNxmG2jamv5EVmjQ/s1600/%D0%A1%D0%BD%D0%B8%D0%BC%D0%BE%D0%BA.PNG&quot; style=&quot;display: block; padding: 1em 0; text-align: center; &quot;&gt;&lt;img alt=&quot;&quot; border=&quot;0&quot; data-original-height=&quot;364&quot; data-original-width=&quot;699&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhqBimmcTe4dw5jot1BjfildkJjCnguqbMJcvm010z-QeomA-iqjByIEyCnOcygfXpA210h5kY3E86fODm3jZUgAUs2TJGsWPQHAQl4nb5KNMVFzAlINSxOzP8zh586v0ZnxtEpfDTi7UpFLbMSmtZCQxaFojjNnvAK25u6WTX6w-hNxmG2jamv5EVmjQ/s1600/%D0%A1%D0%BD%D0%B8%D0%BC%D0%BE%D0%BA.PNG&quot;/&gt;&lt;/a&gt;&lt;/div&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
Это &lt;a title=&quot;Why does the compiler generate a MOV EDI, EDI instruction at the beginning of functions?&quot; href=&quot;https://docs.microsoft.com/en-us/archive/blogs/ishai/why-does-the-compiler-generate-a-mov-edi-edi-instruction-at-the-beginning-of-functions&quot;&gt;точка для внедрения кода&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
Инструкция &lt;code&gt;MOV EDI, EDI&lt;/code&gt; представляет собой двухбайтовый &lt;code&gt;NOP&lt;/code&gt; (операция, которая ничего не делает). Двух байт как раз достаточно для вставки инструкции перехода, чтобы функцию можно было обновить на лету. Предполагается, что инструкция &lt;code&gt;MOV EDI, EDI&lt;/code&gt; будет заменена двухбайтовой инструкцией &lt;code&gt;JMP $-5&lt;/code&gt; (перейти по адресу, записанному на пять байт выше) для передачи управления на пять байтов из так называемого &quot;patch space&quot; (места для патчей), которое вставляется непосредственно перед началом функции. Пять байтов достаточно для полной инструкции перехода, которая может передать управление на любую функцию где-то ещё в адресном пространстве.&lt;br /&gt;
&lt;br /&gt;
Хотя пять байтов из места для патчей до начала функции состоят из пяти однобайтовых инструкций &lt;code&gt;NOP&lt;/code&gt;, точка входа функции использует одну двухбайтовую &lt;code&gt;NOP&lt;/code&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Почему бы не использовать Detours для оперативного обновления функции, тогда место для патчей будет вообще не нужно.&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
Проблема с Detours во время выполнения программы заключается в том, что вы никогда не можете быть уверены, что в момент внесения исправлений Detours другой поток прямо сейчас не выполняет инструкцию, затрагивающую первые пять байтов функции (и вам нужно програрантировать, чтобы в программе не было переходов на эти инструкции, начинающаяся со смещения с 1 по 4). Вы можете захотеть попробовать обойти проблему, приостановив все потоки во время исправления, но это все ещё не помешает кому-либо выполнить &lt;code&gt;CreateRemoteThread&lt;/code&gt; после того, как вы решили, что успешно приостановили все потоки.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Почему бы просто не использовать две инструкции &lt;code&gt;NOP&lt;/code&gt; в точке входа?&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
Ну, поскольку инструкция &lt;code&gt;NOP&lt;/code&gt; использует один тактовый цикл и один конвейер, поэтому две из них будут использовать два такта и два конвейера (инструкции, скорее всего, будут парными, по одной в каждом канале, поэтому совместное выполнение займет один такт). С другой стороны, инструкция &lt;code&gt;MOV EDI, EDI&lt;/code&gt; использует один тактовый цикл и один канал (т.е. на практике инструкция занимает один канал, а другой остается доступным для параллельного выполнения другой инструкции. Можно сказать, что инструкция выполняется за половину цикла). Как бы вы ни прикидывали, инструкция &lt;code&gt;MOV EDI, EDI&lt;/code&gt; выполняется в два раза быстрее двух инструкций &lt;code&gt;NOP&lt;/code&gt;.&lt;br /&gt;
&lt;br /&gt;
С другой стороны, пять &lt;code&gt;NOP&lt;/code&gt;, вставленных перед началом функции (из места для патчей), никогда не выполняются, поэтому не имеет значения, что именно вы используете для заполнения. Это могли быть пять любых произвольных (мусорных) байт, кому какое дело.&lt;br /&gt;
&lt;br /&gt;
Но гораздо важнее, чем подсчет циклов, является то, что использование двухбайтового &lt;code&gt;NOP&lt;/code&gt; позволяет избежать вышеупомянутой проблемой с Detours: если в коде использовались бы две однобайтовые инструкции &lt;code&gt;NOP&lt;/code&gt;, то есть риск, что вы установите свой патч в то же самое время когда поток завершает выполнение первого однобайтового &lt;code&gt;NOP&lt;/code&gt; и собирается начать выполнение второго однобайтового &lt;code&gt;NOP&lt;/code&gt;, в результате чего поток будет трактовать вторую половину вашего &lt;code&gt;JMP $-5&lt;/code&gt; как начало новой инструкции.&lt;br /&gt;
&lt;br /&gt;
В Windows происходит множество патчей функций во время работы, о которых большинство людей даже не подозревают. Возможно, когда-нибудь я напишу о том, как операционная система управляет исправлениями для еще не установленных программ, так что, когда вы устанавливаете программу, патч уже будет стоять, тем самым закрывая окно уязвимости между установкой программы и применения патча.&lt;br /&gt;
&lt;br /&gt;
Читать далее: &lt;a title=&quot;Почему x86-64 функции Windows НЕ начинаются с бессмысленной инструкции MOV EDI, EDI?&quot; href=&quot;https://www.transl-gunsmoker.ru/2022/11/why-dont-Windows-functions-begin-with-MOV-EDI-EDI.html&quot;&gt;Почему x86-64 функции Windows &lt;b&gt;НЕ&lt;/b&gt; начинаются с бессмысленной инструкции MOV EDI, EDI?&lt;/a&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.transl-gunsmoker.ru/feeds/4490045019058651609/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.transl-gunsmoker.ru/2022/11/why-Windows-functions-begin-with-MOV-EDI-EDI.html#comment-form' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688132520265708820/posts/default/4490045019058651609'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688132520265708820/posts/default/4490045019058651609'/><link rel='alternate' type='text/html' href='http://www.transl-gunsmoker.ru/2022/11/why-Windows-functions-begin-with-MOV-EDI-EDI.html' title='Почему все функции Windows начинаются с бессмысленной инструкции MOV EDI, EDI?'/><author><name>GunSmoker</name><uri>http://www.blogger.com/profile/15611696588191431330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhrYMBDZyhmwpngoHu4MbxoNS55hTzfYX9MRyMyiL7lk8FV4zzROyizfpqf_E85yuu8-BtGtcAZg_miWOVH_8I9hdkdBcDd5UkBhBcWo1jkcyacq_Lgcmt9gECy0zV0zNo/s220/avatar.png'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhqBimmcTe4dw5jot1BjfildkJjCnguqbMJcvm010z-QeomA-iqjByIEyCnOcygfXpA210h5kY3E86fODm3jZUgAUs2TJGsWPQHAQl4nb5KNMVFzAlINSxOzP8zh586v0ZnxtEpfDTi7UpFLbMSmtZCQxaFojjNnvAK25u6WTX6w-hNxmG2jamv5EVmjQ/s72-c/%D0%A1%D0%BD%D0%B8%D0%BC%D0%BE%D0%BA.PNG" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688132520265708820.post-1983330890731382169</id><published>2022-07-04T14:14:00.002+03:00</published><updated>2022-07-04T14:15:28.543+03:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="История"/><category scheme="http://www.blogger.com/atom/ns#" term="забавно"/><title type='text'>Рано или поздно особенное перестанет быть особенным</title><content type='html'>Это перевод &lt;a href=&quot;https://devblogs.microsoft.com/oldnewthing/20081006-00/?p=20643&quot; title=&quot;Eventually, nothing is special any more&quot;&gt;Eventually, nothing is special any more&lt;/a&gt;. Автор: Реймонд Чен.&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;http://blogs.msdn.com/oldnewthing/archive/2007/07/27/4072156.aspx#4112818&quot;&gt;Комментатор ulric предположил&lt;/a&gt;, что должны существовать две функции для получения &quot;текущего&quot; окна: одна для обычного повседневного использования и одна для &quot;специального использования&quot;, когда вы хотите взаимодействовать с окнами вне вашего процесса.
&lt;blockquote&gt;Однако мне было бы проще, если бы поведение API по умолчанию заключалось в том, чтобы возвращать &lt;code&gt;HWND&lt;/code&gt; только для текущего процесса, а приложения, которым действительно нужен &lt;code&gt;HWND&lt;/code&gt; из (потенциально) других процессов, должны использовать другой API, который специально создан только для этого.&lt;/blockquote&gt;
Это отличный пример &lt;a href=&quot;http://blogs.msdn.com/oldnewthing/archive/2007/04/03/2014992.aspx#2035078&quot;&gt;предложения того, что Windows уже делает&lt;/a&gt;! Специальная функция стала настолько не особенной, что вы даже не понимаете, что она особенная.&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
Изначально в 16-битной Windows функцией получения &quot;текущего&quot; окна была &lt;code&gt;&lt;a title=&quot;MSDN: GetActiveWindow function (winuser.h)&quot; href=&quot;https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getactivewindow&quot;&gt;GetActiveWindow&lt;/a&gt;&lt;/code&gt;. Она получала активное окно всей системы. Однако одно из основных изменений Win32 - это введение модели асинхронного ввода, при которой окна из разных очередей ввода имеют независимый ввод. Таким образом, одна программа, которая перестала реагировать на ввод, не блокирует ввод других несвязанных окон. Поэтому Win32 изменила значение &lt;code&gt;GetActiveWindow&lt;/code&gt;, чтобы она возвращала &lt;i&gt;активное окно из текущей очереди ввода&lt;/i&gt;.&lt;br /&gt;
&lt;br /&gt;
В 16-битной Windows была только одна входная очередь, глобальная. В 32-разрядной версии Windows каждый поток (или группа потоков, присоединенных к вводу) получает собственную очередь ввода.&lt;br /&gt;
&lt;br /&gt;
В результате такого изменения, когда программа переносилась из 16-битной Windows в 32-битную Windows, она не &quot;видела&quot; окна из других программ, когда вызывала такие функции, как &lt;code&gt;GetFocus&lt;/code&gt; или &lt;code&gt;GetActiveWindow&lt;/code&gt;. &lt;a href=&quot;https://channel9.msdn.com/Showpost.aspx?postid=116704&quot;&gt;Как должен знать каждый программист Win32&lt;/a&gt;, эти состояния являются локальными для вашей входной очереди.&lt;br /&gt;
&lt;br /&gt;
Хорошо, давайте посмотрим, что у нас есть сейчас. &lt;code&gt;GetFocus&lt;/code&gt; и &lt;code&gt;GetActiveWindow&lt;/code&gt; сообщают вам статус вашей входной очереди. Другими словами, в однопоточной программе (которая в 16-битной Windows является единственным типом программ) вызов &lt;code&gt;GetActiveWindow&lt;/code&gt; дает вам активное окно из вашей программы. Он не возвращает активное окно из другой программы.¹ Все именно так, как предложил ulric!&lt;br /&gt;
&lt;br /&gt;
Теперь давайте посмотрим на вторую половину предложения. Если программе действительно нужно получить окно из (потенциально) других процессов, ей придется использовать какую-то другую функцию, специально предназначенную именно для этого. И действительно, такая функция была добавлена: это функция &lt;code&gt;GetForegroundWindow&lt;/code&gt;. Функция &lt;code&gt;GetForegroundWindow&lt;/code&gt; - &lt;b&gt;это и есть та самая специальная функция, предназначенная для получения окон из других процессов&lt;/b&gt;.&lt;br /&gt;
&lt;br /&gt;
Поэтому мы сделали именно то, что рекомендовал ulric - и всё равно получился бардак. Почему?&lt;br /&gt;
&lt;br /&gt;
Потому что особенное не останется особенным надолго.&lt;br /&gt;
&lt;br /&gt;
Это может занять некоторое время, но в конце концов люди обнаруживают, что обычная функция &quot;не работает&quot; (здесь &quot;не работает&quot; может означать что угодно), и обращаются за помощью: &quot;Когда я вызываю &lt;code&gt;GetActiveWindow&lt;/code&gt;, я не получаю глобальное активное окно; мне возвращается окно моего приложения. Как мне получить глобальное?&quot; На самом деле, они, вероятно, даже не формулируют вопрос так чётко. Скорее, это больше похоже на: &quot;Я хочу получить активное окно, но &lt;code&gt;GetActiveWindow&lt;/code&gt; не работает&quot;.&lt;br /&gt;
&lt;br /&gt;
А потом кто-то отвечает: &quot;Да, &lt;code&gt;GetActiveWindow&lt;/code&gt; не работает. Я обнаружил, что &lt;code&gt;GetForegroundWindow&lt;/code&gt; работает намного лучше&quot;.&lt;br /&gt;
&lt;br /&gt;
Затем следует ответ: &quot;Вау, отлично работает! Спасибо!&quot;&lt;br /&gt;
&lt;br /&gt;
В конце концов, даже на улице говорят: &quot;&lt;code&gt;GetActiveWindow&lt;/code&gt; не работает. Используйте вместо неё &lt;code&gt;GetForegroundWindow&lt;/code&gt;&quot;. Вскоре люди используют её для всего: полируют машину, успокаивают ребенка или улучшают свою сексуальную привлекательность.&lt;br /&gt;
&lt;br /&gt;
То, что раньше было функцией, которую можно использовать &quot;только в тех редких случаях, когда она вам действительно нужна&quot;, стало &quot;фактической функцией, которая выполняет свою работу&quot;.&lt;br /&gt;
&lt;br /&gt;
На самом деле немодность активного окна (active window) дошла до того, что люди вообще перестали называть его активным окном! Вместо этого они &lt;a href=&quot;http://blogs.msdn.com/oldnewthing/archive/2008/09/22/8960761.aspx#8961210&quot;&gt;называют его окном переднего плана текущего процесса (foreground window from the current process)&lt;/a&gt;. Это всё равно, что называть наземную линию &quot;проводным сотовым телефоном&quot;.&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;http://blogs.msdn.com/oldnewthing/archive/2007/07/27/4072156.aspx#4133892&quot;&gt;Требование нового флага для получения специального поведения&lt;/a&gt; ничего не меняет. Это та же история, только с другими именами персонажей. &quot;&lt;code&gt;GetFocalWindow&lt;/code&gt;² не работает, если вы не передадите флаг &lt;code&gt;GFW_CROSSPROCESS&lt;/code&gt;&quot;. Скоро все будут передавать &lt;code&gt;GFW_CROSSPROCESS&lt;/code&gt; не потому, что понимают, что он делает, а просто потому, что &quot;мне так сказали делать&quot; и &quot;ничего не заработает, если я его не передаду&quot;.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Примечания&lt;/b&gt;&lt;br /&gt;
¹ Предполагая, что вы не пытаетесь присоединить свой поток к входной очереди какой-либо другой программы. Это довольно безопасное предположение, поскольку функция &lt;code&gt;AttachThreadInput&lt;/code&gt; не существовала в 16-разрядной версии Windows.&lt;br /&gt;
&lt;br /&gt;
² &lt;code&gt;GetFocalWindow&lt;/code&gt; - это воображаемая функция, созданная для примера.</content><link rel='replies' type='application/atom+xml' href='http://www.transl-gunsmoker.ru/feeds/1983330890731382169/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.transl-gunsmoker.ru/2022/07/eventually-nothing-is-special-any-more.html#comment-form' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688132520265708820/posts/default/1983330890731382169'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688132520265708820/posts/default/1983330890731382169'/><link rel='alternate' type='text/html' href='http://www.transl-gunsmoker.ru/2022/07/eventually-nothing-is-special-any-more.html' title='Рано или поздно особенное перестанет быть особенным'/><author><name>GunSmoker</name><uri>http://www.blogger.com/profile/15611696588191431330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhrYMBDZyhmwpngoHu4MbxoNS55hTzfYX9MRyMyiL7lk8FV4zzROyizfpqf_E85yuu8-BtGtcAZg_miWOVH_8I9hdkdBcDd5UkBhBcWo1jkcyacq_Lgcmt9gECy0zV0zNo/s220/avatar.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688132520265708820.post-7840538451891876940</id><published>2022-06-01T13:30:00.007+03:00</published><updated>2022-06-01T13:33:17.611+03:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Совместимость"/><title type='text'>Теперь элемент манифеста activeCodePage можно использовать для установки не только UTF-8</title><content type='html'>Это перевод &lt;a href=&quot;https://devblogs.microsoft.com/oldnewthing/20220531-00/?p=106697&quot; title=&quot;The activeCodePage manifest element can be used for more than just setting UTF-8 as the active code page&quot;&gt;The activeCodePage manifest element can be used for more than just setting UTF-8 as the active code page&lt;/a&gt;. Автор: Реймонд Чен.&lt;br /&gt;
&lt;br /&gt;
В Windows 10 версии 1903 появилось новое свойство манифеста под названием &lt;code&gt;activeCodePage&lt;/code&gt;, которое можно использовать для &lt;a href=&quot;https://docs.microsoft.com/en-us/windows/apps/design/globalizing/use-utf8-code-page#set-a-process-code-page-to-utf-8&quot; title=&quot;MSDN: Use UTF-8 code pages in Windows apps - Set a process code page to UTF-8&quot;&gt;установки кодовой страницы процесса в UTF-8&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
Начиная с Windows Server 2022 (и Windows 11)¹ &lt;a href=&quot;https://docs.microsoft.com/en-us/windows/win32/sbscs/application-manifests#activecodepage&quot; title=&quot;MSDN: Application Manifests - activeCodePage&quot;&gt;вы также можете использовать это свойство для выбора кодовых страниц, отличных от UTF-8&lt;/a&gt;, в манифестах неупакованных приложений².&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;https://en.wikipedia.org/wiki/AppLocale&quot; title=&quot;Wiki: AppLocale&quot;&gt;AppLocale&lt;/a&gt; наконец-то повзрослел!&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
Если вы указываете &lt;code&gt;Legacy&lt;/code&gt; в &lt;code&gt;activeCodePage&lt;/code&gt;, то этим вы говорите, что ваше приложение &quot;активно ненавидит&quot; UTF-8. В таком случае ему будет присвоена системная локаль по умолчанию - при условии, что она существует и не является UTF-8. В противном (исключительном) случае вы получите кодовую страницу 1252 в качестве запасного варианта.&lt;br /&gt;
&lt;br /&gt;
Вы также можете указать имя локали, например &lt;code&gt;en-US&lt;/code&gt;, &lt;code&gt;ru-RU&lt;/code&gt; или &lt;code&gt;ja-JP&lt;/code&gt;, и приложение получит кодовую страницу, соответствующую этой локали. Если локаль не распознается системой, вы получите UTF-8 в качестве запасного варианта.&lt;br /&gt;
&lt;br /&gt;
Этот трюк наиболее полезен для целей совместимости приложений: если у вас есть какое-то устаревшее приложение, которое предполагает определенную локаль, вы можете развернуть манифест рядом с приложением, который говорит: «Мне все равно, какую локаль использует система. Дайте этому приложению языковой стандарт, который он хочет».&lt;br /&gt;
&lt;br /&gt;
Вот сводная таблица, так как я знаю, что люди любят сводные таблицы.&lt;br /&gt;
&lt;br /&gt;
&lt;table class=&quot;cp3&quot; style=&quot;border-collapse: collapse;&quot; border=&quot;1&quot; cellspacing=&quot;0&quot; cellpadding=&quot;3&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;th&gt;&lt;code&gt;activeCodePage&lt;/code&gt; = &lt;/th&gt;
&lt;td&gt;&lt;code&gt;UTF-8&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;имя локали&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Legacy&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th&gt;Регистр важен?&lt;/th&gt;
&lt;td&gt;Да&lt;/td&gt;
&lt;td&gt;Нет&lt;/td&gt;
&lt;td&gt;Да&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th valign=&quot;top&quot;&gt;Предпочтительный результат&lt;/th&gt;
&lt;td valign=&quot;top&quot;&gt;UTF-8&lt;/td&gt;
&lt;td valign=&quot;top&quot;&gt;Указанная локаль,&lt;br /&gt;
если доступна&lt;/td&gt;
&lt;td valign=&quot;top&quot;&gt;Системная локаль,&lt;br /&gt;
если доступна,&lt;br /&gt;
но не UTF-8&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th&gt;Запасной вариант&lt;/th&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;td&gt;UTF-8&lt;/td&gt;
&lt;td&gt;1252&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th&gt;Packaged-приложение&lt;/th&gt;
&lt;td&gt;Поддерживается&lt;/td&gt;
&lt;td&gt;Не поддерживается&lt;/td&gt;
&lt;td&gt;Не поддерживается&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th&gt;Unpackaged-приложение&lt;/th&gt;
&lt;td&gt;Поддерживается&lt;/td&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;
¹ В частности, сборка 20348 и более поздняя версия, которая также включает HoloLens 2 - также известную как Windows Holographic версии 21H1.&lt;br /&gt;
&lt;br /&gt;
² Кодовое название манифестов неупакованных приложений³ было &quot;fusion&quot;, поэтому в документации вы можете видеть термин &quot;fusion-манифесты&quot;.&lt;br /&gt;
&lt;br /&gt;
³ В то время их не называли &quot;манифестами неупакованных приложений&quot;, потому что, когда писался проект fusion, у нас вообще не было упакованных приложений. Их называли просто &quot;манифестами&quot;. Позже, когда для упакованных приложений были введены манифесты, для описания fusion-манифестов был изобретен ретроним &quot;манифесты неупакованных приложений&quot;.</content><link rel='replies' type='application/atom+xml' href='http://www.transl-gunsmoker.ru/feeds/7840538451891876940/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.transl-gunsmoker.ru/2022/06/activecodepage-utf-8.html#comment-form' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688132520265708820/posts/default/7840538451891876940'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688132520265708820/posts/default/7840538451891876940'/><link rel='alternate' type='text/html' href='http://www.transl-gunsmoker.ru/2022/06/activecodepage-utf-8.html' title='Теперь элемент манифеста activeCodePage можно использовать для установки не только UTF-8'/><author><name>GunSmoker</name><uri>http://www.blogger.com/profile/15611696588191431330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhrYMBDZyhmwpngoHu4MbxoNS55hTzfYX9MRyMyiL7lk8FV4zzROyizfpqf_E85yuu8-BtGtcAZg_miWOVH_8I9hdkdBcDd5UkBhBcWo1jkcyacq_Lgcmt9gECy0zV0zNo/s220/avatar.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688132520265708820.post-3083276792526251</id><published>2022-05-24T06:35:00.000+03:00</published><updated>2022-05-24T06:35:12.042+03:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Win32"/><category scheme="http://www.blogger.com/atom/ns#" term="кодинг"/><title type='text'>Как Windows определяет, должно ли вновь созданное окно использовать ориентацию LTR или RTL?</title><content type='html'>Это перевод &lt;a href=&quot;https://devblogs.microsoft.com/oldnewthing/20220523-00/?p=106680&quot; title=&quot;How does Windows decide whether a newly-created window should use LTR or RTL layout?&quot;&gt;How does Windows decide whether a newly-created window should use LTR or RTL layout?&lt;/a&gt; Автор: Реймонд Чен.&lt;br /&gt;
&lt;br /&gt;
В Extended стилях окна можно указать, будет ли оно отображаться слева направо (Left-To-Right - LTR) или справа налево (Right To Left - RTL). Раскладка справа налево используется в языках с написанием справа налево - из которых сегодня наиболее широко используются, вероятно, арабский и иврит. Вы можете попросить Windows сделать окно с ориентацией RTL, установив Extended-стиль &lt;code&gt;WS_&lt;wbr /&gt;EX_&lt;wbr /&gt;LAYOUTRTL&lt;/code&gt;.&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
Если вы не укажете Extended-стиль &lt;code&gt;WS_&lt;wbr /&gt;EX_&lt;wbr /&gt;LAYOUTRTL&lt;/code&gt;, система всё равно может применить этот стиль автоматически на основе следующих правил:&lt;br /&gt;
&lt;br /&gt;
&lt;table class=&quot;cp3&quot; style=&quot;border-collapse: collapse;&quot; border=&quot;1&quot; cellspacing=&quot;0&quot; cellpadding=&quot;3&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;th colspan=&quot;2&quot;&gt;Сценарий&lt;/th&gt;
&lt;th&gt;Правило&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td rowspan=&quot;2&quot;&gt;Дочернее окно&lt;br /&gt;(child window)&lt;/td&gt;
&lt;td&gt;Родитель не указывает &lt;code&gt;WS_&lt;wbr /&gt;EX_&lt;wbr /&gt;NO­INHERIT­LAYOUT&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Наследует &lt;code&gt;WS_&lt;wbr /&gt;EX_&lt;wbr /&gt;LAYOUTRTL&lt;/code&gt; от родителя.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Родитель указывает &lt;code&gt;WS_&lt;wbr /&gt;EX_&lt;wbr /&gt;NO­INHERIT­LAYOUT&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Остаётся LTR.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td rowspan=&quot;2&quot;&gt;Окно верхнего уровня&lt;br /&gt;(top-level window)&lt;/td&gt;
&lt;td&gt;Есть владелец (owned)&lt;/td&gt;
&lt;td&gt;Остаётся LTR.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Нет владельца&lt;/td&gt;
&lt;td&gt;Согласно &lt;code&gt;Get­Process­Default­Layout&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;br /&gt;
В случае окна верхнего уровня без владельца Extended-стиль &lt;code&gt;WS_&lt;wbr /&gt;EX_&lt;wbr /&gt;LAYOUTRTL&lt;/code&gt; устанавливается автоматически, если в настройках по умолчанию процесса установлен бит &lt;code&gt;LAYOUT_&lt;wbr /&gt;RTL&lt;/code&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;a title=&quot;Whether your application should display its content in RTL should be based on the content&quot; href=&quot;https://devblogs.microsoft.com/oldnewthing/20131218-00/?p=2333&quot;&gt;Как я заметил некоторое время назад&lt;/a&gt;, если процесс ни разу не вызывает функцию &lt;code&gt;Set­Process­Default­Layout&lt;/code&gt;, то начальные настройки процесса определяются путём проверки свойства версии &lt;code&gt;File­Description&lt;/code&gt; основного исполняемого файла: &lt;a href=&quot;http://web.archive.org/web/20090101225524/http://msdn.microsoft.com/en-us/goglobal/bb688119.aspx#EDC&quot;&gt;если оно начинается с двух меток LRM&lt;/a&gt; (Left-To-Right Mark, это кодовая точка Unicode U+200E), то в настройки процесса добавляется &lt;code&gt;LAYOUT_&lt;wbr /&gt;RTL&lt;/code&gt;.&lt;br /&gt;
&lt;br /&gt;
Ранее я также отметил, что вы можете &lt;a title=&quot;How do I determine programmatically whether a particular language is LTR or RTL?&quot; href=&quot;https://devblogs.microsoft.com/oldnewthing/20111222-00/?p=8833&quot;&gt;запросить у &lt;code&gt;Get­Locale­Info­Ex&lt;/code&gt; свойство &lt;code&gt;LOCALE_&lt;wbr /&gt;IREADING­LAYOUT&lt;/code&gt;&lt;/a&gt;, чтобы определить, является ли какой-либо конкретный язык LTR или RTL.&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;// Направление:
// 0 = слева направо (например, английский, русский и т.п.)
// 1 = справаналево (например, арабский)
// 2 = сверху вниз, справа налево (например, классический китайский)
// 3 = свурху вниз, слева направо (например, монгольский)

function GetLanguageReadingLayout(const ALanguageName: String): Integer;
begin
  Win32Check(Boolean(
    GetLocaleInfoEx(ALanguageName,
                    LOCALE_IREADINGLAYOUT or LOCALE_RETURN_NUMBER,
                    PWideChar(@Result),
                    SizeOf(Result) div SizeOf(Char))));
end;

function GetSystemDefaultLanguageReadingLayout: Integer;
begin
  Result := GetLanguageReadingLayout(LOCALE_NAME_SYSTEM_DEFAULT);
end;

function GetUserDefaultLanguageReadingLayout: Integer;
begin
  Result := GetLanguageReadingLayout(LOCALE_NAME_USER_DEFAULT);
end;&lt;/pre&gt;
Но обычно вас интересует основной язык для текущего потока, так как именно он больше всего влияет на языковые ресурсы, используемые вашей программой. Вы можете использовать &lt;code&gt;Get­Thread­Preferred­UI­Languages&lt;/code&gt;, чтобы получить все языки, применимые к текущему потоку, а затем передать первый язык в нашу вспомогательную функцию &lt;code&gt;Get­Language­Reading­Layout&lt;/code&gt;. Вызов функции &lt;code&gt;Get­Thread­Preferred­UI­Languages&lt;/code&gt; немного разочаровывает, потому что список применимых языков может меняться асинхронно (если другой поток вызывает &lt;code&gt;Set­Process­Preferred­UI­Languages&lt;/code&gt;). Это вдвойне раздражает, потому что вспомогательная функция &lt;code&gt;Adapt­Fixed­Size­To­Allocated­Result&lt;/code&gt; работает с нуль-терминированными строками и не поддерживает строки, заканчивающиеся двойным нулём, поэтому придётся писать цикл вручную:&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;// Возвращает строку вида
// &#39;ru-RU&#39;#0&#39;ru&#39;#0&#39;en-US&#39;#0&#39;en&#39;#0#0
function GetThreadPreferredUILanguages(const AFlags: DWORD): String;
var
  Count, BufferSize: Integer;
  Error: Cardinal;
begin
  Count := 0;
  BufferSize := 0;
  SetLastError(0);
  Win32Check(WinAPI.Windows.GetThreadPreferredUILanguages(AFlags, @Count, nil, @BufferSize));
  repeat
    SetLength(Result, BufferSize);
    if WinAPI.Windows.GetThreadPreferredUILanguages(AFlags, @Count, PChar(Result), @BufferSize) then
      Error := 0
    else
      Error := GetLastError;
  until Error &lt;&gt; ERROR_INSUFFICIENT_BUFFER;
  Win32Check(Error = 0);
end;&lt;/pre&gt;
Теперь мы можем подключить это к нашей существующей функции:&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;function GetDefaultThreadLanguageReadingLayout: Integer;
begin
  Result := GetLanguageReadingLayout(PChar(GetThreadPreferredUILanguages(MUI_LANGUAGE_NAME or MUI_UI_FALLBACK)));
end;&lt;/pre&gt;
Извините, что получилось так сложно.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Бонусная болтовня&lt;/b&gt;: вся эта логика предполагает, что ваша программа изначально была переведена на язык RTL. Если ваша программа предназначена только для английского/русского языка, &lt;a title=&quot;Whether your application should display its content in RTL should be based on the content&quot; href=&quot;https://devblogs.microsoft.com/oldnewthing/20131218-00/?p=2333&quot;&gt;не отображайте английские/русские строки в RTL&lt;/a&gt;. Как я отметил в этой статье, вы можете добавлять метки в строковые ресурсы, чтобы указать в каком направлении ресурсы ожидают чтения строк.</content><link rel='replies' type='application/atom+xml' href='http://www.transl-gunsmoker.ru/feeds/3083276792526251/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.transl-gunsmoker.ru/2022/05/how-Windows-decide-LTR-RTL-layout.html#comment-form' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688132520265708820/posts/default/3083276792526251'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688132520265708820/posts/default/3083276792526251'/><link rel='alternate' type='text/html' href='http://www.transl-gunsmoker.ru/2022/05/how-Windows-decide-LTR-RTL-layout.html' title='Как Windows определяет, должно ли вновь созданное окно использовать ориентацию LTR или RTL?'/><author><name>GunSmoker</name><uri>http://www.blogger.com/profile/15611696588191431330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhrYMBDZyhmwpngoHu4MbxoNS55hTzfYX9MRyMyiL7lk8FV4zzROyizfpqf_E85yuu8-BtGtcAZg_miWOVH_8I9hdkdBcDd5UkBhBcWo1jkcyacq_Lgcmt9gECy0zV0zNo/s220/avatar.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688132520265708820.post-3189237126748988177</id><published>2021-10-15T17:39:00.003+03:00</published><updated>2021-10-15T17:42:59.318+03:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="кодинг"/><title type='text'>Что означает поле SizeOfImage в структуре MODULEINFO?</title><content type='html'>Это перевод &lt;a href=&quot;https://devblogs.microsoft.com/oldnewthing/20211015-00/?p=105802&quot; title=&quot;What does the SizeOfImage mean in the MODULEINFO structure?&quot;&gt;What does the &lt;code&gt;SizeOfImage&lt;/code&gt; mean in the &lt;code&gt;MODULEINFO&lt;/code&gt; structure?&lt;/a&gt; Автор: Реймонд Чен.&lt;br /&gt;
&lt;br /&gt;
У одного клиента была программа с поддержкой плагинов, и клиент хотел провести анализ занимаемой плагинами памяти. Клиент уже умел отслеживать выделение динамической памяти плагинами (поскольку его модель плагинов была очень ограниченной), но его интересовал объём памяти, занимаемый кодом модуля и сегментами статических данных. Клиент получал информацию о плагине, вызывая &lt;a title=&quot;MSDN: GetModuleInformation Function&quot; href=&quot;https://docs.microsoft.com/en-us/windows/win32/api/psapi/nf-psapi-getmoduleinformation&quot;&gt;функцию &lt;code&gt;GetModuleInformation&lt;/code&gt;&lt;/a&gt; и просматривая поле &lt;code&gt;SizeOfImage&lt;/code&gt;, но ему нужна была помощь в интерпретации этого значения. Будет ли &lt;code&gt;SizeOfImage&lt;/code&gt; учитывать, например, большой статический массив с инициализацией нулями? Клиент знал, что обнулённые статические данные обычно не занимают места в самом файле, но добавляются ли они в &lt;code&gt;SizeOfImage&lt;/code&gt;? И вообще, &lt;code&gt;SizeOfImage&lt;/code&gt; - это размер чего именно?&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
Вспомните, что структура &lt;code&gt;MODULEINFO&lt;/code&gt; формируется загрузчиком ОС, а задача загрузчика - управлять модулями, загруженными процессом. &lt;code&gt;SizeOfImage&lt;/code&gt; в паре с &lt;code&gt;lpBaseOfDll&lt;/code&gt; описывают диапазон виртуальных адресов, который занимает образ модуля (DLL) после загрузки с диска. Загрузчик использует эту информацию для различных целей - в том числе для ответа на флаг &lt;code&gt;GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS&lt;/code&gt;, передаваемый в функцию &lt;code&gt;GetModuleHandleEx&lt;/code&gt;.&lt;br /&gt;
&lt;br /&gt;
Размер модуля в виртуальном адресном пространстве включает в себя его код, статические данные и ресурсы. В целом, он может быть как больше, так и меньше размера файла: файл на диске может содержать байты, которые вообще не отображаются в адресное пространство - например, сертификат цифровой подписи или любую дополнительную полезную нагрузку, добавленную в конец файла. С другой стороны, в виртуальном адресном пространстве есть вещи, которых нет в файле - например, данные с нулевой инициализацией или межсекционное заполнение (padding). Модуль вообще может даже намеренно разметить свое адресное пространство с промежутками между разделами, превышающими минимум, необходимый для поддержания выравнивания.&lt;br /&gt;
&lt;br /&gt;
Если вы хотите измерить объём памяти, занимаемый плагином, вам необходимо принять все эти моменты во внимание. Хотите ли вы подсчитывать данные для чтения/записи отдельно от кода, ресурсов и других данных только для чтения? Не всякий код, ресурсы и данные из виртуального адресного пространства будут в файле.&lt;br /&gt;
&lt;br /&gt;
Решение о том, какие разделы считать, а какие нет - это вопрос, на который можете ответить только вы. В этом вам может помочь сам модуль, в заголовке которого будут поля &lt;code&gt;SizeOfCode&lt;/code&gt;, &lt;code&gt;SizeOfData&lt;/code&gt; и &lt;code&gt;SizeOfUninitializedData&lt;/code&gt;, что избавит вас от необходимости вычислять эти значения самостоятельно. Они охватывают код, данные (любые: и для чтения и для чтения/записи) и данные с нулевой инициализацией соответственно. Эти значения могут стать отправной точкой, если вас не интересуют таблица перемещений (relocation table), ресурсы или таблицы исключений.</content><link rel='replies' type='application/atom+xml' href='http://www.transl-gunsmoker.ru/feeds/3189237126748988177/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.transl-gunsmoker.ru/2021/10/sizeofimage-moduleinfo.html#comment-form' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688132520265708820/posts/default/3189237126748988177'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688132520265708820/posts/default/3189237126748988177'/><link rel='alternate' type='text/html' href='http://www.transl-gunsmoker.ru/2021/10/sizeofimage-moduleinfo.html' title='Что означает поле SizeOfImage в структуре MODULEINFO?'/><author><name>GunSmoker</name><uri>http://www.blogger.com/profile/15611696588191431330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhrYMBDZyhmwpngoHu4MbxoNS55hTzfYX9MRyMyiL7lk8FV4zzROyizfpqf_E85yuu8-BtGtcAZg_miWOVH_8I9hdkdBcDd5UkBhBcWo1jkcyacq_Lgcmt9gECy0zV0zNo/s220/avatar.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688132520265708820.post-6263055455953497615</id><published>2021-08-19T13:43:00.004+03:00</published><updated>2021-08-19T13:43:43.663+03:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="История"/><category scheme="http://www.blogger.com/atom/ns#" term="забавно"/><title type='text'>Почему в заголовочниках Windows полно слов типа &quot;чувак&quot;?</title><content type='html'>Это перевод &lt;a href=&quot;https://devblogs.microsoft.com/oldnewthing/20210818-00/?p=105570&quot; title=&quot;What’s with all of the references to “dude” in the accessibility header files?&quot;&gt;What’s with all of the references to “dude” in the accessibility header files?&lt;/a&gt; Автор: Реймонд Чен.&lt;br /&gt;
&lt;br /&gt;
Если вы читали заголовочнй файл &lt;code&gt;winuser.h&lt;/code&gt;, то вы могли видеть как комментарии ссылаются на объекты как на &quot;чуваков&quot; (&quot;dudes&quot;):
&lt;pre class=&quot;brush:c&quot;&gt;/*
 * For all EVENT_OBJECT events,
 *      hwnd is the dude to Send the WM_GETOBJECT message to (unless NULL,
 *          see above for system things)
 *      ...
 *          The hwnd/idObject pair gets you to the container, the dude you
 *          probably want to talk to most of the time anyway.  The idChild
 *          can then be passed into the acc properties to get the name/value
 *          of it as needed.
 *      ...
 * Now, if the app itself resizes child windows as a result of being
 * sized, USER will generate LOCATIONCHANGEs for those dudes also because
 * it doesn&#39;t know better.
 *      ....
 */&lt;/pre&gt;
Что это ещё за &quot;чуваки&quot;? &lt;a href=&quot;https://twitter.com/LauraCatPJs&quot; title=&quot;Twitter: Laura Butler, CTO UpLift Group&quot;&gt;Автор комментариев&lt;/a&gt; была большим поклонником &lt;a href=&quot;https://www.kinopoisk.ru/film/555/&quot; title=&quot;КиноПоиск: Большой Лебовски (1998)&quot;&gt;Большого Лебовски&lt;/a&gt;?&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
Насколько я знаю, тут нет никакой связи.&lt;br /&gt;
&lt;br /&gt;
Термин &quot;чувак&quot; (dude) был своего рода &quot;крылатой фразой&quot; в команде диспетчера окон Windows 95 и обычно означал &quot;вещь&quot;, &quot;сущность&quot;, &quot;какая-то штука&quot;. Это слово часто встречается во внутренних комментариях. Вот несколько примеров:
&lt;pre class=&quot;brush:c&quot;&gt;// Returns NULL or hMenu if top level dude

// If the dude that has focus is a button, then...

// Realloc the dude to the right size.

// Is the caller a 32-bit dude?

; lParam is the thread ID of the other dude.&lt;/pre&gt;
Популярность термина распространилась через &quot;перекрестное опыление&quot; среди нескольких других команд, в первую очередь - команды оболочки, поэтому вы видите слово &quot;чувак&quot; и в других частях Windows 95. Но большинство из них находится в диспетчере окон.&lt;br /&gt;
&lt;br /&gt;
Дополнительная болтовня: вне пределов диспетчере окон (и тесно связанных с ним команд разработчиков) тоже можно встеретить &quot;чувака&quot;, но в других местах он используется как форма прямого обращения, а не как существительное-заполнитель:
&lt;ul&gt;
&lt;li&gt;&quot;&lt;a href=&quot;https://en.wikiquote.org/wiki/Bill_%26_Ted%27s_Excellent_Adventure&quot; title=&quot;Wikipedia: Bill &amp; Ted&#39;s Excellent Adventure&quot;&gt;Party on, dude!&lt;/a&gt;&quot;&lt;/li&gt;
&lt;li&gt;&quot;Not mine, dude!&quot;&lt;/li&gt;
&lt;li&gt;&quot;No way, dude!&quot;&lt;/li&gt;
&lt;/ul&gt;
Перед проведением исследования я думал, что источником &quot;чуваков&quot; могли бы быть фразы Барта Симпсона &quot;Cowabunga, dude!&quot; и &quot;Don’t have a cow, dude!&quot; - учитывая, что Симпсоны находились в так называемом &quot;золотом веке&quot; как раз во время разработки Windows 95. Однако я не смог найти ни одного примера, подтверждающего эту гипотезу.</content><link rel='replies' type='application/atom+xml' href='http://www.transl-gunsmoker.ru/feeds/6263055455953497615/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.transl-gunsmoker.ru/2021/08/windows.html#comment-form' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688132520265708820/posts/default/6263055455953497615'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688132520265708820/posts/default/6263055455953497615'/><link rel='alternate' type='text/html' href='http://www.transl-gunsmoker.ru/2021/08/windows.html' title='Почему в заголовочниках Windows полно слов типа &quot;чувак&quot;?'/><author><name>GunSmoker</name><uri>http://www.blogger.com/profile/15611696588191431330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhrYMBDZyhmwpngoHu4MbxoNS55hTzfYX9MRyMyiL7lk8FV4zzROyizfpqf_E85yuu8-BtGtcAZg_miWOVH_8I9hdkdBcDd5UkBhBcWo1jkcyacq_Lgcmt9gECy0zV0zNo/s220/avatar.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688132520265708820.post-8032370721960652477</id><published>2021-05-26T12:19:00.004+03:00</published><updated>2021-05-26T14:20:27.239+03:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Win16"/><category scheme="http://www.blogger.com/atom/ns#" term="История"/><category scheme="http://www.blogger.com/atom/ns#" term="Совместимость"/><title type='text'>Почему не зависящие от устройства растровые изображения перевёрнуты вверх ногами?</title><content type='html'>Это перевод &lt;a href=&quot;https://devblogs.microsoft.com/oldnewthing/20210525-00/?p=105250&quot; title=&quot;Why are device-independent bitmaps upside down?&quot;&gt;Why are device-independent bitmaps upside down?&lt;/a&gt; Автор: Реймонд Чен.&lt;br /&gt;
&lt;br /&gt;
Для всех, кто в первый раз знакомится с независимыми от устройства (device-independent) растровыми изображениями Windows, становится сюрпризом тот факт, что они перевёрнуты - в том смысле, что первая строка пикселей соответствует нижней строке изображения, а последующие строки пикселей идут по изображению вверх, при этом последняя строка пикселей соответствует самой верхней строке изображения.&lt;br /&gt;
&lt;br /&gt;
Ладно, не будем называть эти растровые изображения перевёрнутыми вверх ногами. Менее осуждающий термин - называть их восходящими (bottom-up) растровыми изображениями.&lt;br /&gt;
&lt;br /&gt;
Итак, почему растровые изображения, не зависящие от устройства, хранятся снизу вверх?&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
Для совместимости с OS/2.&lt;br /&gt;
&lt;br /&gt;
В OS/2 Presentation Manager начало координатного пространства графики находится в нижнем левом углу экрана, а координата Y увеличивается по мере продвижения к верхней части экрана. Такая система координат совпадает с математически правильной системой координат, что устраняет много путаницы, когда вы начинаете заниматься математикой с вашей графикой.&lt;br /&gt;
&lt;br /&gt;
Я сильно подозреваю, что компьютерная графика подразумевает использование математики.&lt;br /&gt;
&lt;br /&gt;
Если вы используете математическую координатную модель, тогда все математические формулы сохранятся без необходимости вводить &quot;случайные&quot; отрицательные знаки для учёта перевёрнутой оси Y. Углы поворота всегда будут против часовой стрелки. Матрицы преобразования координат будут работать так же, как вы изучали в классе линейной алгебры. Всё отлично получается.&lt;br /&gt;
&lt;br /&gt;
Конечно, если вы не один из таких математиков, то увеличение координаты Y вверх означает, что порядок чтения противоположен порядку увеличения координат.&lt;br /&gt;
&lt;br /&gt;
Что странно.&lt;br /&gt;
&lt;br /&gt;
Windows 2.0 и OS/2 начинали как хорошие друзья, поэтому Windows 2.0 приняла растровый формат OS/2, чтобы способствовать взаимодействию между ними. Как мы все знаем, эта дружба со временем испортилась, но решение о формате файла сохраняется.</content><link rel='replies' type='application/atom+xml' href='http://www.transl-gunsmoker.ru/feeds/8032370721960652477/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.transl-gunsmoker.ru/2021/05/why-are-device-independent-bitmaps-upside-down.html#comment-form' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688132520265708820/posts/default/8032370721960652477'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688132520265708820/posts/default/8032370721960652477'/><link rel='alternate' type='text/html' href='http://www.transl-gunsmoker.ru/2021/05/why-are-device-independent-bitmaps-upside-down.html' title='Почему не зависящие от устройства растровые изображения перевёрнуты вверх ногами?'/><author><name>GunSmoker</name><uri>http://www.blogger.com/profile/15611696588191431330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhrYMBDZyhmwpngoHu4MbxoNS55hTzfYX9MRyMyiL7lk8FV4zzROyizfpqf_E85yuu8-BtGtcAZg_miWOVH_8I9hdkdBcDd5UkBhBcWo1jkcyacq_Lgcmt9gECy0zV0zNo/s220/avatar.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688132520265708820.post-6975427948416033526</id><published>2020-11-17T05:20:00.002+03:00</published><updated>2020-11-17T05:22:10.758+03:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="кодинг"/><category scheme="http://www.blogger.com/atom/ns#" term="хак"/><title type='text'>Почему в выводе echo командной строки с перенаправлением появляется 1? Кто вставляет эти единицы?</title><content type='html'>Это перевод &lt;a href=&quot;https://devblogs.microsoft.com/oldnewthing/20201116-00/?p=104452&quot; title=&quot;Why does my command line redirection echo with an extra 1? Who’s inserting these rogue 1s everywhere?&quot;&gt;Why does my command line redirection echo with an extra 1? Who’s inserting these rogue 1s everywhere?&lt;/a&gt; Автор: Реймонд Чен.&lt;br /&gt;
&lt;br /&gt;
Если вы оставите включённым режим echo в командном файле, и командный файл выполняет команду с перенаправлением, тогда в выводе появится загадочная единичка.&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
&lt;pre&gt;C:\&amp;gt; copy con %TEMP%\test.cmd
dir &amp;gt;nul
^Z
        1 file(s) copied.

C:\&amp;gt; %TEMP%\test.cmd

C:\&amp;gt; dir  1&amp;gt;nul
          &lt;span style=&quot;color: red;&quot;&gt;⇑&lt;/span&gt;
          🤨
&lt;/pre&gt;
&lt;a href=&quot;https://www.imdb.com/title/tt3748528/&quot; title=&quot;IMDB: Изгой-один. Звёздные войны: Истории (2016)&quot;&gt;Изгой 1&lt;/a&gt; оказывается чисто визуальным. В командной строке нет единички. В примере выше список каталогов относится к текущему каталогу, а не к каталогу с именем 1.&lt;br /&gt;
&lt;br /&gt;
Но откуда тогда берётся эта единица?&lt;br /&gt;
&lt;br /&gt;
Вспомните, что &lt;a title=&quot;Опасайтесь цифр перед оператором перенаправления&quot; href=&quot;https://www.transl-gunsmoker.ru/2011/07/beware-of-digits-before-redirection.html&quot;&gt;цифры перед оператором перенаправления указывают перенаправляемый поток&lt;/a&gt; (это одна из причин, по которой у меня есть привычка &lt;a title=&quot;Оператор перенаправления может стоять в середине командной строки&quot; href=&quot;https://www.transl-gunsmoker.ru/2011/07/redirection-operator-can-occur-in.html&quot;&gt;помещать перенаправление в начало командной строки&lt;/a&gt;).&lt;br /&gt;
&lt;br /&gt;
По умолчанию, если поток не указан, перенаправляется поток &lt;code&gt;stdout&lt;/code&gt;, а номер потока &lt;code&gt;stdout&lt;/code&gt;… 1.&lt;br /&gt;
&lt;br /&gt;
Так, головоломка начинает складываться.&lt;br /&gt;
&lt;br /&gt;
Если включён режим echo, командный процессор выводит каждую команду перед ее выполнением. Но это происходит &lt;i&gt;после&lt;/i&gt; того, как командная строка будет проанализирована. А во время синтаксического анализа оператор перенаправления запоминает какой поток перенаправляется. Командный процессор восстанавливает исходную командную строку для вывода из результатов анализа, поэтому когда он видит оператор перенаправления, он послушно помещает вперёд номер потока из анализа.&lt;br /&gt;
&lt;br /&gt;
Если вы не указали явный поток для перенаправления, будет распечатан поток по умолчанию (&lt;code&gt;stdout&lt;/code&gt; = 1), потому что именно этот поток перенаправляется.&lt;br /&gt;
&lt;br /&gt;
Это просто причуда. На функциональность это не влияет. Хотя это выглядит немного странно, когда вы замечаете это в первый раз.</content><link rel='replies' type='application/atom+xml' href='http://www.transl-gunsmoker.ru/feeds/6975427948416033526/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.transl-gunsmoker.ru/2020/11/echo-1.html#comment-form' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688132520265708820/posts/default/6975427948416033526'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688132520265708820/posts/default/6975427948416033526'/><link rel='alternate' type='text/html' href='http://www.transl-gunsmoker.ru/2020/11/echo-1.html' title='Почему в выводе echo командной строки с перенаправлением появляется 1? Кто вставляет эти единицы?'/><author><name>GunSmoker</name><uri>http://www.blogger.com/profile/15611696588191431330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhrYMBDZyhmwpngoHu4MbxoNS55hTzfYX9MRyMyiL7lk8FV4zzROyizfpqf_E85yuu8-BtGtcAZg_miWOVH_8I9hdkdBcDd5UkBhBcWo1jkcyacq_Lgcmt9gECy0zV0zNo/s220/avatar.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688132520265708820.post-5341562143507091019</id><published>2020-09-09T15:50:00.003+03:00</published><updated>2020-09-09T15:57:03.857+03:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="История"/><category scheme="http://www.blogger.com/atom/ns#" term="забавно"/><title type='text'>Как можно заклинить виртуальный картридер?</title><content type='html'>Это перевод &lt;a href=&quot;https://devblogs.microsoft.com/oldnewthing/20200908-00/?p=104186&quot; title=&quot;How is it possible to jam a virtual card reader?&quot;&gt;How is it possible to jam a virtual card reader?&lt;/a&gt; Автор: Реймонд Чен.&lt;br /&gt;
&lt;br /&gt;
Когда-то давно я поддерживал систему с разделением времени, известную как &lt;a href=&quot;https://ru.wikipedia.org/wiki/VM_(%D0%BE%D0%BF%D0%B5%D1%80%D0%B0%D1%86%D0%B8%D0%BE%D0%BD%D0%BD%D0%B0%D1%8F_%D1%81%D0%B8%D1%81%D1%82%D0%B5%D0%BC%D0%B0)&quot; title=&quot;Википедия: VM (операционная система)&quot;&gt;VM/CMS&lt;/a&gt;. Каждый пользователь, вошедший в систему, получал свою собственную виртуальную машину с виртуальными аппаратными устройствами: виртуальным устройством для чтения карт, виртуальным устройством для записи перфокарт и виртуальным принтером.&lt;br /&gt;
&lt;br /&gt;
Хорошо, у вас есть виртуальный картридер. Как вы будете загружать виртуальные карты в свой виртуальный картридер? И зачем вообще это делать?&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
Во времена физических карт вы сохраняли файл, создавая для него колоду перфокарт. И чтобы передать кому-нибудь этот файл, вы передавали ему колоду карт. Затем этот человек включал свою систему и вставлял перфокарты в картридер, чтобы загрузить файл.&lt;br /&gt;
&lt;br /&gt;
Перфорация виртуальной карты и виртуальный кардридер делают всё то же самое - но без использования физических карт.&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;&quot;Перфорация виртуальной карты&quot; - это способ отправки файлов другому человеку. В конечном итоге это стало заменой для отправки электронного сообщения.&lt;/li&gt;
&lt;li&gt;Виртуальный кардридер - это место, куда поступают входящие файлы, например, результаты отправленных вами заданий. Т.е. там находится входящая электронная почта, ожидающая, чтобы ее прочитали¹.&lt;/li&gt;
&lt;li&gt;Виртуальный принтер - ну, это просто принтер. Задания, отправленные на виртуальный принтер, поступают на настоящий принтер, и вы можете потом забрать из принтера результаты печати.&lt;/li&gt;
&lt;/ul&gt;
Команда для проверки электронной почты была &lt;code&gt;Q RDR&lt;/code&gt;: команда &lt;i&gt;Query Reader&lt;/i&gt; проверяла, есть ли что-нибудь в вашем виртуальном картридере. Если да - значит, вы получили почту.&lt;br /&gt;
&lt;br /&gt;
Однажды мы получили отчёт об ошибке с сообщением, которое никто никогда раньше не видел: Card reader jammed (заклинивание в картридере).&lt;br /&gt;
&lt;br /&gt;
Как можно заклинить виртуальный картридер? Вы скормили ему согнутую виртуальную карточку?&lt;br /&gt;
&lt;br /&gt;
Я уже заканчивал свою смену и не дождался решения. Позже я узнал, что один из моих коллег нашёл решение проблемы в руководстве.&lt;br /&gt;
&lt;br /&gt;
Я вот думаю: выключил ли он его виртуально и снова включил?&lt;br /&gt;
&lt;br /&gt;
Бонусная болтовня: задолго до появления VM/CMS перфокарты были настоящими. Я начал программировать как раз тогда, когда перфокарты умирали, но я помню, что в жаркие летние месяцы был один глупый трюк, чтобы немного охладиться: надо было подойти к служебному окну в компьютерном зале и отдать в работу несколько десятков карт. Операторы открывали окно, чтобы забрать ваши перфокарты, и в это время вы получали короткий поток прохладного воздуха из машинного зала³.&lt;br /&gt;
&lt;br /&gt;
¹ Виртуальные перфокарты в вашем ридере не учитываются в вашей квоте хранилища, поэтому была распространена такая уловка: когда у вас закончилась квота, нужно было отправить файл самому себе, пока вы освобождаете место. Однако вам нужно было забрать файл в ближайшее время, потому что система время от времени удаляет слишком старые виртуальные перфокарты².&lt;br /&gt;
&lt;br /&gt;
² Я предполагаю, что вы могли бы считать свой файл с картридера, а затем снова немедленно отправить его себе, тем самым сбрасывая срок годности. Но это был высокорисковый манёвр, поскольку один неверный шаг мог привести к потере файлов.&lt;br /&gt;
&lt;br /&gt;
³ Когда работа была закончена, операторы открывали служебное окно, чтобы вернуть колоду перфокарт вместе с распечаткой результатов работы. Однако вы не могли при этом украсть второй поток прохладного воздуха, потому что вы не знали, чью работу выдавали. Нельзя просто торчать прямо перед окном, потому что операторы подумают, что вы пытаетесь отправить задание. По соглашению, если вы ожидаете результатов задания, вы должны стоять на небольшом, но уважительном расстоянии от окна, чтобы не сбивать с толку операторов. Достаточно близко, чтобы вы могли получить небольшое количество прохладного воздуха, но не настолько близко, чтобы операторы могли подумать, что вы хотите отправить задание.</content><link rel='replies' type='application/atom+xml' href='http://www.transl-gunsmoker.ru/feeds/5341562143507091019/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.transl-gunsmoker.ru/2020/09/how-is-it-possible-to-jam-virtual-card-reader.html#comment-form' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688132520265708820/posts/default/5341562143507091019'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688132520265708820/posts/default/5341562143507091019'/><link rel='alternate' type='text/html' href='http://www.transl-gunsmoker.ru/2020/09/how-is-it-possible-to-jam-virtual-card-reader.html' title='Как можно заклинить виртуальный картридер?'/><author><name>GunSmoker</name><uri>http://www.blogger.com/profile/15611696588191431330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhrYMBDZyhmwpngoHu4MbxoNS55hTzfYX9MRyMyiL7lk8FV4zzROyizfpqf_E85yuu8-BtGtcAZg_miWOVH_8I9hdkdBcDd5UkBhBcWo1jkcyacq_Lgcmt9gECy0zV0zNo/s220/avatar.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688132520265708820.post-9146678118160745681</id><published>2020-09-02T16:19:00.000+03:00</published><updated>2020-09-02T16:19:21.929+03:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Win32"/><category scheme="http://www.blogger.com/atom/ns#" term="кодинг"/><title type='text'>Чему может быть равно свойство Status у WMI класса Win32_ComputerSystem?</title><content type='html'>Это перевод &lt;a href=&quot;https://devblogs.microsoft.com/oldnewthing/20200818-00/?p=104091&quot; title=&quot;What are the possible Status values for Win32_ComputerSystem?&quot;&gt;What are the possible Status values for Win32_ComputerSystem?&lt;/a&gt; Автор: Реймонд Чен.&lt;br /&gt;
&lt;br /&gt;
&lt;a title=&quot;Archived documentation: Win32_ComputerSystem class&quot; href=&quot;http://web.archive.org/web/20190711222946/https://docs.microsoft.com/en-us/windows/win32/cimwin32prov/win32-computersystem&quot;&gt;Документация для WMI класса &lt;code&gt;Win32_Computer­System&lt;/code&gt;&lt;/a&gt; содержит такое описание свойства &lt;code&gt;Status&lt;/code&gt;:&lt;br /&gt;
&lt;blockquote&gt;&lt;b&gt;&lt;code&gt;Status&lt;/code&gt;&lt;/b&gt;&lt;br /&gt;
Тип данных: &lt;b&gt;&lt;code&gt;string&lt;/code&gt;&lt;/b&gt;&lt;br /&gt;
Доступ: только чтение&lt;br /&gt;
Квалификаторы: &lt;a title=&quot;Standard qualifiers&quot; href=&quot;https://docs.microsoft.com/en-us/windows/desktop/WmiSdk/standard-qualifiers&quot;&gt;MaxLen&lt;/a&gt; (10), &lt;a title=&quot;Standard qualifiers&quot; href=&quot;https://docs.microsoft.com/en-us/windows/desktop/WmiSdk/standard-qualifiers&quot;&gt;DisplayName&lt;/a&gt; (&amp;#8220;Status&amp;#8221;)&lt;br /&gt;
&lt;br /&gt;  
Текущее состояние объекта. Могут быть определены различные рабочие и нерабочие состояния. К рабочим статусам относятся: OK, Degraded и Pred Fail. Последнее значение может использоваться, к примеру, жёстким диском с поддержкой SMART, который может функционировать правильно, но прогнозирует сбой в ближайшем будущем. К нерабочим состояниям относятся: Error, Starting, Stopping и Service, которые могут применяться во время переноса зеркала диска, перезагрузки списка разрешений пользователей или другой административной работы.&lt;br /&gt;
&lt;br /&gt;  
Это свойство наследуется от &lt;a title=&quot;CIM_ManagedSystemElement class&quot; href=&quot;https://docs.microsoft.com/en-us/windows/win32/cimwin32prov/cim-managedsystemelement&quot;&gt;&lt;code&gt;&lt;b&gt;CIM_&lt;wbr /&gt;Managed­System­Element&lt;/b&gt;&lt;/code&gt;&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
Значения могут быть следующими:&lt;br /&gt;
&lt;b&gt;OK&lt;/b&gt; (&amp;#8220;OK&amp;#8221;)&lt;br /&gt;
&lt;b&gt;Error&lt;/b&gt; (&amp;#8220;Error&amp;#8221;)&lt;br /&gt;
&lt;b&gt;Degraded&lt;/b&gt; (&amp;#8220;Degraded&amp;#8221;)&lt;br /&gt;
&lt;b&gt;Unknown&lt;/b&gt; (&amp;#8220;Unknown&amp;#8221;)&lt;br /&gt;
&lt;b&gt;Pred Fail&lt;/b&gt; (&amp;#8220;Pred Fail&amp;#8221;)&lt;br /&gt;
&lt;b&gt;Starting&lt;/b&gt; (&amp;#8220;Starting&amp;#8221;)&lt;br /&gt;
&lt;b&gt;Stopping&lt;/b&gt; (&amp;#8220;Stopping&amp;#8221;)&lt;br /&gt;
&lt;b&gt;Service&lt;/b&gt; (&amp;#8220;Service&amp;#8221;)&lt;br /&gt;
&lt;b&gt;Stressed&lt;/b&gt; (&amp;#8220;Stressed&amp;#8221;)&lt;br /&gt;
&lt;b&gt;NonRecover&lt;/b&gt; (&amp;#8220;NonRecover&amp;#8221;)&lt;br /&gt;
&lt;b&gt;No Contact&lt;/b&gt; (&amp;#8220;No Contact&amp;#8221;)&lt;br /&gt;
&lt;b&gt;Lost Comm&lt;/b&gt; (&amp;#8220;Lost Comm&amp;#8221;)&lt;/blockquote&gt;
Что эта чушь пытается нам сказать? И что она говорит нам о возможных значениях свойства &lt;code&gt;Status&lt;/code&gt; для &lt;code&gt;Win32_&lt;wbr /&gt;Computer­System&lt;/code&gt;?&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
Хорошо, сначала я дам ответ, а потом мы его обсудим.&lt;br /&gt;
&lt;br /&gt;
Ответ заключается в том, что &lt;code&gt;Status&lt;/code&gt; для &lt;code&gt;Win32_&lt;wbr /&gt;Computer­System&lt;/code&gt; всегда равен &quot;ОК&quot;. Сама система не сообщает о каком-либо интересном для себя состоянии, хотя она сообщает о статусе других вещей, например &lt;code&gt;Front­Panel­Reset­Status&lt;/code&gt;.&lt;br /&gt;
&lt;br /&gt;
Тогда почему &lt;code&gt;Win32_&lt;wbr /&gt;Computer­System&lt;/code&gt; вообще имеет свойство &lt;code&gt;Status&lt;/code&gt;, если ничего интересного оно не сообщает?&lt;br /&gt;
&lt;br /&gt;
Подсказка кроется в комментарии, который прятался между описанием и списком значений:&lt;br /&gt;
&lt;blockquote&gt;Это свойство наследуется от &lt;a title=&quot;CIM_ManagedSystemElement class&quot; href=&quot;https://docs.microsoft.com/en-us/windows/win32/cimwin32prov/cim-managedsystemelement&quot;&gt;&lt;code&gt;&lt;b&gt;CIM_&lt;wbr /&gt;Managed­System­Element&lt;/b&gt;&lt;/code&gt;&lt;/a&gt;.&lt;/blockquote&gt;
&lt;code&gt;Win32_&lt;wbr /&gt;Computer­System&lt;/code&gt; имеет свойство &lt;code&gt;Status&lt;/code&gt; не потому, что он хочет сообщать о своём собственном статусе, а потому, что он &lt;i&gt;вынужден&lt;/i&gt; сообщать о своём собственном статусе из-за наследования.&lt;br /&gt;
&lt;br /&gt;
Это также объясняет бессвязное описание: это описание также было унаследовано от &lt;code&gt;CIM_&lt;wbr /&gt;Managed­System­Element&lt;/code&gt;. Класс &lt;code&gt;CIM_&lt;wbr /&gt;Managed­System­Element&lt;/code&gt; должен описывать все возможные элементы системы, поэтому он имеет большое количество доступных значений для &lt;code&gt;Status&lt;/code&gt;, а в документации делается попытка объяснить, что означают эти значения статуса, поскольку они могут применяться к произвольным производным классам.&lt;br /&gt;
&lt;br /&gt;
Тут помогли бы кой-какое переформатирование и дополнительные уточняющие слова:&lt;br /&gt;
&lt;blockquote&gt;Текущее состояние объекта.&lt;br /&gt;
&lt;br /&gt;
Могут быть определены различные рабочие и нерабочие состояния.&lt;br /&gt;
&lt;br /&gt;
К рабочим статусам относятся: OK, Degraded и Pred Fail (Pred Fail может сообщать, к примеру, жёсткий диск с поддержкой SMART, который может функционировать должным образом, но прогнозирует сбой в ближайшем будущем).&lt;br /&gt;
&lt;br /&gt;
К нерабочим состояниям относятся: Error, Starting, Stopping и Service (Service может возвращаться во время перенастройки зеркалирования диска, перезагрузки списка разрешений пользователей или другой административной работы. Такая работа может привести к временному отключению элемента).&lt;br /&gt;
&lt;br /&gt;
Это свойство наследуется от &lt;a title=&quot;CIM_ManagedSystemElement class&quot; href=&quot;https://docs.microsoft.com/en-us/windows/win32/cimwin32prov/cim-managedsystemelement&quot;&gt;&lt;code&gt;&lt;b&gt;CIM_&lt;wbr /&gt;Managed­System­Element&lt;/b&gt;&lt;/code&gt;&lt;/a&gt;. Не все значения статуса, описанные здесь, могут применяться к производному классу.&lt;/blockquote&gt;
Но на самом деле описание этого свойства должно быть помечено как ненаследуемое (даже если свойство унаследовано), а вместо него должно использоваться специальное описание:
&lt;blockquote&gt;Текущее состояние объекта.&lt;br /&gt;
&lt;br /&gt;
Для &lt;code&gt;Win32_&lt;wbr /&gt;Computer­System&lt;/code&gt; всегда равно &quot;OK&quot;.&lt;br /&gt;  
&lt;br /&gt;  
Это свойство наследуется от &lt;a title=&quot;CIM_ManagedSystemElement class&quot; href=&quot;https://docs.microsoft.com/en-us/windows/win32/cimwin32prov/cim-managedsystemelement&quot;&gt;&lt;code&gt;&lt;b&gt;CIM_&lt;wbr /&gt;Managed­System­Element&lt;/b&gt;&lt;/code&gt;&lt;/a&gt;.&lt;/blockquote&gt;
И именно это &lt;a title=&quot;Pull request: Clarify Win32_ComputerSystem Status&quot; href=&quot;https://github.com/MicrosoftDocs/win32/pull/477&quot; target=&quot;_blank&quot;&gt;документация теперь и говорит&lt;/a&gt;.</content><link rel='replies' type='application/atom+xml' href='http://www.transl-gunsmoker.ru/feeds/9146678118160745681/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.transl-gunsmoker.ru/2020/09/status-wmi-win32computersystem.html#comment-form' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688132520265708820/posts/default/9146678118160745681'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688132520265708820/posts/default/9146678118160745681'/><link rel='alternate' type='text/html' href='http://www.transl-gunsmoker.ru/2020/09/status-wmi-win32computersystem.html' title='Чему может быть равно свойство Status у WMI класса Win32_ComputerSystem?'/><author><name>GunSmoker</name><uri>http://www.blogger.com/profile/15611696588191431330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhrYMBDZyhmwpngoHu4MbxoNS55hTzfYX9MRyMyiL7lk8FV4zzROyizfpqf_E85yuu8-BtGtcAZg_miWOVH_8I9hdkdBcDd5UkBhBcWo1jkcyacq_Lgcmt9gECy0zV0zNo/s220/avatar.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688132520265708820.post-3806863219873788878</id><published>2020-08-31T18:20:00.000+03:00</published><updated>2020-08-31T18:20:13.551+03:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="encoding/codepages"/><category scheme="http://www.blogger.com/atom/ns#" term="unicode"/><title type='text'>Существует ли кодовая страница, совпадающая с ASCII и позволяющая сделать двустороннюю конвертацию в Unicode?</title><content type='html'>Это перевод &lt;a href=&quot;https://devblogs.microsoft.com/oldnewthing/20200831-00/?p=104142&quot; title=&quot;Is there a code page that matches ASCII and can round trip arbitrary bytes through Unicode?&quot;&gt;Is there a code page that matches ASCII and can round trip arbitrary bytes through Unicode?&lt;/a&gt; Автор: Реймонд Чен.&lt;br /&gt;
&lt;br /&gt;
Вам может понадобится такая кодовая страница, если у вас есть фрагмент двоичных данных с внедрённым текстом ASCII. Вы бы хотели иметь возможность извлекать текст ASCII и даже манипулировать им, но при этом и также обрабатывать части, не относящиеся к ASCII, как загадочные символы, не имеющие смысла. Но вам также нужно иметь возможность преобразовать их обратно в исходные байты.&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
Например, формат двоичных данных может быть таким: строка ASCII, за которой следует 32-разрядное целое число в формате big-endian. Вы хотите проанализировать строку ASCII, затем взять следующие четыре символа, перевернуть их (преобразовать в little-endian), а затем преобразовать всё вместе обратно в байты, чтобы можно было извлечь целое число (integer).&lt;br /&gt;
&lt;br /&gt;
В современных языках программирования (типа Delphi и C#) есть много удобных средств для управления текстом, но для этого требуются строковые переменные, которые выражаются как последовательность кодов UTF16-LE. Итак, вам нужен способ конвертировать байты в коды UTF16-LE - причём так, что байты меньше 128 сопоставлялись бы с соответствующими символами ASCII, а байты 128 и выше сопоставлялись бы с &lt;i&gt;чем-то&lt;/i&gt; обратимым.&lt;br /&gt;
&lt;br /&gt;
К примеру, кодовая страница UTF-8 не подойдёт, потому что в UTF-8 существуют недопустимые последовательности байтов, которые не будут преобразованы в текст (и обратно). Другой вариант, который вы можете выбрать - это &lt;a href=&quot;https://en.wikipedia.org/wiki/Windows-1252&quot; title=&quot;Wikipedia: Windows-1252&quot;&gt;кодовая страница 1252&lt;/a&gt;, но она также не будет работать, потому что в ней существует несколько неопределенных кодовых позиций (#$81, #$8D, #$8F, #$90, #$9D - прим. пер.). Это означает, что эти байты будут преобразованы в &lt;a title=&quot;Википедия: Заменяющий символ&quot; href=&quot;https://ru.wikipedia.org/wiki/%D0%97%D0%B0%D0%BC%D0%B5%D0%BD%D1%8F%D1%8E%D1%89%D0%B8%D0%B9_%D1%81%D0%B8%D0%BC%D0%B2%D0%BE%D0%BB&quot;&gt;#$FFFD (REPLACEMENT CHARACTER)&lt;/a&gt;, который является специальным символом Unicode, означающим &quot;Здесь был символ, но я не могу выразить его в Unicode&quot;. Обычно этот символ используется для представления ошибок кодирования. (Прим.пер.: или же подобные байты могут быть вообще удалены из результирующей строки Unicode.)&lt;br /&gt;
&lt;br /&gt;
Хотя вы вряд ли будете пробовать, но я также замечу, что двухбайтовые кодовые страницы тоже не будут работать, потому что они берут пары байтов и преобразуют их в Unicode. Это означает, что изменение порядка символов Unicode (для нашей задачи преобразования endian) не позволит сделать обратное преобразование.&lt;br /&gt;
&lt;br /&gt;
Хорошо, я перейду сразу к делу. Кодовая страница, которую я использую для такого рода вещей - это &lt;a href=&quot;https://ru.wikipedia.org/wiki/CP437&quot; title=&quot;Википедия: CP437&quot;&gt;кодовая страница 437&lt;/a&gt; (прим. пер.: DOSLatinUS - кодовая страница, использовавшаяся в первоначальной версии IBM PC 1981 года). Каждый байт в ней определяется и сопоставляется с уникальной кодовой точкой Unicode, и она совпадает с ASCII для первых 128 значений.&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;uses
  StrUtils; // для ReverseString

procedure TForm1.Button1Click(Sender: TObject);
type
  CP437String = type AnsiString(437);
var
  CP437: CP437String;
  UStr: String;
  X: Integer; // = UnicodeString
begin
  // Заполнили строку
  // (в реальной программе вы бы загружали строку из потока данных)
  SetLength(CP437, 256);
  for X := 0 to 255 do
    CP437[X + 1] := AnsiChar(X);

  // Преобразовали CP437 в Unicode
  UStr := String(CP437);

  // Меняем порядок _символов_
  UStr := ReverseString(UStr);

  // Преобразовали Unicode обратно в CP437
  CP437 := CP437String(UStr);

  // Проверяем обратное преобразование
  // Должен быть изменён порядок _байт_
  for X := 0 to 255 do
    if Ord(CP437[X + 1]) &lt;&gt; 255 - X then
      raise Exception.CreateFmt(&#39;Ошибка преобразования для #%d&#39;, [255 - X]);

  // Сообщение должно быть показано
  ShowMessage(&#39;Всё верно&#39;);
end;&lt;/pre&gt;
Я взял все байты 0 до 255 и преобразовал их в строку с помощью кодовой страницы 437. Потом строка переворачивается и конвертируется обратно в байты. После чего проверяем, что результирующие байты также меняются местами.</content><link rel='replies' type='application/atom+xml' href='http://www.transl-gunsmoker.ru/feeds/3806863219873788878/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.transl-gunsmoker.ru/2020/08/is-there-code-page-that-matches-ASCII-and-can-round-trip-arbitrary-bytes-through-Unicode.html#comment-form' title='Комментарии: 4'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688132520265708820/posts/default/3806863219873788878'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688132520265708820/posts/default/3806863219873788878'/><link rel='alternate' type='text/html' href='http://www.transl-gunsmoker.ru/2020/08/is-there-code-page-that-matches-ASCII-and-can-round-trip-arbitrary-bytes-through-Unicode.html' title='Существует ли кодовая страница, совпадающая с ASCII и позволяющая сделать двустороннюю конвертацию в Unicode?'/><author><name>GunSmoker</name><uri>http://www.blogger.com/profile/15611696588191431330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhrYMBDZyhmwpngoHu4MbxoNS55hTzfYX9MRyMyiL7lk8FV4zzROyizfpqf_E85yuu8-BtGtcAZg_miWOVH_8I9hdkdBcDd5UkBhBcWo1jkcyacq_Lgcmt9gECy0zV0zNo/s220/avatar.png'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688132520265708820.post-2129060974518874732</id><published>2020-08-21T14:11:00.004+03:00</published><updated>2020-08-21T14:11:50.753+03:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Win32"/><category scheme="http://www.blogger.com/atom/ns#" term="кодинг"/><title type='text'>Об использовании FILE_FLAG_WRITE_THROUGH и FILE_FLAG_NO_BUFFERING для проецируемых в память файлов</title><content type='html'>Это перевод &lt;a href=&quot;https://devblogs.microsoft.com/oldnewthing/20200819-00/?p=104093&quot; title=&quot;On using FILE_FLAG_WRITE_THROUGH and FILE_FLAG_NO_BUFFERING for memory-mapped files&quot;&gt;On using FILE_FLAG_WRITE_THROUGH and FILE_FLAG_NO_BUFFERING for memory-mapped files&lt;/a&gt;. Автор: Реймонд Чен.&lt;br /&gt;
&lt;br /&gt;
Клиент хотел использовать флаги &lt;code&gt;FILE_FLAG_WRITE_THROUGH&lt;/code&gt; и &lt;code&gt;FILE_FLAG_NO_BUFFERING&lt;/code&gt; для проецируемого в память файла (memory-mapped file), основываясь на этом руководстве в документации для &lt;a href=&quot;https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew&quot; title=&quot;MSDN: CreateFile Function&quot;&gt;функции &lt;code&gt;CreateFile&lt;/code&gt;&lt;/a&gt;:
&lt;blockquote&gt;По этой причине флаг &lt;code&gt;FILE_FLAG_WRITE_THROUGH&lt;/code&gt; часто используется совместно с флагом &lt;code&gt;FILE_FLAG_NO_BUFFERING&lt;/code&gt; в качестве замены для вызова функции &lt;code&gt;FlushFileBuffers&lt;/code&gt; после каждой записи, что может привести к ненужным потерям производительности. Совместное использование этих флагов позволяет избежать штрафов.&lt;/blockquote&gt;
Клиент был обеспокоен тем, повлияет ли такая комбинация флагов на согласованность данных.&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
На самом деле, проблемы с согласованностью данных у клиента будут ещё до того, как он начнёт беспокоиться об этих флагах.&lt;br /&gt;
&lt;br /&gt;
Поскольку клиент использует проецируемый в память файл - у него нет прямого контроля над тем, когда память записывается на диск. Страницы из проецируемых файлов записываются на диск по усмотрению операционной системы. Следовательно, если клиент изменяет две страницы проецируемого файла, то страницы могут быть записаны на диск в любом порядке.&lt;br /&gt;
&lt;br /&gt;
Поскольку вопрос был про целостность данных, клиент, вероятно, заботит вопрос потери данных при отключении питания или вылетах системы - до того, как данные запишутся на диск. А поскольку страницы могут быть записаны в любом порядке, возможны все четыре варианта:&lt;br /&gt;
&lt;br /&gt;
&lt;table style=&quot;border-collapse: collapse; text-align: center;&quot; border=&quot;1&quot; cellspacing=&quot;0&quot; cellpadding=&quot;3&quot;&gt;&lt;tbody&gt;
  &lt;tr&gt;&lt;th&gt;Сохранилась ли страница 1&lt;/th&gt;&lt;th&gt;Сохранилась ли страница 2&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;/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;/tbody&gt;&lt;/table&gt;
&lt;br /&gt;
Вот тебе и целостность данных.&lt;br /&gt;
&lt;br /&gt;
Установка этих флагов для проецируемого файла контролирует, &lt;b&gt;как&lt;/b&gt; операционная система записывает память на диск, но не обеспечивает никакого контроля над тем, &lt;b&gt;когда&lt;/b&gt; память записывается на диск. А без этого контроля у вас не будет никакой согласованности данных.&lt;br /&gt;
&lt;br /&gt;
Обычно при проектировании системы согласованности вы задаёте определенный порядок, в котором данные должны быть записаны на диск. Например, вы можете решить записать новые данные на диск, а затем, когда данные успешно записаны, вы запишете новые метаданные (скажем, путём обновления индекса) - в результате чего новые данные станут активными значениями, а старые данные станут игнорироваться. Именно подобные алгоритмы могут использовать преимущества указанных флагов сквозной записи и буферизации.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Дополнительная болтовня:&lt;/b&gt; использование &lt;code&gt;FILE_FLAG_NO_BUFFERING&lt;/code&gt; для проецируемого в память файла, на самом деле, бесполезно. Флаг &quot;без буферизации&quot; означает, что записи идут прямо на диск - без кэширования в памяти. Но ведь весь смысл отображаемого в память файла состоит в том, что он должен быть кэширован в памяти!</content><link rel='replies' type='application/atom+xml' href='http://www.transl-gunsmoker.ru/feeds/2129060974518874732/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.transl-gunsmoker.ru/2020/08/using-FILEFLAGWRITETHROUGH-FILEFLAGNOBUFFERING-memory-mapped-files.html#comment-form' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688132520265708820/posts/default/2129060974518874732'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688132520265708820/posts/default/2129060974518874732'/><link rel='alternate' type='text/html' href='http://www.transl-gunsmoker.ru/2020/08/using-FILEFLAGWRITETHROUGH-FILEFLAGNOBUFFERING-memory-mapped-files.html' title='Об использовании FILE_FLAG_WRITE_THROUGH и FILE_FLAG_NO_BUFFERING для проецируемых в память файлов'/><author><name>GunSmoker</name><uri>http://www.blogger.com/profile/15611696588191431330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhrYMBDZyhmwpngoHu4MbxoNS55hTzfYX9MRyMyiL7lk8FV4zzROyizfpqf_E85yuu8-BtGtcAZg_miWOVH_8I9hdkdBcDd5UkBhBcWo1jkcyacq_Lgcmt9gECy0zV0zNo/s220/avatar.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688132520265708820.post-8109361774846073106</id><published>2020-05-23T21:19:00.000+03:00</published><updated>2020-05-23T21:19:24.690+03:00</updated><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="обработка ошибок"/><category scheme="http://www.blogger.com/atom/ns#" term="потоки"/><title type='text'>Отладка зависания: все потоки встали на WinHttpGetProxyForUrl</title><content type='html'>Это перевод &lt;a href=&quot;https://devblogs.microsoft.com/oldnewthing/20200501-00/?p=103720&quot; title=&quot;Diagnosing a hang: Everybody stuck in WinHttpGetProxyForUrl&quot;&gt;Diagnosing a hang: Everybody stuck in WinHttpGetProxyForUrl&lt;/a&gt;. Автор: Реймонд Чен.&lt;br /&gt;
&lt;br /&gt;
Клиент сообщил, что их программа рано или поздно полностью встаёт, а все её потоки (750 штук) зависают при вызове &lt;a href=&quot;https://docs.microsoft.com/en-us/windows/win32/api/winhttp/nf-winhttp-winhttpgetproxyforurl&quot; title=&quot;MSDN: WinHttpGetProxyForUrl Function&quot;&gt;&lt;code&gt;WinHttpGetProxyForUrl&lt;/code&gt;&lt;/a&gt; со следующим стеком вызова:&lt;br /&gt;
&lt;pre&gt;ntdll!ZwWaitForSingleObject+0x14
KERNELBASE!WaitForSingleObjectEx+0x8f
winhttp!OutProcGetProxyForUrl+0x160
winhttp!WinHttpGetProxyForUrl+0x349
contoso!submit_web_request+0x232
ntdll!TppWorkpExecuteCallback+0x35e
ntdll!TppWorkerThread+0x474
kernel32!BaseThreadInitThunk+0x14
ntdll!RtlUserThreadStart+0x21&lt;/pre&gt;
(я упростил стек вызова для простоты объяснения)&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
В программе происходит следующее: вы помещаете некоторое задание (work item) в пул потоков, и это задание вызывает &lt;code&gt;WinHttpGetProxyForUrl&lt;/code&gt;. Эта функция является синхронной, но ей нужно делать сетевые запросы HTTP - которые являются асинхронными. Чтобы устранить это противоречие, функция &lt;code&gt;WinHttpGetProxyForUrl&lt;/code&gt; выполняет синхронное ожидание завершения асинхронной работы.&lt;br /&gt;
&lt;br /&gt;
И я предполагаю, что для этого функция &lt;code&gt;WinHttpGetProxyForUrl&lt;/code&gt; использует пул потоков.&lt;br /&gt;
&lt;br /&gt;
Происходит следующее: программа заполняет пул потоков заданиями &lt;code&gt;submit_web_request&lt;/code&gt;. Эти задания вызывают функцию &lt;code&gt;WinHttpGetProxyForUrl&lt;/code&gt;, которая добавляет в очередь свои собственные задания и ожидает их завершения. Но эти задания не могут быть запущены, потому что все потоки пула потоков заняты обработкой заданий &lt;code&gt;submit_web_request&lt;/code&gt;.&lt;br /&gt;
&lt;br /&gt;
Рано или поздно пул потоков может понять, что он ничего не делает, и запустит новый поток, чтобы справиться с накопившимися заданиями. Возможно, этот поток завершит задание для &lt;code&gt;WinHttpGetProxyForUrl&lt;/code&gt; - что позволит продолжить один из потоков для &lt;code&gt;submit_web_request&lt;/code&gt;. Как только этот новый поток пула потоков завершает задание от &lt;code&gt;WinHttpGetProxyForUrl&lt;/code&gt;, он извлекает следующее задание из очереди - и есть вероятность, что он получит очередное задание &lt;code&gt;submit_web_request&lt;/code&gt;, так что теперь мы вернулись к тому, с чего начали, за исключением того, что мы только что создали ещё один застрявший поток в пуле потоков.&lt;br /&gt;
&lt;br /&gt;
Если задания &lt;code&gt;submit_web_request&lt;/code&gt; приходят быстрее, чем &lt;code&gt;WinHttpGetProxyForUrl&lt;/code&gt; может обработать свои собственные задания, пул потоков начнёт заполнятся потоками, заблокированными в &lt;code&gt;submit_web_request&lt;/code&gt;. В итоге пул потоков достигнет своего предела потоков, и всё остановится.&lt;br /&gt;
&lt;br /&gt;
По сути, вы перегружаете пул потоков, заполняя его запросами, которые сами требуют пул потоков. Все потоки пула потоков зависают на обработку ваших запросов, и ни один из них не выполняет задания, сгенерированные вашими запросами.&lt;br /&gt;
&lt;br /&gt;
Это как если бы у вас было много тяжёлого оборудования, которое вам нужно перевезти, поэтому вы нанимаете для перевозки все транспортные компании в городе. Появляется компания А, и они говорят: &quot;Хм, это слишком тяжёлое, чтобы перевезти нашими силами. Давайте мы свяжемся с компаний Б, может быть, они нам помогут&quot;. Компания Б говорит: &quot;Извините, мы не можем сейчас вам помочь. Мы только что получили крупный заказ на перевозку&quot;. Делая заказ во всех доступных транспортных компаниях, вам удаётся помешать любой из них выполнить работу.&lt;br /&gt;
&lt;br /&gt;
Я подозреваю, что эта программа работает в сетевой среде, где &lt;a title=&quot;Википедия: Web Proxy Autodiscovery Protocol&quot; href=&quot;https://ru.wikipedia.org/wiki/Web_Proxy_Autodiscovery_Protocol&quot;&gt;WPAD&lt;/a&gt; работает медленно. Т.е. задания от &lt;code&gt;WinHttpGetProxyForUrl&lt;/code&gt; будут выполнять свою работу дольше, что повышает вероятность того, что задания &lt;code&gt;submit_web_request&lt;/code&gt; будут добавляться быстрее, чем завершаться задания от &lt;code&gt;WinHttpGetProxyForUrl&lt;/code&gt;.&lt;br /&gt;
&lt;br /&gt;
Теперь, когда мы диагностировали проблему: что мы можем сделать, чтобы решить её?&lt;br /&gt;
&lt;br /&gt;
Одна из идей состоит в том, чтобы нанять только одну транспортную компанию и позволить ей решать, сколько ещё компаний им нужно. Поместите все свои вызовы &lt;code&gt;submit_web_request&lt;/code&gt; в один поток и обрабатывайте их по одному. Это займёт только один поток в пуле, оставляя другие потоки доступными. С другой стороны, это означает, что запросы не могут обрабатываться параллельно.&lt;br /&gt;
&lt;br /&gt;
Лучшим решением будет изменить способ использования пула потоков, чтобы вы не держали его в заложниках надолго.&lt;br /&gt;
&lt;br /&gt;
Я не эксперт по WinHttp, но у других людей были некоторые идеи, как это сделать.&lt;br /&gt;
&lt;br /&gt;
Вы можете переключиться на &lt;a title=&quot;MSDN: WinHttpGetProxyForUrlEx Function&quot; href=&quot;https://docs.microsoft.com/en-us/windows/win32/api/winhttp/nf-winhttp-winhttpgetproxyforurlex&quot;&gt;функцию &lt;code&gt;WinHttpGetProxyForUrlEx&lt;/code&gt;&lt;/a&gt;, которая возвращает управление немедленно и вызывает вашу &lt;a title=&quot;MSDN: WinHttpSetStatusCallback Function&quot; href=&quot;https://docs.microsoft.com/en-us/windows/win32/api/winhttp/nf-winhttp-winhttpsetstatuscallback&quot;&gt;функцию обратного вызова&lt;/a&gt;, когда у неё появляется ответ. Тогда функция &lt;code&gt;submit_web_request&lt;/code&gt; может вызвать &lt;code&gt;WinHttpGetProxyForUrlEx&lt;/code&gt; и тоже немедленно выйти. Это освободит поток пула потоков для выполнения другой работы - возможно, даже того самого задания, которое функция &lt;code&gt;WinHttpGetProxyForUrlEx&lt;/code&gt; должна выполнить для своего завершения. Когда &lt;code&gt;WinHttpGetProxyForUrlEx&lt;/code&gt; выполнит свою асинхронную работу, она вызовет функцию обратного вызова, которая доделает любую работу, которую изначально планировала выполнить &lt;code&gt;submit_web_request&lt;/code&gt; после получения информации о прокси.&lt;br /&gt;
&lt;br /&gt;
Короче говоря, нужно идти асинхронно до конца. Это не будет таким уж необоснованным подходом для этой программы, так как сама &lt;code&gt;submit_web_request&lt;/code&gt; моделирует асинхронный запрос: она инициирует запрос и вызывает функцию обратного вызова, предоставляемую вызывающим, передавая в неё ответ от сервера. Поскольку она уже ведёт себя асинхронно, вы можете просто сделать её &lt;i&gt;ещё более&lt;/i&gt; асинхронной.&lt;br /&gt;
&lt;br /&gt;
Другим предложением было полностью убрать вызов &lt;code&gt;WinHttpGetProxyForUrl&lt;/code&gt; и просто передать флаг &lt;code&gt;WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY&lt;/code&gt; в &lt;code&gt;WinHttpOpen&lt;/code&gt;. Это переложит работу по выяснению прокси на функцию &lt;code&gt;WinHttpOpen&lt;/code&gt;, а она сможет выполнить это как часть других своих асинхронных действий. Это кажется хорошей идеей, потому что при этом вы полностью отделяетесь от вопроса выяснения прокси, и вы по-прежнему получаете асинхронное поведение. Ну и вы также получаете удовольствие от исправления ошибки путём удаления кода.&lt;br /&gt;
&lt;br /&gt;
Клиент подтвердил, что переключение на флаг &lt;code&gt;WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY&lt;/code&gt; устранило проблему.</content><link rel='replies' type='application/atom+xml' href='http://www.transl-gunsmoker.ru/feeds/8109361774846073106/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.transl-gunsmoker.ru/2020/05/diagnosing-hang-everybody-stuck-WinHttpGetProxyForUrl.html#comment-form' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688132520265708820/posts/default/8109361774846073106'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688132520265708820/posts/default/8109361774846073106'/><link rel='alternate' type='text/html' href='http://www.transl-gunsmoker.ru/2020/05/diagnosing-hang-everybody-stuck-WinHttpGetProxyForUrl.html' title='Отладка зависания: все потоки встали на WinHttpGetProxyForUrl'/><author><name>GunSmoker</name><uri>http://www.blogger.com/profile/15611696588191431330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhrYMBDZyhmwpngoHu4MbxoNS55hTzfYX9MRyMyiL7lk8FV4zzROyizfpqf_E85yuu8-BtGtcAZg_miWOVH_8I9hdkdBcDd5UkBhBcWo1jkcyacq_Lgcmt9gECy0zV0zNo/s220/avatar.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688132520265708820.post-7143615454262784769</id><published>2020-05-23T17:27:00.000+03:00</published><updated>2020-05-23T17:27:34.432+03:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Win32"/><category scheme="http://www.blogger.com/atom/ns#" term="кодинг"/><title type='text'>Дело о SHGetFolderPath(CSIDL_COMMON_DOCUMENTS), возвращающей ERROR_PATH_NOT_FOUND</title><content type='html'>Это перевод &lt;a href=&quot;https://devblogs.microsoft.com/oldnewthing/20200520-00/?p=103775&quot; title=&quot;The case of the SHGetFolder­Path(CSIDL_COMMON_DOCUMENTS) that returned ERROR_PATH_NOT_FOUND&quot;&gt;The case of the &lt;code&gt;SHGetFolderPath(CSIDL_COMMON_DOCUMENTS)&lt;/code&gt; that returned &lt;code&gt;ERROR_PATH_NOT_FOUND&lt;/code&gt;&lt;/a&gt;. Автор: Реймонд Чен.&lt;br /&gt;
&lt;br /&gt;
У клиента возникла проблема с &lt;a title=&quot;MSDN: SHGetFolderPath Function&quot; href=&quot;https://docs.microsoft.com/en-us/windows/win32/api/shlobj_core/nf-shlobj_core-shgetfolderpathw&quot;&gt;функцией &lt;code&gt;SHGetFolderPath&lt;/code&gt;&lt;/a&gt;. В частности, у них была программа, которая вызывала функцию так:
&lt;pre class=&quot;brush:delphi&quot;&gt;SHGetFolderPath(0, CSIDL_COMMON_DOCUMENTS, 0, SHGFP_TYPE_CURRENT, PChar(pathBuffer));&lt;/pre&gt;
но вызов функции возвращал ошибку $80070003 - это HRESULT-версия для &lt;code&gt;ERROR_PATH_NOT_FOUND&lt;/code&gt;. Ошибка возникает только при запуске из &lt;a href=&quot;https://www.jenkins.io/doc/book/pipeline/&quot; title=&quot;Jenkins User Handbook: Pipeline&quot;&gt;Jenkins pipeline&lt;/a&gt;. Если же программа запускается автономно, то функция завершается успешно и возвращает ожидаемый результат.&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
Трассировка от procmon показала, что приложение пыталось получить доступ к папке &lt;code&gt;C:\Windows\SysWOW64\autobuild\Documents&lt;/code&gt; и потерпело неудачу с ошибкой &lt;code&gt;NAME_NOT_FOUND&lt;/code&gt;. Это и была подсказка к тому, почему всё сломалось.&lt;br /&gt;
&lt;br /&gt;
По умолчанию папка Common Documents имеет значение &lt;code&gt;%PUBLIC%\Documents&lt;/code&gt;. Обычное значение переменной среды &lt;code&gt;PUBLIC&lt;/code&gt; - это &lt;code&gt;C:\Users\Public&lt;/code&gt;, но когда программа запускается как часть Jenkins pipeline, для переменной среды по какой-то причине задается значение &lt;code&gt;autobuild&lt;/code&gt;.&lt;br /&gt;
&lt;br /&gt;
Это означает, что когда программа вызывает &lt;code&gt;SHGetFolderPath&lt;/code&gt; и запрашивает &lt;code&gt;CSIDL_COMMON_DOCUMENTS&lt;/code&gt; - система ищет папку &lt;code&gt;autobuild\Documents&lt;/code&gt;, которая не существует, поэтому возникает ошибка $80070003: &quot;Система не может найти указанный путь&quot;.&lt;br /&gt;
&lt;br /&gt;
Есть ряд переменных среды, которые имеют особое значение, и вы меняете их на свой страх и риск. Вы, вероятно, знаете о таких переменных, как &lt;code&gt;windir&lt;/code&gt;, &lt;code&gt;ProgramFiles&lt;/code&gt; и &lt;code&gt;TEMP&lt;/code&gt;, но существует множество других специальных переменных среды - и &lt;code&gt;PUBLIC&lt;/code&gt; является одной из них.&lt;br /&gt;
&lt;br /&gt;
Вооружившись этой информацией, клиент начал искать, кто портит переменную среды &lt;code&gt;PUBLIC&lt;/code&gt;, и попытаться его остановить.</content><link rel='replies' type='application/atom+xml' href='http://www.transl-gunsmoker.ru/feeds/7143615454262784769/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.transl-gunsmoker.ru/2020/05/case-SHGetFolderPath-CSIDLCOMMONDOCUMENTS-returned-ERRORPATHNOTFOUND.html#comment-form' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688132520265708820/posts/default/7143615454262784769'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688132520265708820/posts/default/7143615454262784769'/><link rel='alternate' type='text/html' href='http://www.transl-gunsmoker.ru/2020/05/case-SHGetFolderPath-CSIDLCOMMONDOCUMENTS-returned-ERRORPATHNOTFOUND.html' title='Дело о SHGetFolderPath(CSIDL_COMMON_DOCUMENTS), возвращающей ERROR_PATH_NOT_FOUND'/><author><name>GunSmoker</name><uri>http://www.blogger.com/profile/15611696588191431330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhrYMBDZyhmwpngoHu4MbxoNS55hTzfYX9MRyMyiL7lk8FV4zzROyizfpqf_E85yuu8-BtGtcAZg_miWOVH_8I9hdkdBcDd5UkBhBcWo1jkcyacq_Lgcmt9gECy0zV0zNo/s220/avatar.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688132520265708820.post-363896812789293917</id><published>2020-03-11T20:01:00.004+03:00</published><updated>2020-03-11T20:01:57.935+03:00</updated><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="не делай так"/><title type='text'>Печальная, но короткая история значения реестра SM_AccessoriesName</title><content type='html'>Это перевод &lt;a href=&quot;https://devblogs.microsoft.com/oldnewthing/20200310-00/?p=103549&quot; title=&quot;The sad but short story of the SM_AccessoriesName registry value&quot;&gt;The sad but short story of the SM_AccessoriesName registry value&lt;/a&gt;. Автор: Реймонд Чен.&lt;br /&gt;
&lt;br /&gt;
SM_AccessoriesName - ещё одна жертва &lt;a title=&quot;Длинная и печальная история ключа Shell Folders&quot; href=&quot;https://www.transl-gunsmoker.ru/2008/12/shell-folders.html&quot;&gt;программ, влезающих в недокументированные разделы реестра&lt;/a&gt;.&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
Это значение реестра никогда не было документировано, так как оно создавалось только для внутреннего использования, и в 64-разрядной версии Windows мы смогли избавиться от него в некоторых обстоятельствах.&lt;br /&gt;
&lt;br /&gt;
Ну, то есть, пока команда по совместимости приложений не нашла &lt;i&gt;Программу С Большим Именем&lt;/i&gt;, которая использовала это значение. Так что нам пришлось вернуть это значение обратно в реестр - по крайней мере, пока они не смогут найти способ поддерживать эту программу без этого значения в реестре (возможно, они смогут исправить программу &lt;a title=&quot;Когда программы предполагают, что система никогда не изменится, эпизод 3&quot; href=&quot;https://www.transl-gunsmoker.ru/2010/04/3.html&quot;&gt;регулировочной прокладкой&lt;/a&gt;)¹.&lt;br /&gt;
&lt;br /&gt;
&quot;Почему бы просто не сломать программу и не сказать разработчику, чтобы он исправил баг в своей программе?&quot;&lt;br /&gt;
&lt;br /&gt;
Легче сказать, чем сделать. Разработчик этой конкретной программы оказывается свирепым конкурентом Microsoft, который в прошлом &lt;a title=&quot;Однажды генеральный директор компании пожаловался в Конгресс на расширения файлов Windows&quot; href=&quot;https://www.transl-gunsmoker.ru/2020/03/that-time-ceo-of-company-complained-to-Congress-about-Windows-file-extensions.html&quot;&gt;громко жаловался всем, кто только будет слушать, что Microsoft намеренно ломает их программы, чтобы получить конкурентное преимущество&lt;/a&gt;. Нарушение работоспособности их программы будет просто приглашением для нового иска: &quot;Microsoft снова пользуется своим монопольным правом для подавления конкурентов&quot;.&lt;br /&gt;
&lt;br /&gt;
Такова жизнь команды совместимости приложений.&lt;br /&gt;
&lt;br /&gt;
¹ Я только что проверил, что в Windows 10 значение реестра окончательно пропало. Удаление ненужного значения заняло, сколько, 10 лет?</content><link rel='replies' type='application/atom+xml' href='http://www.transl-gunsmoker.ru/feeds/363896812789293917/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.transl-gunsmoker.ru/2020/03/sad-story-of-SMAccessoriesName.html#comment-form' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688132520265708820/posts/default/363896812789293917'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688132520265708820/posts/default/363896812789293917'/><link rel='alternate' type='text/html' href='http://www.transl-gunsmoker.ru/2020/03/sad-story-of-SMAccessoriesName.html' title='Печальная, но короткая история значения реестра SM_AccessoriesName'/><author><name>GunSmoker</name><uri>http://www.blogger.com/profile/15611696588191431330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhrYMBDZyhmwpngoHu4MbxoNS55hTzfYX9MRyMyiL7lk8FV4zzROyizfpqf_E85yuu8-BtGtcAZg_miWOVH_8I9hdkdBcDd5UkBhBcWo1jkcyacq_Lgcmt9gECy0zV0zNo/s220/avatar.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688132520265708820.post-7468588052875276918</id><published>2020-03-11T19:59:00.000+03:00</published><updated>2020-03-11T19:59:11.607+03:00</updated><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="забавно"/><title type='text'>Однажды генеральный директор компании пожаловался в Конгресс на расширения файлов Windows</title><content type='html'>Это перевод &lt;a href=&quot;&quot; title=&quot;https://devblogs.microsoft.com/oldnewthing/20190225-00/?p=101068&quot; title=&quot;That time the CEO of a company complained to Congress about Windows file extensions&quot;&gt;That time the CEO of a company complained to Congress about Windows file extensions&lt;/a&gt;. Автор: Реймонд Чен.&lt;br /&gt;
&lt;br /&gt;
В те времена, когда ненавидеть Microsoft было ещё более модно, чем сегодня (да, некоторые вещи никогда не выходят из моды), я помню, как генеральный директор одной технологической компании свидетельствовал перед Конгрессом, чтобы пожаловаться на файловые расширения Windows.&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
Генеральный директор жаловался на антиконкурентную практику и в качестве примера привёл как установка обновления Windows заставила Windows сбросить расширения файлов, используемые продуктом их компании, и переназначить их на обработчик Windows по умолчанию. Это было явным свидетельством антиконкурентного поведения, преднамеренного отключения программного обеспечения, произведённого конкурентом, и принуждения пользователей к использованию программного обеспечения, предоставленного Microsoft.&lt;br /&gt;
&lt;br /&gt;
Инженерной группе Windows было предложено расследовать это обвинение, учитывая только информацию, представленную на &lt;a href=&quot;https://www.c-span.org/&quot;&gt;C-SPAN&lt;/a&gt; (прим. пер.: Cable-Satellite Public Affairs Network - кабельная спутниковая сеть по связям с общественностью, американская сеть кабельного и спутникового телевидения, созданная в 1979 году отраслью кабельного телевидения в качестве некоммерческой государственной службы. Он транслирует многие материалы федерального правительства США, а также другие программы по связям с общественностью). Я не участвовал в расследовании, но я помню, что был сделан вывод о том, что программа компании неправильно регистрировала обработчик расширения файла, поэтому процесс обновления системы приводил к несогласованному состоянию в обработчиках файловых расширений, так что алгоритм разрешения конфликтов в конечном итоге выбирал программы с правильной регистрацией обработчика: программы Windows.&lt;br /&gt;
&lt;br /&gt;
Если бы инженеры этой компании просто взяли телефон и вызвали команду оболочки Windows, они могли бы исправить свой установщик, и все проблемы были бы решены. Это также сделало бы довольной и эту компанию - ну, за исключением того, что дружеские отношения с Microsoft не были частью их повествования.&lt;br /&gt;
&lt;br /&gt;
Поэтому я думаю, что это ещё один вариант идеи &lt;a title=&quot;Как-то раз клиент сообщил об ошибке на карте в Flight Simulator&quot; href=&quot;https://www.transl-gunsmoker.ru/2017/04/customer-reported-error-in-flight-simulator.html&quot;&gt;отправить жалобу сразу Биллу Гейтсу&lt;/a&gt;, но только она делает ещё один шаг вперёд. Вместо того, чтобы идти к генеральному директору Microsoft, вы идёте к ещё более высокому авторитету: правительству.&lt;br /&gt;
&lt;br /&gt;
Вот что происходит, когда Windows имеет не 100% обратную совместимость: есть шанс быть втянутым в расследование Конгресса.&lt;br /&gt;
&lt;br /&gt;
Бонусная болтовня: самое замечательное в отправке этих обвинений в Конгресс заключается в том, что нетехнические СМИ разнесут эту новость повсюду. Даже если вы неправы, это не имеет значения. Это стало правдой (что может сделать Microsoft? Выпустить пресс-релиз с объяснением ошибки? Кто-нибудь хотя бы прочитает его, тем более сообщил бы об этом?).&lt;br /&gt;
&lt;br /&gt;
Прим.пер.: я тут недавно наткнулся на статью в Forbes, написанную в паническом стиле: &quot;Microsoft снова налажала, новое обновление Windows блокирует ваш компьютер, удалите обновление немедленно&quot;, которое на самом деле говорит о &quot;менее одного процента пользователей сталкиваются с проблемами при установке обновления&quot;.</content><link rel='replies' type='application/atom+xml' href='http://www.transl-gunsmoker.ru/feeds/7468588052875276918/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.transl-gunsmoker.ru/2020/03/that-time-ceo-of-company-complained-to-Congress-about-Windows-file-extensions.html#comment-form' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688132520265708820/posts/default/7468588052875276918'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688132520265708820/posts/default/7468588052875276918'/><link rel='alternate' type='text/html' href='http://www.transl-gunsmoker.ru/2020/03/that-time-ceo-of-company-complained-to-Congress-about-Windows-file-extensions.html' title='Однажды генеральный директор компании пожаловался в Конгресс на расширения файлов Windows'/><author><name>GunSmoker</name><uri>http://www.blogger.com/profile/15611696588191431330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhrYMBDZyhmwpngoHu4MbxoNS55hTzfYX9MRyMyiL7lk8FV4zzROyizfpqf_E85yuu8-BtGtcAZg_miWOVH_8I9hdkdBcDd5UkBhBcWo1jkcyacq_Lgcmt9gECy0zV0zNo/s220/avatar.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688132520265708820.post-2029254030929654631</id><published>2020-02-25T13:41:00.000+03:00</published><updated>2020-05-23T17:42:57.257+03:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Win16"/><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="железо"/><title type='text'>Почему существуют тривиальные функции типа CopyRect и EqualRect?</title><content type='html'>Это перевод &lt;a href=&quot;https://devblogs.microsoft.com/oldnewthing/20200224-00/?p=103472&quot; title=&quot;Why are there trivial functions like Copy­Rect and Equal­Rect?&quot;&gt;Why are there trivial functions like Copy­Rect and Equal­Rect?&lt;/a&gt; Автор: Реймонд Чен.&lt;br /&gt;
&lt;br /&gt;
Если вы покопаетесь внутри &lt;code&gt;user32&lt;/code&gt;, вы увидите там некоторые, казалось бы, тривиальные функции - такие как &lt;code&gt;CopyRect&lt;/code&gt; и &lt;code&gt;EqualRect&lt;/code&gt;. Зачем нам вообще нужны целые функции для того, что можно сделать с помощью операторов &lt;code&gt;:=&lt;/code&gt; и &lt;code&gt;=&lt;/code&gt;?&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
Краткий ответ: потому что эти операторы генерируют кучу кода.&lt;br /&gt;
&lt;br /&gt;
Длинный ответ: посмотрите на код, который генерируется при использовании оператора присвоения (копирование прямоугольника):&lt;br /&gt;
&lt;pre&gt;c4 5e f0        les  bx, [bp-10]    ; es:bx -&gt; исходный прямоугольник (source)
26 8b 07        mov  ax, es:[bx]    ; ax = source.left
c4 5e ec        les  bx, [bp-14]    ; es:bx -&gt; целевой прямоугольник (dest)
26 89 07        mov  es:[bx], ax    ; dest.left = ax

c4 5e f0        les  bx, [bp-10]    ; es:bx -&gt; исходный прямоугольник (source)
26 8b 47 02     mov  ax, es:[bx+2]  ; ax = source.top
c4 5e ec        les  bx, [bp-14]    ; es:bx -&gt; целевой прямоугольник (dest)
26 89 47 02     mov  es:[bx+2], ax  ; dest.top = ax

c4 5e f0        les  bx, [bp-10]    ; es:bx -&gt; исходный прямоугольник (source)
26 8b 47 04     mov  ax, es:[bx+4]  ; ax = source.right
c4 5e ec        les  bx, [bp-14]    ; es:bx -&gt; целевой прямоугольник (dest)
26 89 47 04     mov  es:[bx+4], ax  ; dest.right = ax

c4 5e f0        les  bx, [bp-10]    ; es:bx -&gt; исходный прямоугольник (source)
26 8b 47 06     mov  ax, es:[bx+6]  ; ax = source.bottom
c4 5e ec        les  bx, [bp-14]    ; es:bx -&gt; целевой прямоугольник (dest)
26 89 47 06     mov  es:[bx+6], ax  ; dest.bottom = ax&lt;/pre&gt;
Этот код занимает 54 байт! Он получился ужасно неэффективным, потому что процессор 8086 может использовать непрямую адресацию только через регистры &lt;code&gt;bx&lt;/code&gt;, &lt;code&gt;bp&lt;/code&gt;, &lt;code&gt;si&lt;/code&gt; и &lt;code&gt;di&lt;/code&gt;. Регистр &lt;code&gt;bp&lt;/code&gt; зарезервирован для использования в качестве указателя фрейма, так что его можно сразу вычеркнуть. Регистры &lt;code&gt;si&lt;/code&gt; и &lt;code&gt;di&lt;/code&gt; используются как регистры-переменные, поэтому в них почти всегда лежит что-то нужное. Что оставляет нам единственный регистр, который можно использовать для разыменования указателей: &lt;code&gt;bx&lt;/code&gt;.&lt;br /&gt;
&lt;br /&gt;
Поскольку мы работаем с указателем 16:16 - нам также нужен регистр сегмента, а у 8086 есть только четыре сегментных регистра: &lt;code&gt;cs&lt;/code&gt; (сегмент кода), &lt;code&gt;ds&lt;/code&gt; (сегмент данных), &lt;code&gt;ss&lt;/code&gt; (сегмент стека), &lt;code&gt;es&lt;/code&gt; (дополнительный сегмент). У трёх из них уж есть предназначенные цели, поэтому остаётся только один: &lt;code&gt;es&lt;/code&gt;. Так что даже если бы мы могли временно использовать &lt;code&gt;si&lt;/code&gt; или &lt;code&gt;di&lt;/code&gt;, мы всё равно оказались бы в узком месте.&lt;br /&gt;
&lt;br /&gt;
Но если мы переместим код в функцию &lt;code&gt;CopyRect&lt;/code&gt;, то мы можем сэкономить кучу байт:
&lt;pre&gt;c4 5e f0        les  bx, [bp-10]    ; es:bx -&gt; исходный прямоугольник (source)
53              push bx
06              push es
c4 5e ec        les  bx, [bp-14]    ; es:bx -&gt; целевой прямоугольник (dest)
53              push bx
06              push es
9a xx xx xx xx  call CopyRect&lt;/pre&gt;
Всего 15 байт! Экономия почти в ЧЕТЫРЕ раза.&lt;br /&gt;
&lt;br /&gt;
Это была &lt;a title=&quot;Один байт = один доллар&quot; href=&quot;https://www.transl-gunsmoker.ru/2020/02/one-byte-used-to-cost-dollar.html&quot;&gt;эпоха экономии байт&lt;/a&gt;, поэтому любой приём, позволяющий сэкономить несколько байтов, заслуживал рассмотрения - особенно если учесть, что у вас было &quot;всего&quot; 256 Кб памяти¹.&lt;br /&gt;
&lt;br /&gt;
А поскольку копирование и сравнение прямоугольников были весьма частыми операциями, сворачивание кода в функцию позволяло сэкономить много байтов.&lt;br /&gt;
&lt;br /&gt;
Конечно, сегодня требуется уже не так много кода, чтобы скопировать прямоугольник вручную: весь прямоугольник помещается в один 128-битный регистр:
&lt;pre&gt;    mov    eax, [sourcerect]
    movups xmm0, [eax]
    mov    eax, [destrect]
    movups [eax], xmm0&lt;/pre&gt;
&lt;br /&gt;
¹ Слово &quot;всего&quot; взято в кавычки, потому что 256 Кб кажется ужасно малым размером памяти сегодня, но вспомните, что в те времена это было &lt;b&gt;максимумом&lt;/b&gt;, который вы могли поставить в IBM PC XT! По крайней мере, не прибегая к использованию карт расширений.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Бонусное обсуждение:&lt;/b&gt; мы могли бы убрать несколько инструкций, перемещая по два целых числа за раз. Это требует, чтобы два прямоугольника не перекрывались бы в памяти (чтобы избежать наложения данных) - но это, вероятно, безопасное предположение, потому что исходный код в этом случае так же не работал.
&lt;pre class=&quot;brush:delphi&quot;&gt;
var
  R1, R2: PRect;
  V: array[0..5] of Byte;
begin
  R1 := @V[0];
  R2 := @V[1]; // плохая идея&lt;/pre&gt;
Хорошо, раз переключение на перемещение двух целых чисел за раз не нарушает ничего, что ещё не было сломано, давайте сделаем это:
&lt;pre&gt;c4 5e f0        les  bx, [bp-10]    ; es:bx -&gt; исходный прямоугольник (source)
26 8b 07        mov  ax, es:[bx]    ; ax = source.left
26 8b 57 02     mov  dx, es:[bx+2]  ; dx = source.top
c4 5e ec        les  bx, [bp-14]    ; es:bx -&gt; целевой прямоугольник (dest)
26 89 07        mov  es:[bx], ax    ; dest.left = ax
26 89 57 02     mov  es:[bx+2], dx  ; dest.top = dx

c4 5e f0        les  bx, [bp-10]    ; es:bx -&gt; исходный прямоугольник (source)
26 8b 47 04     mov  ax, es:[bx+4]  ; ax = source.right
26 8b 57 06     mov  dx, es:[bx+6]  ; dx = source.bottom
c4 5e ec        les  bx, [bp-14]    ; es:bx -&gt; целевой прямоугольник (dest)
26 89 47 04     mov  es:[bx+4], ax  ; dest.right = ax
26 89 57 06     mov  es:[bx+6], dx  ; dest.bottom = dx&lt;/pre&gt;
Получаем 42 байта (против бывших 54). Уже лучше, но это всё равно почти в три раза больше вызова функции.&lt;br /&gt;
&lt;br /&gt;
Если у нас есть одна дополнительная свободная переменная (скажем, &lt;code&gt;si&lt;/code&gt;), то мы можем сократить этот код ещё больше:
&lt;pre&gt;c4 5e f0        les  bx, [bp-10]    ; es:bx -&gt; исходный прямоугольник (source)
26 8b 07        mov  ax, es:[bx]    ; ax = source.left
26 8b 57 02     mov  dx, es:[bx+2]  ; dx = source.top
26 8b 4f 04     mov  cx, es:[bx+4]  ; cx = source.right
26 8b 77 06     mov  si, es:[bx+6]  ; si = source.bottom
c4 5e ec        les  bx, [bp-14]    ; es:bx -&gt; целевой прямоугольник (dest)
26 89 07        mov  es:[bx], ax    ; dest.left = ax
26 89 57 02     mov  es:[bx+2], dx  ; dest.top = dx
26 89 4f 04     mov  es:[bx+4], cx  ; dest.right = cx
26 89 77 06     mov  es:[bx+6], si  ; dest.bottom = si&lt;/pre&gt;
Всего 36 байт. Уже лучше, но всё ещё более чем в два раза больше, чем вызов функции. И это также стоило нам одного регистра.&lt;br /&gt;
&lt;br /&gt;
Вот ещё один трюк: копирование прямоугольника через стек.
&lt;pre&gt;c4 5e f0        les  bx, [bp-10]    ; es:bx -&gt; исходный прямоугольник (source)
26 ff 37        push es:[bx]        ; push source.left
26 ff 77 02     push es:[bx+2]      ; push source.top
26 ff 77 04     push es:[bx+4]      ; push source.right
26 8b 77 06     push es:[bx+6]      ; push source.bottom
c4 5e ec        les  bx, [bp-14]    ; es:bx -&gt; целевой прямоугольник (dest)
26 8f 47 06     pop  es:[bx+6]      ; pop dest.bottom
26 8f 47 04     pop  es:[bx+4]      ; pop dest.right
26 8f 47 02     pop  es:[bx+2]      ; pop dest.top
26 8f 47        pop  es:[bx]        ; pop dest.left&lt;/pre&gt;
Хм, получается столько же, сколько и с регистрами. Но хотя бы в этот раз нам не потребовался свободный регистр.&lt;br /&gt;
&lt;br /&gt;
Хорошо, а если мы позаимствуем регистр &lt;code&gt;ds&lt;/code&gt;, а также и &lt;code&gt;si&lt;/code&gt; и &lt;code&gt;di&lt;/code&gt;?
&lt;pre&gt;1e              push ds
c5 7e ec        lds  di, [bp-14]
c4 76 f0        les  si, [bp-10]
fc              cld
a5              movsw
a5              movsw
a5              movsw
a5              movsw
1f              pop  ds&lt;/pre&gt;
Ух ты! Тринадцать байт!</content><link rel='replies' type='application/atom+xml' href='http://www.transl-gunsmoker.ru/feeds/2029254030929654631/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.transl-gunsmoker.ru/2020/02/why-are-there-trivial-functions-like-CopyRect-and-EqualRect.html#comment-form' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688132520265708820/posts/default/2029254030929654631'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688132520265708820/posts/default/2029254030929654631'/><link rel='alternate' type='text/html' href='http://www.transl-gunsmoker.ru/2020/02/why-are-there-trivial-functions-like-CopyRect-and-EqualRect.html' title='Почему существуют тривиальные функции типа CopyRect и EqualRect?'/><author><name>GunSmoker</name><uri>http://www.blogger.com/profile/15611696588191431330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhrYMBDZyhmwpngoHu4MbxoNS55hTzfYX9MRyMyiL7lk8FV4zzROyizfpqf_E85yuu8-BtGtcAZg_miWOVH_8I9hdkdBcDd5UkBhBcWo1jkcyacq_Lgcmt9gECy0zV0zNo/s220/avatar.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688132520265708820.post-1842108369279156562</id><published>2020-02-25T13:38:00.001+03:00</published><updated>2020-02-25T13:39:55.077+03:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Win16"/><category scheme="http://www.blogger.com/atom/ns#" term="История"/><category scheme="http://www.blogger.com/atom/ns#" term="железо"/><title type='text'>Один байт = один доллар</title><content type='html'>Это перевод &lt;a href=&quot;https://devblogs.microsoft.com/oldnewthing/20190827-00/?p=102809&quot; title=&quot;One byte used to cost a dollar&quot;&gt;One byte used to cost a dollar&lt;/a&gt;. Автор: Реймонд Чен.&lt;br /&gt;
&lt;br /&gt;
В те времена, когда программное обеспечение распространялось на дискетах (&lt;a title=&quot;История о USB-флоппиках&quot; href=&quot;https://www.transl-gunsmoker.ru/2009/03/usb.html&quot;&gt;помните дискеты?&lt;/a&gt;), для Windows было правило: &lt;i&gt;один байт стоит один доллар&lt;/i&gt;.&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
Другими словами, принимая во внимание стоимость материалов, дополнительное время изготовления, вклад в вес изделия, стоимость замены материалов, которые стали дефектными после того, как они покинули завод (например, во время доставки), &lt;a href=&quot;https://www.transl-gunsmoker.ru/2010/09/windows-95.html&quot; title=&quot;Сколько дискет занимал дистрибутив Windows 95?&quot;&gt;после учёта сжатия данных&lt;/a&gt; и т.д. - дополнительные затраты на добавление ещё одного мегабайта к продукту Windows составляли около &lt;b&gt;одного миллиона долларов&lt;/b&gt;, или около доллара за байт.&lt;br /&gt;
&lt;br /&gt;
Это было милое правило, потому что оно позволяло вам назначить (по общему признанию, несколько искусственную) денежную стоимость раздуванию кода. Может быть, ваша функция даже не стоила своего дискового пространства?&lt;br /&gt;
&lt;br /&gt;
Конечно же, появление компакт-дисков в качестве основного средства распространения программ изменило математику, но беспокойство по поводу размера операционной системы всё ещё оставалось. Насколько я понимаю, компакт-диск Windows Server 2003 был в основном &quot;полным&quot;. Лично вам могло так не казаться, но вспомните, что ваш CD, вероятно, является 32-битной английской версией. Для переводов на другие языки необходимо зарезервировать дополнительное пространство. И не забывайте, что 64-разрядная версия Windows примерно в два раза больше, чем 32-разрядная версия, поскольку она должна содержать две операционные системы: родную 64-разрядную и эмулируемую 32-битную (ну не ровно в два раза, потому что некоторые файлы могут быть общими, а многие 32-битные компоненты могут быть удалены).&lt;br /&gt;
&lt;br /&gt;
А потом дистрибутив перешёл на DVD, а затем и вообще стал полностью сетевым. Интересно, какова цена за байт в настоящее время. Стоимость теперь находится в полосе пропускания, но она всё ещё стоит денег.</content><link rel='replies' type='application/atom+xml' href='http://www.transl-gunsmoker.ru/feeds/1842108369279156562/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.transl-gunsmoker.ru/2020/02/one-byte-used-to-cost-dollar.html#comment-form' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688132520265708820/posts/default/1842108369279156562'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688132520265708820/posts/default/1842108369279156562'/><link rel='alternate' type='text/html' href='http://www.transl-gunsmoker.ru/2020/02/one-byte-used-to-cost-dollar.html' title='Один байт = один доллар'/><author><name>GunSmoker</name><uri>http://www.blogger.com/profile/15611696588191431330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhrYMBDZyhmwpngoHu4MbxoNS55hTzfYX9MRyMyiL7lk8FV4zzROyizfpqf_E85yuu8-BtGtcAZg_miWOVH_8I9hdkdBcDd5UkBhBcWo1jkcyacq_Lgcmt9gECy0zV0zNo/s220/avatar.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688132520265708820.post-717697219406192647</id><published>2020-01-17T00:13:00.001+03:00</published><updated>2020-01-17T00:13:29.106+03:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Win32"/><category scheme="http://www.blogger.com/atom/ns#" term="кодинг"/><category scheme="http://www.blogger.com/atom/ns#" term="не делай так"/><title type='text'>Подводный камень: размещение окна под TopMost окном сделает окно TopMost</title><content type='html'>Это перевод &lt;a href=&quot;https://devblogs.microsoft.com/oldnewthing/20200101-00/?p=103285&quot; title=&quot;Nasty gotcha: Positioning your window beneath a topmost window makes it topmost&quot;&gt;Nasty gotcha: Positioning your window beneath a topmost window makes it topmost&lt;/a&gt;. Автор: Реймонд Чен.&lt;br /&gt;
&lt;br /&gt;
У &lt;a href=&quot;https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-deferwindowpos&quot; title=&quot;MSDN: DeferWindowPos function&quot;&gt;функции &lt;code&gt;DeferWindowPos&lt;/code&gt;&lt;/a&gt; (и, следовательно, любой функции на её основе, типа &lt;a href=&quot;https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setwindowpos&quot; title=&quot;MSDN: SetWindowPos Function&quot;&gt;&lt;code&gt;SetWindowPos&lt;/code&gt;&lt;/a&gt;) есть неприятный подводный камень: если вы укажете &lt;code&gt;hwndInsertAfter&lt;/code&gt;, а окно insert-after будет самым верхним (TopMost), то позиционируемое окно будет размещено сразу после этого окна.&lt;br /&gt;
&lt;br /&gt;
Пока всё идёт нормально.&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
Подводный камень заключается в том, что ваше окно будет размещено в той же зоне, что и окно insert-after (которое лежит в зоне TopMost!)&lt;br /&gt;
&lt;br /&gt;
Другими словами, окно будет сделано TopMost окном - даже если раньше оно не было TopMost окном.&lt;br /&gt;
&lt;br /&gt;
Это будет, вероятно, не тем, что вы ожидали.</content><link rel='replies' type='application/atom+xml' href='http://www.transl-gunsmoker.ru/feeds/717697219406192647/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.transl-gunsmoker.ru/2020/01/topmost-topmost.html#comment-form' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688132520265708820/posts/default/717697219406192647'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688132520265708820/posts/default/717697219406192647'/><link rel='alternate' type='text/html' href='http://www.transl-gunsmoker.ru/2020/01/topmost-topmost.html' title='Подводный камень: размещение окна под TopMost окном сделает окно TopMost'/><author><name>GunSmoker</name><uri>http://www.blogger.com/profile/15611696588191431330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhrYMBDZyhmwpngoHu4MbxoNS55hTzfYX9MRyMyiL7lk8FV4zzROyizfpqf_E85yuu8-BtGtcAZg_miWOVH_8I9hdkdBcDd5UkBhBcWo1jkcyacq_Lgcmt9gECy0zV0zNo/s220/avatar.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688132520265708820.post-8878092926068631082</id><published>2020-01-17T00:02:00.002+03:00</published><updated>2020-01-17T00:02:33.541+03:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Win32"/><category scheme="http://www.blogger.com/atom/ns#" term="Совместимость"/><category scheme="http://www.blogger.com/atom/ns#" term="кодинг"/><title type='text'>Почему я не могу переместить FOLDERID_LocalAppData с помощью SHSetKnownFolderPath?</title><content type='html'>Это перевод &lt;a href=&quot;https://devblogs.microsoft.com/oldnewthing/20200115-00/?p=103329&quot; title=&quot;Why can’t I use SHSetKnownFolderPath to change the location of FOLDERID_LocalAppData?&quot;&gt;Why can’t I use SHSetKnownFolderPath to change the location of FOLDERID_LocalAppData?&lt;/a&gt; Автор: Реймонд Чен.&lt;br /&gt;
&lt;br /&gt;
Заказчик заметил, что его модульный тест начал давать сбой в Windows 10 версии 1803. В частности, он не прошел здесь:
&lt;pre class=&quot;brush:delphi&quot;&gt;SHSetKnownFolderPath(FOLDERID_LocalAppData, 0, 0, temporaryDirectory);&lt;/pre&gt;
с ошибкой &lt;code&gt;E_INVALIDARG&lt;/code&gt;. Что изменилось в Windows 10 версии 1803, из-за чего этот вызов стал недействительным?&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
Изменение в блокировании перемещения папки &lt;code&gt;LocalAppData&lt;/code&gt; (а также её близкого личного друга &lt;code&gt;LocalAppDataLow&lt;/code&gt;) стало лишь подтверждением реальности: да, более ранние версии Windows позволяли перемещать эти каталоги, но в результате профиль пользователя оказывался повреждён. Вам приходилось удалять пользователя и начинать всё сначала.&lt;br /&gt;
&lt;br /&gt;
Причина в том, что в этих каталогах есть некоторые системные файлы, которые постоянно заблокированы и, следовательно, не могут быть перемещены в их новый дом. В следующий раз, когда пользователь войдёт в систему, система попытается снова открыть файлы и не сможет, и дальше всё идёт под откос.&lt;br /&gt;
&lt;br /&gt;
Перемещение этих каталогов заблокировано, чтобы вы не могли выстрелить себе в ногу. Это признание того факта, что это не только не работает, но и портит профиль пользователя.</content><link rel='replies' type='application/atom+xml' href='http://www.transl-gunsmoker.ru/feeds/8878092926068631082/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.transl-gunsmoker.ru/2020/01/why-cant-i-use-shsetknownfolderpath-to-change-location-FOLDERIDLocalAppData.html#comment-form' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688132520265708820/posts/default/8878092926068631082'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688132520265708820/posts/default/8878092926068631082'/><link rel='alternate' type='text/html' href='http://www.transl-gunsmoker.ru/2020/01/why-cant-i-use-shsetknownfolderpath-to-change-location-FOLDERIDLocalAppData.html' title='Почему я не могу переместить FOLDERID_LocalAppData с помощью SHSetKnownFolderPath?'/><author><name>GunSmoker</name><uri>http://www.blogger.com/profile/15611696588191431330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhrYMBDZyhmwpngoHu4MbxoNS55hTzfYX9MRyMyiL7lk8FV4zzROyizfpqf_E85yuu8-BtGtcAZg_miWOVH_8I9hdkdBcDd5UkBhBcWo1jkcyacq_Lgcmt9gECy0zV0zNo/s220/avatar.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688132520265708820.post-7030374603180756230</id><published>2020-01-16T23:37:00.000+03:00</published><updated>2020-01-16T23:37:08.107+03:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Win32"/><category scheme="http://www.blogger.com/atom/ns#" term="История"/><category scheme="http://www.blogger.com/atom/ns#" term="кодинг"/><category scheme="http://www.blogger.com/atom/ns#" term="окна"/><title type='text'>Почему последний выбранный файл появляется первым, когда я выбираю несколько файлов в диалоге открытия файла?</title><content type='html'>Это перевод &lt;a href=&quot;https://devblogs.microsoft.com/oldnewthing/20190409-00/?p=102406&quot; title=&quot;When I select multiple files in the File Open dialog, why does the last item come first?&quot;&gt;When I select multiple files in the File Open dialog, why does the last item come first?&lt;/a&gt; Автор: Реймонд Чен.&lt;br /&gt;
&lt;br /&gt;
Если у вас есть диалог открытия файла, который поддерживает множественный выбор, и вы выбираете несколько файлов, вы можете заметить, что &lt;a title=&quot;&quot; href=&quot;https://blogs.msdn.microsoft.com/oldnewthing/20110509-00/?p=10713#comment-912033&quot;&gt;файл, выбранный последним, указывается первым&lt;/a&gt;. Это раздражает, если порядок файлов при выборе является важным (прим. пер.: например, при добавлении фото в альбом). Почему последний выбранный элемент становится первым в списке?&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
Это случай утечки абстракции.&lt;br /&gt;
&lt;br /&gt;
Диалог открытия файлов показывает список выбранных элементов в том же порядке, в котором они возвращаются методом &lt;a href=&quot;https://docs.microsoft.com/en-us/windows/desktop/api/shobjidl_core/nf-shobjidl_core-ishellview-getitemobject&quot; title=&quot;MSDN: IShellView::GetItemObject Method&quot;&gt;&lt;code&gt;IShellView.GetItemObject&lt;/code&gt;&lt;/a&gt; расширения пространства имён:&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;var
  pdto: IDataObject;
begin
  ShellView.GetItemObject(SVGIO_SELECTION, IID_PPV_ARGS, pdto);&lt;/pre&gt;
Если используется расширение, предоставленное системой, то оно возвращает выбранные элементы в том порядке, в котором они отображаются в представлении (view), за исключением того, что текущий фокусированный элемент всегда идёт первым.&lt;br /&gt;
&lt;br /&gt;
Хорошо, тогда почему сфокусированный предмет стоит первым?&lt;br /&gt;
&lt;br /&gt;
Для того, чтобы получить лучшее контекстное меню.&lt;br /&gt;
&lt;br /&gt;
Когда вы вызываете контекстное меню для группы файлов, оболочка должна решить, какой обработчик контекстного меню будет вызван для предоставления действий (verb). К примеру, вы могли бы запросить своё контекстное меню у каждого отдельного файла, а затем как-то получить итоговое меню. Возможно, вы показали бы &lt;i&gt;пересечение&lt;/i&gt; всех контекстных меню (общие элементы). Может быть, вы показали бы &lt;i&gt;объединение&lt;/i&gt; всех контекстных меню - и если пользователь выбирает действие, которое недоступно для каких-то элементов, то вы просто игнорируете элементы, которые не могут его выполнить.&lt;br /&gt;
&lt;br /&gt;
Но всё это было бы достаточно сложно, хотя бы потому, что сопоставление действий с другом контекстном меню - это эвристика и догадки. Если в двух контекстных меню есть действие &quot;Сделать текущим&quot; - означает ли это, что они эквивалентны?&lt;br /&gt;
&lt;br /&gt;
Поэтому, вместо того, чтобы создавать большое количество контекстных меню и пытаться объединить их не совсем бессмысленным способом, оболочка просто выбирает один из элементов и говорит: &quot;Окей, ты - ответственный за меню&quot;. И действия от этого файла будут действиями для всего выделения. (Также не забывайте, что у нас было аж 4 &lt;a href=&quot;https://www.transl-gunsmoker.ru/2013/03/why-does-explorer-use-term-KB-instead-KiB.html&quot; title=&quot;Почему Проводник показывает сокращение KB вместо KiB?&quot;&gt;МБ&lt;/a&gt; памяти для работы).&lt;br /&gt;
&lt;br /&gt;
Окей, мы приближаемся к ответу.&lt;br /&gt;
&lt;br /&gt;
Если вы выберете несколько файлов, а затем щёлкните правой кнопкой мыши на выделении, оболочка должна выбрать кого-то, кто будет создавать контекстное меню. И алгоритм, который она использует - это &quot;Я выберу сфокусированный элемент&quot;. А способ выбора, заключается в установке сфокусированного элемента в качестве первого элемента.&lt;br /&gt;
&lt;br /&gt;
Теперь мы можем раскручиваться в обратную сторону. Сфокусированный элемент ставится первым элементом при выборе. В диалоге открытия файлы элементы в строке ввода идут в том же порядке, в котором они отображаются при выборе. В результате сфокусированный элемент (a.k.a. последний выбранный) появляется первым в строке редактирования.&lt;br /&gt;
&lt;br /&gt;
Какое-то время спустя (я не знаю когда точно), в метод &lt;code&gt;IShellView.GetItemObject&lt;/code&gt; был добавлен флаг &lt;code&gt;SVGIO_FLAG_VIEWORDER&lt;/code&gt;, который сообщает представлению: &quot;Не помещай сфокусированный элемент первым. Просто верни мне всё в том же порядке, в котором они отображаются&quot;.&lt;br /&gt;
&lt;br /&gt;
Когда этот флаг был добавлен, никто не почесался модифицировать диалог открытия файла, чтобы тот использовал его. Диалог по-прежнему запрашивает выбранные элементы старомодным способом, а старомодный способ - вернуть выбранный элемент первым.&lt;br /&gt;
&lt;br /&gt;
Но подождите, вы здесь не совсем беспомощны. Если вы так возмущены, то можете &lt;a href=&quot;https://www.microsoft.com/store/apps/app/9nblggh4r32n&quot; title=&quot;Центр отзывов&quot;&gt;отправить просьбу&lt;/a&gt; использовать флаг &lt;code&gt;SVGIO_FLAG_VIEWORDER&lt;/code&gt; в диалоге открытия файла, чтобы выбранные элементы отображались бы в том же порядке. Если эта просьба получит достаточно голосов, то что-нибудь произойдёт (может быть). Эй, это сработало для персидского календаря.</content><link rel='replies' type='application/atom+xml' href='http://www.transl-gunsmoker.ru/feeds/7030374603180756230/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.transl-gunsmoker.ru/2020/01/why-does-last-item-come-first.html#comment-form' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688132520265708820/posts/default/7030374603180756230'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688132520265708820/posts/default/7030374603180756230'/><link rel='alternate' type='text/html' href='http://www.transl-gunsmoker.ru/2020/01/why-does-last-item-come-first.html' title='Почему последний выбранный файл появляется первым, когда я выбираю несколько файлов в диалоге открытия файла?'/><author><name>GunSmoker</name><uri>http://www.blogger.com/profile/15611696588191431330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhrYMBDZyhmwpngoHu4MbxoNS55hTzfYX9MRyMyiL7lk8FV4zzROyizfpqf_E85yuu8-BtGtcAZg_miWOVH_8I9hdkdBcDd5UkBhBcWo1jkcyacq_Lgcmt9gECy0zV0zNo/s220/avatar.png'/></author><thr:total>0</thr:total></entry></feed>