Что нового

Ошибки Помогите найти ошибку при использовании ресурса bmp

Twilight_Wolf

Новичок
Сообщения
16
Репутация
0
Добрый день. Возникла потребность искать на экране картинку по образцу (bmp) и получать координаты её места нахождения.
Начал с того, что набросал простой скрипт:
Код:
#include <ScreenCapture.au3>
#include "BmpSearch.au3"
#include <WinAPI.au3>


_GDIPlus_Startup()
$hSource = _ScreenCapture_Capture()
$hBmp1 = _GDIPlus_BitmapCreateFromFile(@ScriptDir & "\cat.bmp")
$hFind1 = _GDIPlus_BitmapCreateHBITMAPFromBitmap($hBmp1)
$aCoords1 = _BmpSearch($hSource, $hFind1, 1)

If @error Then
  Exit
  Else
  MouseClick("left", $aCoords1[1][2]+5, $aCoords1[1][3]+5, 1, 0)
    EndIf


Так всё работает.
Но, очень уж захотелось поместить шаблон картинки для поиска в сам скомпилированный скрипт, чтобы было всё красиво и всего один файл (т. к. картинок для поиска будет условно пара десятков).
Набросал второй вариант скрипта:
Код:
#Region
#AutoIt3Wrapper_Res_File_Add=cat.bmp, 2, 200
#EndRegion

#include <ScreenCapture.au3>
#include "BmpSearch.au3"
#include <WinAPI.au3>


_GDIPlus_Startup()
$hSource = _ScreenCapture_Capture()
$hInstance = _WinAPI_GetModuleHandle(0)
$hBitmap = _WinAPI_LoadBitmap($hInstance, 200)
$hFind1 = _GDIPlus_BitmapCreateHBITMAPFromBitmap($hBitmap)
$aCoords1 = _BmpSearch($hSource, $hFind1, 1)

If @error Then
  Exit
 Else
  MouseClick("left", $aCoords1[1][2], $aCoords1[1][3], 1, 0)
    EndIf


И вот тут вылезла засада: при попытке запуска скомпилированного варианта скрипт вываливается в ошибку (выжимка из журнала Windows):
Имя сбойного приложения: test.exe, версия: 0.0.0.0, метка времени: 0x5ea6a576
Имя сбойного модуля: gdiplus.dll, версия: 10.0.14393.0, метка времени: 0x57898f85
Код исключения: 0xc0000005
Смещение ошибки: 0x00056e80
Идентификатор сбойного процесса: 0x2708
Время запуска сбойного приложения: 0x01d61c761c74fcc5
Проверял Resource Hacker - картинка в exe файл добавляется. Помогите, пожалуйста, разобраться, что я делаю не так? Как корректно достать картинку из exe на лету и использовать её в качестве шаблона для поиска аналогичного участка на экране?
 

InnI

AutoIT Гуру
Сообщения
4,912
Репутация
1,429
Код:
$hSource = _ScreenCapture_Capture()
$hInstance = _WinAPI_GetModuleHandle(0)
$hBitmap = _WinAPI_LoadBitmap($hInstance, 200)
$hImage = _GDIPlus_BitmapCreateFromHBITMAP($hBitmap)
$hFind1 = _GDIPlus_BitmapCreateHBITMAPFromBitmap($hImage)
$aCoords1 = _BmpSearch($hSource, $hFind1, 1)
 
Автор
T

Twilight_Wolf

Новичок
Сообщения
16
Репутация
0
Код:
$hSource = _ScreenCapture_Capture()
$hInstance = _WinAPI_GetModuleHandle(0)
$hBitmap = _WinAPI_LoadBitmap($hInstance, 200)
$hImage = _GDIPlus_BitmapCreateFromHBITMAP($hBitmap)
$hFind1 = _GDIPlus_BitmapCreateHBITMAPFromBitmap($hImage)
$aCoords1 = _BmpSearch($hSource, $hFind1, 1)
Благодарю и снимаю шляпу!)
 
Автор
T

Twilight_Wolf

