1

Тема: AHK: Поместить изображение по ссылке в буфер обмена

Мне необходимо поместить изображение в буфер обмена, я пытаюсь сделать это следующим кодом:


hObject:=comObjCreate("WinHttp.WinHttpRequest.5.1")
hObject.open("GET", "https://www.etonline.com/_next/image?url=https%3A%2F%2Fwww.etonline.com%2Fsites%2Fdefault%2Ffiles%2Fstyles%2F640x360%2Fpublic%2Fimages%2F2023-06%2FGettyImages-1258514215.jpg%3Fh%3D71ff8dab&w=1024&q=75")

hObject.send()
clipboard := hObject.responseBody

Но получаю ошибку, что hObject.responseBody является объектом.
Пытался найти другие способы это сделать, допустим так:


hObject:=comObjCreate("WinHttp.WinHttpRequest.5.1")
hObject.open("GET", "https://www.etonline.com/_next/image?url=https%3A%2F%2Fwww.etonline.com%2Fsites%2Fdefault%2Ffiles%2Fstyles%2F640x360%2Fpublic%2Fimages%2F2023-06%2FGettyImages-1258514215.jpg%3Fh%3D71ff8dab&w=1024&q=75")

hObject.send()
copyImage(hObject.responseBody)

copyImage(rawImage)
{
	hMem := DllCall("GlobalAlloc", "UInt", 0x42, "Ptr", rawImage.MaxIndex() + 1)
    pMem := DllCall("GlobalLock", "Ptr", hMem, "Ptr")
    DllCall("RtlMoveMemory", "Ptr", pMem, "Ptr", &rawImage, "Ptr", rawImage.MaxIndex() + 1)
    DllCall("GlobalUnlock", "Ptr", hMem)
    DllCall("OpenClipboard", "Ptr", 0)
    DllCall("EmptyClipboard")
    DllCall("SetClipboardData", "UInt", 2, "Ptr", hMem)
    DllCall("CloseClipboard")
}

Но ничего не происходит.

Важно! Я НЕ хочу чтобы изображение сначала сохранялось в файл.

Win10: LTSC (21H2); AHK: ANSI (v1.1.36.02)

2

Re: AHK: Поместить изображение по ссылке в буфер обмена

На скорую руку как-то так:

#Requires AutoHotkey v1

url := "https://www.etonline.com/_next/image?url=https%3A%2F%2Fwww.etonline.com%2Fsites%2Fdefault%2Ffiles%2Fstyles%2F640x360%2Fpublic%2Fimages%2F2023-06%2FGettyImages-1258514215.jpg%3Fh%3D71ff8dab&w=1024&q=75"
whr := ComObjCreate("WinHttp.WinHttpRequest.5.1")
whr.Open("GET", url, true)
whr.Send()
whr.WaitForResponse()
StreamToClipboard(whr.ResponseStream)

StreamToClipboard(Stream) {
    static flags := (GMEM_MOVEABLE := 0x2) | (GMEM_ZEROINIT := 0x40)
         , szBITMAP := 16 + A_PtrSize*2, szBITMAPINFOHEADER := 40, CF_DIB := 0x8

    DllCall("Ole32\GetHGlobalFromStream", "Ptr", ComObjValue(Stream), "PtrP", pData)

    DllCall("OpenClipboard", "Ptr", A_ScriptHwnd)
    DllCall("EmptyClipboard")

    pngFormat := DllCall("RegisterClipboardFormat", "Str", "PNG")
    DllCall("SetClipboardData", "UInt", pngFormat, "Ptr", pData)

    hBitmap := (new GDIp).HBitmapFromStream(Stream)
    VarSetCapacity(DIBSECTION, size := A_PtrSize = 4 ? 84 : 104, 0)
    DllCall("GetObject", "Ptr", hBitmap, "Int", size, "Ptr", &DIBSECTION)
    dataSize := NumGet(DIBSECTION, szBITMAP + 20, "UInt")
    objSize := szBITMAPINFOHEADER + dataSize
    hObj := DllCall("GlobalAlloc", "UInt", flags, "Ptr", objSize, "Ptr")
    pObj := DllCall("GlobalLock", "Ptr", hObj, "Ptr")
    DllCall("RtlMoveMemory", "Ptr", pObj, "Ptr", &DIBSECTION + szBITMAP, "Ptr", szBITMAPINFOHEADER)
    DllCall("RtlMoveMemory", "Ptr", pObj + szBITMAPINFOHEADER, "Ptr", NumGet(DIBSECTION, 16 + A_PtrSize), "Ptr", dataSize)
    DllCall("SetClipboardData", "UInt", CF_DIB, "Ptr", hObj)

    DllCall("CloseClipboard")
    DllCall("DeleteObject", "Ptr", hBitmap)
}

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", "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)
    }

    HBitmapFromStream(Stream) {
        pIStream := ComObjQuery(Stream, "{0000000C-0000-0000-C000-000000000046}")
        DllCall("gdiplus\GdipCreateBitmapFromStream", "Ptr", pIStream, "PtrP", pBitmap)
        ObjRelease(pIStream)
        DllCall("gdiplus\GdipCreateHBITMAPFromBitmap", "Ptr", pBitmap, "PtrP", hBitmap, "UInt", 0xFFFFFFFF)
        DllCall("gdiplus\GdipDisposeImage", "Ptr", pBitmap)
        Return hBitmap
    }
}

