You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 15:58:09