如何在自定义dll文件中调用函数

时间:2017-07-13 09:15:27

标签: go

现在我得到了这样的功能描述

int ReadCard(char *room,char *gate,char *stime, char *guestname, char *guestid, char *lift, char *track1, char *track2, long *cardno, int *st, int *Breakfast);`

这就是我做的事情

room := ""
gate := ""
stime := ""
guestname := ""
guestid := ""
lift := ""
track1 := ""
track2 := ""
cardno := int64(0)
st := int64(0)
Breakfast := int64(0)

MAINDLL, _ := syscall.LoadLibrary("xxxxx.dll")
defer syscall.FreeLibrary(MAINDLL)
readCard, _ := syscall.GetProcAddress(MAINDLL, "ReadCard")

r1, r2, errno := syscall.Syscall12(readCard,
    11,
    uintptr(unsafe.Pointer(&room)),
    uintptr(unsafe.Pointer(&gate)),
    uintptr(unsafe.Pointer(&stime)),
    uintptr(unsafe.Pointer(&guestname)),
    uintptr(unsafe.Pointer(&guestid)),
    uintptr(unsafe.Pointer(&lift)),
    uintptr(unsafe.Pointer(&track1)),
    uintptr(unsafe.Pointer(&track2)),
    uintptr(cardno),
    uintptr(st),
    uintptr(Breakfast),
    0)

fmt.Println(r1, "\n", r2, "\n", errno)`

当我运行此代码时,我得到了

r1:0    r2:917452    errno:<nil>

但是当我打印房间或其他参数时,它的价值不会改变, dll文件中的这个函数会改变我发送的参数,我怎么能得到那些值?

1 个答案:

答案 0 :(得分:0)

To retrieve C's NULL terminated string, use pointer to pre-allocated byte slice instead of string. You need to make sure that the byte slice has enough space for storing the character, otherwise your program may crash due to memory access violation.

For long * and int * type, address to the variable is needed, e.g. use uintptr(unsafe.Pointer(&cardno)) instead of uintptr(cardno). In your current implementation, the value pointed by the variable is converted to a pointer. All uintptr(cardno), uintptr(st), uintptr(Breakfast) point to the same pointer (pointer to NULL in C) since they're all initialized to 0.

The code looks like:

const maxSize = 100 //Adjust the value to your need

room := make([]byte, maxSize)
gate := make([]byte, maxSize)
stime := make([]byte, maxSize)
guestname := make([]byte, maxSize)
guestid := make([]byte, maxSize)
lift := make([]byte, maxSize)
track1 := make([]byte, maxSize)
track2 := make([]byte, maxSize)

//The following variable type is correct for 64-bit DLL.
//For 32-bit DLL, instead of int64, use int32.
cardno := int64(0)
st := int64(0)
Breakfast := int64(0)

MAINDLL, _ := syscall.LoadLibrary("xxxxx.dll")
defer syscall.FreeLibrary(MAINDLL)
readCard, _ := syscall.GetProcAddress(MAINDLL, "ReadCard")

r1, r2, errno := syscall.Syscall12(readCard,
    11,
    uintptr(unsafe.Pointer(&room[0])),
    uintptr(unsafe.Pointer(&gate[0])),
    uintptr(unsafe.Pointer(&stime[0])),
    uintptr(unsafe.Pointer(&guestname[0])),
    uintptr(unsafe.Pointer(&guestid[0])),
    uintptr(unsafe.Pointer(&lift[0])),
    uintptr(unsafe.Pointer(&track1[0])),
    uintptr(unsafe.Pointer(&track2[0])),
    uintptr(unsafe.Pointer(&cardno)),
    uintptr(unsafe.Pointer(&st)),
    uintptr(unsafe.Pointer(&Breakfast)),
    0)

fmt.Println(r1, "\n", r2, "\n", errno)

Note that in the above example, we're using &room[0] intead of room, since we need to pass address of underlying byte array instead of address of the slice descriptor (See https://blog.golang.org/go-slices-usage-and-internals).

Finally, the byte slice which holds NULL terminated string must be converted to go's string, e.g. using:

func cstr(buf []byte) string {
    str := string(buf)
    for i, r := range str {
        if r == 0 {
            return string(buf[:i])
        }
    }
    return str
}

//usage example
sRoom := cstr(room)
//see the difference
fmt.Printf("`%s` => `%s`\n", string(room), sRoom)

Other implementation example, see How can I convert a null-terminated string in a byte buffer to a string in Go?.