1

Тема: AHK: Сохранить скриншот в файл

Можно каким-то простым способом сохранить скриншот в файл?
Например получаем его в буфер обмена Send !{PRINTSCREEN} и далее содержимое буфера сохранить в файл например C:\file.png

2 (изменено: teadrinker, 2017-11-29 19:23:16)

Re: AHK: Сохранить скриншот в файл

Например, так можно (PrintScreen не нужен):

SaveScreenshotToFile(0, 0, A_ScreenWidth, A_ScreenHeight, A_ScriptDir . "\test.png")
;Run, % A_ScriptDir . "\test.png"

SaveScreenshotToFile(x, y, w, h, filePath)  {
   hBitmap := GetHBitmapFromScreen(x, y, w, h)
   gdip := new GDIplus
   pBitmap := gdip.BitmapFromHBitmap(hBitmap)
   DllCall("DeleteObject", Ptr, hBitmap)
   gdip.SaveBitmapToFile(pBitmap, filePath)
   gdip.DisposeImage(pBitmap)
}

GetHBitmapFromScreen(x, y, w, h)  {
   hDC := DllCall("GetDC", Ptr, 0, Ptr)
   hBM := DllCall("CreateCompatibleBitmap", Ptr, hDC, Int, w, Int, h, Ptr)
   pDC := DllCall("CreateCompatibleDC", Ptr, hDC, Ptr)
   oBM := DllCall("SelectObject", Ptr, pDC, Ptr, hBM, Ptr)
   DllCall("BitBlt", Ptr, pDC, Int, 0, Int, 0, Int, w, Int, h, Ptr, hDC, Int, x, Int, y, UInt, 0x00CC0020)
   DllCall("SelectObject", Ptr, pDC, Ptr, oBM)
   DllCall("DeleteDC", Ptr, pDC)
   DllCall("ReleaseDC", Ptr, 0, Ptr, hDC)
   Return hBM  ; should be deleted with DllCall("DeleteObject", Ptr, hBM)
}

class GDIplus   {
   __New()  {
      if !DllCall("GetModuleHandle", Str, "gdiplus", Ptr)
         DllCall("LoadLibrary", Str, "gdiplus")
      VarSetCapacity(si, A_PtrSize = 8 ? 24 : 16, 0), si := Chr(1)
      DllCall("gdiplus\GdiplusStartup", PtrP, pToken, Ptr, &si, Ptr, 0)
      this.token := pToken
   }
   
   __Delete()  {
      DllCall("gdiplus\GdiplusShutdown", Ptr, this.token)
      if hModule := DllCall("GetModuleHandle", Str, "gdiplus", Ptr)
         DllCall("FreeLibrary", Ptr, hModule)
   }
   
   BitmapFromHBitmap(hBitmap, Palette := 0)  {
      DllCall("gdiplus\GdipCreateBitmapFromHBITMAP", Ptr, hBitmap, Ptr, Palette, PtrP, pBitmap)
      return pBitmap  ; should be deleted with this.DisposeImage(pBitmap)
   }
   
   SaveBitmapToFile(pBitmap, sOutput, Quality=75)  {
      SplitPath, sOutput,,, Extension
      if Extension not in BMP,DIB,RLE,JPG,JPEG,JPE,JFIF,GIF,TIF,TIFF,PNG
         return -1

      DllCall("gdiplus\GdipGetImageEncodersSize", UIntP, nCount, UIntP, nSize)
      VarSetCapacity(ci, nSize)
      DllCall("gdiplus\GdipGetImageEncoders", UInt, nCount, UInt, nSize, Ptr, &ci)
      if !(nCount && nSize)
         return -2
      
      Loop, % nCount  {
         sString := StrGet(NumGet(ci, (idx := (48+7*A_PtrSize)*(A_Index-1))+32+3*A_PtrSize), "UTF-16")
         if !InStr(sString, "*." Extension)
            continue
         
         pCodec := &ci+idx
         break
      }
      
      if !pCodec
         return -3

      if RegExMatch(Extension, "i)^J(PG|PEG|PE|FIF)$") && Quality != 75  {
         DllCall("gdiplus\GdipGetEncoderParameterListSize", Ptr, pBitmap, Ptr, pCodec, UintP, nSize)
         VarSetCapacity(EncoderParameters, nSize, 0)
         DllCall("gdiplus\GdipGetEncoderParameterList", Ptr, pBitmap, Ptr, pCodec, UInt, nSize, Ptr, &EncoderParameters)
         Loop, % NumGet(EncoderParameters, "UInt")  {
            elem := (24+A_PtrSize)*(A_Index-1) + 4 + (pad := A_PtrSize = 8 ? 4 : 0)
            if (NumGet(EncoderParameters, elem+16, "UInt") = 1) && (NumGet(EncoderParameters, elem+20, "UInt") = 6)  {
               p := elem+&EncoderParameters-pad-4
               NumPut(Quality, NumGet(NumPut(4, NumPut(1, p+0)+20, "UInt")), "UInt")
               break
            }
         }      
      }
      
      if A_IsUnicode
         pOutput := &sOutput
      else  {
         VarSetCapacity(wOutput, StrPut(sOutput, "UTF-16")*2, 0)
         StrPut(sOutput, &wOutput, "UTF-16")
         pOutput := &wOutput
      }
      E := DllCall("gdiplus\GdipSaveImageToFile", Ptr, pBitmap, Ptr, pOutput, Ptr, pCodec, UInt, p ? p : 0)
      return E ? -5 : 0
   }
   
