1. 程式人生 > 其它 >使用C語言獲取小程式二維碼

使用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 來獲取小程式碼就可以了。