最近在工作中的一个项目遇到了需要向Windows中的Mailslot(邮槽)发送消息的需求。因为我们的程序是用Go语言写的,之前有使用过Named Pipes来实现Windows平台上的IPC,使用的是go-winio库,但是这个库里面没有针对Mailslot的支持。作为不会写C/C++的我,本身想着用一个简单的程序作为中间件,监听一个Named Pipe来转发消息到Mailslot,毕竟这种简单的例子Google一下一大堆的。

然后做好了之后,我仔(wu)细(liao)看了一下C里面向Mailslot发送消息的代码

int send(char const *msg)
{
	HANDLE sHandle;
	DWORD bWritten;

	if ((sHandle = CreateFile(
			SlotName,
			GENERIC_WRITE,
			FILE_SHARE_READ,
			NULL,
			OPEN_EXISTING,
			FILE_ATTRIBUTE_NORMAL,
			NULL)) == INVALID_HANDLE_VALUE)
	{
		printf("CreateFile failed with error %d\n", GetLastError());
		return GetLastError();
	}

	if (WriteFile(sHandle, msg, strlen(msg), &bWritten, NULL) == 0)
	{
		printf("WriteFile failed with error %d\n", GetLastError());
		return GetLastError();
	}
	CloseHandle(sHandle);
	return 0;
}

咦,是不是可以用syscall来做这个事情?

package main

import (
	"errors"
	"fmt"
	"syscall"
)

func main() {
	n, err := Write(`\\.\mailslot\destination`, []byte("hello world!"))
	if err != nil {
		panic(err)
	}
	fmt.Printf("%d bytes written to mailslot\n", n)
}

func Write(mailslotPath string, message []byte) (int, error) {
	pathPtr, _ := syscall.UTF16PtrFromString(mailslotPath)
	h, err := syscall.CreateFile(pathPtr,
				syscall.GENERIC_WRITE,
				syscall.FILE_SHARE_READ,
				&syscall.SecurityAttributes{},
				syscall.OPEN_EXISTING,
				syscall.FILE_ATTRIBUTE_NORMAL,
				0)
	if err != nil {
		fmt.Printf("Failed to connect to mailslot: %s\n", err)
		return -1, err
	}
	if h == syscall.InvalidHandle {
		return -1, errors.New("invalid handle")
	}
	defer syscall.CloseHandle(h)

	return syscall.Write(h, message)
}

可以看到,syscall.CreateFile和C里面的CreateFile参数接受的参数是一致的,连里面一些constant的参数名都是一样。真tm方便。