Новичок
Сообщения
16
Репутация
0
Код:
$hSource = _ScreenCapture_Capture()
$hInstance = _WinAPI_GetModuleHandle(0)
$hBitmap = _WinAPI_LoadBitmap($hInstance, 200)
$hImage = _GDIPlus_BitmapCreateFromHBITMAP($hBitmap)
$hFind1 = _GDIPlus_BitmapCreateHBITMAPFromBitmap($hImage)
$aCoords1 = _BmpSearch($hSource, $hFind1, 1)

Разрешите ещё один дилетантский вопрос задать: как правильно и в какой момент следует применять функцию _WinAPI_DeleteObject?
_WinAPI_DeleteObject($hImage) или _WinAPI_DeleteObject($hBitmap) ? (Если я правильно понял, то после MouseClick в моем простейшем примере?)
Сообщение автоматически объединено:

Тоже хорошая функция, спасибо!)
 

InnI

AutoIT Гуру
Сообщения
4,912
Репутация
1,429
Здесь нужно понимать разницу между объектами GdiPlus и Gdi32. Объекты GdiPlus нужно удалять через функции
Код:
_GDIPlus_ImageDispose()
; или
_GDIPlus_BitmapDispose()
Причём без разницы какую выбрать.
Но объекты Gdi32 нужно удалять только через _WinAPI_DeleteObject(). Если вы попробуете удалить $hSource (объект Gdi32) функцией из GdiPlus, то AutoIt аварийно завершит работу. А если наоборот - удалить $hImage через _WinAPI_DeleteObject(), то даже ошибки не будет. Но фактически объект не удалится, т.е. память не будет освобождена. В справке у функций указано, как удалять созданные объекты. Но, например, в русской справке есть ошибка: для функции _GDIPlus_BitmapCreateFromHBITMAP() указано удаление через _WinAPI_DeleteObject(), а нужно через _GDIPlus_BitmapDispose(), как в примере после описания.

Лучше всего сразу, когда объект больше не нужен. Но можно и в конце перед выходом все очищать.
А вот если, например, в цикле созданные объекты будут сохраняться в одной и той же переменной, то автоматически они из памяти удаляться не будут и перед перезаписью переменной объект нужно удалить. Теоретически все объекты GdiPlus будут удалены после завершения скрипта. А вот объекты Gdi32 так и будут занимать память, поэтому их нужно обязательно удалять.
 
Автор
T

Twilight_Wolf

Новичок
Сообщения
16
Репутация
0
Благодарю за столь развёрнутый ответ :smile: Чувствую, как становлюсь образованнее) Теперь мне следует переосмыслить что за объекты хранятся в переменных $hSource, $hBitmap, $hImage, $hFind1 какие из них следует удалять соответствующими функциями _WinAPI_DeleteObject() и _GDIPlus_BitmapDispose() в конце каждого цикла :scratch: Строки из примера выше были позаимствованы со смежных форумов и составлены, аналогично крупноузловому конструктору методом аналогии и общего представления, что должно получиться в итоге). Благодаря Вам, у меня появился уникальный шанс глубоко и в деталях разобраться в тонкостях работы с Autoit :wink:
Сообщение автоматически объединено:

Что-то я опять перемудрил)). Тепереча либо скрипт скомпилированный вылетает с ошибкой в процессе работы, либо до бесконечности ищет картинку...
Код:
Func _Main()
$i = 0
While 1
sleep(100)
_GDIPlus_Startup()
$hSource = _ScreenCapture_Capture()
$hInstance = _WinAPI_GetModuleHandle(0)
$hBitmap = _WinAPI_LoadBitmap($hInstance, 200)
$hImage = _GDIPlus_BitmapCreateFromHBITMAP($hBitmap)
$hFind1 = _GDIPlus_BitmapCreateHBITMAPFromBitmap($hImage)
$aCoords1 = _BmpSearch($hSource, $hFind1, 1)

;проверяем наличие нужной картинки на странице браузера и если её нет, обновляем страницу и ищем снова.
;если после  20 попыток  нужной картинки так и не нашлось, завершаем работу.

If @error Then
  Send("{F5}")
  sleep(7000)
  $i = $i + 1
  If $i > 20 Then
   _WinAPI_DeleteObject($hSource)
   Exit
  EndIf
   _WinAPI_DeleteObject($hSource)
  Else
  MouseClick("left", $aCoords1[1][2]+5, $aCoords1[1][3]+5, 1, 0)
   _WinAPI_DeleteObject($hSource)
   _GDIPlus_BitmapDispose($hImage)
        ExitLoop
