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

C++使用cURL实现Valorant登录时Release模式报Invalid Parameter错误

问题描述

我正在开发一款使用cURL实现Valorant账号登录的C++程序,代码在Visual Studio 2019 Debug模式下运行正常,但切换到Release模式时抛出Invalid Parameter错误。经调研初步判断问题与libcurl相关,但未找到公开解决方案。项目同时使用了nlohmann/json库和base64解码代码片段,经排查二者与该错误无关。项目采用默认配置,我也曾新建项目迁移全部代码,问题仍可复现。

完整代码

#include <iostream>
#include "json.hpp"
#include <curl/curl.h>
#include <string>
#include <sstream>
#include "base64.hpp"
#include <stdio.h>

using json = nlohmann::json;


CURL* hnd = curl_easy_init();


size_t WriteCallback(char* contents, size_t size, size_t nmemb, void* userp)
{
    ((std::string*)userp)->append((char*)contents, size * nmemb);
    return size * nmemb;
}

void authCookies()
{

    curl_easy_setopt(hnd, CURLOPT_CUSTOMREQUEST, "POST");
    curl_easy_setopt(hnd, CURLOPT_URL, "https://auth.riotgames.com/api/v1/authorization");

    struct curl_slist* headers = NULL;
    headers = curl_slist_append(headers, "Content-Type: application/json");
    curl_easy_setopt(hnd, CURLOPT_HTTPHEADER, headers);

    curl_easy_setopt(hnd, CURLOPT_POSTFIELDS, "{\"client_id\":\"play-valorant-web-prod\",\"nonce\":\"1\",\"redirect_uri\":\"https://playvalorant.com/opt_in\",\"response_type\":\"token id_token\"}");

    CURLcode ret = curl_easy_perform(hnd);
}


std::string getAuthToken(std::string username, std::string password)
{
    std::string token = "";
    curl_easy_setopt(hnd, CURLOPT_CUSTOMREQUEST, "PUT");
    curl_easy_setopt(hnd, CURLOPT_URL, "https://auth.riotgames.com/api/v1/authorization");

    struct curl_slist* headers = NULL;
    headers = curl_slist_append(headers, "Content-Type: application/json");
    curl_easy_setopt(hnd, CURLOPT_HTTPHEADER, headers);
    std::string response;
    json toSend = {
        {"type", "auth"},
        {"username", username},
        {"password", password},
        {"remember", false},
        {"language", "en_US"}
    };

    std::string sendText = toSend.dump(4);
    curl_easy_setopt(hnd, CURLOPT_POSTFIELDS, sendText.c_str());
    curl_easy_setopt(hnd, CURLOPT_WRITEDATA, &response);
    CURLcode ret = curl_easy_perform(hnd);
    json resp = json::parse(response);

    //parse the uri and look for a token
    std::string uri = std::string(resp["response"]["parameters"]["uri"]);
    size_t loc = uri.find("#access_token=");

    for (int i = loc + 14; i < uri.length(); i++)
    {
        if (uri[i] != '&') { token += uri[i]; }
        else { break; }
    }
    return token;
}

void cookieReauth()
{
    std::string response;
    curl_easy_setopt(hnd, CURLOPT_CUSTOMREQUEST, "GET");
    curl_easy_setopt(hnd, CURLOPT_URL, "https://auth.riotgames.com/authorize?redirect_uri=https%3A%2F%2Fplayvalorant.com%2Fopt_in&client_id=play-valorant-web-prod&response_type=token%20id_token");
    curl_easy_setopt(hnd, CURLOPT_POSTFIELDS, "");
    curl_easy_setopt(hnd, CURLOPT_WRITEDATA, &response);
    CURLcode ret = curl_easy_perform(hnd);
}

