调用Go中的NotifyIpInterfaceChange导致了访问冲突异常。

huangapple go评论141阅读模式
英文:

Calling NotifyIpInterfaceChange from Go caused Access Violation exceptions

问题

这是我第一次从Go中调用本地库进行调用。

我正在尝试使用Windows库设置事件钩子,以便监听网络接口的变化,到目前为止,我已经成功地使用NotifyAddrChange设置了一个监听器。

现在我正在尝试使用以下代码来使用NotifyIpInterfaceChange

package main

import (
	"golang.org/x/sys/windows"
	"log"
	"syscall"
	"unsafe"
)

var (
	modiphlpapi = windows.NewLazySystemDLL("iphlpapi.dll")

	procNotifyIpInterfaceChange = modiphlpapi.NewProc("NotifyIpInterfaceChange")
)

type context struct{}

func main() {
	log.Printf("Loaded [iphlpapi.dll] at {%#v}", modiphlpapi.Handle())
	log.Printf("Found [NotifyIpInterfaceChange] at {%#v}", procNotifyIpInterfaceChange.Addr())

	context := &context{}
	interfaceChange := windows.Handle(0)

	ret, _, errNum := procNotifyIpInterfaceChange.Call(syscall.AF_UNSPEC, syscall.NewCallback(callback), uintptr(unsafe.Pointer(context)), 0, uintptr(interfaceChange))
	log.Printf("%#v %#v", ret, errNum)

}

func callback(callerContext, row, notificationType uintptr) uintptr {
	log.Printf("callback invoked by Windows API (%#v %#v %#v)", callerContext, row, notificationType)
	return 0
}

代码可以正常编译并启动,没有任何问题,但是一旦调用函数,我就会得到以下异常:

D:\>event-listen_type2.exe
2017/06/22 22:12:39 Loaded [iphlpapi.dll] at {0x7ffac96f0000}
2017/06/22 22:12:39 Found [NotifyIpInterfaceChange] at {0x7ffac96f7e20}
Exception 0xc0000005 0x1 0x0 0x7ffac96f7edb
PC=0x7ffac96f7edb

syscall.Syscall6(0x7ffac96f7e20, 0x5, 0x0, 0x454170, 0x54d360, 0x0, 0x0, 0x0, 0xc042015350, 0xc042015300, ...)
        /usr/local/Cellar/go/1.8.3/libexec/src/runtime/syscall_windows.go:174 +0x6b
github.com/LiamHaworth/windows-network-events/vendor/golang.org/x/sys/windows.(*Proc).Call(0xc04203a620, 0xc042050300, 0x5, 0x5, 0x30, 0x4b12e0, 0x1, 0xc042050300)
        /Users/liam/git/go_path/src/github.com/LiamHaworth/windows-network-events/vendor/golang.org/x/sys/windows/dll_windows.go:139 +0x5c1
github.com/LiamHaworth/windows-network-events/vendor/golang.org/x/sys/windows.(*LazyProc).Call(0xc042050270, 0xc042050300, 0x5, 0x5, 0x1, 0xc04201a000, 0xc04202df78, 0x4043a3)
        /Users/liam/git/go_path/src/github.com/LiamHaworth/windows-network-events/vendor/golang.org/x/sys/windows/dll_windows.go:309 +0x66
main.main()
        /Users/liam/git/go_path/src/github.com/LiamHaworth/windows-network-events/main_windows.go:25 +0x229
rax     0x0
rbx     0xc3f10
rcx     0x1fb5cd87abfd0000
rdi     0x0
rsi     0x454170
rbp     0xc04202dc00
rsp     0x8fdf0
r8      0x8fb78
r9      0x7ffac96fb4c0
r10     0x0
r11     0x8fcf0
r12     0x0
r13     0xffffffee
r14     0x0
r15     0xaa
rip     0x7ffac96f7edb
rflags  0x10246
cs      0x33
fs      0x53
gs      0x2b

通过一些搜索,我知道异常类型0xc0000005是CPU在程序尝试访问未分配给它的内存时抛出的访问冲突,但是通过查看我的代码,我无法确定发生了什么。所有传递的指针都是应用程序中的项目。

在这里任何帮助都将是非常宝贵的。

英文:

This is the first time I have had to make calls to native libraries from Go.

I am trying to setup event hooks with the windows libraries to listen for network interface changes, so far I have been successful with setting up a listener with NotifyAddrChange.

Now I am trying with NotifyIpInterfaceChange with the following code

package main

import (
	"golang.org/x/sys/windows"
	"log"
	"syscall"
	"unsafe"
)

var (
	modiphlpapi = windows.NewLazySystemDLL("iphlpapi.dll")

	procNotifyIpInterfaceChange = modiphlpapi.NewProc("NotifyIpInterfaceChange")
)

