Конференция "WinAPI" » Узнать хендлы открытых процессом файлов [D6, WinXP]
 
  • Виталий (10.04.08 14:57) [0]
    Уважаемые мастера, подскажите пожалуйста как по номеру процесса получить хендлы и пути файлов, которые он открыл?
    Если можно скажите какая последовательность вызовов функций  должна быть и какие точно функции для этого нужны

    Заранее спасибо.
  • Виталий (10.04.08 14:57) [1]
    О еще, может процессы где то в себе держат этот список, как его получить?
  • Сергей М. © (10.04.08 15:12) [2]
    И что ты с ними намерен делать ?

    Ты их потерял что ли ?)
  • Виталий (10.04.08 15:43) [3]
    Я хочу программно получить путь к файлу который открыт в Ворде.
    Я запускаю свою программу нахожу процесс WinWord и хочу увидеть какой файл(ы) он открыл
  • Виталий (10.04.08 15:43) [4]
    Это для сбора информации кто какой пользователь с чем работает
  • Сергей М. © (10.04.08 16:14) [5]
    Ты в курсе, что WinWord являет собой полноценный сервер OLE-автоматизации, (который способен сам сообщить о файлах, с которыми он работает, если его об этом спросят на его языке) ?

    Или ты про такую слыхом не слыхивал ?
  • Виталий (10.04.08 16:36) [6]
    В курсе, только мне не только ВинОфис нужно отлавливать а любую программу, окно которой активно в данный момент. Ворд это я к примеру.
    Ладно Ворд не нравится, возьмем Сполайт. Он то не сообщит о себе.

    Ты лучше скажи знаеш как это делать или как всегда в гугль идти?
  • Сергей М. © (10.04.08 16:40) [7]
    Ну раз ты на ходу меняешь формулировку задачи, то как всегда)
  • Riply © (10.04.08 17:21) [8]
    > [0] Виталий   (10.04.08 14:57)
    > подскажите пожалуйста как по номеру процесса получить хендлы и пути файлов, которые он открыл?

    Попробуй с помощью следующих функций:

    NtQuerySystemInformation(..,SystemHandleInformation,..)
    DuplicateHandle
    NtQueryObject

    P.S.
    Возможно, вместо NtQuerySystemInformation, можно использовать NtQueryInformationProcess для конкретного процесса.
    Не знаю, надо смотреть.
  • Riply © (10.04.08 17:22) [9]
    >  [8] Riply ©   (10.04.08 17:21)

    P.P.S.
    Будь осторожен с Pipe -ми :)
  • slow!alfamoon!com (10.04.08 18:14) [10]
    Riply: вроде проблема с пайпами в сп2 решилась, или я неправильно помню?
    Автор: http://slow.alfamoon.com/?module=filesdb&id=1&fid=12&get=1 только тут про сокеты, это тоже файлы, и для всех процессов, так что добавишь фильтрацию по PID нужного процесса
  • Riply © (10.04.08 22:04) [11]
    > [10] slow!alfamoon!com   (10.04.08 18:14)
    > Riply: вроде проблема с пайпами в сп2 решилась, или я неправильно помню?

    Давно это было, но до сих пор пайповая проблемма сидит у меня в голове с меткой "не решенная" :)
    Напомни, пожалуйста, на основании чего ты считаешь, что она решилась ?
  • Виталий (11.04.08 12:47) [12]
    Riply, slow!alfamoon!com - Спасибо за примеры. хоть кто-то не издевается, оказывается есть еще тут нормальные люди.

    Собсно почитал МС-Рема и Багеля, и решил проблему. Кстати Пайпы меня не интересуют, только физические файлы.
  • Rouse_ © (11.04.08 15:50) [13]
    С пайпами проблема не решилась :) Как висели так и висят :) Надо просто уметь их идентифицировать :)
  • slow!alfamoon!com (11.04.08 15:57) [14]

    > Напомни, пожалуйста, на основании чего ты считаешь, что
    > она решилась ?
    >

    в каком-то обсуждении на wasm.ru вроде пробегало, надо посмотреть будет.
  • Riply © (11.04.08 16:34) [15]
    > [13] Rouse_ ©   (11.04.08 15:50)
    > С пайпами проблема не решилась :) Как висели так и висят :) Надо просто уметь их идентифицировать :)

    Сколько не билась над их идентификацией (имея на руках только информацию от NtQuerySystemInformation), ничего у меня не вышло :(.
    На самом незначительном запросе - висла.
    Саш, подскажи как надо делать.
  • slow!alfamoon!com (11.04.08 16:36) [16]
    2 Riply собственно вариант решения это запрос в отдельном потоке, был предложен еще давно и ничего нового не придумано
  • Riply © (11.04.08 16:42) [17]
    > [16] slow!alfamoon!com   (11.04.08 16:36)
    > 2 Riply собственно вариант решения это запрос в отдельном потоке,
    > был предложен еще давно и ничего нового не придумано

    Терминирование нити ?
    Я не считаю это за "вариант решения". IMHO, так некузяво, что дальше некуда :)
    Кто-то из форумчан хорошо по этому поводу сказал: "Ведь по живому режешь".
    Я с ним согласна :)
  • Rouse_ © (11.04.08 16:46) [18]

    > Riply ©   (11.04.08 16:34) [15]

    Т.к. нужно искать только файловые хэндлы, то перед вызовом NtQueryObject нужно сказать NtQueryInformationFile и проверить результат, если STATUS_SUCCESS, то повиснуть не должны ;)
    Т.е. что-то вроде этого:

     function GetFileNameThread(lpParameters: Pointer): DWORD; stdcall;
     var
       FileNameInfo: FILE_NAME_INFORMATION;
       ObjectNameInfo: TOBJECT_NAME_INFORMATION;
       IoStatusBlock: IO_STATUS_BLOCK;
       pThreadParam: TGetFileNameThreadParam;
       dwReturn: DWORD;
     begin
       ZeroMemory(@FileNameInfo, SizeOf(FILE_NAME_INFORMATION));
       pThreadParam := PGetFileNameThreadParam(lpParameters)^;
       Result := NtQueryInformationFile(pThreadParam.hFile, @IoStatusBlock,
         @FileNameInfo, MAX_PATH * 2, FileNameInformation);
       if Result = STATUS_SUCCESS then
       begin
         Result := NtQueryObject(pThreadParam.hFile, ObjectNameInformation,
           @ObjectNameInfo, MAX_PATH * 2, @dwReturn);
         if Result = STATUS_SUCCESS then
         begin
           pThreadParam.Status := Result;
           WideCharToMultiByte(CP_ACP, 0,
             @ObjectNameInfo.Name.Buffer[ObjectNameInfo.Name.MaximumLength -
             ObjectNameInfo.Name.Length],
             ObjectNameInfo.Name.Length, @pThreadParam.Data[0],
             MAX_PATH, nil, nil);
         end
         else
         begin
           pThreadParam.Status := STATUS_SUCCESS;
           Result := STATUS_SUCCESS;
           WideCharToMultiByte(CP_ACP, 0,
             @FileNameInfo.FileName[0], IoStatusBlock.Information,
             @pThreadParam.Data[0],
             MAX_PATH, nil, nil);
         end;
       end;
       PGetFileNameThreadParam(lpParameters)^ := pThreadParam;
       ExitThread(Result);
     end;

     function GetFileNameFromHandle(hFile: THandle): String;
     var
       lpExitCode: DWORD;
       pThreadParam: TGetFileNameThreadParam;
       hThread: THandle;
     begin
       Result := '';
       ZeroMemory(@pThreadParam, SizeOf(TGetFileNameThreadParam));
       pThreadParam.hFile := hFile;
       hThread := CreateThread(nil, 0, @GetFileNameThread, @pThreadParam, 0, PDWORD(nil)^);
       if hThread <> 0 then
       try
         case WaitForSingleObject(hThread, 100) of
           WAIT_OBJECT_0:
           begin
             GetExitCodeThread(hThread, lpExitCode);
             if lpExitCode = STATUS_SUCCESS then
               Result := pThreadParam.Data;
           end;
           WAIT_TIMEOUT:
             TerminateThread(hThread, 0);
         end;
       finally
         CloseHandle(hThread);
       end;
     end;



    Я ж тебе помоемому давал уже этот код...
  • slow!alfamoon!com (11.04.08 17:02) [19]
    2 Rouse_
    NtQueryObject тоже виснет. На wasm.ru обсуждали

    http://www.wasm.ru/forum/viewtopic.php?id=7728
  • Rouse_ © (11.04.08 17:04) [20]
    Ну не знаю... У меня этот код отрабатывает четко, ни одного срыва потока нет, хоть код и оставлен на всякий случай... Если без NtQueryInformationFile тогда виснет...
  • Rouse_ © (11.04.08 17:07) [21]
    А, ну понятно, там запрашивается FileModeInformation, а я прошу FileNameInformation :)
  • Riply © (11.04.08 17:07) [22]
    > [18] Rouse_ ©   (11.04.08 16:46)

    > Т.к. нужно искать только файловые хэндлы, то перед вызовом NtQueryObject нужно сказать
    > NtQueryInformationFile и проверить результат, если STATUS_SUCCESS, то повиснуть не должны ;)

    Саш, насколько я помню, на самом запросе NtQueryInformationFile мы виснем с не меньшим успехом,
    просто зависание не такое "глубокое" :)
    Но нить терминировать все равно приходиться.
    Могу еще раз проверить, если ошибаюсь.

    P.S.
    Кто-то говорил, что решил данную проблемму "путем анализа таблицы Handle`ов"
    или что-то в этом духе. Не помню. Поиск по архивам старых веток ничего не дал.
  • Rouse_ © (11.04.08 17:08) [23]
    Проверь...
  • Riply © (11.04.08 17:09) [24]
    > [21] Rouse_ ©   (11.04.08 17:07)
    > А, ну понятно, там запрашивается FileModeInformation, а я прошу FileNameInformation :)

    Так. Бросаю все дела и иду проверять :)
  • Riply © (11.04.08 17:10) [25]
    >  [24] Riply ©   (11.04.08 17:09)

    P.S.
    Надеюсь что много времени это не займет :)
  • Rouse_ © (11.04.08 17:26) [26]
    Код у меня на сайте в разделе WinAPI возьми, чтоб самой не собирать...
  • Rouse_ © (11.04.08 17:40) [27]
    я сейчас изменил свой код на вот такой:

           WAIT_TIMEOUT:
           begin
             TerminateThread(hThread, 0);
             Result := 'Thread terminated';
           end;



    сработок не было :)
  • Riply © (11.04.08 17:41) [28]
    >  [26] Rouse_ ©   (11.04.08 17:26)
    > Код у меня на сайте в разделе WinAPI возьми, чтоб самой не собирать...

    Хорошо.

    Пока протестила у себя (на своем коде).
    Добавила проверку NtQueryInformationFile(FileNameInformation).
    Не помогло:
    Если пробегаться по всем процессам системы, то терминировать нить приходится 25-26 раз.

    Сейчас скачаю оригинальный код и попробую тоже самое с ним.
  • Rouse_ © (11.04.08 17:48) [29]
    обманул я тебя :) действительно приходится срывать... оть-же... Не обращал на этот момент внимание... Там у меня логика работы, что ищется процесс и путь к файлу, если не находит - в лог ничего не выводиться... Блин... Лан будем думать...
  • Riply © (11.04.08 18:00) [30]
    > [29] Rouse_ ©   (11.04.08 17:48)
    > обманул я тебя :)

    А я уж было стала готовить отчет: чем твой пример компилировался, да на какой системе тестировался :)

    > Лан будем думать...

    Повторюсь: к сожалению, не помню кто с нашего форума (из мастеров)
    говорил что решил проблемму "путем анализа таблицы Handle`ов". Или что-то в таком духе.
  • Riply © (12.04.08 04:50) [31]
    Вот ведь надо же было напоминать об этой проблемме.
    Вместо того чтобы делом заниматься, Handl`ы тут перечисляю :)

    Если кому интересно вот некоторые результаты:
    Как я уже говорила, давным-давно пыталась найти хоть какой-то запрос,
    на котором не виснем, и по результату (или по возвращаемой ошибке) которого
    можно было бы хотя бы разбить объекты типа файл на две группы:
    "Опасную" и "Безопасную".

    В этом случае, для "Опасной" группы, если уж так приспичило создаем нить,
    а с другой работаем "напрямую".

    Меня постигла неудача - самые невинные запросы приводили к зависанию.
    Сейчас я подумала, а может попробовать в запросе не напрямую,
    а как-то косвенно обратиться к Handl`у ?
    И вот что у меня получилось:

    По аналогии с методикой, используемой в ReopenFile (подсказанной guav - ом),
    попробуем запросить атрибуты у файла с пустым именем,
    указав в качестве его родителя наш "подопытный" Handle.
    Т.е. делаем вид, что он нас вовсе не интересует, а интересует только ребенок :)

    var
    EmptyName: UNICODE_STRING;
    BaseInfo: FILE_BASIC_INFORMATION;
    ObjectAttr: OBJECT_ATTRIBUTES;
    begin
    RtlInitUnicodeString(@EmptyName, nil);
    InitializeObjectAttributes(@ObjectAttr, @EmptyName, OBJ_CASE_INSENSITIVE, pThreadParam.hFile, nil);
    Result := NtQueryAttributesFile(@ObjectAttr, @BaseInfo);



    Если Result = STATUS_SUCCESS, то (в зависимости от типа теста)
    обходимся без нити, либо внутри нити не делаем запросы, ведущие к зависанию.

    Результаты при пробежке по всем процессам системы:
    RequestCount          1248 // Всего запросов для объектов типа FILE
    SuccessRequestCount    801 // из них успешных
    ErrorRequestCount      447 // и с ошибкой. Здесь, кроме файлов в стандартном понимании, еще куча всего :)

    Итого в двух случаях из трех можно обойтись без нити.
    И только треть остается под вопросом.
    (Выигрыш по времени в разы :)

    Тестирование на коде от Rouse дало более интересные результаты:
    (У себя я шла по всем объектам без исключений и какой либо фильтрации )

    Исходный код без изменений:
    ---------------------------
    NameCount:  433
    TickCount:  15015
    TerminateCount:  25

    Запрос нити для всех объектов, но внутри нити предварительная проверка на NtQueryAttributesFile
    ---------------------------
    NameCount:  429
    TickCount:  6000
    TerminateCount:  0

    Работаем вообще без нитей, основываясь на показаниях NtQueryAttributesFile :)
    ---------------------------
    NameCount:  429
    TickCount:  937
    TerminateCount:  0

    В последнем случае выигыш уже не в разы а на порядки, уж не говоря о "нерезаньи по-живому" :)

    Есть маленькая неувязочка.
    Мы потеряли четыре файла.
    Кто они такие (может нормальные пайпы или еще кто) и почему потерялись,
    оставляю изучение этого вопроса заинтересованным лицам. :)
    Как подсказку к разбирательствам могу дать следующее:
    я отбрасывала запрос на имя не анализируя результат.
    Т.е если Result <> STATUS_SUCCESS, то бросала это дело.
    На самом деле анализ может что-то подсказать.
    Возвращаемые ошибки, которые удалось засечь:
    STATUS_UNSUCCESSFUL, STATUS_INVALID_DEVICE_REQUEST, STATUS_INVALID_PARAMETER,
    STATUS_OBJECT_NAME_INVALID, STATUS_OBJECT_NAME_NOT_FOUND, STATUS_NOT_SUPPORTED

    Ну и напоследок приведу основные изменения Сашином коде,
    которые надо сделать для тестирования описанного выше безобразия:

    function GetFileNameDirect(hFile: THandle; var FileName: string): NTSTATUS; // - integer !!!
    var
    ObjectNameInfo: TOBJECT_NAME_INFORMATION;
    dwReturn: DWORD;
    // !!!! Это другое определение UNICODE_STRING, не как у Саши в данном примере
    EmptyName: UNICODE_STRING;
    BaseInfo: FILE_BASIC_INFORMATION;
    ObjectAttr: OBJECT_ATTRIBUTES;
    begin
     FileName := '';
     RtlInitUnicodeString(@EmptyName, nil);
     InitializeObjectAttributes(@ObjectAttr, @EmptyName, OBJ_CASE_INSENSITIVE, hFile, nil);
     Result := NtQueryAttributesFile(@ObjectAttr, @BaseInfo);
     if Result = STATUS_SUCCESS then
      begin
       Result := NtQueryObject(hFile, ObjectNameInformation,
                               @ObjectNameInfo, MAX_PATH * 2, @dwReturn);
       if Result = STATUS_SUCCESS then
        begin
         SetLength(FileName, MAX_PATH);
         WideCharToMultiByte(CP_ACP, 0, @ObjectNameInfo.Name.Buffer[ObjectNameInfo.Name.MaximumLength -
             ObjectNameInfo.Name.Length], ObjectNameInfo.Name.Length, PChar(FileName),
             MAX_PATH, nil, nil);
        end
      end;
    end;



    Чуть измененная GetFileNameThread:

    function GetFileNameThread(lpParameters: Pointer): DWORD; stdcall;
    var
    FileNameInfo: FILE_NAME_INFORMATION;
    ObjectNameInfo: TOBJECT_NAME_INFORMATION;
    IoStatusBlock: IO_STATUS_BLOCK;
    pThreadParam: TGetFileNameThreadParam;
    dwReturn: DWORD;
    // !!!! Это другое определение UNICODE_STRING, не как у Саши в данном примере
    EmptyName: UNICODE_STRING;
    BaseInfo: FILE_BASIC_INFORMATION;
    ObjectAttr: OBJECT_ATTRIBUTES;
    begin
    ZeroMemory(@FileNameInfo, SizeOf(FILE_NAME_INFORMATION));
    pThreadParam := PGetFileNameThreadParam(lpParameters)^;

    // RtlInitUnicodeString(@EmptyName, nil);
    // InitializeObjectAttributes(@ObjectAttr, @EmptyName, OBJ_CASE_INSENSITIVE, pThreadParam.hFile, nil);
    // Result := NtQueryAttributesFile(@ObjectAttr, @BaseInfo);
       Result := STATUS_SUCCESS;// Для теста внутри нити закоментировать эту строчку и открыть предыдущие

     if Result = STATUS_SUCCESS then
      begin
       // Не убирайте эту строчку - на ней зависание не такое "глубокое"  :)
       if GetFileType(pThreadParam.hFile) <> FILE_TYPE_PIPE
        then Result := NtQueryInformationFile(pThreadParam.hFile, @IoStatusBlock,
                                        @FileNameInfo, MAX_PATH * 2, FileNameInformation);

       if Result = STATUS_SUCCESS then
        begin
         Result := NtQueryObject(pThreadParam.hFile, ObjectNameInformation,
                                 @ObjectNameInfo, MAX_PATH * 2, @dwReturn);
         if Result = STATUS_SUCCESS then
          begin
           pThreadParam.Status := Result;
           WideCharToMultiByte(CP_ACP, 0,
             @ObjectNameInfo.Name.Buffer[ObjectNameInfo.Name.MaximumLength -
             ObjectNameInfo.Name.Length],
             ObjectNameInfo.Name.Length, @pThreadParam.Data[0],
             MAX_PATH, nil, nil);
          end
         else
          begin
           pThreadParam.Status := STATUS_SUCCESS;
           Result := STATUS_SUCCESS;
           WideCharToMultiByte(CP_ACP, 0,
             @FileNameInfo.FileName[0], IoStatusBlock.Information,
             @pThreadParam.Data[0],
             MAX_PATH, nil, nil);
          end;
        end;
      end;

     PGetFileNameThreadParam(lpParameters)^ := pThreadParam;
     ExitThread(Result);
    end;



    Изменения в главной процедуре:
    if GetFileNameDirect(hFile, FilePath) <> STATUS_SUCCESS
    then ;//FilePath := GetFileNameFromHandle(hFile);



    Уфф... Вроде все.

    В заключении хочу сказать, что то что у меня не нашлось объектов,
    на которых NtQueryAttributesFile ведет к зависанию, вовсе не означает,
    что их не существует :)
  • Игорь Шевченко © (12.04.08 12:03) [32]
    Riply ©   (12.04.08 04:50) [31]

    Можно нескромный вопрос - а нафига такие извращения ?
    Все равно ты не получишь со 100% гарантией список открытых файлов (хендлов) чужого (да и своего тоже) процесса, к чему мучиться ?

    Я просто к чему - сам страдал в свое время ерундой, потом сообразил, что лучше самодвижущееся пресс-папье изобрести.

    Кому надо узнать список открытых хендлов, нехай запустит handles.exe или Process Explorer, авторы этих инструментов известны, а главное, применяют в корне другой подход.

    И это правильно.
  • Riply © (12.04.08 17:07) [33]
    > [32] Игорь Шевченко ©   (12.04.08 12:03)
    > Все равно ты не получишь со 100% гарантией список открытых файлов (хендлов) чужого (да и своего тоже) процесса, к чему мучиться ?

    Когда FileExists возвращает False это не означает что файл не существует, imho. Зачем она нужна ? :)

    > Можно нескромный вопрос - а нафига такие извращения ?

    Вопрос скромный :)
    Начнем с того, что свой блок для работы с Handl`ми (не обязательно "файловыми") я написала только для
    себя и не собираюсь его предлагать другим.
    Не потому что жадная, а потому что нехай запустит "handles.exe или Process Explorer". :)
    Мой блок работает так как надо мне и умеет то, что нужно мне.
    А чтобы что-то работало так как надо ее величеству Riply, может написать только Riply :)
    И он мне в работе неоднократно помогал в работе.  
    Это раз.

    Второе:
    мне очень не нравиться, когда у меня за спиной висят нерешенные задачи.
    А я люблю себя чуствовать комфортно. :)

    Третье:
    этот вопрос интересует многих и мне будет приятно, если мое решение окажется правильным и принесет кому-то пользу :)

    Четвертое:
    Во время "страдания ерундой" я лучше начинаю понимать (чуствовать) как работает система
    и получаю новые знания.


    Есть еще пятое, шестое и т.д. Если будет интересно, продолжу перечисление :)

    Вот так и получается, Игорь, что все это я делала для себя любимой :)
  • Riply © (12.04.08 17:10) [34]
    > [33] Riply ©   (12.04.08 17:07)

    Нулевой пункт забыла:

    Мне интересно !  :)
  • Игорь Шевченко © (12.04.08 23:52) [35]
    Riply ©   (12.04.08 17:07) [33]

    Я к чему - и Process Explorer и handles.exe используют для получения информации ядерный драйвер.

    Как ты понимаешь, и Руссиновичу и брату его во Христе Когсуэллу тоже интересно "как это там система работает", весьма вероятно, что было интересно даже больше, чем тебе. Следовательно, они пришли к выводу, что попытки получить такую информацию из пользовательского режима рано или поздно закончатся неудачей. Вот и Шрайбер тоже рекомендует для получения информации пользоваться драйвером - у него, у драйвера, возможностей больше - можно читать напрямую те же таблицы, из которых получается информация, возвращаемая NtQueryxxxxx, и главное, что в это время тебе никто не мешает. А от вызова NtQueryxxxx до вызова NtQueryzzzz в системе может произойти масса событий, в результате чего информация собранная обоими вызовами будет настолько не соответствовать действительности, что потеряет всякий смысл.
  • Riply © (13.04.08 00:54) [36]
    > [35] Игорь Шевченко ©   (12.04.08 23:52)

    Вынуждена полностью согласиться со всеми утверждениями.
    К сожалению, до написания драйвера я еще не доросла. Уровень не тот :(
    Правда, "еще не вечер" :)

    А пока идет обучение, вынуждена довольствоваться тем,
    что удается получить в пользовательском режиме :)
  • Игорь Шевченко © (13.04.08 00:59) [37]

    > К сожалению, до написания драйвера я еще не доросла. Уровень
    > не тот :(


    Да это несложно. Покупается/ищется книга Свена Шрайбера "Недокументированные возможности Windows 2000" - там как раз таких драйверов, которые читают ядерные таблицы, штук несколько, и по шагам расписано их написание. Я серьезно.
  • Riply © (13.04.08 01:22) [38]
    > [37] Игорь Шевченко ©   (13.04.08 00:59)
    > Да это несложно. Покупается/ищется книга Свена Шрайбера "Недокументированные возможности Windows 2000" -
    > там как раз таких драйверов, которые читают ядерные таблицы, штук несколько, и по шагам расписано их написание.
    > Я серьезно.

    Ой, Игорь, я ведь могу последовать совету и сунуться в эту область :)
    И дорги назад уже не будет. Уж я то себя знаю. Так и завязну там :)
 
Конференция "WinAPI" » Узнать хендлы открытых процессом файлов [D6, WinXP]
Есть новые Нет новых   [134432   +20][b:0][p:0.004]