Why 发表于 2026-6-9 19:03:26

取电脑登录QQ,最新版QQ可取



注:我不是原作者,原作者不记得是谁了,最开始是易语言代码,转成C++了的
// qq.hpp - 获取本机已登录 QQ 账号(WinHTTP 版)
// 功能:通过本地 QQ 的 ptlogin 接口(localhost:4301)获取当前已登录的 QQ 账号信息。
// 依赖:需要 nlohmann/json 库(json.hpp)
// 编译:链接 winhttp.lib
#pragma once

#include <windows.h>
#include <winhttp.h>
#include <string>
#include <vector>
#include <random>
#include <stdexcept>

#pragma comment(lib, "winhttp.lib")

namespace qq_local {

    /**
   * @brief QQ 账号信息结构体
   */
    struct QQInfo {
      std::string uin;      ///< QQ 号(字符串形式)
      std::string nickname; ///< 昵称(可能为空)
    };

    /**
   * @brief 忽略 SSL 证书错误(因为本地 4301 端口使用的是自签名证书)
   * @param hRequest WinHTTP 请求句柄
   */
    inline void IgnoreSslErrors(HINTERNET hRequest) {
      DWORD flags = SECURITY_FLAG_IGNORE_UNKNOWN_CA |
            SECURITY_FLAG_IGNORE_CERT_CN_INVALID |
            SECURITY_FLAG_IGNORE_CERT_DATE_INVALID;
      WinHttpSetOption(hRequest, WINHTTP_OPTION_SECURITY_FLAGS,
            &flags, sizeof(flags));
    }

    /**
   * @brief 获取本机当前已登录的所有 QQ 账号
   *
   * 该函数向本地 QQ 服务(localhost.ptlogin2.qq.com:4301)发送请求,
   * 获取已登录账号的 JSON 列表并解析。
   *
   * @return std::vector<QQInfo> 包含所有已登录账号的 vector,若无登录账号则为空。
   *
   * @throws std::runtime_error 当网络请求失败、响应解析失败或接口不可用时抛出异常。
   *
   * @note 使用前必须确保 QQ 客户端正在运行且已登录至少一个账号。
   * @note 该函数会忽略本地 SSL 证书错误,仅用于本地调试。
   * @note 需要 nlohmann/json 库支持。
   */
    inline std::vector<QQInfo> GetLocalQQ() {
      // 1. 生成随机数作为 pt_local_tk(防缓存)
      std::random_device rd;
      std::mt19937 gen(rd());
      std::uniform_int_distribution<long long> dist(1000000000LL, 2000000000LL);
      long long randNum = dist(gen);
      std::string randStr = std::to_string(randNum);
      std::wstring randWstr(randStr.begin(), randStr.end());

      std::wstring path = L"/pt_get_uins?callback=ptui_getuins_CB&pt_local_tk=-" + randWstr;

      // 2. 初始化 WinHTTP 会话
      HINTERNET hSession = WinHttpOpen(L"Mozilla/5.0",
            WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
            WINHTTP_NO_PROXY_NAME,
            WINHTTP_NO_PROXY_BYPASS, 0);
      if (!hSession) {
            throw std::runtime_error("WinHttpOpen 失败");
      }

      // 3. 连接到本地 QQ 服务(端口 4301)
      HINTERNET hConnect = WinHttpConnect(hSession, L"localhost.ptlogin2.qq.com", 4301, 0);
      if (!hConnect) {
            WinHttpCloseHandle(hSession);
            throw std::runtime_error("WinHttpConnect 失败,请确认 QQ 已启动");
      }

      // 4. 创建请求(HTTPS)
      HINTERNET hRequest = WinHttpOpenRequest(hConnect, L"GET", path.c_str(),
            nullptr, WINHTTP_NO_REFERER,
            WINHTTP_DEFAULT_ACCEPT_TYPES,
            WINHTTP_FLAG_SECURE);
      if (!hRequest) {
            WinHttpCloseHandle(hConnect);
            WinHttpCloseHandle(hSession);
            throw std::runtime_error("WinHttpOpenRequest 失败");
      }

      IgnoreSslErrors(hRequest);

      // 5. 添加请求头(Referer 和 Cookie)
      std::wstring headers = L"Referer: https://ui.ptlogin2.qq.com/\r\n";
      headers += L"Cookie: pt_local_token=-" + randWstr;
      if (!WinHttpAddRequestHeaders(hRequest, headers.c_str(), (DWORD)-1,
            WINHTTP_ADDREQ_FLAG_ADD)) {
            WinHttpCloseHandle(hRequest);
            WinHttpCloseHandle(hConnect);
            WinHttpCloseHandle(hSession);
            throw std::runtime_error("WinHttpAddRequestHeaders 失败");
      }

      // 6. 发送请求
      if (!WinHttpSendRequest(hRequest, nullptr, 0, nullptr, 0, 0, 0)) {
            WinHttpCloseHandle(hRequest);
            WinHttpCloseHandle(hConnect);
            WinHttpCloseHandle(hSession);
            throw std::runtime_error("请求发送失败,请确认 QQ 已登录且本地服务正常");
      }

      // 7. 接收响应
      if (!WinHttpReceiveResponse(hRequest, nullptr)) {
            WinHttpCloseHandle(hRequest);
            WinHttpCloseHandle(hConnect);
            WinHttpCloseHandle(hSession);
            throw std::runtime_error("接收响应失败");
      }

      // 8. 读取响应数据
      std::string response;
      DWORD bytesRead = 0;
      char buffer;
      while (WinHttpReadData(hRequest, buffer, sizeof(buffer), &bytesRead) && bytesRead > 0) {
            response.append(buffer, bytesRead);
      }

      WinHttpCloseHandle(hRequest);
      WinHttpCloseHandle(hConnect);
      WinHttpCloseHandle(hSession);

      // 9. 从响应中提取 JSON 字符串(包裹在 JavaScript 回调中)
      std::string marker = "var var_sso_uin_list=";
      size_t start = response.find(marker);
      if (start == std::string::npos) {
            marker = "var var_sso_uin_list =";
            start = response.find(marker);
            if (start == std::string::npos) {
                throw std::runtime_error("响应中未找到 var_sso_uin_list,QQ 可能未登录或接口已变更");
            }
      }
      start += marker.length();
      size_t end = response.find(";", start);
      if (end == std::string::npos) {
            throw std::runtime_error("JSON 结束符 ';' 未找到");
      }
      std::string jsonStr = response.substr(start, end - start);

      // 10. 解析 JSON(需要 nlohmann/json.hpp)
      nlohmann::json qqList;
      try {
            qqList = nlohmann::json::parse(jsonStr);
      }
      catch (const nlohmann::json::parse_error& e) {
            throw std::runtime_error("JSON 解析失败: " + std::string(e.what()));
      }

      if (!qqList.is_array()) {
            return {};
      }

      // 11. 转换为 QQInfo 结构体
      std::vector<QQInfo> result;
      for (const auto& item : qqList) {
            QQInfo info;
            if (item.contains("uin")) {
                if (item["uin"].is_string()) {
                  info.uin = item["uin"].get<std::string>();
                }
                else if (item["uin"].is_number()) {
                  uint64_t uin_num = item["uin"].get<uint64_t>();
                  info.uin = std::to_string(uin_num);
                }
            }
            if (item.contains("nickname") && item["nickname"].is_string()) {
                info.nickname = item["nickname"].get<std::string>();
            }
            if (!info.uin.empty()) {
                result.push_back(info);
            }
      }
      return result;
    }

} // namespace qq_local

页: [1]
查看完整版本: 取电脑登录QQ,最新版QQ可取