windowsgowinapicross-compilingcgo

Unknown type name in CGo Linux to Windows cross compilation


I'm trying out some Windows API functions using CGO. I have a dockerfile which builds the binary using the command. I am cross compiling for Windows from Linux

    export PKG_CONFIG_PATH=/usr/x86_64-w64-mingw32/lib/pkgconfig;

    GOOS=windows GOARCH=amd64 CGO_ENABLED=1 \
        CGO_CFLAGS="-isystem /usr/x86_64-w64-mingw32/include -I/usr/x86_64-w64-mingw32/lib/pkgconfig -I/usr/x86_64-w64-mingw32/include \
        -I /usr/x86_64-w64-mingw32/include/gst-include/glib-2.0 \
        -I/usr/share/mingw-w64/include -I/usr/x86_64-w64-mingw32/include/gst-include/gstreamer-1.0" \
        CGO_LDFLAGS="-L/usr/x86_64-w64-mingw32/lib -L/usr/x86_64-w64-mingw32/lib/pkgconfig -Luser32" \
        CXX=x86_64-w64-mingw32-g++ \
        CC=x86_64-w64-mingw32-gcc go build -o bin/yaoe.exe main.go

I'm trying to compile the following code with CGO

windows_helpers.h

#ifndef WINHELPERS_H
#define WINHELPERS_H

#include <windows.h>

LPCSTR wrapMAKEINTRESOURCEA(UINT_PTR i);
void CQueryDisplayConfig();

#endif // WINHELPERS_H

windows_helpers.c

#include "winhelpers_windows.h"

// wrapMAKEINTRESOURCEA wraps the MAKEINTRESOURCEA macro in a callable function
LPCSTR wrapMAKEINTRESOURCEA(UINT_PTR i) {
    return MAKEINTRESOURCEA(i);
}

void CQueryDisplayConfig() {
    UINT32 flags = 0x00000001;
    UINT32 numPathArrayElements;
    DISPLAYCONFIG_PATH_INFO *pathArray;
    UINT32 numModeInfoArrayElements;
    DISPLAYCONFIG_MODE_INFO *modeInfoArray;
    DISPLAYCONFIG_TOPOLOGY_ID *currentTopologyId;

    LONG queryReturn = QueryDisplayConfig(flags, &numPathArrayElements,
                                          &pathArray, &numModeInfoArrayElements,
                                          &modeInfoArray, &currentTopologyId);
}

And this is how I include it in my .go file

/*
#cgo CFLAGS: -I . -I/usr/x86_64-w64-mingw32/include
#cgo LDFLAGS: -L . -luser32 -l/usr/x86_64-w64-mingw32/lib
#include "winhelpers_windows.h"
*/
import "C"

I get the following error

winhelpers_windows.c: In function 'CQueryDisplayConfig':
winhelpers_windows.c:11:5: error: unknown type name 'DISPLAYCONFIG_PATH_INFO'
   11 |     DISPLAYCONFIG_PATH_INFO *pathArray;
      |     ^~~~~~~~~~~~~~~~~~~~~~~
winhelpers_windows.c:13:5: error: unknown type name 'DISPLAYCONFIG_MODE_INFO'
   13 |     DISPLAYCONFIG_MODE_INFO *modeInfoArray;
      |     ^~~~~~~~~~~~~~~~~~~~~~~
winhelpers_windows.c:14:5: error: unknown type name 'DISPLAYCONFIG_TOPOLOGY_ID'
   14 |     DISPLAYCONFIG_TOPOLOGY_ID *currentTopologyId;
      |     ^~~~~~~~~~~~~~~~~~~~~~~~~
winhelpers_windows.c:16:24: warning: implicit declaration of function 'QueryDisplayConfig'; did you mean 'CQueryDisplayConfig'? [-Wimplicit-function-declaration]
   16 |     LONG queryReturn = QueryDisplayConfig(flags, &numPathArrayElements,
      |                        ^~~~~~~~~~~~~~~~~~
      |                        CQueryDisplayConfig

I have checked inside /usr/x86_64-w64-mingw32/include and the typedefs do actually exist

yaoe@2a0b8c6ab378:/usr/x86_64-w64-mingw32/include$ grep -Rn DISPLAYCONFIG_TOPOLOGY_ID
wingdi.h:2255:  } DISPLAYCONFIG_TOPOLOGY_ID;
winuser.h:5621:  WINUSERAPI LONG WINAPI QueryDisplayConfig (UINT32 flags, UINT32 *numPathArrayElements, DISPLAYCONFIG_PATH_INFO *pathArray, UINT32 *numModeInfoArrayElements, DISPLAYCO
NFIG_MODE_INFO *modeInfoArray, DISPLAYCONFIG_TOPOLOGY_ID *currentTopologyId);

--------------------

yaoe@2a0b8c6ab378:/usr/x86_64-w64-mingw32/include$ grep -Rn DISPLAYCONFIG_PATH_INFO
wingdi.h:2238:  typedef struct DISPLAYCONFIG_PATH_INFO {
wingdi.h:2242:  } DISPLAYCONFIG_PATH_INFO;
winuser.h:5620:  WINUSERAPI LONG WINAPI SetDisplayConfig (UINT32 numPathArrayElements, DISPLAYCONFIG_PATH_INFO *pathArray, UINT32 numModeInfoArrayElements, DISPLAYCONFIG_MODE_INFO *mo
deInfoArray, UINT32 flags);
winuser.h:5621:  WINUSERAPI LONG WINAPI QueryDisplayConfig (UINT32 flags, UINT32 *numPathArrayElements, DISPLAYCONFIG_PATH_INFO *pathArray, UINT32 *numModeInfoArrayElements, DISPLAYCO
NFIG_MODE_INFO *modeInfoArray, DISPLAYCONFIG_TOPOLOGY_ID *currentTopologyId);

The typedefs are inside wingdi.h which is being included by windows.h

Not quite sure what is going wrong. Microsoft docs say simply including windows.h should be enough, and sure enough every other header that contains these typedefs are included inside windows.h


Solution

  • As @IInspectable mentioned, the error had to do with SetDisplayConfig and QueryDisplayConfig being introduced in Windows 7, and since I had not provided any compilation target, MinGW failed to find the function definitions.

    Explicitly telling the compiler to compile for Windows 10 and above fixed the issue

    // Compile for windows 10 and above
    #define _WIN32_WINNT_WIN10 0x0A00
    #define WINVER 0x0A00