取电脑登录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]