EndIf
WEnd
EndFunc

Func _Add()
$i = 0
While 1
sleep(100)
_GDIPlus_Startup()
$hSource = _ScreenCapture_Capture()
$hInstance = _WinAPI_GetModuleHandle(0)
$hBitmap = _WinAPI_LoadBitmap($hInstance, 201)
$hImage = _GDIPlus_BitmapCreateFromHBITMAP($hBitmap)
$hFind1 = _GDIPlus_BitmapCreateHBITMAPFromBitmap($hImage)
$aCoords1 = _BmpSearch($hSource, $hFind1, 1)

If @error Then
  $i = $i + 1
    sleep(1000)
  If $i > 20 Then
   _WinAPI_DeleteObject($hSource)
 Send("{F5}")
  _Main()
  ExitLoop
  EndIf
   _WinAPI_DeleteObject($hSource)
Else
  MouseClick("left", $aCoords1[1][2]+15, $aCoords1[1][3]+15, 1, 0)
   _WinAPI_DeleteObject($hSource)
   _GDIPlus_BitmapDispose($hImage)
   ExitLoop
EndIf
WEnd
   EndFunc

В данном случае функции выполняются последовательно (приведена часть скрипта) и скрипту не удаётся найти вторую картинку... Видимо я как-то неправильно понимаю механизм создания и удаления графических объектов... Подсобите советом, пожалуйста :smile: И ещё нескромный вопрос: что хранится в переменной $hFind1 и надо-ли её очищать ? При попытке её очистить _GDIPlus_BitmapDispose($hFind1) работа программы завершается с ошибкой (виндовая ошибка - работа программы была прекращена)... P.S. Если убрать все функции удаления графических объектов, то скрипт работает как надо (или я так считаю:biggrin:), но боюсь, что это нехорошая идея, которая приведёт со временем к утечке памяти....
 
Последнее редактирование:

InnI

AutoIT Гуру
Сообщения
4,912
Репутация
1,429
Если я правильно понял, то так (проверял на ярлыках рабочего стола)
Код:
#AutoIt3Wrapper_UseX64=N
#AutoIt3Wrapper_Res_File_Add=cat.bmp, 2, 200
#AutoIt3Wrapper_Res_File_Add=dog.bmp, 2, 201
#AutoIt3Wrapper_Res_File_Add=bat.bmp, 2, 202

#include <ScreenCapture.au3>
#include "BmpSearch.au3"
#include <WinAPI.au3>

_GDIPlus_Startup()
$hInstance = _WinAPI_GetModuleHandle(0)
$iResNum = 200

While 1
  For $n = 0 To 2
    If Not _FindNext($iResNum + $n) Then Exit
  Next
WEnd

Func _FindNext($iNum)
  Sleep(100)
  Local $hSource, $aCoords
  Local $hBitmap = _WinAPI_LoadBitmap($hInstance, $iNum)
  Local $hImage = _GDIPlus_BitmapCreateFromHBITMAP($hBitmap)
  Local $hFind = _GDIPlus_BitmapCreateHBITMAPFromBitmap($hImage)
  For $i = 1 To 20
    $hSource = _ScreenCapture_Capture()
    $aCoords = _BmpSearch($hSource, $hFind, 1)
    If Not @error Then
      _WinAPI_DeleteObject($hSource)
      MouseClick("left", $aCoords[1][2] + 5, $aCoords[1][3] + 5, 1, 0)
      ExitLoop
    EndIf
    _WinAPI_DeleteObject($hSource)
    Send("{F5}")
    Sleep(3000)
  Next
  _WinAPI_DeleteObject($hFind)
  _WinAPI_DeleteObject($hBitmap)
  _GDIPlus_BitmapDispose($hImage)
  If $i = 21 Then Return SetError(1, _GDIPlus_Shutdown(), 0)
  Return 1
EndFunc  ;==>_FindNext
Сообщение автоматически объединено:

Кстати, ваша "часть скрипта" нормально работает при таком вызове
Код:
_Main()
_Add()

$hFind1 содержит объект Gdi32 и удалять его нужно через _WinAPI_DeleteObject() там же, где и $hSource удаляется.
 
