Как я могу получить переменную c из cgo? - PullRequest
1 голос
/ 17 февраля 2020
package main

/*
#include <malloc.h>
#include <windows.h>
HDC *hdcArr

BOOL CALLBACK EnumProc(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) {
    for (int i = 0; i < (_msize(hdcArr) / sizeof(HDC)); i++) {
        if (hdcArr[i] == NULL) {
            hdcArr[i] = hdcMonitor;
            break;
        }
    }
    return TRUE;
}
void Init() {
    int count = GetSystemMetrics(SM_CMONITORS);
    hdcArr = (HDC*)malloc(sizeof(HDC) * count);
    memset(hdcArr, 0, sizeof(HDC) * count);
}
HDC* GetHDC() {
    return *hdcArr;
}
*/
import "C"
import (
    "fmt"
    "reflect"
    "unsafe"
    ".../w32"
)
func main() {
    var hdc w32.HDC
    hdc = w32.GetDC(0)
    C.Init()
    w32.EnumDisplayMonitors(hdc, nil, reflect.ValueOf(C.EnumProc).Pointer(), 0)
    t := (*[]w32.HDC)(unsafe.Pointer(&C.hdcArr))
    cx := w32.GetDeviceCaps((*t)[0], w32.HORZRES)
    fmt.Println(cx)
}

Я написал исходный код, как указано выше.

Я хочу импортировать массив cgo HD C в массив w32.HD C, чтобы знать значения ширины и высоты каждого монитора.
Однако, если вы импортируете t: = (* [] w32.HDC) unsafe.Pointer (& C.hdcArr)) и вызываете cx: = w32.GetDeviceCaps ((* t) [0], w32.HORZRES), возвращается только 0.

Как я могу использовать cgo, чтобы найти ширину и высоту нескольких мониторов?

1 Ответ

0 голосов
/ 17 февраля 2020
package main

/*
#cgo LDFLAGS: -lgdi32
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <windows.h>
HDC *hdcArr;
int count;

BOOL CALLBACK EnumProc(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) {
    int i;
    for (i = 0; i < (_msize(hdcArr) / sizeof(HDC)); i++) {
        if (hdcArr[i] == NULL) {
            hdcArr[i] = CreateCompatibleDC(hdcMonitor);
            break;
        }
    }
    return TRUE;
}
void Init() {
    count = GetSystemMetrics(SM_CMONITORS);
    hdcArr = (HDC*)malloc(sizeof(HDC) * count);
    memset(hdcArr, 0, sizeof(HDC) * count);
}
*/
import "C"

import (
    "fmt"
    "reflect"
    "unsafe"

    "github.com/JamesHovious/w32"
)

func main() {
    C.Init()
    hdc := w32.GetDC(0)
    w32.EnumDisplayMonitors(hdc, nil, reflect.ValueOf(C.EnumProc).Pointer(), 0)
    w32.ReleaseDC(0, hdc)
    t := (*[256]w32.HDC)(unsafe.Pointer(C.hdcArr))[:C.count:C.count]
    for _, dc := range t {
        cx := w32.GetDeviceCaps(dc, w32.HORZRES)
        fmt.Println(cx)
        w32.DeleteDC(dc)
    }
    C.free(unsafe.Pointer(C.hdcArr))
}

Для вас очень важно понять, что указатель на C -Array - это просто адрес памяти без какой-либо информации о размере (следовательно, почему ваш t-массив был пустым). Вот почему вы должны сначала привести его к большому массиву (*[256]w32.HDC), а затем нарезать его до нужного размера [:C.count:C.count]

Именно поэтому я сделал count глобальной переменной.

Другая проблема, с которой вы столкнулись, заключалась в том, что дескрипторы hd c, переданные EnumProc, действительны только внутри обратного вызова. Чтобы сделать их постоянными и пригодными для использования вне области обратного вызова, вы должны позвонить CreateCompatibleDC(hdcMonitor);. Чтобы использовать эту функцию с cgo, вам нужно будет включить lib gdi32 через #cgo LDFLAGS: -lgdi32

Как только вы закончите использовать эти D C, вы должны снова освободить их с помощью w32.DeleteDC(dc)

Также не забудьте освободить свой некорректный массив с помощью C.free(unsafe.Pointer(C.hdcArr))

Мой совет: всякий раз, когда вы используете WinApi, внимательно читайте документацию msdn. Это займет некоторое время, но избавит вас от многих проблем

Вы также можете сделать это полностью за golang без cgo:

package main

import (
    "fmt"
    "syscall"

    "github.com/JamesHovious/w32"
)

func EnumProc(hMonitor w32.HMONITOR, hdcMonitor w32.HDC, lprcMonitor *w32.RECT, dwData w32.LPARAM) uintptr {
    fmt.Println(w32.GetDeviceCaps(hdcMonitor, w32.HORZRES))
    return w32.TRUE
}

func main() {
    hdc := w32.GetDC(0)
    w32.EnumDisplayMonitors(hdc, nil, syscall.NewCallback(EnumProc), 0)
    w32.ReleaseDC(0, hdc)
}

или даже более плавно:

func EnumProc(hMonitor w32.HMONITOR, hdcMonitor w32.HDC, lprcMonitor *w32.RECT, dwData w32.LPARAM) uintptr {
    horzres := lprcMonitor.Right - lprcMonitor.Left
    fmt.Println(horzres)
    return w32.TRUE
}

func main() {
    w32.EnumDisplayMonitors(nil, nil, syscall.NewCallback(EnumProc), 0)
}
...