Тема в принципе заезженная, поэтому решил написать в ветке FAQ (да и вообще, моей любимой ).
В общем, проблема такая. Когда делаешь какую-либо программу на Delphi 7, и в ней, допустим, пытаешься создать какой-либо файл в корне диска, то в Windows XP все работает нормально, а в Windows 7 — файл вместо корня диска создается в папке C:UsersVadimAppDataLocalVirtualStore.
Ну, как и следует, видимо из соображений безопасности. Для того, чтобы создать файл именно в корне диска C — приходиться запускать программу с правами администратора, щелкать по кнопке OK в UAC-окне. Проблема в следующем, что мне надоело каждый раз принудительно щелкать правой кнопкой мыши и запускать программу с администраторскими правами.
Решение есть — поставить в свойствах галочку — запустить программу с правами администратора. Но оно меня не очень устраивает, поскольку, если сменить расположение программы, то галочку приходится ставить заново, да и вообще, выходит мне так придеться ставить галочки почти для каждой написанной мною программы? — Не выход. Отключать UAC я тоже не хочу, все же раз придумали эту штуку как средство защиты, пускай есть, пригодится. Потому пришёл к выводу, что надо сделать так, чтобы программа сама запускала себя сразу с правами администратора. Погуглив, я нашёл способ сделать такое с помощью manifest-файла, правда везде этот способ описывался для Visual Studio C++. А мне надо для Delphi 7, все же, хоть и мертвый язык, для меня он привычней
Что делать, если не запускается или не уcтанавливается программа на Windows
Вот, стал гуглить, как запустить программу с правами администратора для Delphi, а параллельно наткнулся на код, который определяет, запущена ли программа уже с правами администратора или нет:
const SECURITY_NT_AUTHORITY: TSIDIdentifierAuthority = (Value: (0, 0, 0, 0, 0, 5)); SECURITY_BUILTIN_DOMAIN_RID = $00000020; DOMAIN_ALIAS_RID_ADMINS = $00000220; function IsAdmin: Boolean; var hAccessToken: THandle; ptgGroups: PTokenGroups; dwInfoBufferSize: DWORD; psidAdministrators: PSID; x: Integer; bSuccess: BOOL; begin Result := False; bSuccess := OpenThreadToken(GetCurrentThread, TOKEN_QUERY, True, hAccessToken); if not bSuccess then begin if GetLastError = ERROR_NO_TOKEN then bSuccess := OpenProcessToken(GetCurrentProcess, TOKEN_QUERY, hAccessToken); end; if bSuccess then begin GetMem(ptgGroups, 1024); bSuccess := GetTokenInformation(hAccessToken, TokenGroups, ptgGroups, 1024, dwInfoBufferSize); CloseHandle(hAccessToken); if bSuccess then begin AllocateAndInitializeSid(SECURITY_NT_AUTHORITY, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, psidAdministrators); for x := 0 to ptgGroups.GroupCount — 1 do if EqualSid(psidAdministrators, ptgGroups.Groups[x].Sid) then begin Result := True; Break; end; FreeSid(psidAdministrators); end; FreeMem(ptgGroups); end; end; procedure TForm1.Button1Click(Sender: TObject); begin if isAdmin then ShowMessage(‘Logged in as Administrator’); end;
Но, проблема в том, что этот код не рабочий. Он всегда выдает сообщение, что программа запущена с правами администратора.
Микропрограммное обеспечение Autocom/Delphi || Решение проблемы
Поэтому, я нашёл другой выход из этой ситуации, правда кривой на мой взгляд. А именно, программе без администраторских прав запрещено создавать exe-файлы в корне диска, даже если в файл будет создаваться в VirtualStore. Потому я пытаюсь создать какую-либо exe-шку в корне диска, и, если это сделать не получается, то программа запущена без администраторских прав, иначе все хорошо Способ, понятное дело, очень не хороший, так как программа с таким именем может уже существовать в корне диска, да и в целом, левый способ. Хотя, если бы можно было внедрять manifest-файл в Delphi, так же, как и в Visual Stidio C++, то проблема определения, запущена ли программа с администраторскими правами, отпадет сама
Но это только половина дела, определить, как запущена программа, ведь если она запущена не под админом, то ей стоит перезапустить саму себя уже с администраторскими правами. Вот, опять погуглил эту проблему, нашёл код с функцией CreateProcessWithLogonW:
Но эта функция всегда выводит мне сообщение «Краххх..»
Не знаю, почему так. Кто-то говорит, что это из-за того, что пароль стоит пустой, и таким образом это такая защита.
В общем, проблема такая, что надо программно запустить самого себя с правами администратора. Сделать это надо средствами Delphi 7, было бы обидно, что если такая возможность была только в C++.
Vadik(R)
Погуглив далее, я всё же нашёл рабочую функцию запуска программы с правами администратора:
procedure RunAsAdministrator(const source: string); var shExecInfo: PSHELLEXECUTEINFOA; begin New(shExecInfo); shExecInfo^.cbSize := sizeof(SHELLEXECUTEINFO); shExecInfo^.fMask := 0; shExecInfo^.Wnd := 0; shExecInfo^.lpVerb := ‘runas’; shExecInfo^.lpFile := PAnsiChar(ExtractFileName(source)); shExecInfo^.lpParameters := »; shExecInfo^.lpDirectory := PAnsiChar(ExtractFilePath(source)); shExecInfo^.nShow := SW_SHOWNORMAL; shExecInfo^.hInstApp := 0; ShellExeCuteex(shExecInfo); Dispose(shExecInfo); shExecInfo := nil; end;
Вот. Правда так и не научился нормально определять, запущена ли программа уже с правами администратора или нет.
Более того, научился вставлять свой манифест в программу, в результате чего происходит автоматический запрос прав администратора
Теперь хотелось бы пойти дальше. Каким образом тогда программы, типа антивируса касперского при запуске больше не запрашивают уровень администратора? То есть, они запросили его однажды при первом запуске (при установке), что-то где-то прописали и теперь всегда запускаются с правами администратора, без окошка UAC. Как они такое сделали?
sinkopa
Теперь хотелось бы пойти дальше. Каким образом тогда программы, типа антивируса касперского при запуске больше не запрашивают уровень администратора? То есть, они запросили его однажды при первом запуске (при установке), что-то где-то прописали и теперь всегда запускаются с правами администратора, без окошка UAC. Как они такое сделали?
А просто они (антивири) уже давно службы и следовательно запускаются не от имени пользователя, а от имени системы. улавливаете разницу?
А что касается определения того есть ли админские права.
Вот код юнита (источник — форум на
Ссылка скрыта от гостей
Пример использования
. uses unitIsAdmin; . if IsUserAnAdmin then showmessage(‘running in administrator mode’);
Источник: codeby.net
Не запускается delphi 7 на windows 7
Tuasun › Блог › Установка программы Делфи на компьютер
Установка программы Делфи на компьютер Скачайте программу с ЯД или скопируйте с СД-диска на компьютер – системный диск С в корень. Прежде чем устанавливать программу, подготовьте ваш компьютер:
— отключите антивирус (Касперский, НОД, Аваст или McAfee удалить!)
— отключите системный защитник Windows
— на Windows 7 Prof, Windows 8 / 10 отключить обязательную проверку электронной подписи драйвера:
ОТКЛЮЧЕНИЕ ЭЛЕКТРОННОЙ ПОДПИСИ ДРАЙВЕРА НА WINDOWS10 ДЛЯ УСТАНОВКИ ДИАГНОСТИЧЕСКОГО ОБОРУДОВАНИЯ И ЕГО НОРМАЛЬНОЙ РАБОТЫ:
1.Пуск — Параметры — Обновление и Восстановление — Восстановление — Перезапустить компьютер
2. Диагностика — Доп. параметры — параметры загрузки — Перезагрузить
3. Отключить обязательную проверку подписи драйвера — отключить антивирус (либо приостановить)
Windows 8.1: откройте панель справа => выберите «Параметры» => Изменение параметров компьютера => Обновление и восстановление => Восстановление => в пункте «Особые варианты загрузки» выберите «Перезагрузить сейчас».
После перезагрузки перед вами откроется меню в котором нужно выбрать «Поиск и устранение неисправностей».
откройте «Дополнительные параметры». Нажмите кнопку «Перезагрузить». После перезагрузки перед вами откроется меню настройки, в котором вы сможете как отключить некоторые настройки, так и включить различные варианты загрузки Windows. Действия здесь нужно проводить кнопками F1-F9 или 1-9, в нашем случаи нужно отключить обязательную проверку подписи драйверов, нажимаем клавишу F7 или 7. На 7-ке:
1. Перезагрузите ПК — при начале загрузки ОС несколько раз нажмите на клавиатуре кнопку F8, пока не появится список на черном фоне. Выберите «Отключение обязательной проверки электронной подписи драйвера. Заходим в Системный защитник (если это ОС 8 и 10), отключаем защиту в реальном времени, далее листам вниз, находим «Добавить в исключение», добавляем папку в программой, которая должна у Вас храниться в корне системного диска С вашего ПК. Теперь можно приступать к установке Программы. Запускаем MAIN.EXE от имени администратора
Жмем на стрелочку далее Нажимаем «Старт», жмем «ДА», сохраняем файл на рабочий стол ПК. Отправляем файл для активации на почту продавца, у кого купили или мне (платно активирую, так как кейген был мною куплен.
Программу на время активации НЕ ЗАКРЫВАЕМ! Мы вышлем активированный файл. Сохраните его на рабочий стол своего ПК с заменой Возвращаемся к окну программы. Жмем «Старт», выбираем «НЕТ», выбираем файл активации с рабочего стола Все. Программа запустится. В настройках можно поменять язык Источник
Не запускается delphi 7 на windows 7
Вста.вьте/смонтируйте диск с Delphi и запустите установщик, если он не запустился сам. UAC (если он включен) потребует от вас элевации – соглашайтесь. Тут же выскочит помощник по совместимости программ, сообщая, что Delphi 7 не полностью совместима с Windows 7 2. Вы можете нажать на “Поиск решений в Интернете”, но это не сильно поможет: 3. Сейчас мы просто нажимаем “Запуск программы”. Откроется меню установщика: 4. Выбираем “Delphi 7”, запускается мастер установки программы: 5. Сама установка проходит без каких-либо проблем или неожиданностей – всё, как обычно. Можно просто жать “Next”. Единственный момент: выбор места для установки Есть два варианта:
Либо вы соглашаетесь с выбором по-умолчанию (в Program Files). В этом случае для работоспособности Delphi 7 после установки вам нужно будет сделать два дополнительных действия (см. ниже).
Либо вы устанавливаете Delphi в любую другую папку, не требующую прав администратора для записи (да хоть в Мои документы). Этот способ хоть и проще, но не рекомендуется.
В любом случае, далее установка проходит как обычно
При запуске Delphi 7 снова появится помощник по совместимости программ, сообщая, что с этой программой есть проблемы
Как и ранее, поиск решений ничего не даст. В этот раз мы закроем окно, предварительно установив галочку “Больше не показывать это сообщение”. Чтобы исправить это, нам нужно разрешить обычным пользователям машины изменять содержимое этой папки. Для этого откройте папку с установленной Delphi (C:Program FilesBorlandDelphi 7), щёлкните правой кнопкой по папке Bin и выберите “Свойства”. Перейдите на вкладку “Безопасность” и нажмите “Изменить”:
Установите галочку “Изменение” для группы “Пользователи”. Закройте все окна по OK.
Измените права аналогичным образом для подпапки Projects (этот шаг можно пропустить, если вы храните свои проекты в другом месте). Источник
Установка Delphi 7 на Windows 7 и решение проблем совместимости
Delphi 7 – интегрированная среда на языке Delphi, предназначения для разработки ПО для Microsoft Windows. Но прежде чем вы станете разработчиком, вам необходимо правильно установить и настроить Delphi 7, чтобы не возникало проблем с совместимостью. Особенно это актуально для пользователей, которые установили Windows 7 – здесь ошибки появляются так часто, что без инструкции не разобраться. Поэтому давайте посмотрим, как установить Delphi 7 на Windows 7.
Установка
Если вы не хотите решать проблемы с совместимостью, настоятельно рекомендуете задуматься об установке более современной версии Delphi.
Вставьте диск или смонтируйте образ. Если установщик не запустится автоматически, откройте его вручную.
На экране появится сообщение об обнаружении проблем совместимости. Можно нажать «Поиск решений», однако вряд ли вам поможет эта функция. Поэтому просто нажмите «Запуск программы», чтобы приступить к инсталляции. В главном меню выберите «Delphi 7», чтобы запустить мастера установки.Никаких неожиданностей при инсталляции не будет: серийный номер, лицензионное соглашение, тип установки (полный, компактный, ручной). Единственный момент – выбор папки, в которую сохранятся все файлы программы. Здесь есть два варианта:
- Оставить путь по умолчанию (Program Files), тогда вам придется выполнить два дополнительных действия, чтобы заставить программу работать корректно.
- Выбрать любую папку, не требующую для записи прав администратора (например, «Мои документы»). Этот способ проще, но использовать его не рекомендуется.
Дальше нужно лишь дождаться окончания инсталляции и попытаться запустить Delphi 7 на Windows 7.
После инсталляции
При попытке включить программу появится сообщение об обнаружении ошибки совместимости. Поиск решения в интернете ничего не даст, поэтому надеяться нужно только на свои силы.
Если вы выбрали вариант со стандартной установкой и скопировали все файлы приложения в директорию «Program Files», вам нужно изменить разрешения.
- Пройдите по пути C:Program FilesBorlandDelphi 7.
- Найдите папку «Bin» и щелкните на неё правой кнопкой.
- Откройте «Свойства» и перейдите на вкладку «Безопасность».
- Нажмите «Изменить» и установите для группы пользователи галочку «Разрешить» в строке «Изменение».
- Выйдите, нажав кнопку «ОК».
Первый запуск программы необходимо произвести от имени администратора. Второй и последующий старт Delphi 7 можно делать в обычном режиме.
Дело в том, что во время первого запуска перезаписываются файлы в директории с программой. Современные же системы Windows по умолчанию запрещают любые манипуляции с этими файлами, что в итоге приводит к появлению ошибок.
Добавление справочной системы
После изменения разрешений Delphi 7 можно использовать для написания программ. Но если вы нажмете клавишу F1, чтобы вызвать справку, то получите сообщение о том, что старая справочная система больше не является частью Windows.
В сообщении должна быть ссылка на статью из базы знаний Microsoft. Перейдите в неё и скачайте обновления для справочной системы. Важно правильно указать версию Windows, а также пройти проверку подлинности ОС.
После этого Delphi 7 будет работать в привычном режиме, не выдавая больше никаких ошибок совместимости.
Источник: onecomp.ru
Блог GunSmoker-а (переводы)
. when altering one’s mind becomes as easy as programming a computer, what does it mean to be human.
четверг, 4 августа 2011 г.
Недокументированный CreateProcess
Прим.пер.: эта статья была написана относительно давно. Некоторые возможности, упомянутые в ней, с тех пор стали документированы. В этом случае я заменил придуманные автором имена на те, которые сейчас используются в документации MSDN. И в любом случае, стандартное предупреждение — использовать хаки только как последнее средство.
Этот мануал является частью новой серии, которая будет сосредоточена на некоторых не-GUI вопросах, связанных с программированием в Windows. Предметом этого мануала будет Win32 API функция CreateProcess . Эта статья разделена на несколько секций, каждая из которых описывает приятный факт о CreateProcess , который можно использовать в своих интересах. То, что я буду описывать, нельзя найти в документации Microsoft, но эти вещи были обнаружены многими людьми на протяжении многих лет путём множества экспериментов. Вся информация, собранная здесь, была найдена в различных источниках — особенно в старых публикациях таких изданий, как «Windows Developer Journal», начиная с середины 90-х годов, а также старых сообщениях USENET.
Прежде чем я начну говорить про недокументированные штуки, я хотел бы кратко рассказать про то, что делает CreateProcess , и как её использовать в вашем коде. Если вы знакомы с CreateProcess , то просто пропустите эту секцию
function CreateProcess( lpApplicationName: PChar; // имя исполняемого модуля для запуска lpCommandLine: PChar; // командная строка lpProcessAttributes: PSecurityAttributes; // SD lpThreadAttributes: PSecurityAttributes; // SD fInheritHandles: BOOL; // опции наследования описателей dwCreationFlags: DWORD;, // флаги создания lpEnvironment: Pointer;, // новый блок переменных окружения lpCurrentDirectory: PChar; // имя текущей папки var lpStartupInfo: TStartupInfo; // стартовая информация var lpProcessInformation: TProcessInformation; // информация процесса ): BOOL; stdcall;
Функция может оказаться немного сложна для понимания на первый взгляд. К счастью, большинство параметров в CreateProcess можно опустить и стандартный способ создания нового процесса выглядит следующим образом:
var SI: TStartupInfo; PI: TProcessInformation; Exe: String; begin FillChar(SI, SizeOf(SI), 0); SI.cb := SizeOf(SI); Exe := ‘cmd.exe’; UniqueString(Exe); if CreateProcess(nil, PChar(Exe), 0, 0, False, 0, 0, 0, SI, PI) then begin // Опционально: ждём завершения процесса WaitForSingleObject(pi.hProcess, INFINITE); CloseHandle(pi.hProcess); CloseHandle(pi.hThread); end;
Пример выше просто запускает новый экземпляр cmd.exe. Однако у функции есть множество опций, позволяющих контролировать то, как запускается программа. Некоторые из этих настроек указываются непосредственно в параметрах функции, но большинство из них передаются в записи TStartupInfo :
type TStartupInfo = record cb: DWORD; lpReserved: PChar; lpDesktop: PChar; lpTitle: PChar; dwX: DWORD; dwY: DWORD; dwXSize: DWORD; dwYSize: DWORD; dwXCountChars: DWORD; dwYCountChars: DWORD; dwFillAttribute: DWORD; dwFlags: DWORD; wShowWindow: Word; cbReserved2: Word; lpReserved2: PByte; hStdInput: THandle; hStdOutput: THandle; hStdError: THandle; end;
Запись TStartupInfo также документирована в MSDN, но часть интересной информации не указана — её-то мы сейчас и будем изучать. Вся статья будет в основном вращаться вокруг записи TStartupInfo и её недокументированных полей: lpReserved , lpReserved2 и cbReserved2 .
Чуть позже я объясню, для чего на самом деле используются эти зарезервированные поля, но сначала давайте посмотрим на поле dwFlags и то, что он делает. Тут будет подходящим заглянуть в MSDN:
STARTF_USESHOWWINDOW = $01
Если это значение не указано, то поле wShowWindow игнорируется.
STARTF_USESIZE = $02
Если это значение не указано, то поля dwXSize и dwYSize игнорируются.
STARTF_USEPOSITION = $04
Если это значение не указано, то поля dwX и dwY игнорируются.
STARTF_USECOUNTCHARS = $08
Если это значение не указано, то поля dwXCountChars и dwYCountChars игнорируются.
STARTF_USEFILLATTRIBUTE = $10
Если это значение не указано, то поле dwFillAttribute игнорируется.
STARTF_RUNFULLSCREEN = $20
Указывает, что процесс следует запускать в полноэкранном режиме. В противном случае — в оконном.
STARTF_FORCEONFEEDBACK = $40
Указывает, что курсор сохраняет форму песочных часов до двух секунд после вызова CreateProcess .
STARTF_FORCEOFFFEEDBACK = $80
Указывает, что курсор не меняет форму во время запуска процесса. Используется обычный курсор.
STARTF_USESTDHANDLES = $100
Устанавливает описатели стандартного ввода, вывода и канала ошибок на указанные в полях hStdInput , hStdOutput и hStdError записи TStartupInfo .
Эти девять значений (или битовых флагов) могут быть указаны по отдельности или вместе — через OR. Таблица выше довольно скучна, потому что кроме флага START_USESTDHANDLES там нет ничего особо интересного. Однако, посмотрев на диапазон значений (от $01 до $100), мы увидим, что используется только 9 флагов (битов) из 32 возможных — что оставляет 23 флага, которые ещё не определены.
Для начала нам хватит вводной информации, давайте посмотрим на что-то более интересное.
Определяем, запущены ли мы через ярлык (shortcut)
OK, первый трюк, который я вам покажу — это определение, запущены ли мы через ярлык (т.е. двойным щелчком по .lnk файлу) или напрямую — через Проводник Windows, диалог Run или программно. Это настолько просто, что мне удивительно, почему это не документировано.
Существует недокументированный флаг, который я назову STARTF_TITLEISLINKNAME (прим.пер.: сейчас флаг документирован, имя изменено. В оригинале — STARTF_TITLESHORTCUT ). Этот битовый флаг имеет числовое значение $800. Windows устанавливает его, когда приложение запускается через ярлык. Так что любая программа может узнать, как её запустили — анализом своей собственной записи TStartupInfo :
// Возвращает True, если нас запустили через ярлык; False — в противном случае // Также возвращает имя файла ярлыка (если доступно) function GetShortcutName(out ALinkName: String): Boolean; const STARTF_TITLEISLINKNAME = $800; var SI: TStartupInfo; begin FillChar(SI, SizeOf(SI), 0); SI.cb := SizeOf(SI); GetStartupInfo(SI); if (si.dwFlags and STARTF_TITLEISLINKNAME) <> 0 then begin ALinkName := SI.lpTitle; Result := True; end else Result := False; end;
В общем-то, тут всё сводится к проверке флага, но надо пояснить один момент: когда установлен флаг STARTF_TITLEISLINKNAME , поле lpTitle записи TStartupInfo указывает на строку, содержащую полный путь к файлу ярлыка, который использовался для запуска вашего приложения. Представьте, что на вашем рабочем столе есть ярлык, который запускает Блокнот (notepad.exe). Когда запускается notepad.exe, то его TStartupInfo.lpTitle содержит такой текст:
C:Documents and SettingsJamesDesktopNotepad.lnk
Очень клёво, да? Ну, я надеюсь, что я подогрел ваш аппетит, так что мы двигаемся к следующей недокументированной возможности!
Указание, на каком мониторе нужно запускать процесс
Следующая недокументированная возможность — очередной флаг записи TStartupInfo . Флаг имеет значение $400 и я назвал его STARTF_MONITOR .
Когда в поле dwFlags указан флаг STARTF_MONITOR , поле hStdOutput записи TStartupInfo используется для указания описателя монитора, на котором нужно запускать новый процесс. Вы можете получить описатель монитора от любой функции перечисления экранов (прим.пер.: в Delphi — это свойство Handle у элементов массива Monitors объекта Screen из модуля Forms ).
Тут есть определённые ограничения, о которых нужно сказать. Вы можете спросить, как это работает, если поле hStdOutput используется для описателя канала вывода. Ответ прост — когда указан недокументированный флаг STARTF_MONITOR , то флаг STARTF_USESTDHANDLES игнорируется. Это означает, что эти два флага нельзя использовать одновременно, а поля hStdInput , hStdOutput и hStdError трактуются разными способами, в зависимости от установленных флагов.
Следующее ограничение очевидно: когда вы запускаете новый процесс с помощью CreateProcess , тут нет никакой концепции мониторов, окон и прочих GUI-вещей. Оконная программа (если она оконная) должна сама явно вызвать CreateWindow для создания своих окон, своего GUI. Вот тут-то и выходит на сцену ограничение флага STARTF_MONITOR . Когда процесс вызывает CreateWindow , он может явно указать, где именно создавать окно — указанием числовых значений координат и размеров окна. Это означает, что одна программа не может указать другой, как ей создавать окна, если только сама программа это явно не позволит — указанием специальных параметров при создании окна (прим.пер.: указанием CW_USEDEFAULT и CW_USEDEFAULT для координат в случае WinAPI и poDefaultPosOnly или poDefault в свойстве в Position случае VCL).
Так что только когда сама программа позволяет системе указывать положение окна, используется монитор, указываемый в CreateProcess . К примеру, так работает игра Пасьянс. Но этот подход не сработает с Блокнотом — он, похоже, всегда явно указывает координаты окна.
Заметьте, что функция ShellExecuteEx использует эту возможность CreateProcess для реализации своих собственных опций монитора (см. флаг SEE_MASK_HMONITOR ).
Запуск хранителя экрана (screensaver)
В старых версиях SDK Microsoft Windows был описан флаг, называемый STARTF_SCREENSAVER со значением $80000000. Сейчас этот флаг более не документирован. Когда задаётся этот флаг, то процесс запускается с приоритетом NORMAL_PRIORITY , но как только этот новый процесс делает первый вызов GetMessage , то его приоритет автоматически опускается до IDLE_PRIORITY . Эта функциональность может быть немного полезна для хранителей экрана и полностью бесполезна для большинства приложений. Кажется, это поведение было спроектировано для быстрого старта хранителя экрана и последующего «нормального» его выполнения, без заметного влияния на систему.
Кажется, что только процессу WinLogon (winlogon.exe) позволено использовать флаг STARTF_SCREENSAVER во время активации хранителя экрана, так что этот флаг бесполезен в других сценариях.
Устаревшая функциональность Диспетчера программ
Вы помните Диспетчер программ (Program Manager) из Windows 3.1? Он существует даже сегодня (в Windows XP): зайдите в командную строку или диалог «Выполнить» и наберите «progman» — запустится знакомая оболочка Диспетчера программ. Даже во времена Windows 3.1 (редакций home и NT) у CreateProcess существовала недокументированное поведение. Я собираюсь поделиться с вами этой информацией — даже хотя сегодня она практически бесполезна, но узнать о ней будет интересно.
Если вы посмотрите на определение TStartupInfo , то вы увидите поле lpReserved . Это поле вообще-то постоянно используется, но только Диспетчером программ, когда он запускает программы.
Это поле указывает на строковый буфер в таком формате:
dde.#,hotkey.#,ntvdm.#
- Часть «dde.» указывает идентификатор DDE, который дочерний процесс может использовать для общения с Диспетчером программ. Когда дочерний процесс отправляет progman-у сообщение WM_DDE_REQUEST с этим ИД, то progman отвечает сообщением WM_DDE_DATA . Это сообщение содержит, среди всего прочего, описание progman, его индекс иконки и рабочую папку для дочернего процесса.
Конечно же, если у вас есть два ваших приложения, разработанных специально для взаимодействия друг с другом, и вы хотите передавать второму приложению данные (по какой-то причине) иным способом, нежели через командную строку, то поле lpReserved будет вам полезным.
Устаревшая функциональность ShellExecuteEx
Функция ShellExecuteEx появилась аж в Windows NT 3.1 (но не в «обычной» 3.1). Она принимает единственный параметр — указатель на запись TShellExecuteInfo . Вы можете посмотреть определение этой структуры в MSDN (довольно скучно!). Но в ней есть несколько полей, которые тоже не документированы.
Во-первых, это поле hMonitor . ShellExecuteEx использует это поле для контроля монитора, на котором следует появиться запускаемому процессу. Для реализации этой возможности ShellExecuteEx вызывает CreateProcess , указывая обсуждаемый выше флаг STARTF_MONITOR .
Следующее интересное поле — это поле hIcon . В нём указывается описатель иконки открываемого файла, если поле флагов содержит флаг SEE_MASK_ICON со значением $10. Мне не удалось найти информацию по применению этой возможности — всё, что я могу сказать, так это то, что эту возможность использовали консольные приложения Windows NT 3.1/3.5 (новые программы всегда игнорируют иконку). Для реализации этой возможности ShellExecuteEx использует недокументированный флаг в CreateProcess , который я назову STARTF_ICON . Странно, но этот флаг численно равен флагу STARTF_MONITOR : $400. Видимо, подразумевается, что иконка передаётся консольным программам, а монитор — визуальным.
Последнее интересное поле — это dwHotKey (используемого только при наличии флага SEE_MASK_HOTKEY ). Оно предполагается для назначения hot-key дочернему процессу, так что вы можете активировать приложение в любое время нажатием этой комбинации. Однако мне не удалось заставить работать эту возможность. Может быть, она была удалена из системы.
И снова, ShellExecuteEx использует недокументированный флаг CreateProcess — это флаг STARTF_USEHOTKEY со значением $200. Когда указывается этот флаг, поле hStdInput должно содержать значение hot-key вместо канала ввода (см. WM_SETHOTKEY).
И иконка и hot-key являются странными возможностями CreateProcess , во-первых потому, что эта функциональность, кажется дублируется параметром lpReserved и, во-вторых, она убрана из современных версий Windows. Если кто-то владеет другой информацией по этой теме — я буду счастлив её услышать!
Передача произвольных данных дочернему процессу
Последний недокументированный трюк несколько отличается от упомянутых выше, так что я решил оставить его под конец. Запись TStartupInfo содержит два поля lpReserved2 и cbReserved2 . Эти два поля предоставляют возможность передачи произвольных данных от одного процесса к запускаемому без необходимости вызова VirtualAllocEx / WriteProcessMemory (прим.пер.: помните, что это хак; в 99% случаев намного предпочтительнее передавать данные через командную строку или анонимную память). Поле cbReserved2 является 16-битным целым и указывает размер буфера, на который указывает lpReserved2 . Это означает, что lpReserved2 может иметь размер до 65535 байт.
Пример ниже иллюстрирует возможность передачи произвольного буфера от одного процесса другому. Когда процесс-B запускается процессом-A, он показывает MessageBox, говорящий «Hello from Process A!»:
program ProcessB; uses Windows, SysUtils; var SI: TStartupInfo; begin FillChar(SI, SizeOf(SI), 0); SI.cb := SizeOf(SI); GetStartupInfo(SI); // Покажем, что послал нам процесс A MessageBox(0, PChar(SI.lpReserved2), ‘Process B’, MB_OK); end.
Пока всё хорошо — мы узнали, про приятный способ передачи произвольных параметров между приложениями без необходимости использовать командную строку. Но тут есть проблема. В примере выше процесс B обязан быть собран абсолютно без поддержки C run-time (прим.пер.: как несложно сообразить, это применимо лишь к программам на MS VS, но не программам Delphi, если, конечно же, вы зачем-то вручную будете её включать в ваши программы). Причина этого довольно сложна, но я попытаюсь её объяснить.
Microsoft C run-time (включая Visual Studio.NET) использует эту возможность lpReserved2 для реализации C функций exec , system и spawn . Когда этими подпрограммами создаётся новый процесс, C run-time нужно передать дочернему процессу копии открытых файловых описателей (открытых через fopen / open , а не через CreateFile ).
lpReserved2 используется как механизм для передачи этих файловых описателей между программами, использующими MSVC. До вызова CreateProcess подготавливается буфер для lpReserved2 , который имеет такой формат (псевдо-код):
type TArgs = record count: DWORD; flags: array[1..count] of Byte; handles: array[1..count] of THandle; end;
Первое поле в буфере lpReserved2 является 32-битным целым числом передаваемых описателей. Сразу за ним идёт массив байт с этим числом элементов — флаги описателей. Эти флаги представляют собой файловый атрибуты, которые были использованы, когда файлы были открыты — т.е. вещи вроде «read-only», «write-append», «text-mode», «binary-mode» и т.п. А за этим массивом следует массив собственно описателей.
- Перечисляются все открытые «run-time» описатели файлов.
- Описатель Win32 каждого такого файла отмечается как «наследуемый» (inheritable).
- Число найденных описателей записывается в первое поле буфера для lpReserved2 .
- Атрибуты каждого описателя записываются друг за другом в буфер.
- В буфер записываются файловые описатели.
- Наконец, вызывается CreateProcess с установленным в True полем bInheritHandles .
- Вызывается GetStartupInfo и проверяется, что lpReserved2 указывает на буфер.
- Извлекается первое 32-битное число — это будет число переданных описателей (размерность массивов).
- Текущее состояние ввода-вывода процесса инициализируется полученным числом описателей открытых файлов.
- Цикл по флагам восстанавливает состояние описателей.
- Цикл по описателям восстанавливает таблицу открытых файлов.
Если вы хотите использовать lpReserved2 в ваших собственных программах, то вам нужно быть очень осторожными и точно убедиться, что либо вы используете описанный выше формат, либо не используете C run-time. Иначе дочерний процесс вылетит (или станет нестабилен) — потому что он будет ожидать буфер lpReserved2 в определённом формате.
Обойти эту проблему не сложно. Просто установите первые 4 байта буфера в ноль — указывая, что массив описателей имеет нулевую длину. Любая C run-time-подобная логика будет опущена. А вы можете расположить свои реальные данные после этого нулевого маркера.
Примечание: очевидно, этот метод не работает под 64-битной Windows Vista.
Итоги по CreateProcess
Это была скорее полноценная статья, чем мануал. Надеюсь, вы нашли что-то полезное или узнали о CreateProcess кое-что новое, что вы не знали раньше.
Я не прочь услышать любые ваши комментарии по этой статье. Если вы заметили мои ошибки или неточности — поправьте меня. И если у вас есть больше информации по этой теме — поделитесь ею!
Источник: www.transl-gunsmoker.ru