type context struct{}

func main() {
	log.Printf("Loaded [iphlpapi.dll] at {%#v}", modiphlpapi.Handle())
	log.Printf("Found [NotifyIpInterfaceChange] at {%#v}", procNotifyIpInterfaceChange.Addr())

	context := &context{}
	interfaceChange := windows.Handle(0)

	ret, _, errNum := procNotifyIpInterfaceChange.Call(syscall.AF_UNSPEC, syscall.NewCallback(callback), uintptr(unsafe.Pointer(context)), 0, uintptr(interfaceChange))
	log.Printf("%#v %#v", ret, errNum)

}

func callback(callerContext, row, notificationType uintptr) uintptr {
	log.Printf("callback invoked by Windows API (%#v %#v %#v)", callerContext, row, notificationType)
	return 0
}

The code compiles fine and starts up without any issue, the problem happens once the function is invoke, then I get the following exception

D:\>event-listen_type2.exe
2017/06/22 22:12:39 Loaded [iphlpapi.dll] at {0x7ffac96f0000}
2017/06/22 22:12:39 Found [NotifyIpInterfaceChange] at {0x7ffac96f7e20}
Exception 0xc0000005 0x1 0x0 0x7ffac96f7edb
PC=0x7ffac96f7edb

syscall.Syscall6(0x7ffac96f7e20, 0x5, 0x0, 0x454170, 0x54d360, 0x0, 0x0, 0x0, 0xc042015350, 0xc042015300, ...)
        /usr/local/Cellar/go/1.8.3/libexec/src/runtime/syscall_windows.go:174 +0x6b
github.com/LiamHaworth/windows-network-events/vendor/golang.org/x/sys/windows.(*Proc).Call(0xc04203a620, 0xc042050300, 0x5, 0x5, 0x30, 0x4b12e0, 0x1, 0xc042050300)
        /Users/liam/git/go_path/src/github.com/LiamHaworth/windows-network-events/vendor/golang.org/x/sys/windows/dll_windows.go:139 +0x5c1
github.com/LiamHaworth/windows-network-events/vendor/golang.org/x/sys/windows.(*LazyProc).Call(0xc042050270, 0xc042050300, 0x5, 0x5, 0x1, 0xc04201a000, 0xc04202df78, 0x4043a3)
        /Users/liam/git/go_path/src/github.com/LiamHaworth/windows-network-events/vendor/golang.org/x/sys/windows/dll_windows.go:309 +0x66
main.main()
        /Users/liam/git/go_path/src/github.com/LiamHaworth/windows-network-events/main_windows.go:25 +0x229
rax     0x0
rbx     0xc3f10
rcx     0x1fb5cd87abfd0000
rdi     0x0
rsi     0x454170
rbp     0xc04202dc00
rsp     0x8fdf0
r8      0x8fb78
r9      0x7ffac96fb4c0
r10     0x0
r11     0x8fcf0
r12     0x0
r13     0xffffffee
r14     0x0
r15     0xaa
rip     0x7ffac96f7edb
rflags  0x10246
cs      0x33
fs      0x53
gs      0x2b

From some googling I know that exception type 0xc0000005 is a access violation thrown by the CPU when a program tries to access memory not allocated to it but looking through my code I can't tell where that is happening. All pointers passed are for items in the application.

Any help here would be magnificent.

答案1

得分: 1

根据文档,NotifyIpInterfaceChange 的最新参数是 in/out 类型,并且需要是指向 HANDLE 的指针。将系统调用更改为:

ret, _, errNum := procNotifyIpInterfaceChange.Call(syscall.AF_UNSPEC,
    syscall.NewCallback(callback),
    uintptr(unsafe.Pointer(context)), 
    0, 
    uintptr(unsafe.Pointer(&interfaceChange)))   //this must be pointer

编辑:
如评论中提到的和 这个 go-nuts 讨论 中提到的,对于多线程回调,即使我们不使用 cgo,也需要添加 import "C"

英文:

According to documentation, the latest parameter of NotifyIpInterfaceChange is both in/out and need to be a pointer to HANDLE. Change the system call to:

ret, _, errNum := procNotifyIpInterfaceChange.Call(syscall.AF_UNSPEC,
	syscall.NewCallback(callback),
	uintptr(unsafe.Pointer(context)), 
    0, 
    uintptr(unsafe.Pointer(&interfaceChange)))   //this must be pointer

EDIT:
As mentioned in the comment and this go-nuts discussion, for multi-threaded callback, import "C" need to be added, even if we don't use cgo.

huangapple
  • 本文由 发表于 2017年6月22日 20:41:10
  • 转载请务必保留本文链接:https://go.coder-hub.com/44699710.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定