   DisposeImage(pBitmap)  {
      return DllCall("gdiplus\GdipDisposeImage", Ptr, pBitmap)
   }
}
Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Skype dmitry_fiveg

3

Re: AHK: Сохранить скриншот в файл

Что даёт поиск:
AutoHotkey: сохранение скриншота экрана в файл;
AHK: Скриншот выделенной части экрана поверх всех окон;
AHK: Сохранить маленький произвольный кусок экрана.

4

Re: AHK: Сохранить скриншот в файл

Вариант, что я сейчас привёл, самый простой, не требует никаких библиотек.

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

5

Re: AHK: Сохранить скриншот в файл

Думал, хотя бы «спасибо» скажут.

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

6

Re: AHK: Сохранить скриншот в файл

Оставь надежду, всяк сюда входящий. (с)
По идее, ThrowSum должен оправдать свой ник и какую-нибудь сумму тебе бросить. Наверно, твоё алчное подсознание за это и зацепилось.

7

Re: AHK: Сохранить скриншот в файл

Вряд ли моё подсознание расценило этот ник, как обещание, тут скорее призыв или крик о помощи.

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

8

Re: AHK: Сохранить скриншот в файл

А можешь опционально добавить сохранение с курсором мыши.

По вопросам возмездной помощи пишите письма
E-Mail: serzh82saratov@mail.ru
OS: Win7x64, AutoHotkey_L v1.1.30.01 (Unicode 32-bit).

9

Re: AHK: Сохранить скриншот в файл

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

Итак, teadrinker огромное спасибо, вы мне и в прошлые разы помогали. Ссылки которые дал уважаемый ypppu, я смотрел еще до старта темы пользуясь поиском по форуму, за это тоже благодарен, но все решения показались мне громоздкими, я надеялся что скрин можно сохранить парой строк понимая что они делают.

teadrinker пишет:

Например, так можно (PrintScreen не нужен)

А спросить я хотел по поводу этого скрипта, я поставил его просто на хоткей и он отлично сохраняет скрины, можно и таймштампы поставить в названии файла и размеры скриншота задать. Только вот образовалась такая проблема: в одном моём цикле делается 2 скриншота, до и после. Цикла всего 3. А проблема вот в чем:
делается скрин 1 - сохраненная картинка по-моему от предыдущего теста
скрин 2 - картинка сохранена со данными скрина 1

И так далее со сдвигом в единицу. Создается ощущение, что там где должна быть сохранена картинка 2, берется из кэша картинка 1 и сохраняется. Вот причину этого я пока не понял, возможно у меня в коде проблемы, хотя там простые циклы из которых вызывается функция SaveScreenshotToFile.

10

Re: AHK: Сохранить скриншот в файл

ThrowSum пишет:

можно и таймштампы поставить

teadrinker
Кстати, ещё бы опционально добавление текста в указанный угол или центр картинки, и в коллекцию.

По вопросам возмездной помощи пишите письма
E-Mail: serzh82saratov@mail.ru
OS: Win7x64, AutoHotkey_L v1.1.30.01 (Unicode 32-bit).

11 (изменено: teadrinker, 2017-12-01 02:05:25)

Re: AHK: Сохранить скриншот в файл

serzh82saratov пишет:

А можешь опционально добавить сохранение с курсором мыши.

; SaveScreenshotToFile(A_ScriptDir . "\test.png", [0, 0, A_ScreenWidth, A_ScreenHeight], true)
SaveScreenshotToFile(A_ScriptDir . "\test.png")
Run, % A_ScriptDir . "\test.png"

SaveScreenshotToFile(filePath, oCoords := "", captureCursor := false)  {
/*
oCoords — массив координат области экрана вида [x, y, w, h], например:
[0, 0, A_ScreenWidth, A_ScreenHeight] или [300, 300, 500, 500]
если массив пустой либо один из элементов пустой или не число — будет сделан скриншот всего виртуального экрана
captureCursor — если true, курсор будет включён в скриншот
*/
   for k, v in ["x", "y", "w", "h"]
      if ( (%v% := oCoords[k]) + 0 = "" && empty := true )
         break
      
   if empty  {
      Sysget, x, 76
      Sysget, y, 77   
      Sysget, w, 78
      Sysget, h, 79
   }
   hBitmap := GetHBitmapFromScreen(x, y, w, h, captureCursor)
   gdip := new GDIplus
   pBitmap := gdip.BitmapFromHBitmap(hBitmap)
   DllCall("DeleteObject", Ptr, hBitmap)
   gdip.SaveBitmapToFile(pBitmap, filePath)
   gdip.DisposeImage(pBitmap)
}

GetHBitmapFromScreen(x, y, w, h, captureCursor)  {
   hDC := DllCall("GetDC", Ptr, 0, Ptr)
   hBM := DllCall("CreateCompatibleBitmap", Ptr, hDC, Int, w, Int, h, Ptr)
   hMDC := DllCall("CreateCompatibleDC", Ptr, hDC, Ptr)
   oBM := DllCall("SelectObject", Ptr, hMDC, Ptr, hBM, Ptr)
   DllCall("BitBlt", Ptr, hMDC, Int, 0, Int, 0, Int, w, Int, h, Ptr, hDC, Int, x, Int, y, UInt, 0x00CC0020)
   ( captureCursor && CaptureCursor(hMDC, x, y) )
   DllCall("SelectObject", Ptr, hMDC, Ptr, oBM)
   DllCall("DeleteDC", Ptr, hMDC)
   DllCall("ReleaseDC", Ptr, 0, Ptr, hDC)
   Return hBM  ; should be deleted with DllCall("DeleteObject", Ptr, hBM)
}