Не факт, что тут всё идеально корректно, давно не пишу сложных кодов на AHK v1.

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

3

Re: AHK: Поместить изображение по ссылке в буфер обмена

А без GDIp можно как-то?

Win10: LTSC (21H2); AHK: ANSI (v1.1.36.02)

4

Re: AHK: Поместить изображение по ссылке в буфер обмена

Наверняка можно, но на v1 наработок нет, могу посмотреть на v2.

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

5

Re: AHK: Поместить изображение по ссылке в буфер обмена

Вот так вышло без GDIp:

#Requires AutoHotkey v1

url := "https://www.etonline.com/_next/image?url=https%3A%2F%2Fwww.etonline.com%2Fsites%2Fdefault%2Ffiles%2Fstyles%2F640x360%2Fpublic%2Fimages%2F2023-06%2FGettyImages-1258514215.jpg%3Fh%3D71ff8dab&w=1024&q=75"
whr := ComObjCreate("WinHttp.WinHttpRequest.5.1")
whr.Open("GET", url, true)
whr.Send()
whr.WaitForResponse()
StreamToClipboard(whr.ResponseStream)

StreamToClipboard(Stream) {
    static flags := (GMEM_MOVEABLE := 0x2) | (GMEM_ZEROINIT := 0x40)
         , szBITMAP := 16 + A_PtrSize * 2, szBITMAPINFOHEADER := 40, CF_DIB := 0x8
         , IID_IStream := "{0000000C-0000-0000-C000-000000000046}"

    DllCall("Ole32\GetHGlobalFromStream", "Ptr", IStream := ComObjQuery(Stream, IID_IStream), "PtrP", pData)

    DllCall("OpenClipboard", "Ptr", A_ScriptHwnd)
    DllCall("EmptyClipboard")

    pngFormat := DllCall("RegisterClipboardFormat", "Str", "PNG")
    DllCall("SetClipboardData", "UInt", pngFormat, "Ptr", pData)

    hBitmap := HBitmapFromStream(IStream)
    VarSetCapacity(DIBSECTION, size := A_PtrSize = 4 ? 84 : 104, 0)
    DllCall("GetObject", "Ptr", hBitmap, "Int", size, "Ptr", &DIBSECTION)
    dataSize := NumGet(DIBSECTION, szBITMAP + 20, "UInt")
    objSize := szBITMAPINFOHEADER + dataSize
    hObj := DllCall("GlobalAlloc", "UInt", flags, "Ptr", objSize, "Ptr")
    pObj := DllCall("GlobalLock", "Ptr", hObj, "Ptr")
    DllCall("RtlMoveMemory", "Ptr", pObj, "Ptr", &DIBSECTION + szBITMAP, "Ptr", szBITMAPINFOHEADER)
    DllCall("RtlMoveMemory", "Ptr", pObj + szBITMAPINFOHEADER, "Ptr", NumGet(DIBSECTION, 16 + A_PtrSize), "Ptr", dataSize)
    NumPut(-NumGet(pObj + 8, "Int"), pObj + 8, "Int")
    DllCall("SetClipboardData", "UInt", CF_DIB, "Ptr", hObj)

    DllCall("CloseClipboard")
    DllCall("DeleteObject", "Ptr", hBitmap)
    ObjRelease(IStream)
}

