使用C语言获取小程序二维码

微信的开发者网站上其实已经对如何获取小程序码有比较详细的说明了,详情可见 https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/qr-code.html。这里以接口B为例

一句话总结就是,以 HTTPS POST 向 https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=ACCESS_TOKEN 发起调用。post 参数是一个json格式的字符串。

属性 类型 默认值 必填 说明
access_token string 接口调用凭证
scene string 最大32个可见字符,只支持数字,大小写英文以及部分特殊字符:!#$&'()*+,/:;=?@-._~,其它字符请自行编码为合法字符(因不支持%,中文无法使用 urlencode 处理,请使用其他编码方式)
page string 主页 必须是已经发布的小程序存在的页面(否则报错),例如 pages/index/index, 根路径前不要填加 /,不能携带参数(参数请放在scene字段里),如果不填写这个字段,默认跳主页面
width number 430 小程序码宽度,单位是px,最小280px,最大 1280px
auto_color boolean false 自动配置线条颜色,如果颜色依然是黑色,则说明不建议配置主色调,默认 false
line_color Object {"r":0,"g":0,"b":0} auto_color 为 false 时生效,使用 rgb 设置颜色 例如 {"r":"xxx","g":"xxx","b":"xxx"} 十进制表示
is_hyaline boolean false 是否需要透明底色,为 true 时,生成透明底色的小程序

这里我们使用 libcurl 来实现,首先需要拿到小程序的 access_token ,小程序的 access_token 需要使用小程序的 appid 和 AppSecret 获取,详情见 接口调用凭证

获取 access_token 的一句话总结就是: 以 HTTPS GET 方式调用 https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET

综上所述,我们的目标就是用C语言发起一个 GTTPS GET 拿到 access_token , 然后用这个access_token 发起一个 GTTPS POST ,拿到小程序码。

这里直接给出使用 libcurl 发送 HTTPS Get 请求和发送 HTTPS POST 请求,并post 数据的示例代码

#ifndef WXACODE_H
#define WXACODE_H
#include <stdio.h>
#include <string.h>
#include <curl/curl.h>
#include <memory.h>
#include <assert.h>
#include <stdlib.h>

#ifdef _WIN32
#pragma comment(lib, "ws2_32.lib")
#pragma comment(lib, "Crypt32.lib")
#pragma comment(lib, "Wldap32.lib")
#endif // _WIN32

#ifndef __cplusplus
typedef int bool;
#define true 1
#define false 0
#endif

#ifdef __cplusplus
extern "C" {
#endif // __cplusplus


/*存储以POST方式需要发送的数据*/
struct Request
{
    char* readptr;
    size_t sizeleft;
};


struct Response
{
    unsigned char* response;
    size_t size;
};

/*发送数据的回调函数*/
static size_t send_callback(char* dest, size_t size, size_t nmemb, void* userp)
{
    struct Request* wt = (struct Request*)userp;
    size_t buffer_size = size * nmemb;

    if (wt->sizeleft) {
        /* copy as much as possible from the source to the destination */
        size_t copy_this_much = wt->sizeleft;
        if (copy_this_much > buffer_size)
            copy_this_much = buffer_size;
        memcpy(dest, wt->readptr, copy_this_much);

        wt->readptr += copy_this_much;
        wt->sizeleft -= copy_this_much;
        return copy_this_much; /* we copied this many bytes */
    }

    return 0; /* no more data left to deliver */
}

/*接收数据的回调函数*/
static size_t receive_callback(void* data, size_t size, size_t nmemb, void* userp)
{
    size_t realsize = size * nmemb;
    struct Response* mem = (struct Response*)userp;

    unsigned char* ptr = (char*)realloc(mem->response, mem->size + realsize + 1);
    if (ptr == NULL)
        return 0;  /* out of memory! */

    mem->response = ptr;
    memcpy(&(mem->response[mem->size]), data, realsize);
    mem->size += realsize;
    mem->response[mem->size] = 0;

    return realsize;
}

/*如果定义了 USE_SPECCIFIC_CACERT_PEM ,那么函数第一个参数应该指向一个 catcert.pem 证书*/
#ifdef USE_SPECCIFIC_CACERT_PEM
int handle_callback(const char* certpem, const char* url, const char* parameter, size_t size_send, void** responsedata, size_t* size_receive, char** content_type, bool post, bool verbose)
#else
int handle_callback(const char* url, const char* parameter, size_t size_send, void** responsedata, size_t* size_receive, char** content_type, bool post, bool verbose)
#endif // USE_SPECCIFIC_CACERT_PEM
{
    assert(url);
    CURL* curl;
    CURLcode res;
    struct Request request;
    struct Response response = { 0 };
    struct curl_slist* headers = NULL;
    int result = 0;

    /*如果parameter非空,那么有数据要发送,就申请相应的发送数据的空间*/
    bool hasParameter = false;
    if (parameter != NULL)
    {
        hasParameter = true;
        size_t len = strlen(parameter);
        size_t min = len < size_send ? len : size_send;
        request.readptr = malloc(min + 1);
        if (request.readptr == NULL)
        {
            return -1; /*out of memory*/
        }
        memset(request.readptr, 0, min + 1);
        memcpy(request.readptr, parameter, min);
        request.sizeleft = min;
    }

    res = curl_global_init(CURL_GLOBAL_DEFAULT);
    if (res != CURLE_OK)
    {
        fprintf(stderr, "curl_global_init() failed: %s\n", curl_easy_strerror(res));
        return 1;
    }
    curl = curl_easy_init();
    if (curl)
    {
        curl_easy_setopt(curl, CURLOPT_URL, url);
        if (post)
        {
            curl_easy_setopt(curl, CURLOPT_POST, 1L);
        }

        /*如果有数据需要发送,就设置相应的数据发送回调函数,要发送的数据和数据长度*/
        if (hasParameter)
        {
            curl_easy_setopt(curl, CURLOPT_READFUNCTION, send_callback);
            curl_easy_setopt(curl, CURLOPT_READDATA, &request);
            curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, (long)request.sizeleft);
        }

        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, receive_callback);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void*)&response);

        headers = curl_slist_append(headers, "Content-Type: application/json");

        /*如果定义了 USE_SPECCIFIC_CACERT_PEM ,那么设置SSL认证证书*/
