Andrew Gerrand | 5bc444d | 2014-12-10 11:35:11 +1100 | [diff] [blame] | 1 | # Calling a Windows DLL |
| 2 | |
| 3 | There are a few ways to call "C" code from inside Go |
| 4 | |
Ian Lance Taylor | 0f16ea9 | 2016-06-23 19:37:04 -0700 | [diff] [blame] | 5 | First way: Dynamically load a dll, then call a method on it. You can call the method via "syscallXX" (the XX is number of parameters, but if it has few than that, like if you need seven parameter, then syscall9 will still work, you just tell it the number of arguments is 7). |
Andrew Gerrand | 5bc444d | 2014-12-10 11:35:11 +1100 | [diff] [blame] | 6 | |
| 7 | A sample program that calls Windows DLLs from Go: |
| 8 | |
nathany | e7bf74d | 2014-12-10 22:51:09 -0800 | [diff] [blame] | 9 | ```go |
Andrew Gerrand | 5bc444d | 2014-12-10 11:35:11 +1100 | [diff] [blame] | 10 | package main |
| 11 | |
| 12 | import ( |
| 13 | "fmt" |
| 14 | "syscall" |
| 15 | "unsafe" |
| 16 | ) |
| 17 | |
| 18 | func abort(funcname string, err error) { |
| 19 | panic(fmt.Sprintf("%s failed: %v", funcname, err)) |
| 20 | } |
| 21 | |
| 22 | var ( |
| 23 | kernel32, _ = syscall.LoadLibrary("kernel32.dll") |
| 24 | getModuleHandle, _ = syscall.GetProcAddress(kernel32, "GetModuleHandleW") |
| 25 | |
| 26 | user32, _ = syscall.LoadLibrary("user32.dll") |
| 27 | messageBox, _ = syscall.GetProcAddress(user32, "MessageBoxW") |
| 28 | ) |
| 29 | |
| 30 | const ( |
| 31 | MB_OK = 0x00000000 |
| 32 | MB_OKCANCEL = 0x00000001 |
| 33 | MB_ABORTRETRYIGNORE = 0x00000002 |
| 34 | MB_YESNOCANCEL = 0x00000003 |
| 35 | MB_YESNO = 0x00000004 |
| 36 | MB_RETRYCANCEL = 0x00000005 |
| 37 | MB_CANCELTRYCONTINUE = 0x00000006 |
| 38 | MB_ICONHAND = 0x00000010 |
| 39 | MB_ICONQUESTION = 0x00000020 |
| 40 | MB_ICONEXCLAMATION = 0x00000030 |
| 41 | MB_ICONASTERISK = 0x00000040 |
| 42 | MB_USERICON = 0x00000080 |
| 43 | MB_ICONWARNING = MB_ICONEXCLAMATION |
| 44 | MB_ICONERROR = MB_ICONHAND |
| 45 | MB_ICONINFORMATION = MB_ICONASTERISK |
| 46 | MB_ICONSTOP = MB_ICONHAND |
| 47 | |
| 48 | MB_DEFBUTTON1 = 0x00000000 |
| 49 | MB_DEFBUTTON2 = 0x00000100 |
| 50 | MB_DEFBUTTON3 = 0x00000200 |
| 51 | MB_DEFBUTTON4 = 0x00000300 |
| 52 | ) |
| 53 | |
| 54 | func MessageBox(caption, text string, style uintptr) (result int) { |
| 55 | var nargs uintptr = 4 |
| 56 | ret, _, callErr := syscall.Syscall9(uintptr(messageBox), |
| 57 | nargs, |
| 58 | 0, |
| 59 | uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(text))), |
| 60 | uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(caption))), |
| 61 | style, |
| 62 | 0, |
| 63 | 0, |
| 64 | 0, |
| 65 | 0, |
| 66 | 0) |
| 67 | if callErr != 0 { |
| 68 | abort("Call MessageBox", callErr) |
| 69 | } |
| 70 | result = int(ret) |
| 71 | return |
| 72 | } |
| 73 | |
| 74 | func GetModuleHandle() (handle uintptr) { |
| 75 | var nargs uintptr = 0 |
| 76 | if ret, _, callErr := syscall.Syscall(uintptr(getModuleHandle), nargs, 0, 0, 0); callErr != 0 { |
| 77 | abort("Call GetModuleHandle", callErr) |
| 78 | } else { |
| 79 | handle = ret |
| 80 | } |
| 81 | return |
| 82 | } |
| 83 | |
| 84 | func main() { |
| 85 | defer syscall.FreeLibrary(kernel32) |
| 86 | defer syscall.FreeLibrary(user32) |
| 87 | |
| 88 | fmt.Printf("Return: %d\n", MessageBox("Done Title", "This test is Done.", MB_YESNOCANCEL)) |
| 89 | } |
| 90 | |
| 91 | func init() { |
| 92 | fmt.Print("Starting Up\n") |
| 93 | } |
Andrew Gerrand | 5bc444d | 2014-12-10 11:35:11 +1100 | [diff] [blame] | 94 | ``` |
| 95 | |
| 96 | |
| 97 | Second way is via syscall.NewProc (etc.) instead of syscall.GetProcAddress. These are basically some helper methods over the syscall ones, you saw above, and are available in Windows only: http://golang.org/src/pkg/syscall/dll_windows.go |
| 98 | |
nathany | e7bf74d | 2014-12-10 22:51:09 -0800 | [diff] [blame] | 99 | ```go |
Andrew Gerrand | 5bc444d | 2014-12-10 11:35:11 +1100 | [diff] [blame] | 100 | package main |
| 101 | |
| 102 | import ( |
Dave Day | 0d6986a | 2014-12-10 15:02:18 +1100 | [diff] [blame] | 103 | "fmt" |
| 104 | "syscall" |
| 105 | "unsafe" |
Andrew Gerrand | 5bc444d | 2014-12-10 11:35:11 +1100 | [diff] [blame] | 106 | ) |
| 107 | |
| 108 | func main() { |
Dave Day | 0d6986a | 2014-12-10 15:02:18 +1100 | [diff] [blame] | 109 | var mod = syscall.NewLazyDLL("user32.dll") |
| 110 | var proc = mod.NewProc("MessageBoxW") |
| 111 | var MB_YESNOCANCEL = 0x00000003 |
Andrew Gerrand | 5bc444d | 2014-12-10 11:35:11 +1100 | [diff] [blame] | 112 | |
Dave Day | 0d6986a | 2014-12-10 15:02:18 +1100 | [diff] [blame] | 113 | ret, _, _ := proc.Call(0, |
Dave Day | 0d6986a | 2014-12-10 15:02:18 +1100 | [diff] [blame] | 114 | uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr("This test is Done."))), |
Danyil Bohdan | 2c4387a | 2016-08-19 11:23:16 +0300 | [diff] [blame] | 115 | uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr("Done Title"))), |
Dave Day | 0d6986a | 2014-12-10 15:02:18 +1100 | [diff] [blame] | 116 | uintptr(MB_YESNOCANCEL)) |
| 117 | fmt.Printf("Return: %d\n", ret) |
| 118 | |
Andrew Gerrand | 5bc444d | 2014-12-10 11:35:11 +1100 | [diff] [blame] | 119 | } |
Andrew Gerrand | 5bc444d | 2014-12-10 11:35:11 +1100 | [diff] [blame] | 120 | ``` |
| 121 | |
nathany | e7bf74d | 2014-12-10 22:51:09 -0800 | [diff] [blame] | 122 | A third way would be to call into libraries basically by "linking" against the library, using the "[[cgo]]" method (this way works in Linux and Windows): |
Andrew Gerrand | 5bc444d | 2014-12-10 11:35:11 +1100 | [diff] [blame] | 123 | |
| 124 | This way would look something like this |
| 125 | |
nathany | e7bf74d | 2014-12-10 22:51:09 -0800 | [diff] [blame] | 126 | ```go |
Andrew Gerrand | 5bc444d | 2014-12-10 11:35:11 +1100 | [diff] [blame] | 127 | import ("C") |
| 128 | ... |
| 129 | C.MessageBoxW(...) |
Andrew Gerrand | 5bc444d | 2014-12-10 11:35:11 +1100 | [diff] [blame] | 130 | ``` |
| 131 | |
nathany | e7bf74d | 2014-12-10 22:51:09 -0800 | [diff] [blame] | 132 | See [[cgo]] for further details. |