Последнее редактирование:
Автор
T

Twilight_Wolf

Новичок
Сообщения
16
Репутация
0
Премного благодарен! Из Ваших ответов и примеров я получаю гораздо больше полезной информации, чем просто ответ на свой вопрос:good:
А причину, по которой мой скрипт не всегда отрабатывает корректно, я кажется понял: иногда искомая картинка на экране отрисовывается не совсем четко, т. е. возможны незначительные и неуловимые на глаз отличия в очертаниях и всё - скрипт её не узнаёт...но это уже совсем другая песня...

Пользуясь случаем я бы хотел досконально разобраться в том, что и как делает наш скрипт:

$hSource = _ScreenCapture_Capture() - тут мы получаем скин всего экрана (надо удалять через _WinAPI_DeleteObject() в конце каждого цикла)
$hInstance = _WinAPI_GetModuleHandle(0) - тут мы получаем дискриптор?! модуля для указанного модуля (звучит непонятно, но вроде удалять не надо )
$hBitmap = _WinAPI_LoadBitmap($hInstance, 200) - тут загружаем указанный bitmap ресурс (картинку?) из модуля исполняемого файла ( куда загружаем? в конце цикла удалять ничего не надо? )
$hImage = _GDIPlus_BitmapCreateFromHBITMAP($hBitmap) - тут создаем объект Bitmap (картинку?) используя дескриптор Bitmap (не очень понятно зачем, если мы уже загрузили картину в предыдущей функции. удаляем в конце каждого цикла через _GDIPlus_BitmapDispose())
$hFind1 = _GDIPlus_BitmapCreateHBITMAPFromBitmap($hImage) - тут создаёт дескриптор Bitmap из объекта Bitmap (вообще ничего не понимаю. удаляем в конце каждого цикла через _WinAPI_DeleteObject() )
$aCoords1 = _BmpSearch($hSource, $hFind1, 1) - тут получаем координаты верхнего левого края найденной картинки (вопросов нет)

Дополнительные дилетансткие вопросы, если позволите, от седого студента:

1. если аналогичных функций из моего примера много, то переменные обязательно называть иначе, например $hSource, $hSource1, $hSource2 и т.д. , или можно везде использовать одно и то же название, но в теле функции объявлять из как Local ?
2. как быстро удаляются объекты через _GDIPlus_BitmapDispose() и _WinAPI_DeleteObject() ? нужно-ли создавать искусственную паузу после вызова функции удаления, или скрит продолжит отрабатываться далее только после завершения процесса удаления?
3. что будет, если попытаться удалить несуществующий объект?
4. If @error Then тут @error относится ко всему, что выполнялось до этой конструкции, или только к последней выполняемой функции, в нашем случае $aCoords1 = _BmpSearch($hSource, $hFind1, 1) ? Говоря другими словами, @error примет положительное значение когда _BmpSearch возвратит ошибку, или когда любая из перечисленных выше функций возвратит ошибку, например _ScreenCapture_Capture ?
 

InnI

AutoIT Гуру
Сообщения
4,912
Репутация
1,429
Поймите меня правильно. Если я вам помог с кодом и немного объяснил его работу, то это не значит, что я готов углубляться в тонкости работы с графикой Windows и ресурсами приложений. Это довольно сложный материал и в двух словах его не объяснить. На каждое моё объяснение у вас будут появляться ещё вопросы (что и происходит). На все ваши вопросы вы можете найти ответы в справке, на этом или официальном форуме и в интернете. Желаю удачи в самообучении.
 
Автор
T

Twilight_Wolf

Новичок
Сообщения
16
Репутация
0
Я понял :yes: Благодарю за то, что не остались равнодушным и надеюсь, в дальнейшем ещё не раз порадуете ёмкими и лаконичными ответами по существу в виде кусочка кода, решающего конкретную задачу, а я наверняка ещё чего-нибудь вздумаю учудить:mocking: Почти на все вышепоставленные вопросы я уже нашёл примерные ответы (оказывается я не первый, кто их задаёт))) ) и картина понимания процессов стала несколько более четкой) Желаю Вам титанического терпения, дабы иметь возможность просвещать умы новоиспеченных чайников, вроде меня, коих, я уверен, хоть пруд пруди))))
 
Верх