2016-12-19 23 views
0

Я пытаюсь использовать syscall с user32.dll, чтобы получить содержимое буфера обмена. Я ожидаю, что это будут данные изображения с экрана печати.Как получить буфер данных изображения из памяти буфера обмена (uintptr)?

Прямо сейчас у меня есть это:

if opened := openClipboard(0); !opened { 
    fmt.Println("Failed to open Clipboard") 
} 

handle := getClipboardData(CF_BITMAP) 

// get buffer 

img, _, err := Decode(buffer) 

Мне нужно, чтобы получить данные в читаемый буфер с помощью ручки.

У меня было некоторое вдохновение от AllenDang/w32 и atotto/clipboard на github. Ниже будет работать на текст, основанный на реализации atotto в:

text := syscall.UTF16ToString((*[1 << 20]uint16)(unsafe.Pointer(handle))[:]) 

Но как я могу получить буфер, содержащий данные изображения, я могу расшифровать?

[Update]

Going раствором @kostix при условии, я взломал вместе рабочий пример наполовину:

image.RegisterFormat("bmp", "bmp", bmp.Decode, bmp.DecodeConfig) 

if opened := w32.OpenClipboard(0); opened == false { 
    fmt.Println("Error: Failed to open Clipboard") 
} 

//fmt.Printf("Format: %d\n", w32.EnumClipboardFormats(w32.CF_BITMAP)) 
handle := w32.GetClipboardData(w32.CF_DIB) 
size := globalSize(w32.HGLOBAL(handle)) 
if handle != 0 { 
    pData := w32.GlobalLock(w32.HGLOBAL(handle)) 
    if pData != nil { 
     data := (*[1 << 25]byte)(pData)[:size] 
     // The data is either in DIB format and missing the BITMAPFILEHEADER 
     // or there are other issues since it can't be decoded at this point 
     buffer := bytes.NewBuffer(data) 
     img, _, err := image.Decode(buffer) 
     if err != nil { 
      fmt.Printf("Failed decoding: %s", err) 
      os.Exit(1) 
     } 

     fmt.Println(img.At(0, 0).RGBA()) 
    } 

    w32.GlobalUnlock(w32.HGLOBAL(pData)) 
} 
w32.CloseClipboard() 

AllenDang/w32 содержит большую часть того, что вам нужно, но иногда вам нужно реализовать что-то сами, как globalSize():

var (
    modkernel32 = syscall.NewLazyDLL("kernel32.dll") 
    procGlobalSize = modkernel32.NewProc("GlobalSize") 
) 

func globalSize(hMem w32.HGLOBAL) uint { 
    ret, _, _ := procGlobalSize.Call(uintptr(hMem)) 

    if ret == 0 { 
     panic("GlobalSize failed") 
    } 

    return uint(ret) 
} 

Может быть, кто-то придет с решением, чтобы получить данные в формате BMP. Пока я буду идти по другому пути.

+0

Вам нужно знать, какие большие данные «CF_BITMAP». Что делает 'getClipboardData'? – JimB

+0

Ну, CF_BITMAP просто сообщает вызов, какой тип данных мы запрашиваем, он равен 2. Размер изображения/растрового изображения будет связан с разрешением, но я не мог придумать число, если это то, что ты имеешь в виду. getClipboardData возвращает uintptr, который должен быть указателем на данные изображения. Я просто не знаю, как правильно его получить. Я не совсем понимаю, как работает текстовая строка, возможно, вы или кто-то еще здесь знает, как она работает и какова будет версия буфера. – Kesarion

+0

С быстрым взглядом на 'github.com/atotto/clipboard' ваш пример не будет работать над текстом, потому что' handle' не является указателем на строку с нулевым завершением, это WINAPI HANDLE. То же самое касается растрового изображения, указатель отличается от РУЧКИ. На самом деле это не просто «извлечение» некоторой памяти с помощью указателя, вы должны использовать API окон для доступа к растровому изображению через данный дескриптор. – JimB

ответ

1

@JimB правильно: user32!GetClipboardData() возвращает HGLOBAL и комментарий Пример over there предлагает использовать kernel32!GlobalLock(): а) глобально блокировать эту ручку, и б) дать правильный указатель на память, указанных им.

Вам понадобится kernel32!GlobalUnlock() ручка после того, как вы закончите с ней.

Что касается преобразования указателей, полученных из функций API Win32, в нечто, читаемое Go, обычный трюк - это литье указателя на безумно большой фрагмент. Процитировать «Включение C массивов в Go дольки» из "the Go wiki article on cgo":

Чтобы создать фрагмент Go подкрепленная массива C (без копирования исходных данных), нужно приобрести эту длину во время выполнения и использовать тип преобразование в указатель на очень большой массив, а затем отрезать его до нужной длины (также помните, чтобы установить кепку, если вы используете Go 1.2> или более поздней) (см. http://play.golang.org/p/XuC0xqtAIC для runnable пример):

import "C" 
import "unsafe" 
... 
var theCArray *C.YourType = C.getTheArray() 
length := C.getTheArrayLength() 
slice := (*[1 << 30]C.YourType)(unsafe.Pointer(theCArray))[:length:length] 

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

В вашем случае это будет проще:

h := GlobalLock() 
defer GlobalUnlock(h) 
length := somehowGetLengthOfImageInTheClipboard() 
slice := (*[1 << 30]byte)(unsafe.Pointer((uintptr(h)))[:length:length] 

Тогда вам нужно на самом деле читать растрового изображения.

Это зависит от формата независимого от устройства битового массива (DIB), доступного для экспорта из буфера обмена.

См. this и this для начала.

Как обычно, определения BITMAPINFOHEADER и т. Д. Легко доступны в Интернете на сайте MSDN.