CaptureCursor(hDC, x, y)  {
   VarSetCapacity(CURSORINFO, szCI := 4*2 + A_PtrSize + 8, 0)
   NumPut(szCI, CURSORINFO)
   DllCall("GetCursorInfo", Ptr, &CURSORINFO)
   bShow   := NumGet(CURSORINFO, 4, "UInt")
   hCursor := NumGet(CURSORINFO, 4*2)
   xCursor := NumGet(CURSORINFO, 4*2 + A_PtrSize, "Int")
   yCursor := NumGet(CURSORINFO, 4*2 + A_PtrSize + 4, "Int")

   if bShow && hCursor := DllCall("CopyIcon", Ptr, hCursor, Ptr)  {
      VarSetCapacity(ICONINFO, 4*2 + A_PtrSize*3, 0)
      DllCall("GetIconInfo", Ptr, hCursor, Ptr, &ICONINFO)
      bIcon    := NumGet(ICONINFO, "UInt")
      xHotspot := NumGet(ICONINFO, 4, "UInt")
      yHotspot := NumGet(ICONINFO, 4*2, "UInt")
      hBMMask  := NumGet(ICONINFO, 4*2 + A_PtrSize)
      hBMColor := NumGet(ICONINFO, 4*2 + A_PtrSize*2)

      DllCall("DrawIcon", Ptr, hDC, Int, xCursor - xHotspot - x, Int, yCursor - yHotspot - y, Ptr, hCursor)
      DllCall("DestroyIcon", Ptr, hCursor)
      ( hBMMask && DllCall("DeleteObject", Ptr, hBMMask) )
      ( hBMColor && DllCall("DeleteObject", Ptr, hBMColor) )
   }
}

class GDIplus   {
   __New()  {
      if !DllCall("GetModuleHandle", Str, "gdiplus", Ptr)
         DllCall("LoadLibrary", Str, "gdiplus")
      VarSetCapacity(si, A_PtrSize = 8 ? 24 : 16, 0), si := Chr(1)
      DllCall("gdiplus\GdiplusStartup", PtrP, pToken, Ptr, &si, Ptr, 0)
      this.token := pToken
   }
   
   __Delete()  {
      DllCall("gdiplus\GdiplusShutdown", Ptr, this.token)
      if hModule := DllCall("GetModuleHandle", Str, "gdiplus", Ptr)
         DllCall("FreeLibrary", Ptr, hModule)
   }
   
   BitmapFromHBitmap(hBitmap, Palette := 0)  {
      DllCall("gdiplus\GdipCreateBitmapFromHBITMAP", Ptr, hBitmap, Ptr, Palette, PtrP, pBitmap)
      return pBitmap  ; should be deleted with this.DisposeImage(pBitmap)
   }
   
   SaveBitmapToFile(pBitmap, sOutput, Quality=75)  {
      SplitPath, sOutput,,, Extension
      if Extension not in BMP,DIB,RLE,JPG,JPEG,JPE,JFIF,GIF,TIF,TIFF,PNG
         return -1

      DllCall("gdiplus\GdipGetImageEncodersSize", UIntP, nCount, UIntP, nSize)
      VarSetCapacity(ci, nSize)
      DllCall("gdiplus\GdipGetImageEncoders", UInt, nCount, UInt, nSize, Ptr, &ci)
      if !(nCount && nSize)
         return -2
      
      Loop, % nCount  {
         sString := StrGet(NumGet(ci, (idx := (48+7*A_PtrSize)*(A_Index-1))+32+3*A_PtrSize), "UTF-16")
         if !InStr(sString, "*." Extension)
            continue
         
         pCodec := &ci+idx
         break
      }
      
      if !pCodec
         return -3

      if RegExMatch(Extension, "i)^J(PG|PEG|PE|FIF)$") && Quality != 75  {
         DllCall("gdiplus\GdipGetEncoderParameterListSize", Ptr, pBitmap, Ptr, pCodec, UintP, nSize)
         VarSetCapacity(EncoderParameters, nSize, 0)
         DllCall("gdiplus\GdipGetEncoderParameterList", Ptr, pBitmap, Ptr, pCodec, UInt, nSize, Ptr, &EncoderParameters)
         Loop, % NumGet(EncoderParameters, "UInt")  {
            elem := (24+A_PtrSize)*(A_Index-1) + 4 + (pad := A_PtrSize = 8 ? 4 : 0)
            if (NumGet(EncoderParameters, elem+16, "UInt") = 1) && (NumGet(EncoderParameters, elem+20, "UInt") = 6)  {
               p := elem+&EncoderParameters-pad-4
               NumPut(Quality, NumGet(NumPut(4, NumPut(1, p+0)+20, "UInt")), "UInt")
               break
            }
         }      
      }
      
      if A_IsUnicode
         pOutput := &sOutput
      else  {
         VarSetCapacity(wOutput, StrPut(sOutput, "UTF-16")*2, 0)
         StrPut(sOutput, &wOutput, "UTF-16")
         pOutput := &wOutput
      }
      E := DllCall("gdiplus\GdipSaveImageToFile", Ptr, pBitmap, Ptr, pOutput, Ptr, pCodec, UInt, p ? p : 0)
      return E ? -5 : 0
   }
   
   DisposeImage(pBitmap)  {
      return DllCall("gdiplus\GdipDisposeImage", Ptr, pBitmap)
   }
}
serzh82saratov пишет:

Кстати, ещё бы опционально добавление текста в указанный угол или центр картинки, и в коллекцию.

Это уже выходит за рамки простой функции. Тут тогда придётся принимать в качестве параметров все свойства текста — шрифт, размер, написание, жирность, цвет, перенос, выравнивание.
ThrowSum, нужен пример кода, как вызываете функцию.

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

12

Re: AHK: Сохранить скриншот в файл

Это уже выходит за рамки простой функции

А за чем простая функция, главное функциональная. Параметры текста в массиве передавать, там же и его местоположение. Оно и нужно, потому что непросто.

По вопросам возмездной помощи пишите письма
E-Mail: serzh82saratov@mail.ru
OS: Win7x64, AutoHotkey_L v1.1.30.01 (Unicode 32-bit).

13

Re: AHK: Сохранить скриншот в файл

Ну так-то можно много всяких добавлений и усложнений придумать. Например, добавить своё изображение в скриншот, изменить размер всего изображения (пропорционально или непропорционально), сделать какой-либо цвет прозрачным или ещё что угодно. Но это уже не скриншот будет, это просто выходит за рамки указанной темы.

Можно сделать так: добавить ещё один параметр "userFunc" со ссылкой на пользовательскую функцию, которая перед сохранением будет как-то изменять итоговый hMDC, как сейчас это делает CaptureCursor().

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

14

Re: AHK: Сохранить скриншот в файл

serzh82saratov, Gdip_TextToGraphics, нет?
Только какое это имеет отношение к сохранению скриншота в файл. Вам нужен текст, кому-то нужны стрелки, заливка на хостинг, gui, ...
Кому надо, комбинируют готовые простые ф-ии; кому хочется особого, запихивают все в одну супер-пупер "функциональную".

15

Re: AHK: Сохранить скриншот в файл

Я себе может что и состряпаю если понадобится, но речь про новичков. Добавить текст в картинку для них задача нетривиальная, а зачастую им не то что функции, а параметры добавить сложно. Автоматическое добавление даты и ещё какой инфы на скриншот, помоему дело распространённое. Чтобы стрелки добавлять, файл можно после сохранения открывать в редакторе по умолчанию, до этого большинство само допетрит, и по другому это всё равно никак не сделать. Насчёт показа в гуи, можно вместо пути указать звёздочку, чтобы битмап возращало.
Думаю такая функция не помешает в коллекции и тем кто может это сделать сам, для того она и есть чтобы не писать самому, а брать готовое. Ну а если сделать заливку на хостинг, то вообще мастхэв.

По вопросам возмездной помощи пишите письма
E-Mail: serzh82saratov@mail.ru
OS: Win7x64, AutoHotkey_L v1.1.30.01 (Unicode 32-bit).

16

Re: AHK: Сохранить скриншот в файл

С текстом завтра попробую, а с заливкой на хостинг у меня уже есть основной скриншотный скрипт, который всё никак выложить не могу. Но там сложность есть — регистрироваться нужно и получать что-то типа id.

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

17

Re: AHK: Сохранить скриншот в файл

teadrinker пишет:

ThrowSum, нужен пример кода, как вызываете функцию.

Поставил больший Sleep перед и после функции, теперь работает, возможно из-за этого проблема была.

18 (изменено: teadrinker, 2017-12-02 08:59:38)

Re: AHK: Сохранить скриншот в файл

С текстом так вышло:

oRect := [ 100, 100, A_ScreenWidth - 200, A_ScreenHeight - 200 ]
oText := { fontFamily: "Verdana"
         , fontOptions: "cBlue s10 bold q5"
         , textOptions: "x" . A_ScreenWidth - 250 . " y40 right"
         , text: "Серый Форум ©`nAll rights reserved`n" . A_Year . "." . A_MM . "." . A_DD }
               
SaveScreenshotToFile(A_ScriptDir . "\test.png", oRect, true, oText)
Run, % A_ScriptDir . "\test.png"
ExitApp
               
SaveScreenshotToFile(filePath, oRect := "", captureCursor := false, oText := "")  {
/*
oRect — массив координат области экрана вида [x, y, w, h], например:
[0, 0, A_ScreenWidth, A_ScreenHeight] или [300, 300, 500, 500]
если массив пустой либо один из элементов пустой или не число — будет сделан скриншот всего виртуального экрана

captureCursor — если true, курсор будет включён в скриншот

oText — объект вида:
{ fontFamily:  "calibri"
, fontOptions: "cRed s24 w800 italic q5"   ; как в команде Gui, Font
, textOptions: "x10 y10 w200 center"       ; как в команде Gui, Add, Text
                                           ; x и y отсчитываются от левого верхнего края скриншота (не экрана)
                                           ; w нужно, если указано right или center, если w не указано, считается равным 0; h не нужно
                                           ; если указано right и текст не помещается по ширине w, он будет сдвинут влево
, text:        "Создано:`n01.12.2017" }
*/
   for k, v in ["x", "y", "w", "h"]
      if ( (%v% := oRect[k]) + 0 = "" && empty := true )
         break
      
   if empty  {
      Sysget, x, 76
      Sysget, y, 77
      Sysget, w, 78
      Sysget, h, 79
   }
   hBitmap := GetHBitmapFromScreen(x, y, w, h, captureCursor, oText)
   gdip := new GDIplus
   pBitmap := gdip.BitmapFromHBitmap(hBitmap)
   DllCall("DeleteObject", Ptr, hBitmap)
   gdip.SaveBitmapToFile(pBitmap, filePath)
   gdip.DisposeImage(pBitmap)
}

