1 (изменено: pserg, 2013-09-03 21:51:01)

Тема: AHK: Windows7 и SysHeader321

Здравствуйте!
Была для XP написана программка, в которой шло обращение к диспетчеру задач и выбирались заголовки вкладки "Процессы" и записи по нужным процессам.
Запускаю эту программу в Win7 x64 - список процессов есть, а заголовки пропали.
Вот кусок из той программы (проверял - в XP пишет в файл список заголовков, в w7 - нет):


;SetTitleMatchMode, RegEx
#Persistent
#NoEnv
PathRoot = %A_Temp%\
    If not WinExist("Диспетчер задач Windows") {
      Run, Taskmgr.exe
      WinWait, Диспетчер задач Windows
    }
    ; Переключиться на вкладку "Процессы"
    SendMessage, 0x1330, 1,, SysTabControl321      ; 0x1330 is TCM_SETCURFOCUS.
    Sleep, 0
    SendMessage, 0x130C, 1,, SysTabControl321     ; 0x130C is TCM_SETCURSEL.
    ControlGet, hwndHeader, Hwnd,, SysHeader321      ; Хэндл заголовочного контрола.
    Headers := HeaderGetText(hwndHeader)         ; Список заголовков.
    Loop, Parse, Headers, `n ;(%A_Tab% OR %A_Space%)
      {
        FileAppend,
              (
;%A_LoopField%`n
), %PathRoot%File.csv
      }
      
HeaderGetText(hWnd)    ; ======== Функция, возвращающая список заголовков Диспетчера задач Windows=======
{
  MaxName := 100     ; Максимальная длина заголовка.
  Delimiter := "`n"  ; Разделитель заголовков в списке.

  PROCESS_VM_OPERATION := 0x8, PROCESS_VM_READ := 0x10
  PROCESS_VM_WRITE := 0x20,    MEM_COMMIT := 0x1000
  MEM_DECOMMIT := 0x4000,      PAGE_READWRITE = 0x4
  HDI_TEXT := 0x2,             HDM_GETITEMCOUNT := 0x1200
  HDM_GETITEMA := 0x1203

  VarSetCapacity(Buf, MaxName, 0)  ; Буфер под заголовок.
  VarSetCapacity(hdi, 48, 0)       ; Структура HDITEM, её размер 48 байт.
                                   ; В неё будет скидываться информация о каждом
                                   ; заголовке в ответ на сообщение HDM_GETITEM.

  ; PID процесса, создавшего заголовочный контрол.
  DllCall("GetWindowThreadProcessId", "uint",   hWnd
                                    , "uint *", PID)

  hProcess := DllCall("OpenProcess", "uint", PROCESS_VM_READ
                                           | PROCESS_VM_WRITE
                                           | PROCESS_VM_OPERATION
                                   , "int", FALSE
                                   , "uint", PID)
  If (hProcess = 0) {
    MsgBox, Не удалось открыть процесс.
    Return
  }

  ; Выделение памяти в адресном пространстве процесса
  ; под структуру HDITEM и строку заголовка.
  phdi := DllCall("VirtualAllocEx", "uint", hProcess
                                  , "uint", 0
                                  , "uint", MaxName + 48
                                  , "uint", MEM_COMMIT
                                  , "uint", PAGE_READWRITE)
  If (phdi = 0) {
    MsgBox, Ошибка выделения памяти в процессе.
    Goto, Close
  }

  ; Запись нужных значений в структуру.
  NumPut(HDI_TEXT, hdi), NumPut(phdi+48, hdi, 8), NumPut(MaxName, hdi, 16)

  ; Структура копируется в память процесса.
  Ret := DllCall("WriteProcessMemory", "uint", hProcess
                                     , "uint", phdi
                                     , "uint", &hdi
                                     , "uint", 48
                                     , "uint", 0)
  If (Ret = 0) {
    MsgBox, Ошибка записи в память процесса.
    Goto, Free
  }

  ; Запрос количества заголовков.
  Count := DllCall("SendMessageA", "uint", hWnd
                                 , "uint", HDM_GETITEMCOUNT
                                 , "uint", 0
                                 , "uint", 0)
  If (Count < 0) {
    MsgBox, Ошибка определения количества заголовков.
    Goto, Free
  }

  Loop, % Count  ; Запрос информации о каждом заголовке.
  {
    DllCall("SendMessageA", "uint", hWnd
                          , "uint", HDM_GETITEMA
                          , "uint", A_Index - 1
                          , "uint", phdi)

    ; Заголовок копируется из памяти процесса в местный буфер.
    Ret := DllCall("ReadProcessMemory", "uint", hProcess
                                      , "uint", phdi+48
                                      , "uint", &Buf
                                      , "uint", MaxName
                                      , "uint", 0)
    If (Ret = 0) {
      MsgBox, Ошибка чтения из памяти процесса.
      Goto, Free
    }

    VarSetCapacity(Buf, -1)  ; Обновление переменной.
    Headers .= Buf . (A_Index != Count ? Delimiter : "")
  }

  Free:  ; Освобождение памяти в процессе.
  DllCall("VirtualFreeEx", "uint", hProcess
                         , "uint", phdi
                         , "uint", MaxName+48
                         , "uint", MEM_DECOMMIT)

  Close:  ; Освобождение хэндла процесса.
  DllCall("CloseHandle", "uint", hProcess)

  Return Headers
}            ; ======== End  =======

