Как обеспечить потокобезопасность работы с SHGetFileInfo в Delphi
Работа с потоками в программировании на Delphi может быть довольно сложной задачей, особенно когда дело касается взаимодействия с внешними функциями, такими как SHGetFileInfo. Эта функция предназначена для получения информации о файлах и директориях, и используется, например, для получения иконок файлов. Однако, при работе с потоками, существует проблема потокобезопасности, которая может привести к ошибкам при одновременном доступе к этой функции из разных потоков.
Описание проблемы
Функция GetFileIcon предназначена для получения иконки файла. Она вызывает SHGetFileInfo, который должен вернуть дескриптор иконки. В Delphi XE2 на Windows 7 64 бит, функция может возвращать 0, если вызывается в потоке, в то время как в главном потоке работает корректно. Похоже на проблему инициализации оболочки, так как после некоторого времени функция начинает работать и в потоке. Однако, в обновлении указано, что SHGetFileInfo не потокобезопасен и требует синхронизации доступа.
Подтвержденный ответ
Согласно документации, перед вызовом SHGetFileInfo необходимо инициализировать Component Object Model (COM) с помощью CoInitialize или OleInitialize. В GUI приложениях COM инициализируется в главном потоке, но для других потоков это нужно делать явно.
Кроме того, важно корректно обрабатывать ошибки, проверяя возвращаемое значение вызова SHGetFileInfo, как описано в документации.
Пример программы, демонстрирующей инициализацию COM в потоке и получение иконки файла:
{$APPTYPE CONSOLE}
uses
Classes,
Windows,
ActiveX,
ShellAPI;
var
hThread: THandle;
ThreadId: Cardinal;
function ThreadFunc(Parameter: Pointer): Integer;
var
shfi: TSHFileInfo;
begin
CoInitialize(nil);
try
if ShGetFileInfo('C:\Windows\explorer.exe', 0, shfi, SizeOf(shfi), SHGFI_ICON or SHGFI_LARGEICON) = 0 then
begin
Writeln('ShGetFileInfo Failed');
Result := 1;
exit;
end;
Writeln(shfi.hIcon);
finally
CoUninitialize;
end;
Result := 0;
end;
begin
hThread := BeginThread(nil, 0, ThreadFunc, nil, 0, ThreadId);
WaitForSingleObject(hThread, INFINITE);
CloseHandle(hThread);
Readln;
end.
Альтернативный ответ
Так как SHGetFileInfo не потокобезопасен, для одновременного доступа из разных потоков необходимо использовать механизмы синхронизации. Например, можно использовать TCriticalSection для сериализации доступа к функции:
uses
SysUtils,
Classes,
SyncObjs,
Windows,
ActiveX,
ShellAPI;
var
hThreads: THandleArray;
ThreadId: Cardinal;
Lock: TCriticalSection;
function ThreadFunc(Parameter: Pointer): Integer;
var
shfi: TSHFileInfo;
fname: string;
begin
CoInitialize(nil);
try
fname := 'c:\desktop\file'+IntToStr(Integer(Parameter))+'.exe';
Lock.Acquire;
try
if ShGetFileInfo(PChar(fname), 0, shfi, SizeOf(shfi), SHGFI_ICON or SHGFI_LARGEICON) = 0 then
begin
Writeln('ShGetFileInfo Failed');
Result := 1;
exit;
end;
Writeln(shfi.hIcon);
finally
Lock.Release;
end;
finally
CoUninitialize;
end;
Result := 0;
end;
begin
Lock := TCriticalSection.Create;
// Создание потоков и выполнение программы аналогично предыдущему примеру
end.
В этом примере каждая функция ThreadFunc получает доступ к SHGetFileInfo после захвата блокировки, что обеспечивает потокобезопасность вызова функции.
Заключение
При работе с SHGetFileInfo в многопоточных приложениях на Delphi важно не только инициализировать COM, но и использовать механизмы синхронизации для обеспечения потокобезопасности. Использование TCriticalSection или аналогичных инструментов позволит избежать ошибок при одновременном доступе к функции из разных потоков.
Описание контекста: Вопрос касается обеспечения потокобезопасности при работе с функцией `SHGetFileInfo` в среде разработки Delphi.
Комментарии и вопросы
Получайте свежие новости и обновления по Object Pascal, Delphi и Lazarus прямо в свой смартфон. Подпишитесь на наш Telegram-канал delphi_kansoftware и будьте в курсе последних тенденций в разработке под Linux, Windows, Android и iOS
Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.