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

GetLogicalDrives无法识别VPN断开,如何避免GetFileAttributesEx调用30秒卡顿?

问题解决方案

问题根因

  • GetLogicalDrives 只会读取系统缓存的驱动器列表,映射的网络驱动器断开后,系统不会立刻更新缓存,因此会误判为可用
  • GetFileAttributesEx 是同步API,访问断开的SMB共享时,会遵循系统默认的SMB会话超时规则(通常为30秒),导致线程阻塞

可行解决方案

方案1:前置状态/连通性检测(推荐,改造成本最低)

在调用GetFileAttributesEx之前,先执行低成本的状态校验,确认正常后再执行文件属性查询,避免无意义的阻塞:

  • 先调用WNetGetConnectionAPI查询V盘的映射连接状态,如果返回ERROR_NOT_CONNECTED直接判定驱动器不可用,该API的响应速度远快于文件查询
  • 额外增加VPN对端主机的连通性检测:优先检测共享盘对应主机的445端口(SMB服务端口),设置1~2秒超时,端口不通直接判定不可用;如果允许ICMP协议也可以用ping检测,超时设置为2秒以内
    这样可以过滤掉绝大多数VPN断开场景,不会走到卡顿的文件查询逻辑。

方案2:将检测逻辑移入独立工作线程

如果不想改动现有检测逻辑,直接把整个驱动器检测的代码放到单独的后台线程执行,阻塞只会发生在后台线程,不会影响主程序的响应。检测到状态变化(断开/重连)时,再通过消息、回调等方式通知主线程更新状态即可。

方案3:使用异步文件API替代同步调用

调用CreateFileW时传入FILE_FLAG_OVERLAPPED标志开启异步模式,查询文件属性的请求可以自定义超时时间,超过2秒没有返回就主动调用CancelIoEx取消请求,判定驱动器不可用,完全避免长时间阻塞。

重连检测实现

可以保持原有的10秒轮询逻辑,每次轮询先做前置连通性/状态检测,确认网络恢复后再执行文件属性校验,即可及时捕获VPN重连状态。

改造代码示例

#include <winsock2.h>
#include <windows.h>
#include <stdio.h>
#pragma comment(lib, "ws2_32.lib")
#pragma comment(lib, "mpr.lib")

// 检测指定IP的445端口是否可通,超时1000毫秒
BOOL CheckSMBPortAvailable(const char* ip) {
    WSADATA wsa;
    SOCKET s = INVALID_SOCKET;
    struct sockaddr_in addr;
    DWORD timeout = 1000;
    BOOL ret = FALSE;

    WSAStartup(MAKEWORD(2,2), &wsa);
    s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (s == INVALID_SOCKET) goto cleanup;

    // 设置连接超时
    setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, (char*)&timeout, sizeof(timeout));
    addr.sin_family = AF_INET;
    addr.sin_port = htons(445);
    addr.sin_addr.s_addr = inet_addr(ip); // 替换为你家电脑VPN内网的IP

    if (connect(s, (struct sockaddr*)&addr, sizeof(addr)) == 0) {
        ret = TRUE;
    }

cleanup:
    if (s != INVALID_SOCKET) closesocket(s);
    WSACleanup();
    return ret;
}

int main()
{
#define DRIVE 'V'  // VPN drive
#define FILE "V:\\Folder\\File.txt"  // file on VPN drive
    WIN32_FILE_ATTRIBUTE_DATA attribute;
    char drivePath[4] = {DRIVE, ':', '\\', 0};
    char remotePath[MAX_PATH] = {0};

    for ( ; ; ) {
        Sleep(10000);  // 10 seconds
        // 前置1:检测映射盘连接状态
        if (WNetGetConnection(drivePath, remotePath, &(DWORD){MAX_PATH}) == ERROR_NOT_CONNECTED) {
            fprintf(stdout, "Drive %c: is unavailable\n", DRIVE);
            continue;
        }
        // 前置2:检测SMB端口连通性
        if (!CheckSMBPortAvailable("192.168.1.100")) { // 替换为实际对端IP
            fprintf(stdout, "Drive %c: is unavailable\n", DRIVE);
            continue;
        }
        // 再执行文件校验
        if (GetLogicalDrives() & (1 << (DRIVE - 'A'))) {
            fprintf(stdout, "Drive %c: is available\n", DRIVE);
            if (!GetFileAttributesEx(FILE, GetFileExInfoStandard, &attribute)) {
                fprintf(stdout, "GetFileAttributesEx() failed, error = %d\n", GetLastError());
                continue;
            }
            fprintf(stdout, "All is ok\n");
        }
        else fprintf(stdout, "Drive %c: is unavailable\n", DRIVE);
    }
    return 0;
}

内容的提问来源于stack exchange,提问作者Codybear

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.23 19:54:01