GetHBitmapFromScreen(x, y, w, h, captureCursor, oText)  {
   hDC := DllCall("GetDC", Ptr, 0, Ptr)
   hBM := DllCall("CreateCompatibleBitmap", Ptr, hDC, Int, w, Int, h, Ptr)
   hMDC := DllCall("CreateCompatibleDC", Ptr, hDC, Ptr)
   oBM := DllCall("SelectObject", Ptr, hMDC, Ptr, hBM, Ptr)
   DllCall("BitBlt", Ptr, hMDC, Int, 0, Int, 0, Int, w, Int, h, Ptr, hDC, Int, x, Int, y, UInt, 0x00CC0020)
   ( captureCursor && CaptureCursor(hMDC, x, y) )
   ( IsObject(oText) && DrawText(hMDC, oText) )
   DllCall("SelectObject", Ptr, hMDC, Ptr, oBM)
   DllCall("DeleteDC", Ptr, hMDC)
   DllCall("ReleaseDC", Ptr, 0, Ptr, hDC)
   Return hBM  ; should be deleted with DllCall("DeleteObject", Ptr, hBM)
}

CaptureCursor(hDC, x, y)  {
   VarSetCapacity(CURSORINFO, szCI := 4*2 + A_PtrSize + 8, 0)
   NumPut(szCI, CURSORINFO)
   DllCall("GetCursorInfo", Ptr, &CURSORINFO)
   bShow   := NumGet(CURSORINFO, 4, "UInt")
   hCursor := NumGet(CURSORINFO, 4*2)
   xCursor := NumGet(CURSORINFO, 4*2 + A_PtrSize, "Int")
   yCursor := NumGet(CURSORINFO, 4*2 + A_PtrSize + 4, "Int")

   if bShow && hCursor := DllCall("CopyIcon", Ptr, hCursor, Ptr)  {
      VarSetCapacity(ICONINFO, 4*2 + A_PtrSize*3, 0)
      DllCall("GetIconInfo", Ptr, hCursor, Ptr, &ICONINFO)
      bIcon    := NumGet(ICONINFO, "UInt")
      xHotspot := NumGet(ICONINFO, 4, "UInt")
      yHotspot := NumGet(ICONINFO, 4*2, "UInt")
      hBMMask  := NumGet(ICONINFO, 4*2 + A_PtrSize)
      hBMColor := NumGet(ICONINFO, 4*2 + A_PtrSize*2)

      DllCall("DrawIcon", Ptr, hDC, Int, xCursor - xHotspot - x, Int, yCursor - yHotspot - y, Ptr, hCursor)
      DllCall("DestroyIcon", Ptr, hCursor)
      ( hBMMask && DllCall("DeleteObject", Ptr, hBMMask) )
      ( hBMColor && DllCall("DeleteObject", Ptr, hBMColor) )
   }
}

DrawText(hDC, options)  {
   static WM_CTLCOLORSTATIC := 0x138, WM_GETFONT := 0x31, TRANSPARENT := 1
        , DT_NOCLIP := 0x100, DT_CENTER := 0x1, DT_RIGHT := 0x2, DT_LEFT := 0
   
   Gui, New, +hwndhGui
   Gui, Font, % options.fontOptions, % options.fontFamily
   Gui, Add, Text, % options.textOptions . " hwndhText", % options.text
   
   hTextDC := DllCall("GetDC", Ptr, hText, Ptr)
   DllCall("SendMessage", Ptr, hGui, UInt, WM_CTLCOLORSTATIC, Ptr, hTextDC, Ptr, hText)
   clr := DllCall("GetTextColor", Ptr, hTextDC)
   DllCall("ReleaseDC", Ptr, 0, Ptr, hTextDC)
   
   hFont := DllCall("SendMessage", Ptr, hText, UInt, WM_GETFONT, Ptr, 0, Ptr, 0, Ptr)
   Gui, Destroy
   
   obj := DllCall("SelectObject", Ptr, hDC, Ptr, hFont, Ptr)
   DllCall("SetBkMode", Ptr, hDC, Int, TRANSPARENT)
   DllCall("SetTextColor", Ptr, hDC, UInt, clr)
   
   RegExMatch(options.textOptions, "i)(^|\s)\Kx(?<X>\d+)", text)
   RegExMatch(options.textOptions, "i)(^|\s)\Ky(?<Y>\d+)", text)
   RegExMatch(options.textOptions, "i)(^|\s)\Kw(?<W>\d+)", text)
   
   left  := textX = "" ? 0 : textX
   top   := textY = "" ? 0 : textY
   right := textW = "" ? left : left + textW
   
   VarSetCapacity(RECT, 16, 0)
   NumPut(left  , RECT, 0)
   NumPut(top   , RECT, 4)
   NumPut(right , RECT, 8)
   
   RegExMatch(options.textOptions, "i)(^|\s)\K(?<Align>right|center)", text)
   align := {right: DT_RIGHT, center: DT_CENTER, "": DT_LEFT}[textAlign]
   
   DllCall("DrawText", Ptr, hDC, Str, options.text, Int, -1, Ptr, &RECT, UInt, DT_NOCLIP|align)
   DllCall("SelectObject", Ptr, hDC, Ptr, obj, Ptr)
   DllCall("DeleteObject", Ptr, hFont)
}

