Skip to content

Instantly share code, notes, and snippets.

@luncliff
Last active June 2, 2019 06:53
Show Gist options
  • Save luncliff/50b2ce1158d7309ac995c6cf343c9bfe to your computer and use it in GitHub Desktop.
Save luncliff/50b2ce1158d7309ac995c6cf343c9bfe to your computer and use it in GitHub Desktop.
Short practice for using Windows DLL in Go Language
package main
//
// References
// - https://medium.com/jettech/breaking-all-the-rules-using-go-to-call-windows-api-2cbfd8c79724
//
import (
"math/rand"
"os"
"path/filepath"
"plugin"
"runtime"
"syscall"
"testing"
)
func testDllPath() string {
name := "module1.dll"
wd, _ := os.Getwd()
mpath := filepath.Join(wd, "build", "Debug", name)
return mpath
}
func TestTryLoadWithPath(t *testing.T) {
mpath := testDllPath()
if _, err := plugin.Open(mpath); err != nil {
t.Log(err) // this can fail if not implemented
}
}
func TestSyscallWindowsDLL(t *testing.T) {
if runtime.GOOS != "windows" {
t.SkipNow()
}
mpath := testDllPath()
p, err := syscall.LoadDLL(mpath)
if err != nil {
t.Fatal(err)
}
defer p.Release()
t.Log("handle", p.Handle)
fn, err := p.FindProc("is_available")
if err != nil {
t.Fatal(err)
}
t.Logf("proc %s %x", fn.Name, fn.Addr())
arg0 := uint32(16)
ret1, _, err := fn.Call(uintptr(arg0))
if err != syscall.Errno(0) {
t.Log(err)
}
t.Log(ret1, ":=", fn.Name, "(", arg0, ")")
}
func TestSyscallParallel(t *testing.T) {
if runtime.GOOS != "windows" {
t.SkipNow()
}
mpath := testDllPath()
p, err := syscall.LoadDLL(mpath)
if err != nil {
t.Fatal(err)
}
defer p.Release()
fn, err := p.FindProc("is_available")
if err != nil {
t.Fatal(err)
}
requests := make(chan uint32, 10)
defer close(requests)
serve := func(arg0 uint32) (uintptr, error) {
ret1, _, err := fn.Call(uintptr(arg0))
if err != syscall.Errno(0) {
return ret1, err
}
return ret1, nil
}
worker := func(id uint32, reqs <-chan uint32) {
for req := range reqs {
ret, err := serve(req)
if err != nil {
t.Log(id, "failed", err)
} else {
t.Log(id, ret, "<--", req)
}
}
}
go worker(0xA, requests)
go worker(0xB, requests)
go worker(0xC, requests)
go worker(0xD, requests)
for i := 0; i < 50; i++ {
requests <- rand.Uint32()
}
}
cmake_minimum_required(VERSION 3.14)
project(module1 LANGUAGES CXX)
add_library(module1 SHARED
cpp_export.h
cpp_libmain.cpp
cpp_export_c.cpp
)
set_target_properties(module1
PROPERTIES
CXX_STANDARD 20
)
#pragma once
// clang-format off
# if defined(_MSC_VER) // MSVC
# define _HIDDEN_
# ifdef _WINDLL
# define _INTERFACE_ __declspec(dllexport)
# else
# define _INTERFACE_ __declspec(dllimport)
# endif
# elif defined(__GNUC__) || defined(__clang__)
# define _INTERFACE_ __attribute__((visibility("default")))
# define _HIDDEN_ __attribute__((visibility("hidden")))
# else
# error "unexpected compiler"
# endif // compiler check
// clang-format on
#include <cstdint>
namespace space1
{
_INTERFACE_ bool is_available(uint32_t idx) noexcept;
}
#include "cpp_export.h"
extern "C"
{
_INTERFACE_ uint32_t is_available(uint32_t idx) noexcept
{
return space1::is_available(idx);
}
}
#include "cpp_export.h"
namespace space1
{
bool is_available(uint32_t idx) noexcept
{
return idx < 4;
}
} // namespace space1
@luncliff
Copy link
Author

luncliff commented Jun 2, 2019

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment