AutoHotkey v2发送Socket消息崩溃问题求助(附v1可用代码)
AutoHotkey v2 Socket发送消息多次后崩溃的修复
问题背景
我在AutoHotkey v1中可以正常向127.0.0.1:65432发送Socket消息,可用代码如下:
#Persistent #NoEnv SetBatchLines, -1 InitWinsock() { VarSetCapacity(wsaData, 32, 0) result := DllCall("Ws2_32\WSAStartup", "UShort", 0x0202, "UInt", &wsaData) if (result != 0) { MsgBox, Failed to initialize Winsock. Error: %result% return false } return true } SendToggleCommand() { if (!InitWinsock()) { return } sock := DllCall("Ws2_32\socket", "Int", 2, "Int", 1, "Int", 6) ; AF_INET, SOCK_STREAM, IPPROTO_TCP if (sock = -1) { MsgBox, Failed to create socket. return } VarSetCapacity(addr, 16, 0) NumPut(2, addr, 0, "UShort") ; AF_INET NumPut(DllCall("Ws2_32\htons", "UShort", 65432), addr, 2, "UShort") NumPut(0x0100007F, addr, 4, "UInt") ; IP 127.0.0.1:65432 result := DllCall("Ws2_32\connect", "UInt", sock, "UInt", &addr, "Int", 16) if (result != 0) { error_code := DllCall("Ws2_32\WSAGetLastError") MsgBox, Failed to connect to the server. Error: %error_code% DllCall("Ws2_32\closesocket", "UInt", sock) return } toggleStr := "TOGGLE`n" VarSetCapacity(buffer, StrLen(toggleStr)) ; StrPut(toggleStr, &buffer, StrLen(toggleStr) + 1, "UTF-8") sendResult := DllCall("Ws2_32\send", "UInt", sock, "Ptr", &buffer, "Int", StrLen(toggleStr), "Int", 0) if (sendResult = StrLen(toggleStr)) { ; MsgBox, Data sent successfully! OutputDebug, % "Data sent successfully!" } else { ; MsgBox, Failed to send the complete data. OutputDebug, % "Failed to send the complete data.." } DllCall("Ws2_32\closesocket", "UInt", sock) DllCall("Ws2_32\WSACleanup") } ; Ctrl+F7 ^F7::SendToggleCommand()
但在AutoHotkey v2中编写的代码虽能发送消息,但执行5-7次后程序崩溃,原v2代码如下:
#Requires AutoHotkey v2.0 InitWinsock() { wsaData := Buffer(32, 0) result := DllCall("Ws2_32\WSAStartup", "UShort", 0x0202, "Ptr", wsaData.Ptr) if result != 0 { MsgBox("Failed to initialize Winsock. Error: " result) return false } return true } CleanupWinsock() { DllCall("Ws2_32\WSACleanup") } SendToggleCommand() { if !InitWinsock() { return } sock := DllCall("Ws2_32\socket", "Int", 2, "Int", 1, "Int", 6) if sock = -1 { MsgBox("Failed to create socket.") CleanupWinsock() return } ; 127.0.0.1:65432 addr := Buffer(16, 0) NumPut("UShort", 2, addr, 0) ; AF_INET NumPut("UShort", DllCall("Ws2_32\htons", "UShort", 65432), addr, 2) NumPut("UInt", 0x0100007F, addr, 4) ; IP 127.0.0.1 result := DllCall("Ws2_32\connect", "Int", sock, "Ptr", addr.Ptr, "Int", addr.Size) if result != 0 { error_code := DllCall("Ws2_32\WSAGetLastError", "Int") MsgBox("Failed to connect to the server. Error: " error_code) DllCall("Ws2_32\closesocket", "Int", sock) CleanupWinsock() return } toggleStr := "TOGGLE`n" utf8Buffer := Buffer(StrPut(toggleStr, "UTF-8") - 1) StrPut(toggleStr, utf8Buffer.Ptr, utf8Buffer.Size, "UTF-8") sendResult := DllCall("Ws2_32\send", "Int", sock, "Ptr", utf8Buffer.Ptr, "Int", utf8Buffer.Size, "Int", 0) if sendResult = utf8Buffer.Size { OutputDebug("Data sent successfully!") } else { OutputDebug("Failed to send the complete data.") } DllCall("Ws2_32\closesocket", "Int", sock) CleanupWinsock() } ; Ctrl+F7 ^F7::SendToggleCommand()
崩溃原因分析
Winsock的WSAStartup和WSACleanup必须成对且全局唯一调用:
- 每次执行
SendToggleCommand都调用一次初始化和清理,会导致Winsock内部引用计数异常,重复操作会耗尽内部资源,最终引发崩溃。 - v2代码中用
Int类型接收Socket句柄不合理,Socket句柄是无符号整数,类型不匹配会导致错误判断。
修复后的AutoHotkey v2代码
将Winsock初始化移到全局仅执行一次,清理放在程序退出时执行,同时修正参数类型:
#Requires AutoHotkey v2.0 ; 全局初始化Winsock,仅执行一次 if !InitWinsock() { ExitApp() } ; 程序退出时自动清理Winsock OnExit(*) => CleanupWinsock() InitWinsock() { wsaData := Buffer(32, 0) result := DllCall("Ws2_32\WSAStartup", "UShort", 0x0202, "Ptr", wsaData.Ptr) if result != 0 { MsgBox("Failed to initialize Winsock. Error: " result) return false } return true } CleanupWinsock() { DllCall("Ws2_32\WSACleanup") } SendToggleCommand() { ; 用UInt接收Socket句柄,符合Windows API规范 sock := DllCall("Ws2_32\socket", "Int", 2, "Int", 1, "Int", 6, "UInt") if sock = 0xFFFFFFFF { ; 无符号的-1,代表创建失败 MsgBox("Failed to create socket.") return } ; 构造服务器地址 addr := Buffer(16, 0) NumPut("UShort", 2, addr, 0) ; AF_INET NumPut("UShort", DllCall("Ws2_32\htons", "UShort", 65432), addr, 2) NumPut("UInt", 0x0100007F, addr, 4) ; IP 127.0.0.1 ; 连接服务器 result := DllCall("Ws2_32\connect", "UInt", sock, "Ptr", addr.Ptr, "Int", addr.Size) if result != 0 { error_code := DllCall("Ws2_32\WSAGetLastError") MsgBox("Failed to connect to the server. Error: " error_code) DllCall("Ws2_32\closesocket", "UInt", sock) return } ; 准备发送的UTF-8数据 toggleStr := "TOGGLE`n" utf8Len := StrPut(toggleStr, "UTF-8") utf8Buffer := Buffer(utf8Len - 1) StrPut(toggleStr, utf8Buffer.Ptr, utf8Len, "UTF-8") ; 发送数据 sendResult := DllCall("Ws2_32\send", "UInt", sock, "Ptr", utf8Buffer.Ptr, "Int", utf8Buffer.Size, "Int", 0) if sendResult = utf8Buffer.Size { OutputDebug("Data sent successfully!") } else { OutputDebug("Failed to send the complete data.") } ; 关闭Socket释放资源 DllCall("Ws2_32\closesocket", "UInt", sock) } ; Ctrl+F7触发发送 ^F7::SendToggleCommand()
修复要点
- 全局唯一初始化/清理:避免重复调用Winsock初始化和清理函数,防止内部资源计数异常。
- 修正句柄类型:用
UInt接收Socket句柄,正确判断创建失败的返回值。 - 完善资源释放:确保所有错误分支都能正确关闭Socket,避免资源泄漏。
内容的提问来源于stack exchange,提问作者circlelee1981
相关产品推荐
相关产品推荐