std::string getEntitlement(std::string token)
{
    std::string out = "";

    curl_easy_setopt(hnd, CURLOPT_CUSTOMREQUEST, "POST");
    curl_easy_setopt(hnd, CURLOPT_URL, "https://entitlements.auth.riotgames.com/api/token/v1");

    struct curl_slist* headers = NULL;
    std::string auth = "Authorization: Bearer " + token;
    headers = curl_slist_append(headers, auth.c_str());
    headers = curl_slist_append(headers, "Content-Type: application/json");
    curl_easy_setopt(hnd, CURLOPT_POSTFIELDS, "");

    curl_easy_setopt(hnd, CURLOPT_WRITEDATA, &out);
    curl_easy_setopt(hnd, CURLOPT_HTTPHEADER, headers);

    CURLcode ret = curl_easy_perform(hnd);

    json resp = json::parse(out);
    return std::string(resp["entitlements_token"]);
}

struct valorantUser
{
    std::string displayName;
    std::string puuid;
    std::string name;
    std::string tag;
};

std::vector<valorantUser> getUsersByPuuid(std::string token, std::string entitlement, json puuidList, std::string region)
{
    std::vector<valorantUser> userList;
    std::string response;
    curl_easy_setopt(hnd, CURLOPT_CUSTOMREQUEST, "PUT");
    curl_easy_setopt(hnd, CURLOPT_URL, std::string("https://pd." + region + ".a.pvp.net/name-service/v2/players").c_str());

    struct curl_slist* headers = NULL;

    std::string auth = "Authorization: Bearer " + token;
    std::string ent = "Entitlements: " + entitlement;
    headers = curl_slist_append(headers, auth.c_str());
    headers = curl_slist_append(headers, ent.c_str());
    headers = curl_slist_append(headers, "Content-Type: application/json");
    curl_easy_setopt(hnd, CURLOPT_HTTPHEADER, headers);

    std::string toSend = puuidList.dump();
    curl_easy_setopt(hnd, CURLOPT_POSTFIELDS, toSend.c_str());


    curl_easy_setopt(hnd, CURLOPT_WRITEDATA, &response);
    CURLcode ret = curl_easy_perform(hnd);
    
    json resData = json::parse(response);
    for (int i = 0; i < resData.size(); i++)
    {
        valorantUser dat;
        dat.displayName = std::string(resData[i]["DisplayName"]);
        dat.puuid = std::string(resData[i]["Subject"]);
        dat.name = std::string(resData[i]["GameName"]);
        dat.tag = std::string(resData[i]["TagLine"]);
        userList.push_back(dat);
    }

    return userList;
}

std::string getTokenPuuid(std::string token)
{
    std::vector<std::string> toks;
    std::stringstream test(token);
    std::string cur;
    while (std::getline(test, cur, '.'))
    {
        toks.push_back(cur);
    }

    json decoded = json::parse(base64_decode(toks[1]));
    
    return decoded["sub"];
}

int main()
{
    
    curl_easy_setopt(hnd, CURLOPT_WRITEFUNCTION, WriteCallback);
    curl_easy_setopt(hnd, CURLOPT_COOKIEJAR, "");
    
    authCookies(); //get auth cookies
    std::string token = getAuthToken("username", "password"); //get auth token
    cookieReauth(); //get more auth cookies
    
    std::string entitlement = getEntitlement(token); //get entitlement token
    
    std::string puuid = getTokenPuuid(token); //get user puuid from token
    json puuidList = json::array({puuid});
    valorantUser botData = getUsersByPuuid(token, entitlement, puuidList, "na")[0]; //get user name from puuid

    std::cout << "Signed in as " << botData.name << "#" << botData.tag;
}

错误截图

错误截图1
错误截图2

补充测试信息

注释掉所有业务函数后错误消失,仅取消authCookies()函数的注释错误就会复现,且异常直到代码执行到末尾时才会触发。测试用main函数代码如下:

int main()
{

    curl_easy_setopt(hnd, CURLOPT_WRITEFUNCTION, WriteCallback);
    curl_easy_setopt(hnd, CURLOPT_COOKIEJAR, "");
    
    /*
    authCookies(); //get auth cookies
    std::string token = getAuthToken("username", "password"); //get auth token
    cookieReauth(); //get more auth cookies

    std::string entitlement = getEntitlement(token); //get entitlement token

    std::string puuid = getTokenPuuid(token); //get user puuid from token
    json puuidList = json::array({ puuid });
    valorantUser botData = getUsersByPuuid(token, entitlement, puuidList, "na")[0]; //get user name from puuid

    std::cout << "Signed in as " << botData.name << "#" << botData.tag;*/
    curl_easy_cleanup(hnd);
    return 0;
}

修复方案

问题根因

  1. 缺少libcurl全局环境初始化:未调用curl_global_init(),全局CURL句柄初始化时curl环境还未就绪,Release模式下时序检查更严格直接触发错误。
  2. authCookies函数中未设置CURLOPT_WRITEDATA,导致WriteCallback回调收到的userp为空指针,调用std::string::append触发非法参数错误。
  3. 请求头curl_slist使用后未释放,存在内存泄漏隐患。

修复步骤

  1. 移除全局CURL句柄的初始化,改为main函数内先初始化curl全局环境再创建句柄:
// 移除原来的全局CURL* hnd = curl_easy_init(); 改为全局声明,main内初始化
CURL* hnd = nullptr;

int main()
{
    // 第一步先初始化curl全局环境
    curl_global_init(CURL_GLOBAL_ALL);
    hnd = curl_easy_init();
    if (!hnd) {
        std::cerr << "curl初始化失败" << std::endl;
        return 1;
    }
    curl_easy_setopt(hnd, CURLOPT_WRITEFUNCTION, WriteCallback);
    curl_easy_setopt(hnd, CURLOPT_COOKIEJAR, "");
    
    authCookies(); 
    std::string token = getAuthToken("username", "password");
    cookieReauth();
    
    std::string entitlement = getEntitlement(token);
    std::string puuid = getTokenPuuid(token);
    json puuidList = json::array({puuid});
    valorantUser botData = getUsersByPuuid(token, entitlement, puuidList, "na")[0];

    std::cout << "Signed in as " << botData.name << "#" << botData.tag;
    // 程序结束前清理
    curl_easy_cleanup(hnd);
    curl_global_cleanup();
    return 0;
}
  1. 给authCookies函数添加CURLOPT_WRITEDATA配置,同时释放请求头内存:
void authCookies()
{
    std::string dummy_resp;
    curl_easy_setopt(hnd, CURLOPT_CUSTOMREQUEST, "POST");
    curl_easy_setopt(hnd, CURLOPT_URL, "https://auth.riotgames.com/api/v1/authorization");

    struct curl_slist* headers = NULL;
    headers = curl_slist_append(headers, "Content-Type: application/json");
    curl_easy_setopt(hnd, CURLOPT_HTTPHEADER, headers);

    curl_easy_setopt(hnd, CURLOPT_POSTFIELDS, "{\"client_id\":\"play-valorant-web-prod\",\"nonce\":\"1\",\"redirect_uri\":\"https://playvalorant.com/opt_in\",\"response_type\":\"token id_token\"}");
    curl_easy_setopt(hnd, CURLOPT_WRITEDATA, &dummy_resp);
    CURLcode ret = curl_easy_perform(hnd);
    curl_slist_free_all(headers);
}
  1. 所有使用curl_slist_append创建的请求头,在curl_easy_perform执行完成后都调用curl_slist_free_all释放内存。
  2. 可选优化:不要用CURLOPT_CUSTOMREQUEST设置请求方法,POST请求用CURLOPT_POST、PUT请求用CURLOPT_PUT替代,避免潜在的方法识别错误。

按以上步骤修改后,Release模式的Invalid Parameter错误即可解决。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.09 15:36:01