💡 Инструкция: В каждом вопросе выберите один ответ из пяти. На весь тест отведено 85 минут. Код лучше сначала разобрать вручную: правильный вариант должен объяснять механизм, а не только совпадать с одним запуском.
Вопрос 1 из 30
Проследите обработку дескриптора в HandleOwner: какой вывод о владении ресурсом следует из проверки `INVALID_HANDLE_VALUE` и блока `finally`?
Pascal Delphi / Pascal · Дескрипторы Windows Копировать
// Проследите фактическое поведение HandleOwner.
program HandleOwner;
uses Winapi.Windows, System.SysUtils;
var H: THandle;
begin
H := CreateFile('data.bin', GENERIC_READ, FILE_SHARE_READ, nil,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
if H = INVALID_HANDLE_VALUE then
RaiseLastOSError;
try
Writeln('opened');
finally
CloseHandle(H);
end;
end.
Любой Windows handle закрывается `CloseHandle`.
Нулевое значение служит универсальным признаком ошибки любого API.
Дескриптор Windows закрывают функцией, соответствующей способу получения.
Все значения `THandle` закрываются через `CloseHandle`, включая GDI-объекты и ключи реестра.
Дескриптор можно освободить через `Free`, если хранить его в объектном поле.
Вопрос 11 из 30
Какую архитектурную границу создаёт реализация `TWindowsProcessLauncher.Launch` вокруг `ShellExecuteEx`?
Pascal Delphi / Pascal · Границы применимости Копировать
// Платформенный вызов изолирован, а полученный HANDLE проверяется и закрывается.
program WinApiAdapter;
{$APPTYPE CONSOLE}
uses System.SysUtils, Winapi.Windows, Winapi.ShellAPI;
type
IProcessLauncher = interface
function Launch(const FileName, Args: string): Cardinal;
end;
TWindowsProcessLauncher = class(TInterfacedObject, IProcessLauncher)
function Launch(const FileName, Args: string): Cardinal;
end;
function TWindowsProcessLauncher.Launch(const FileName, Args: string): Cardinal;
var
Info: TShellExecuteInfo;
begin
FillChar(Info, SizeOf(Info), 0);
Info.cbSize := SizeOf(Info);
Info.fMask := SEE_MASK_NOCLOSEPROCESS;
Info.lpFile := PChar(FileName);
Info.lpParameters := PChar(Args);
if not ShellExecuteEx(@Info) then
RaiseLastOSError;
if Info.hProcess = 0 then
raise EInvalidOpException.Create('process handle was not returned');
try
Result := GetProcessId(Info.hProcess);
if Result = 0 then
RaiseLastOSError;
finally
CloseHandle(Info.hProcess);
end;
end;
begin
end.
Вызовы `external` удобнее размещать прямо в формах рядом с обработчиками.
Прямой WinAPI-вызов обычно безопаснее RTL-обёртки, потому что содержит меньше уровней кода.
Прямой WinAPI-вызов предпочитают оболочке RTL как более безопасный вариант.
Платформенный вызов изолирован за узким интерфейсом, поэтому остальной код не зависит от WinAPI и может получить подменную реализацию.
Если обёртка не даёт один флаг, лучше отказаться от адаптера и вызвать API во всех местах.
Вопрос 26 из 30
Две версии `MessageLoopWork` сохраняют порядок `function WindowProc(Wnd: HWND; Msg: UINT; WParam: WPARAM; LParam: LPARAM): LR…` → `Result := DefWindowProc(Wnd, Msg, WParam, LParam);`. Какое свойство останется общим для обеих?
Pascal Delphi / Pascal · Цикл сообщений Копировать
// Определите реальную гарантию в MessageLoopWork.
program MessageLoopWork;
uses Winapi.Windows;
function WindowProc(Wnd: HWND; Msg: UINT; WParam: WPARAM; LParam: LPARAM): LRESULT; stdcall;
begin
if Msg = WM_COMMAND then
begin
Sleep(8000);
Result := 0;
Exit;
end;
Result := DefWindowProc(Wnd, Msg, WParam, LParam);
end;
begin end.
Долгий обработчик сообщения не блокирует интерфейс, если не вызывает `ProcessMessages`.
Цикл сообщений работает независимо от потока, который создал окно.
Оконные сообщения обрабатываются потоком, создавшим окно.
Длительный обработчик окна не мешает интерфейсу, если не вызывает `Application.ProcessMessages`.
Окно можно безопасно изменять из любого потока, имеющего его `Handle`.