class GDIplus   {
   __New()  {
      if !DllCall("GetModuleHandle", Str, "gdiplus", Ptr)
         DllCall("LoadLibrary", Str, "gdiplus")
      VarSetCapacity(si, A_PtrSize = 8 ? 24 : 16, 0), si := Chr(1)
      DllCall("gdiplus\GdiplusStartup", PtrP, pToken, Ptr, &si, Ptr, 0)
      this.token := pToken
   }
   
   __Delete()  {
      DllCall("gdiplus\GdiplusShutdown", Ptr, this.token)
      if hModule := DllCall("GetModuleHandle", Str, "gdiplus", Ptr)
         DllCall("FreeLibrary", Ptr, hModule)
   }
   
   BitmapFromHBitmap(hBitmap, Palette := 0)  {
      DllCall("gdiplus\GdipCreateBitmapFromHBITMAP", Ptr, hBitmap, Ptr, Palette, PtrP, pBitmap)
      return pBitmap  ; should be deleted with this.DisposeImage(pBitmap)
   }
   
   SaveBitmapToFile(pBitmap, sOutput, Quality=75)  {
      SplitPath, sOutput,,, Extension
      if Extension not in BMP,DIB,RLE,JPG,JPEG,JPE,JFIF,GIF,TIF,TIFF,PNG
         return -1

      DllCall("gdiplus\GdipGetImageEncodersSize", UIntP, nCount, UIntP, nSize)
      VarSetCapacity(ci, nSize)
      DllCall("gdiplus\GdipGetImageEncoders", UInt, nCount, UInt, nSize, Ptr, &ci)
      if !(nCount && nSize)
         return -2
      
      Loop, % nCount  {
         sString := StrGet(NumGet(ci, (idx := (48+7*A_PtrSize)*(A_Index-1))+32+3*A_PtrSize), "UTF-16")
         if !InStr(sString, "*." Extension)
            continue
         
         pCodec := &ci+idx
         break
      }
      
      if !pCodec
         return -3

      if RegExMatch(Extension, "i)^J(PG|PEG|PE|FIF)$") && Quality != 75  {
         DllCall("gdiplus\GdipGetEncoderParameterListSize", Ptr, pBitmap, Ptr, pCodec, UintP, nSize)
         VarSetCapacity(EncoderParameters, nSize, 0)
         DllCall("gdiplus\GdipGetEncoderParameterList", Ptr, pBitmap, Ptr, pCodec, UInt, nSize, Ptr, &EncoderParameters)
         Loop, % NumGet(EncoderParameters, "UInt")  {
            elem := (24+A_PtrSize)*(A_Index-1) + 4 + (pad := A_PtrSize = 8 ? 4 : 0)
            if (NumGet(EncoderParameters, elem+16, "UInt") = 1) && (NumGet(EncoderParameters, elem+20, "UInt") = 6)  {
               p := elem+&EncoderParameters-pad-4
               NumPut(Quality, NumGet(NumPut(4, NumPut(1, p+0)+20, "UInt")), "UInt")
               break
            }
         }      
      }
      
      if A_IsUnicode
         pOutput := &sOutput
      else  {
         VarSetCapacity(wOutput, StrPut(sOutput, "UTF-16")*2, 0)
         StrPut(sOutput, &wOutput, "UTF-16")
         pOutput := &wOutput
      }
      E := DllCall("gdiplus\GdipSaveImageToFile", Ptr, pBitmap, Ptr, pOutput, Ptr, pCodec, UInt, p ? p : 0)
      return E ? -5 : 0
   }
   
   DisposeImage(pBitmap)  {
      return DllCall("gdiplus\GdipDisposeImage", Ptr, pBitmap)
   }
}
Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Skype dmitry_fiveg

19

Re: AHK: Сохранить скриншот в файл

teadrinker пишет:

С текстом завтра попробую, а с заливкой на хостинг у меня уже есть основной скриншотный скрипт, который всё никак выложить не могу. Но там сложность есть — регистрироваться нужно и получать что-то типа id.

Ты не про imgur.com? Не пойму как там API token получить.

По вопросам возмездной помощи пишите письма
E-Mail: serzh82saratov@mail.ru
OS: Win7x64, AutoHotkey_L v1.1.30.01 (Unicode 32-bit).

20 (изменено: teadrinker, 2018-01-10 03:22:24)

Re: AHK: Сохранить скриншот в файл

Регистрируешься на сайте, переходишь по ссылке, указываешь имя приложения (любое), выбираешь "OAuth 2 autorization without callback url", указываешь email. Получаешь Client ID и Client Secret. Последний уже не помню для чего нужен, в приложении используется Client ID.

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

21

Re: AHK: Сохранить скриншот в файл

Спасибо, код отсюда заработал!

По вопросам возмездной помощи пишите письма
E-Mail: serzh82saratov@mail.ru
OS: Win7x64, AutoHotkey_L v1.1.30.01 (Unicode 32-bit).

22 (изменено: serzh82saratov, 2018-01-10 05:12:59)

Re: AHK: Сохранить скриншот в файл

Можешь выложить свой код по теме imgur API, по ссылке только х32.
Или как bitmap преобразовать в binarydata.

По вопросам возмездной помощи пишите письма
E-Mail: serzh82saratov@mail.ru
OS: Win7x64, AutoHotkey_L v1.1.30.01 (Unicode 32-bit).

23

Re: AHK: Сохранить скриншот в файл

Только руки дошли:

SetWorkingDir, %A_ScriptDir%
SetBatchLines, -1

if !FileExist("AHK.png")
   URLDownloadToFile, https://autohotkey.com/assets/images/ahk-logo-no-text241x78-180.png, AHK.png