HBitmapFromStream(IStream) {
    static CLSID_WICImagingFactory      := "{CACAF262-9370-4615-A13B-9F5539DA4C0A}"
         ,  IID_IWICImagingFactory      := "{EC5EC8A9-C395-4314-9C77-54D7A935FF70}"
         , GUID_WICPixelFormat32bppBGRA := "{6FDDC324-4E03-4BFE-B185-3D77768DC90F}"
         , opt := WICDecodeMetadataCacheOnDemand := 0

    IWICImagingFactory := ComObjCreate(CLSID_WICImagingFactory, IID_IWICImagingFactory)
    ; IWICImagingFactory::CreateDecoderFromFilename
    DllCall(NumGet(NumGet(IWICImagingFactory + 0) + A_PtrSize * 4), "Ptr", IWICImagingFactory, "Ptr", IStream
                                                                  , "Ptr", 0, "UInt", opt, "PtrP", IWICBitmapDecoder)
    ; IWICBitmapDecoder::GetFrame
    DllCall(NumGet(NumGet(IWICBitmapDecoder + 0) + A_PtrSize * 13), "Ptr", IWICBitmapDecoder, "UInt", 0, "PtrP", IWICBitmapFrameDecode)
    ; IWICBitmapFrameDecode::GetSize
    DllCall(NumGet(NumGet(IWICBitmapFrameDecode + 0) + A_PtrSize * 3), "Ptr", IWICBitmapFrameDecode, "UIntP", width, "UIntP", height)

    VarSetCapacity(GUID, 16, 0)
    DllCall("Ole32\CLSIDFromString", "WStr", GUID_WICPixelFormat32bppBGRA, "Ptr", &GUID)
    hr := DllCall("Windowscodecs\WICConvertBitmapSource", "Ptr", &GUID, "Ptr", IWICBitmapFrameDecode, "PtrP", DestBitmapFrameDecode)
    if (hr = 0)
        ObjRelease(IWICBitmapFrameDecode), IWICBitmapFrameDecode := DestBitmapFrameDecode

    stride := width * 4, size := stride * height, VarSetCapacity(buf, size, 0)
    ; IWICBitmapFrameDecode::CopyPixels
    DllCall(NumGet(NumGet(IWICBitmapFrameDecode + 0) + A_PtrSize * 7), "Ptr", IWICBitmapFrameDecode, "Ptr", 0, "UInt", stride, "UInt", size, "Ptr", &buf)
    hBitmap := CreateDIBSection(width, height, pBits)
    
    DllCall("RtlMoveMemory", "Ptr", pBits, "Ptr", &buf, "Ptr", size)
    for k, v in [ IWICBitmapFrameDecode, IWICBitmapDecoder, IWICImagingFactory ]
        ObjRelease(v)
    return hBitmap
}

CreateDIBSection(w, h, ByRef ppvBits := 0, bpp := 32) {
    hDC := DllCall("GetDC", "Ptr", 0, "Ptr")
    VarSetCapacity(BITMAPINFO, 40, 0)
    NumPut(40 , BITMAPINFO,  0)
    NumPut( w , BITMAPINFO,  4)
    NumPut(-h , BITMAPINFO,  8)
    NumPut( 1 , BITMAPINFO, 12)
    NumPut(bpp, BITMAPINFO, 14)
    hBM := DllCall("CreateDIBSection", "Ptr", hDC, "Ptr", &BITMAPINFO, "UInt", 0
                                     , "PtrP", ppvBits, "Ptr", 0, "UInt", 0, "Ptr")
    DllCall("ReleaseDC", "Ptr", 0, "Ptr", hDC)
    return hBM
}
Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

6

Re: AHK: Поместить изображение по ссылке в буфер обмена

Phoenixxx_Czar пишет:

А без GDIp можно как-то?

А почему спросили, разве GDIp может не быть в системе?

По вопросам возмездной помощи пишите на E-Mail: serzh82saratov@mail.ru Telegram: https://t.me/sergiol982
Win10x64 AhkSpy, Hotkey, ClockGui

7 (изменено: __Михаил__, 2024-02-04 16:39:44)

Re: AHK: Поместить изображение по ссылке в буфер обмена

Второй вариант в стандартном Paint (Win 10) всё нормально вставляется. В старом v3.31 ошибка (установлен был на жёсткий диск с Win 7 вроде ~2013г):

Paint.NET
Изображение в Буфере обмена не удается распознать. Попробуйте скопировать его еще раз с помощью приложения, через которое оно было получено.

Первый вариант работает в обоих версиях Paint.

Win11x64, AHK v1.1.37.01 (Unicode 64-bit), AHK v2.0.17| AHK-Wiki | Переменные и выражения | RegEx101

8

Re: AHK: Поместить изображение по ссылке в буфер обмена

Да, заметил тоже, в некоторые приложения второй вариант вставляется некорректно, позже попробую исправить.

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