Прошу помощи в восстановлении работоспособности!

2

Re: AHK: Windows7 и SysHeader321

А как он должен был работать? Есть подозрение, что восстанавливать работоспособность сего не имеет смысла, т. к. чтобы получить любую информацию о процессах, обращаться к диспетчеру задач нет необходимости.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

3

Re: AHK: Windows7 и SysHeader321

А как он должен был работать?

получаем список заголовков столбцов, затем в цикле выводим их через ";" в CSV-файл. дальше получаем список нужных процессов с данными из тех же столбцов диспетчера задач и также пишем их в файл. Данные собираются и анализируются после прогона - не было ли утечек памяти, роста загрузки ядра, не падал ли процесс во время прогона (менялся ли PID) и т.п. Удобнее, чем счетчики производительности, тем более, настраивать ничего не нужно, универсально для любой ОС (работало на NT 4.0, w2000, w2k3, Vista, XP). Единственно, один раз выбрать нужные столбцы в диспетчере - и все.

чтобы получить любую информацию о процессах, обращаться к диспетчеру задач нет необходимости

Если можно пояснить, как это применительно к моему случаю, буду рад.

4

Re: AHK: Windows7 и SysHeader321

Через WMI, в общих чертах так:

for k in ComObjGet("winmgmts:").InstancesOf("Win32_Process")
   Text .= "`r`n" k.Name A_Tab k.ProcessId A_Tab (k.WorkingSetSize//1024)

Sort, Text
Text := "Имя образа" A_Tab "ID процесса" A_Tab "Память`r`n" Text

Run, notepad.exe,,, PID
WinWait, ahk_pid %PID%
ControlSetText, Edit1, % Text

Если пояснишь подробнее, что именно нужно (все процессы, или только некоторые, какие столбцы), могу написать что-то более конкретное.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

5

Re: AHK: Windows7 и SysHeader321

teadrinker пишет:

Если пояснишь подробнее, что именно нужно (все процессы, или только некоторые, какие столбцы), могу написать что-то более конкретное.

Спасибо за ответ.
Подробнее о задаче: Для проверки работы полигона запускаю приложение (в состав может входить разное количество процессов, в зависимости от проекта). По-хорошему, хотелось бы выводить список процессов с возможностью установки флагов для выбора, но что-то это у меня не получилось.
Список желаемых столбцов (взял из tsskmgr w7): Имя образа, Ид процесса, ЦП, Память (частный рабочий набор), Выделенная память, Дескрипторы, Счетчик потоков, Объекты USER, Объекты GDI. Вроде все, но могут потребоваться Описание и Пользователь.

6

Re: AHK: Windows7 и SysHeader321

pserg
В семёрке надо скрипт запускать с правами администратора. Тогда доступ к Диспетчеру задач он получает. Но это не все проблемы, конечно. Надо, вероятно, подгонять размеры под x64, и есть ещё вопрос кодировки строк. Вы какую версию АНК используете?

7

Re: AHK: Windows7 и SysHeader321

YMP
Вы какую версию АНК используете?

использую версию AutoHotkeyU32.exe 1.1.9.1, т.к. запускать полученные скрипты приходится и под 32-разрядными ОС.

8

Re: AHK: Windows7 и SysHeader321

Без диспетчера:

strProcrssesNames := "autohotkey.exe|explorer.exe|smss.exe"   ; список имён процессов через |, можно задавать через ini-файл
keys := 1|2|4|8|16|256|1024   ; необходимые ключи, описаны в функции. Это не строка, без кавычек!

PI := GetProcessesInfo(strProcrssesNames, keys)

for k, v in PI
{
   Text .= (A_Index = 1 ? "" : "`r`n`r`n")
   for k, v in v
      Text .= (A_Index = 1 ? "" : "`r`n") v.Description " = " v.Value
}

Run, notepad.exe,,, PID
WinWait, ahk_pid %PID%
ControlSetText, Edit1, % Text

GetProcessesInfo(StringProcesses, keys)
{
/*
Имя образа                      = 1
ИД процесса                     = 2
ЦП                              = 4
Память (частный рабочий набор)  = 8
Выделенная память               = 16
Дескрипторы                     = 32
Счетчик потоков                 = 64
Объекты USER                    = 128
Объекты GDI                     = 256
Описание                        = 512
Пользователь                    = 1024
*/
   SetBatchLines, -1
   WMI := ComObjGet("winmgmts:"), ProcessesInfo := []
   Loop, parse, StringProcesses, |
   {
      Processes := WMI.ExecQuery("Select * from Win32_Process Where Name = '" A_LoopField "'")
      if (Processes.Count = 0)
      {
         ProcessesInfo.Insert("")
         continue
      }
      
      For Process in Processes
      {
         Info := {}, PID := Process.ProcessId
         
         if (keys & 4 || keys & 8 || keys & 16)
            Collection := WMI.ExecQuery("Select * from Win32_PerfFormattedData_PerfProc_Process Where IDProcess=" PID)
         
         keys & 1 ? Info.1 := {Value: Process.Name, Description: "Имя образа"}
         
         keys & 2 ? Info.2 := {Value: PID, Description: "ИД процесса"}
         
         if keys & 4
            for k in Collection
               Info.4 := {Value: k.PercentProcessorTime, Description: "ЦП"}
         
         if keys & 8
            for k in Collection
               Info.8 := {Value: (k.WorkingSetPrivate)//1024, Description: "Память (частный рабочий набор)"}
            
         if keys & 16
            for k in Collection
               Info.16 := {Value: (k.PrivateBytes)//1024, Description: "Выделенная память"}
         
         keys & 32 ? Info.32 := {Value: Process.HandleCount, Description: "Дескрипторы"}
         
         keys & 64 ? Info.64 := {Value: Process.ThreadCount, Description: "Счетчик потоков"}
         
         keys & 128 ? Info.128 := {Value: GetGuiResources(PID, 1), Description: "Объекты USER"}
         
         keys & 256 ? Info.256 := {Value: GetGuiResources(PID, 0), Description: "Объекты GDI"}
         
         keys & 512 ? Info.512 := {Value: Process.Description, Description: "Описание"}
         
         (keys & 1024 && Process.GetOwner((Sid := ComVar()).ref) = 0) ? Info.1024 := {Value: Sid[], Description: "Пользователь"}

         ProcessesInfo.Insert(Info)
      }
   }
   return ProcessesInfo
}

GetGuiResources(PID, flag)
{
   hProcess := DllCall("OpenProcess", UInt, PROCESS_QUERY_INFORMATION := 0x400, UInt, 0, UInt, PID)
   Count := DllCall("GetGuiResources", Ptr, hProcess, UInt, flag)
   DllCall("CloseHandle", Ptr, hProcess)
   Return Count
}

ComVar(Type=0xC)   ; http://l.autohotkey.net/docs/commands/ComObjActive.htm#ByRefEx
{
    static base := { __Get: "ComVarGet", __Set: "ComVarSet", __Delete: "ComVarDel" }
    ; Create an array of 1 VARIANT.  This method allows built-in code to take
    ; care of all conversions between VARIANT and AutoHotkey internal types.
    arr := ComObjArray(Type, 1)
    ; Lock the array and retrieve a pointer to the VARIANT.
    DllCall("oleaut32\SafeArrayAccessData", "ptr", ComObjValue(arr), "ptr*", arr_data)
    ; Store the array and an object which can be used to pass the VARIANT ByRef.
    return { ref: ComObjParameter(0x4000|Type, arr_data), _: arr, base: base }
}
ComVarGet(cv, p*) { ; Called when script accesses an unknown field.
    if p.MaxIndex() = "" ; No name/parameters, i.e. cv[]
        return cv._[0]
}
ComVarSet(cv, v, p*) { ; Called when script sets an unknown field.
    if p.MaxIndex() = "" ; No name/parameters, i.e. cv[]:=v
        return cv._[0] := v
}
ComVarDel(cv) { ; Called when the object is being freed.
    ; This must be done to allow the internal array to be freed.
    DllCall("oleaut32\SafeArrayUnaccessData", "ptr", ComObjValue(cv._))
}
Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

9

Re: AHK: Windows7 и SysHeader321

Что касается того скрипта, читающего заголовки, то, как оказалось, запускать его от админа не надо — надо как-то определять битность процесса Диспетчера задач. В Win7 x64 этот процесс 64-битный, если Диспетчер запущен вручную. Если же он запущен 32-битным скриптом, тогда он тоже 32-битный. Для этих двух случаев нужно использовать разные смещения в структуре HDITEM. Пока не знаю, как определить битность другого процесса.

10

Re: AHK: Windows7 и SysHeader321

Вот так у меня работает при любом запуске Диспетчера задач. Проверял на Win7 SP1 64 и XP SP2 32.


#Persistent
#NoEnv
PathRoot = %A_Temp%\
    If not WinExist("Диспетчер задач Windows") {
      Run, Taskmgr.exe
      WinWait, Диспетчер задач Windows
    }
    ; Переключиться на вкладку "Процессы"
    SendMessage, 0x1330, 1,, SysTabControl321     ; 0x1330 is TCM_SETCURFOCUS.
    Sleep, 0
    SendMessage, 0x130C, 1,, SysTabControl321     ; 0x130C is TCM_SETCURSEL.
    ControlGet, hwndHeader, Hwnd,, SysHeader321   ; Хэндл заголовочного контрола.
    Headers := HeaderGetText(hwndHeader)          ; Список заголовков.

    MsgBox, % Headers

    Loop, Parse, Headers, `n ;(%A_Tab% OR %A_Space%)
      {
        FileAppend, `;%A_LoopField%`n, %PathRoot%File.csv
      }
      
HeaderGetText(hWnd)    ; ======== Функция, возвращающая список заголовков Диспетчера задач Windows=======
{
  MaxName := 100     ; Максимальная длина заголовка.
  Delimiter := "`n"  ; Разделитель заголовков в списке.
  
  PROCESS_VM_OPERATION := 0x8, PROCESS_VM_READ := 0x10
  PROCESS_VM_WRITE := 0x20,    MEM_COMMIT := 0x1000
  MEM_DECOMMIT := 0x4000,      PAGE_READWRITE := 0x4
  HDI_TEXT := 0x2,             HDM_GETITEMCOUNT := 0x1200
  HDM_GETITEMW := 0x120B,      PROCESS_QUERY_INFORMATION := 0x400
  
   ; PID процесса, создавшего заголовочный контрол.
  DllCall("GetWindowThreadProcessId", "ptr",   hWnd
                                    , "uint *", PID)

  hProcess := DllCall("OpenProcess", "uint", PROCESS_VM_READ
                                           | PROCESS_VM_WRITE
                                           | PROCESS_VM_OPERATION
                                           | PROCESS_QUERY_INFORMATION
                                   , "int", FALSE
                                   , "uint", PID
                                   , "ptr")
  If (hProcess = 0) {
    MsgBox, Не удалось открыть процесс.
    Return
  }

  StructSize := 48
  TextMaxOffset := 16

  If (A_Is64bitOS) {
     Wow64 = 0
    DllCall("IsWow64Process", "ptr", hProcess, "uint *", Wow64)
    If (!Wow64) {
        StructSize := 64
        TextMaxOffset := 24
     }
  }

  VarSetCapacity(Buf, MaxName * 2, 0) ; Буфер под заголовок.
  VarSetCapacity(hdi, StructSize, 0)  ; Структура HDITEM, её размер 48 или 64 байта.
                                      ; В неё будет скидываться информация о каждом
                                      ; заголовке в ответ на сообщение HDM_GETITEM.

  ; Выделение памяти в адресном пространстве процесса
  ; под структуру HDITEM и строку заголовка.
  phdi := DllCall("VirtualAllocEx", "ptr", hProcess
                                  , "ptr", 0
                                  , "uint", StructSize + (MaxName + 1) * 2
                                  , "uint", MEM_COMMIT
                                  , "uint", PAGE_READWRITE
                                  , "ptr")
  If (phdi = 0) {
    MsgBox, Ошибка выделения памяти в процессе.
    Goto, Close
  }

  ; Запись нужных значений в структуру.
  NumPut(HDI_TEXT, hdi, 0, "uint"), NumPut(phdi+StructSize, hdi, 8, "ptr")
  NumPut(MaxName, hdi, TextMaxOffset, "uint")

  ; Структура копируется в память процесса.
  Ret := DllCall("WriteProcessMemory", "ptr", hProcess
                                     , "ptr", phdi
                                     , "ptr", &hdi
                                     , "ptr", StructSize
                                     , "ptr", 0)
  If (Ret = 0) {
    MsgBox, Ошибка записи в память процесса.
    Goto, Free
  }

  ; Запрос количества заголовков.
  Count := DllCall("SendMessageW", "ptr", hWnd
                                 , "uint", HDM_GETITEMCOUNT
                                 , "ptr", 0
                                 , "ptr", 0)
  If (Count < 0) {
    MsgBox, Ошибка определения количества заголовков.
    Goto, Free
  }

  Loop, % Count  ; Запрос информации о каждом заголовке.
  {
    DllCall("SendMessageW", "ptr", hWnd
                          , "uint", HDM_GETITEMW
                          , "ptr", A_Index - 1
                          , "ptr", phdi)

    ; Заголовок копируется из памяти процесса в местный буфер.
    Ret := DllCall("ReadProcessMemory", "ptr", hProcess
                                      , "ptr", phdi + StructSize
                                      , "ptr", &Buf
                                      , "ptr", MaxName * 2
                                      , "ptr", 0)
    If (Ret = 0) {
      MsgBox, Ошибка чтения из памяти процесса.
      Goto, Free
    }

    VarSetCapacity(Buf, -1)  ; Обновление переменной.
    Headers .= Buf . (A_Index != Count ? Delimiter : "")
  }

  Free:  ; Освобождение памяти в процессе.
  DllCall("VirtualFreeEx", "ptr", hProcess
                         , "ptr", phdi
                         , "ptr", StructSize + (MaxName + 1) * 2
                         , "uint", MEM_DECOMMIT)

  Close:  ; Освобождение хэндла процесса.
  DllCall("CloseHandle", "ptr", hProcess)

  Return Headers
}            ; ======== End  =======

11

Re: AHK: Windows7 и SysHeader321

YMP пишет:

Вот так у меня работает при любом запуске Диспетчера задач

Спасибо, список хэндлов получил.
Буду вживлять в свой скрипт.

12 (изменено: Selja8, 2014-07-13 16:39:32)

Re: AHK: Windows7 и SysHeader321

teadrinker пишет:

Через WMI, в общих чертах так:

for k in ComObjGet("winmgmts:").InstancesOf("Win32_Process")
   Text .= "`r`n" k.Name A_Tab k.ProcessId A_Tab (k.WorkingSetSize//1024)

Sort, Text
Text := "Имя образа" A_Tab "ID процесса" A_Tab "Память`r`n" Text

Run, notepad.exe,,, PID
WinWait, ahk_pid %PID%
ControlSetText, Edit1, % Text

Если пояснишь подробнее, что именно нужно (все процессы, или только некоторые, какие столбцы), могу написать что-то более конкретное.

Помогите пожалуйста получить список существующих процессов с выводом в блокнот.

, Имя.exe `n

"Запятая" "пробелл" "Имя процесса.*" "Переход на следущую строку".
В заранее спасибо.

Спасибо, с этим уже разобрался.