ClientID := "1234abcde5678fg"  ; указать свой ClientID
imagePath := "AHK.png"         ; указать путь к изображению

SendImageToImgur(imagePath, ClientID)
Return

SendImageToImgur(imagePath, ClientID, JpegQuality := "")  {
   oGdi := new GDIp
   hBitmap := LoadPicture(imagePath, "GDI+")
   pBitmap := oGdi.CreateBitmapFromHBITMAP(hBitmap)
   
   SplitPath, imagePath,,, ext
   buff := ext
   if ( (size := oGdi.SaveBitmap(pBitmap, buff, JpegQuality, true)) < 0 )  {
      Errors := { 2: "Could not get a list of encoders on system"
                , 3: "Could not find matching encoder for specified file format"
                , 4: "Could not save the bitmap to buffer" }
      MsgBox, 48, Ошибка записи в буфер, % Errors[-size]
      Return
   }
   DllCall("DeleteObject", Ptr, hBitmap)
   oGdi.DisposeImage(pBitmap)
   
   strBase64 := CryptBinaryToStringBASE64(&buff, size, true)
   http := ComObjCreate("Msxml2.XMLHTTP")
   http.open("POST", "https://api.imgur.com/3/image", true)
   http.onreadystatechange := Func("StateChange").Bind(http)
   http.SetRequestHeader("authorization", "Client-ID " . ClientID)
   http.send(strBase64)
   VarSetCapacity(strBase64, 0), VarSetCapacity(buff, 0)
}