#ifdef USE_SPECCIFIC_CACERT_PEM
        curl_easy_setopt(curl, CURLOPT_CAINFO, certpem)
#elif defined(DISABLE_CURL_SSL_VERIFY) // 如果定义了 DISABLE_CURL_SSL_VERIFY 证书认证,那么就设置curl不强制认证服务器
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, false);
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0);
#endif // USE_SPECCIFIC_CACERT_PEM


        if (verbose)
        {
            curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
        }

        res = curl_easy_perform(curl);
        if (res == CURLE_OK)
        {
            long http_code = 0;
            curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
            if (http_code == 200)
            {
                curl_easy_getinfo(curl, CURLINFO_CONTENT_TYPE, content_type);
                /*用户需要自行释放 responsedata */
                *responsedata = response.response;
                *size_receive = response.size;
            }
            else
            {
                result = -2;
                fprintf(stderr, "net work error with http code %ld", http_code);
            }
        }
        else
        {
            result = 2;
            fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
        }

        curl_slist_free_all(headers);
        curl_easy_cleanup(curl);
    }
    curl_global_cleanup();

    if (hasParameter)
    {
        if (request.readptr)
        {
            free(request.readptr);
            request.readptr = NULL;
        }
    }

    return result;
}

#ifdef __cplusplus
    extern "C" }
#endif // __cplusplus

#endif // !WXACODE_H

将上边的示例保存为一个头文件wxacode.h,下面是一个使用示例

//filename main.c
#define DISABLE_CURL_SSL_VERIFY
#include "wxacode.h"

int main()
{
    const char* url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET";
    char* response;
    char* content_type;
    size_t len = 0;
    int result = handle_callback(url, NULL, 0, &response, &len, &content_type, false, false);
    if (result == 0)
    {
        size_t size = strlen(response);
        printf("len=%zd,size=%zd\r\n", len, size);
        printf("Content type:%s\r\n", content_type);
        printf("Result:%s\r\n", response);
    }
}

上面的代码输出内容如下

len=74,size=74
Content type:application/json; encoding=utf-8
Result:{"errcode":40013,"errmsg":"invalid appid rid: 60c48178-1b38d167-06dd1f2e"}

下面是函数原型

int handle_https_callback(const char* url, bool post, const char* parameter, size_t size_send, void** responsedata, size_t* size_receive, char** content_type, bool verbose)
参数 说明
url 想要访问的url
post 是否发起 post
parameter 如果以 post 方式发起请求,parameter 是想要发送的数据,如果不是以 post 方式发起,此参数为 NULL
size_send 要发送的数据长度,实际上发送的数据长度以 parameter 的长度和 size_send 中去较小值
responsedata 服务器发送回来的数据
size_receive 服务器发送回来的数据的长度
content_type 如果是获取 access_token ,那么这里的 content_type 应该是 "application/json; encoding=utf-8",如果是小程序,那么这里应该是"Image/jpeg"
verbose 设置是否打印 curl 调用过程中的信息

再在此基础上,获取 access_token ,然后用 access_token 来获取小程序码就可以了。

上一篇:easy-excel 解决百万数据导入导出,性能很强


下一篇:Python基础教程:r‘‘, b‘‘, u‘‘, f‘‘ 的含义