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)
}
I wrote the source code as above.
What I want is to import cgo's HDC array into a w32.HDC array to know the width and height values of each monitor.
However, if you import t: = (* [] w32.HDC) unsafe.Pointer (& C.hdcArr))
and call cx: = w32.GetDeviceCaps ((* t) [0], w32.HORZRES)
Only 0 is returned.
How can I use cgo to find the width and height of multiple monitors?
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 := unsafe.Slice(C.hdcArr, C.count)
for _, dc := range t {
cx := w32.GetDeviceCaps(dc, w32.HORZRES)
fmt.Println(cx)
w32.DeleteDC(dc)
}
C.free(unsafe.Pointer(C.hdcArr))
}
It's very important for you to understand, that a pointer to a C-Array is just a memory address without any information about size (hence why your t array was empty). This is why you have to use unsafe.Slice
function, which returns a slice whose underlying array starts at hdcArr
and whose length and capacity are count
.
That's why i made count
a global variable.
The other issue you ran into was that the hdc handles passed to EnumProc
were only valid inside the callback. To make them permanent and usable outside the callback scope, you have to call CreateCompatibleDC(hdcMonitor);
.
To use this function with cgo you will have to include the lib gdi32 via #cgo LDFLAGS: -lgdi32
Once you are done using these DC's you have to free them again with w32.DeleteDC(dc)
Also don't forget to free your malloced array with C.free(unsafe.Pointer(C.hdcArr))
My advice: Whenever you use a WinApi, carefully read the msdn documentation. It takes some time, but saves you lots of trouble
You can also do this entirely in golang without 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)
}
or even smoother:
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)
}