StateChange(http)  {
   if !(http.readyState = 4)
      Return
   
   responsetext := http.responsetext
   if (http.status != 200)  {
      MsgBox, % "Ошибка отправки файла на imgur.com! Status: " . http.status . "`nResponsetext: " . responsetext
      Return
   }
   RegExMatch(responsetext, """link"":""(.*?)""", match)
   link := StrReplace(match1, "\")
   Gui, Imgur: Default
   try Gui, Destroy
   Gui, Font, s9
   Gui, Add, Edit, % "w250 h" . (link ? 24 : 120), % link ? link : responsetext
   Gui, Font
   Gui, Add, Button, x+-80 y+7 w80 h24 gCopyResponse Default, Copy
   GuiControl, Focus, Button1
   Gui, Show,, Imgur.com
}

CopyResponse()  {
   GuiControlGet, Clipboard, Imgur:, Edit1
   Gui, Imgur: Destroy
}

CryptBinaryToStringBASE64(pData, Bytes, NOCRLF = "")  {
   static CRYPT_STRING_BASE64 := 1, CRYPT_STRING_NOCRLF := 0x40000000
   CRYPT := CRYPT_STRING_BASE64 | (NOCRLF ? CRYPT_STRING_NOCRLF : 0)
   
   DllCall("Crypt32\CryptBinaryToString", Ptr, pData, UInt, Bytes, UInt, CRYPT, Ptr, 0, UIntP, Chars)
   VarSetCapacity(OutData, Chars * (A_IsUnicode ? 2 : 1))
   DllCall("Crypt32\CryptBinaryToString", Ptr, pData, UInt, Bytes, UInt, CRYPT, Str, OutData, UIntP, Chars)
   Return OutData
}

class GDIp  {
   __New() {
      if !DllCall("GetModuleHandle", Str, "gdiplus", Ptr)
         DllCall("LoadLibrary", Str, "gdiplus")
      VarSetCapacity(si, A_PtrSize = 8 ? 24 : 16, 0), si := Chr(1)
      DllCall("gdiplus\GdiplusStartup", UPtrP, pToken, Ptr, &si, Ptr, 0)
      this.token := pToken
   }
   
   __Delete()  {
      DllCall("gdiplus\GdiplusShutdown", Ptr, this.token)
      if hModule := DllCall("GetModuleHandle", Str, "gdiplus", Ptr)
         DllCall("FreeLibrary", Ptr, hModule)
   }

   CreateBitmapFromHBITMAP(hBitmap, Palette=0)  {
      DllCall("gdiplus\GdipCreateBitmapFromHBITMAP", Ptr, hBitmap, Ptr, Palette, PtrP, pBitmap)
      return pBitmap
   }

   SaveBitmap(pBitmap, ByRef info, Quality := 75, tobuff := "")
   {
      ; info — если копируем в буфер, тогда расширение файла, если в файл, тогда путь к файлу
      if tobuff
         Extension := info
      else
         SplitPath, info,,, Extension
      if Extension not in BMP,DIB,RLE,JPG,JPEG,JPE,JFIF,GIF,TIF,TIFF,PNG
         return -1

      DllCall("gdiplus\GdipGetImageEncodersSize", UintP, nCount, UintP, nSize)
      VarSetCapacity(ci, nSize)
      DllCall("gdiplus\GdipGetImageEncoders", UInt, nCount, UInt, nSize, Ptr, &ci)
      if !(nCount && nSize)
         return -2
      
      Loop, % nCount  {
         sString := StrGet(NumGet(ci, (idx := (48+7*A_PtrSize)*(A_Index-1))+32+3*A_PtrSize), "UTF-16")
         if !InStr(sString, "*." Extension)
            continue
         
         pCodec := &ci+idx
         break
      }
      
      if !pCodec
         return -3
      
      if RegExMatch(Extension, "i)^J(PG|PEG|PE|FIF)$") && Quality != 75  {
         DllCall("gdiplus\GdipGetEncoderParameterListSize", Ptr, pBitmap, Ptr, pCodec, UintP, nSize)
         VarSetCapacity(EncoderParameters, nSize, 0)
         DllCall("gdiplus\GdipGetEncoderParameterList", Ptr, pBitmap, Ptr, pCodec, UInt, nSize, Ptr, &EncoderParameters)
         Loop, % NumGet(EncoderParameters, "UInt")
         {
            elem := (24+A_PtrSize)*(A_Index-1) + 4 + (pad := A_PtrSize = 8 ? 4 : 0)
            if (NumGet(EncoderParameters, elem+16, "UInt") = 1) && (NumGet(EncoderParameters, elem+20, "UInt") = 6)
            {
               p := elem+&EncoderParameters-pad-4
               NumPut(Quality, NumGet(NumPut(4, NumPut(1, p+0)+20, "UInt")), "UInt")
               break
            }
         }      
      }
      if !tobuff
         E := DllCall("gdiplus\GdipSaveImageToFile", Ptr, pBitmap, WStr, info, Ptr, pCodec, UInt, p ? p : 0)
      else  {
         DllCall( "ole32\CreateStreamOnHGlobal", UInt, 0, Int, 1, PtrP, pStream )
         if !E := DllCall( "gdiplus\GdipSaveImageToStream", Ptr, pBitmap, Ptr, pStream, Ptr, pCodec, UInt, p ? p : 0 )  {
            DllCall( "ole32\GetHGlobalFromStream", Ptr, pStream, PtrP, hData )
            pData := DllCall( "GlobalLock", Ptr, hData, Ptr )
            nSize := DllCall( "GlobalSize", Ptr, hData, Ptr )
            VarSetCapacity( info, 0), VarSetCapacity( info, nSize, 0 )
            DllCall( "RtlMoveMemory", Ptr, &info, Ptr, pData, UInt, nSize )
            DllCall( "GlobalUnlock", Ptr, hData )
            DllCall( "GlobalFree", Ptr, hData )
         }
         ObjRelease(pStream)
      }
      return E ? -4 : tobuff ? nSize : 0
   }
   
   DisposeImage(pBitmap)  {
      return DllCall("gdiplus\GdipDisposeImage", Ptr, pBitmap)
   }
}

Нужно указать свой Client ID, в коде неверный.

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

24

Re: AHK: Сохранить скриншот в файл

Оказалось, перемудрил маленько, всё проще:

#NoEnv
SetBatchLines, -1

if (!imagePath := A_Args[1])
   FileSelectFile, imagePath,,, Выберите файл изображения для загрузки на imgur.com, Images (*.png; *.bmp; *.tiff; *.tif; *.jpeg; *.jpg; *.gif)
if !imagePath
   ExitApp
SendImageToImgur(imagePath, ClientID := "1234abcd5678ef")
Return

ImgurGuiClose:
   ExitApp

SendImageToImgur(imagePath, ClientID, JpegQuality := "")  {
   oFile := FileOpen(imagePath, "r")
   oFile.Pos := 0
   oFile.RawRead(buff, size := oFile.length)
   oFile.Close()
   strBase64 := CryptBinaryToStringBASE64(&buff, size, true)
   http := ComObjCreate("Msxml2.XMLHTTP")
   http.open("POST", "https://api.imgur.com/3/image", true)
   http.onreadystatechange := Func("StateChange").Bind(http)
   http.SetRequestHeader("authorization", "Client-ID " . ClientID)
   http.send(strBase64)
   VarSetCapacity(strBase64, 0), VarSetCapacity(buff, 0)
}

StateChange(http)  {
   if !(http.readyState = 4)
      Return
   
   responsetext := http.responsetext
   if (http.status != 200)  {
      MsgBox, % "Ошибка отправки файла на imgur.com! Status: " . http.status . "`nResponsetext: " . responsetext
      ExitApp
   }
   RegExMatch(responsetext, """link"":""(.*?)""", match)
   link := StrReplace(match1, "\")
   Gui, Imgur: Default
   Gui, Font, s9
   Gui, Add, Edit, % "w250 h" . (link ? 24 : 120), % link ? link : responsetext
   Gui, Font
   Gui, Add, Button, x+-80 y+7 w80 h24 gCopyResponse Default, Copy
   GuiControl, Focus, Button1
   Gui, Show,, Imgur.com
}

CopyResponse()  {
   GuiControlGet, Clipboard, Imgur:, Edit1
   Gui, Imgur: Destroy
   ExitApp
}

CryptBinaryToStringBASE64(pData, Bytes, NOCRLF = "")  {
   static CRYPT_STRING_BASE64 := 1, CRYPT_STRING_NOCRLF := 0x40000000
   CRYPT := CRYPT_STRING_BASE64 | (NOCRLF ? CRYPT_STRING_NOCRLF : 0)
   
   DllCall("Crypt32\CryptBinaryToString", Ptr, pData, UInt, Bytes, UInt, CRYPT, Ptr, 0, UIntP, Chars)
   VarSetCapacity(OutData, Chars * (A_IsUnicode ? 2 : 1))
   DllCall("Crypt32\CryptBinaryToString", Ptr, pData, UInt, Bytes, UInt, CRYPT, Str, OutData, UIntP, Chars)
   Return OutData
}
Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Skype dmitry_fiveg

25

Re: AHK: Сохранить скриншот в файл

А вариант со скриншотом можешь заодно запилить, чтобы без файла.

По вопросам возмездной помощи пишите письма
E-Mail: serzh82saratov@mail.ru
OS: Win7x64, AutoHotkey_L v1.1.30.01 (Unicode 32-bit).