Introduction
This tip describes how we can create a simple window in Go programming language using Win32 API functions.
This tip only shows the steps of creating a simple window in Go. For getting more information about the Go language, you can visit Go Document website - https://golang.org/doc.
Go is an open source programming language that makes it easy to build simple, reliable, and efficient software.
Go is expressive, concise, clean, and efficient. Its concurrency mechanisms make it easy to write programs that get the most out of multicore and networked machines, while its novel type system enables flexible and modular program construction. Go compiles quickly to machine code yet has the convenience of garbage collection and the power of run-time reflection. It's a fast, statically typed, compiled language that feels like a dynamically typed, interpreted language.
Using the Code
I assume that you have the basic knowledge on Win32 API.
For this project, I’ve used the 1.5.1 64-bit version of Go.
To use Win32 API in Go language, we need the ‘w32
’ package. The package can be found on Git Hub website - https://github.com/AllenDang/w32
Here is our Window creation code:
The first statement in a Go source file must be package name. Since our program is an executable program, so the package name must be main
. It tells the Go compiler that the package should compile as an executable program instead of a shared library:
package main
In Go, import
keyword is used for importing package into other packages. So, we import our necessary packages using the import
keyword in this way:
import "./w32" // I had putted the w32 package inside the project source folder -
We also need to import the following packages:
import "syscall"
import "unsafe"
Unsafe
is a built-in package of Go language. It provides facilities for low-level programming including operations that violate the type system. We need it for converting uintptr
type to pointer and also the package contains a function called Sizeof
. We will use the Sizeof
function to get the size of WNDCLASSEX
structure.
This is a simple windows resource id to uint16
pointer converter function. We need it to pass the IDI_APPLICATION
and IDC_ARROW
constant to LoadIcon
and LoadCursor
function argument. In Go, type conversion expression is T(v) where it converts the value v
to the type T
:
func MakeIntResource(id uint16) (*uint16) {
return (*uint16)(unsafe.Pointer(uintptr(id)))
}
This is our window message handler function which handles only the WM_DESTROY
message to make sure that our window will close properly on close
event:
func WndProc(hWnd w32.HWND, msg uint32, wParam, lParam uintptr) (uintptr) {
switch msg {
case w32.WM_DESTROY:
w32.PostQuitMessage(0)
default:
return w32.DefWindowProc(hWnd, msg, wParam, lParam)
}
return 0
}
The following WinMain
function creates our window and it runs a standard application loop:
func WinMain() int {
Get our application instance handle in a local variable called hInstance
. In Go language, the :=
operator works as a declaration and as initialization at once:
hInstance := w32.GetModuleHandle("")
Since w32
package defines only Unicode (UTF16) compatible WinAPI functions, so we have to use the StringToUTF16Ptr
function from syscall
package to convert Go string
type to UTF16 pointer:
lpszClassName := syscall.StringToUTF16Ptr("WNDclass")
Here we declare variable using ‘var
’ keyword:
var wcex w32.WNDCLASSEX
Assignments are simply done using ‘=
’ operator:
wcex.Size = uint32(unsafe.Sizeof(wcex))
wcex.Style = w32.CS_HREDRAW | w32.CS_VREDRAW
wcex.WndProc = syscall.NewCallback(WndProc)
wcex.ClsExtra = 0
wcex.WndExtra = 0
wcex.Instance = hInstance
wcex.Icon = w32.LoadIcon(hInstance, MakeIntResource(w32.IDI_APPLICATION))
wcex.Cursor = w32.LoadCursor(0, MakeIntResource(w32.IDC_ARROW))
wcex.Background = w32.COLOR_WINDOW + 11
In Go language, nil
is equivalent of NULL
. Since we are not providing any menu for our window, we assign the MenuName
field with nil
:
wcex.MenuName = nil
wcex.ClassName = lpszClassName
wcex.IconSm = w32.LoadIcon(hInstance, MakeIntResource(w32.IDI_APPLICATION))
Here, we register our class using RegisterClassEx
function of w32 package. We pass the address of wcex
variable to its parameter -
w32.RegisterClassEx(&wcex)
Then we create our window using CreateWindowEx
function of w32
package. Our window size will be 400x400:
hWnd := w32.CreateWindowEx(
0, lpszClassName, syscall.StringToUTF16Ptr("Simple Go Window!"),
w32.WS_OVERLAPPEDWINDOW | w32.WS_VISIBLE,
w32.CW_USEDEFAULT, w32.CW_USEDEFAULT, 400, 400, 0, 0, hInstance, nil)
We use the ShowWindow
function of w32
package to show our window on screen:
w32.ShowWindow(hWnd, w32.SW_SHOWDEFAULT)
w32.UpdateWindow(hWnd)
The following codes will run standard application loop until we close our window. It will be trying to get message from our window:
var msg w32.MSG
for {
if w32.GetMessage(&msg, 0, 0, 0) == 0 {
break
}
w32.TranslateMessage(&msg)
w32.DispatchMessage(&msg)
}
return int(msg.WParam)
}
This is our program entry point function. It calls the WinMain
function to create and show our window:
func main() {
WinMain()
return
}
Now we have our source code. I’ve used main.go
as the source file name.
Compile the source file using the following commands:
go.exe build -ldflags "-H windowsgui" main.go
The ‘ -ldflags "-H windowsgui"
‘ flags will remove the black console window from our application.
Now, run the main.exe file and see your Simple Go Window!
Request to Readers
If you have any ideas about a new project on Go language that will help people to learn the language more easily, you can inform me through comments.
Conclusion
Go is a very nice programming language. We should use it wisely to get proper benefits from it. And I hope, this tip has helped beginner programmers.