WeChatHelper3.1.0.41.dll接口适用所有语言,今天我来讲一下用VC++(win32)来做个实例调用。
第一步:添加rapidjson类库
VC++用到的JSON库为:rapidjson,rapidjson头文件经在项目目录中了,我们把它包含到项目中:
第二步:创建HTTP类
VC++用wininet进行HTTP通信,
HttpHelper.h
#pragma once
#include <iostream>
#include <windows.h>
#include <wininet.h>
using namespace std;
//每次读取的字节数
#define READ_BUFFER_SIZE 4096
enum HttpInterfaceError
{
Hir_Success = 0, //成功
Hir_InitErr, //初始化失败
Hir_ConnectErr, //连接HTTP服务器失败
Hir_SendErr, //发送请求失败
Hir_QueryErr, //查询HTTP请求头失败
Hir_404, //页面不存在
Hir_IllegalUrl, //无效的URL
Hir_CreateFileErr, //创建文件失败
Hir_DownloadErr, //下载失败
Hir_QueryIPErr, //获取域名对应的地址失败
Hir_SocketErr, //套接字错误
Hir_UserCancel, //用户取消下载
Hir_BufferErr, //文件太大,缓冲区不足
Hir_HeaderErr, //HTTP请求头错误
Hir_ParamErr, //参数错误,空指针,空字符
Hir_UnknowErr,
};
enum HttpRequest
{
Hr_Get,
Hr_Post
};
class HttpHelper
{
public:
HttpHelper(void);
~HttpHelper(void);
public:
// 通过HTTP请求:Get或Post方式获取JSON信息
const std::string RequestData(const std::string& strUrl,
HttpRequest type = Hr_Get,
std::string lpHeader = "",
std::string lpPostData = "");
protected:
// 关闭句柄
void Release();
// 释放句柄
void ReleaseHandle(HINTERNET& hInternet);
// 解析URL地址
void ParseURLWeb(std::string strUrl, std::string& strHostName, std::string& strPageName, WORD& sPort);
private:
HINTERNET m_hSession;
HINTERNET m_hConnect;
HINTERNET m_hRequest;
HttpInterfaceError m_error;
};
HttpHelper.cpp
#include "HttpHelper.h"
#include <fstream>
#pragma comment(lib, "Wininet.lib")
#include <tchar.h>
HttpHelper::HttpHelper(void) :m_hSession(NULL), m_hConnect(NULL), m_hRequest(NULL)
{
}
HttpHelper::~HttpHelper(void)
{
Release();
}
// 通过HTTP请求:Get或Post方式获取JSON信息
const std::string HttpHelper::RequestData(const std::string& lpUrl,
HttpRequest type,
std::string strHeader,
std::string strPostData)
{
std::string strRet = "";
try
{
if (lpUrl.empty())
{
throw Hir_ParamErr;
}
Release();
m_hSession = InternetOpen(_T("Http-connect"), INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, NULL); //局部
if (NULL == m_hSession)
{
throw Hir_InitErr;
}
INTERNET_PORT port = INTERNET_DEFAULT_HTTP_PORT;
std::string strHostName = "";
std::string strPageName = "";
ParseURLWeb(lpUrl, strHostName, strPageName, port);
printf("lpUrl:%s,\nstrHostName:%s,\nstrPageName:%s,\nport:%d\n", lpUrl.c_str(), strHostName.c_str(), strPageName.c_str(), (int)port);
m_hConnect = InternetConnectA(m_hSession, strHostName.c_str(), port, NULL, NULL, INTERNET_SERVICE_HTTP, NULL, NULL);
if (NULL == m_hConnect)
{
throw Hir_ConnectErr;
}
std::string strRequestType;
if (Hr_Get == type)
{
strRequestType = "GET";
}
else
{
strRequestType = "POST";
}
m_hRequest = HttpOpenRequestA(m_hConnect, strRequestType.c_str(), strPageName.c_str(), "HTTP/1.1", NULL, NULL, INTERNET_FLAG_RELOAD, NULL);
if (NULL == m_hRequest)
{
throw Hir_InitErr;
}
DWORD dwHeaderSize = (strHeader.empty()) ? 0 : strlen(strHeader.c_str());
BOOL bRet = FALSE;
if (Hr_Get == type)
{
bRet = HttpSendRequestA(m_hRequest, strHeader.c_str(), dwHeaderSize, NULL, 0);
}
else
{
DWORD dwSize = (strPostData.empty()) ? 0 : strlen(strPostData.c_str());
bRet = HttpSendRequestA(m_hRequest, strHeader.c_str(), dwHeaderSize, (LPVOID)strPostData.c_str(), dwSize);
}
if (!bRet)
{
throw Hir_SendErr;
}
char szBuffer[READ_BUFFER_SIZE + 1] = { 0 };
DWORD dwReadSize = READ_BUFFER_SIZE;
if (!HttpQueryInfoA(m_hRequest, HTTP_QUERY_RAW_HEADERS, szBuffer, &dwReadSize, NULL))
{
throw Hir_QueryErr;
}
if (NULL != strstr(szBuffer, "404"))
{
throw Hir_404;
}
while (true)
{
bRet = InternetReadFile(m_hRequest, szBuffer, READ_BUFFER_SIZE, &dwReadSize);
if (!bRet || (0 == dwReadSize))
{
break;
}
szBuffer[dwReadSize] = '\0';
strRet.append(szBuffer);
}
}
catch (HttpInterfaceError error)
{
m_error = error;
}
return std::move(strRet);
}
// 解析URL地址 [3/14/2017/shike]
void HttpHelper::ParseURLWeb(std::string strUrl, std::string& strHostName, std::string& strPageName, WORD& sPort)
{
sPort = 80;
string strTemp(strUrl);
std::size_t nPos = strTemp.find("http://");
if (nPos != std::string::npos)
{
strTemp = strTemp.substr(nPos + 7, strTemp.size() - nPos - 7);
}
nPos = strTemp.find('/');
if (nPos == std::string::npos) //没有找到
{
strHostName = strTemp;
}
else
{
strHostName = strTemp.substr(0, nPos);
}
std::size_t nPos1 = strHostName.find(':');
if (nPos1 != std::string::npos)
{
std::string strPort = strTemp.substr(nPos1 + 1, strHostName.size() - nPos1 - 1);
strHostName = strHostName.substr(0, nPos1);
sPort = (WORD)atoi(strPort.c_str());
}
if (nPos == std::string::npos)
{
return;
}
strPageName = strTemp.substr(nPos, strTemp.size() - nPos);
}
// 关闭句柄
void HttpHelper::Release()
{
ReleaseHandle(m_hRequest);
ReleaseHandle(m_hConnect);
ReleaseHandle(m_hSession);
}
// 释放句柄
void HttpHelper::ReleaseHandle(HINTERNET& hInternet)
{
if (hInternet)
{
InternetCloseHandle(hInternet);
hInternet = NULL;
}
}
第三步:解析JSON
和其它语言一样,我就用获取即时聊天数据为实例:
聊天记录对象是根据聊天记录数据和类型创建的,我们来看一下聊天记录的JSON数据结构:
{
"cmdid": 7,
"maxid": 5,
"count": 75,
"status": "ok",
"qq": "2376140244",
"data": [
{
"localId": 42,
"MsgSvrID": "2062239765205091112",
"StrTalker": "18618087204@chatroom",
"StrContent": "<?xml version=\"1.0\"?>\n<msg>\n\t<img aeskey=\"611118464e36d48e86c891c79e25b07c\" encryver=\"1\" cdnthumbaeskey=\"611118464e36d48e86c891c79e25b07c\" cdnthumburl=\"3058020100044c304a0201000204be9026a302032f5d0302045938f0b702045fefffb90425617570696d675f383038376264343062646134646465335f31363039353634303839373537020401090a020201000405004c56fb00\" cdnthumblength=\"4043\" cdnthumbheight=\"140\" cdnthumbwidth=\"56\" cdnmidheight=\"0\" cdnmidwidth=\"0\" cdnhdheight=\"0\" cdnhdwidth=\"0\" cdnmidimgurl=\"3058020100044c304a0201000204be9026a302032f5d0302045938f0b702045fefffb90425617570696d675f383038376264343062646134646465335f31363039353634303839373537020401090a020201000405004c56fb00\" length=\"24332\" md5=\"692efd877bce4ad7dc41becca0d2278d\" hevc_mid_size=\"24332\" />\n</msg>\n",
"CreateTime": 1609564091,
"IsSender": 0,
"type": 3,
"SubType": 0,
"CompressContent": "",
"BytesExtra": {
"wxid": "wxid_y7hw81zn588b12",
"thumb": "keepmoving8\\FileStorage\\Image\\Thumb\\2021-01\\e03c67108d6c39c020c48696dbd36916_t.dat",
"image": "keepmoving8\\FileStorage\\Image\\2021-01\\4953c7f182c4741eec97c9ab6bfe1eb4.dat",
"video": ""
}
},
{
"localId": 43,
"MsgSvrID": "3036343329427015238",
"StrTalker": "2656683682@chatroom",
"StrContent": "收满不收弹头40",
"CreateTime": 1609564122,
"IsSender": 0,
"type": 1,
"SubType": 0,
"CompressContent": "",
"BytesExtra": {
"wxid": "wxid_87rw855vmv4h22",
"thumb": "",
"image": "",
"video": ""
}
},
{
"localId": 44,
"MsgSvrID": "6558585969683208319",
"StrTalker": "JI282940039",
"StrContent": "要的滴滴我",
"CreateTime": 1609564128,
"IsSender": 0,
"type": 1,
"SubType": 0,
"CompressContent": "",
"BytesExtra": {
"wxid": "",
"thumb": "",
"image": "",
"video": ""
}
}
]
}
上面记录中有三种记录:
1.群图片消息,群发送的微信ID和图片的地址已显示在扩展数据中。
2.群文本消息,群发送的微信ID显示在扩展数据中。
3.普通好友文本消息,扩展数据都是空。
根据JSON数据结构,我们在创建RecordObject对象
RecordObject.h
#pragma once
#include <list>
using namespace std;
struct RecordBytesExtra
{
char wxid[0x40] = { 0 };// 群消息时发送者微信ID"
char thumb[0x200] = { 0 }; // 图片类型时缩微图路径"
char image[0x200] = { 0 };// 图片或视频类型时图片路径"
char video[0x200] = { 0 };//视频类型时MP4路径
};
struct RecordData
{
int localId = 0;//记录ID
char MsgSvrID[255] = { 0 };//消息ID
char StrTalker[0x40] = { 0 };//消息微信ID
char StrContent[0x4000] = { 0 };//内容
int CreateTime = 0;//创建时间
int IsSender = 0;//是否自己发送
int type = 0;//消息类型
int SubType = 0;//子类型
char CompressContent[0x40] = { 0 }; //这里为转账金额信息,有+与-表示:收到与转出
RecordBytesExtra BytesExtra;//扩展消息
};
class RecordObject
{
public:
RecordObject();
RecordObject(const char * jsonData);
int cmdid = 0;
int maxid = 0;//记录索引
int count = 0;//信息条数
char qq[20] = { 0 };//本人QQ
char status[20] = { 0 };//状态
list<RecordData> data;//消息主体数组
};
RecordObject.cpp
#include "RecordObject.h"
#include <stdio.h>
#include <string.h>
#include<fstream>
#include<iostream>
#include <windows.h>
#include <tchar.h>
#include "rapidjson.h"
#include "document.h"
#include "stringbuffer.h"
#include "filereadstream.h"
#include "filewritestream.h"
#include "writer.h"
using namespace rapidjson;
RecordObject::RecordObject()
{
}
RecordObject::RecordObject(const char * jsonData)
{
rapidjson::Document document;
document.Parse(jsonData);
if (document.HasParseError())
{
return;
}
rapidjson::Value::ConstMemberIterator iter = document.FindMember("cmdid");
if (iter != document.MemberEnd()) {
cmdid = iter->value.GetInt();
}
iter = document.FindMember("cmdid");
if (iter != document.MemberEnd()) {
cmdid = iter->value.GetInt();
}
iter = document.FindMember("maxid");
if (iter != document.MemberEnd()) {
maxid = iter->value.GetInt();
}
iter = document.FindMember("count");
if (iter != document.MemberEnd()) {
count = iter->value.GetInt();
}
iter = document.FindMember("qq");
if (iter != document.MemberEnd()) {
sprintf_s(qq, "%s", iter->value.GetString());
}
iter = document.FindMember("status");
if (iter != document.MemberEnd()) {
sprintf_s(status, "%s", iter->value.GetString());
}
const rapidjson::Value& dataValue = document["data"];
if (dataValue.IsArray())
{
for (rapidjson::SizeType i = 0; i < dataValue.Size(); ++i)
{
const rapidjson::Value& obj = dataValue[i];
RecordData recordData;
recordData.localId = obj["localId"].GetInt();
sprintf_s(recordData.MsgSvrID, "%s", obj["MsgSvrID"].GetString());
sprintf_s(recordData.StrTalker, "%s", obj["StrTalker"].GetString());
sprintf_s(recordData.StrContent, "%s", obj["StrContent"].GetString());
recordData.CreateTime = obj["CreateTime"].GetInt();
recordData.IsSender = obj["IsSender"].GetInt();
recordData.type = obj["type"].GetInt();
recordData.SubType = obj["SubType"].GetInt();
sprintf_s(recordData.CompressContent, "%s", obj["CompressContent"].GetString());
const rapidjson::Value& subObj = obj["BytesExtra"];
if (subObj.IsObject())
{
sprintf_s(recordData.BytesExtra.wxid, "%s", subObj["wxid"].GetString());
sprintf_s(recordData.BytesExtra.thumb, "%s", subObj["thumb"].GetString());
sprintf_s(recordData.BytesExtra.image, "%s", subObj["image"].GetString());
sprintf_s(recordData.BytesExtra.video, "%s", subObj["video"].GetString());
}
data.push_back(recordData);
}
}
}
第四步:对数据编码和解码
因为 VC++是用 Unicode字符集,而Json都是用Utf-8,所以要对它们进行相互转码
Common.h
#pragma once
#include <vector>
#include <iostream>
#include <iterator>
#include <regex>
#include <ctime>
#include <string.h>
#include <io.h>
#include <direct.h>
#include <shlobj.h>
#include <string>
#include <codecvt>
#include <locale>
#include <iostream>
using namespace std;
char* UnicodeToUtf8(wchar_t* unicode);
char* UnicodeToUtf8(const wchar_t* unicode);
wchar_t* UTF8ToUnicode(char* str);
wchar_t* UTF8ToUnicode(const char* str);
wchar_t* UTF8ToUnicode(string text);
Common.cpp
#include "common.h"
/*
//unicode תUtf8
*/
char* UnicodeToUtf8(const wchar_t* unicode)
{
int len;
len = WideCharToMultiByte(CP_UTF8, 0, unicode, -1, NULL, 0, NULL, NULL);
char* szUtf8 = (char*)malloc(len + 1);
if (szUtf8 != 0) {
memset(szUtf8, 0, len + 1);
}
WideCharToMultiByte(CP_UTF8, 0, unicode, -1, szUtf8, len, NULL, NULL);
return szUtf8;
}
char* UnicodeToUtf8(wchar_t* unicode)
{
int len;
len = WideCharToMultiByte(CP_UTF8, 0, unicode, -1, NULL, 0, NULL, NULL);
char* szUtf8 = (char*)malloc(len + 1);
if (szUtf8 != 0) {
memset(szUtf8, 0, len + 1);
}
WideCharToMultiByte(CP_UTF8, 0, unicode, -1, szUtf8, len, NULL, NULL);
return szUtf8;
}
/*
//Utf8תunicode
*/
wchar_t* UTF8ToUnicode(const char* str)
{
int textlen = 0;
wchar_t* result;
textlen = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0);
result = (wchar_t*)malloc((textlen + 1) * sizeof(wchar_t));
if (result != 0)
{
memset(result, 0, (textlen + 1) * sizeof(wchar_t));
}
MultiByteToWideChar(CP_UTF8, 0, str, -1, (LPWSTR)result, textlen);
return result;
}
wchar_t* UTF8ToUnicode(char* str)
{
int textlen = 0;
wchar_t* result;
textlen = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0);
result = (wchar_t*)malloc((textlen + 1) * sizeof(wchar_t));
if (result != 0)
{
memset(result, 0, (textlen + 1) * sizeof(wchar_t));
}
MultiByteToWideChar(CP_UTF8, 0, str, -1, (LPWSTR)result, textlen);
return result;
}
wchar_t* UTF8ToUnicode(string text)
{
const char * str = text.c_str();
int textlen = 0;
wchar_t* result;
textlen = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0);
result = (wchar_t*)malloc((textlen + 1) * sizeof(wchar_t));
if (result != 0)
{
memset(result, 0, (textlen + 1) * sizeof(wchar_t));
}
MultiByteToWideChar(CP_UTF8, 0, str, -1, (LPWSTR)result, textlen);
return result;
}
第五步:在主程序中应用
在vc++中我们用SetTimer来定时刷新即时聊天记录
// CWechatHelper.cpp : 定义应用程序的入口点。
//
#include "framework.h"
#include "CWechatHelper.h"
#include "resource.h"
#include "common.h"
#include "HttpHelper.h"
#include "RecordObject.h"
#define MAX_LOADSTRING 100
INT_PTR CALLBACK DialogProc(HWND, UINT, WPARAM, LPARAM);
HWND hwndDlg;
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
//加载窗口
DialogBox(hInstance, MAKEINTRESOURCE(IDD_DIALOG1), NULL, DialogProc);
MSG msg = {};
// 5.1获取消息
while (GetMessage(&msg, 0, 0, 0))
{
// 5.2翻译消息
TranslateMessage(&msg);
// 5.3转发到消息回调函数
DispatchMessage(&msg);
}
}
//追加文本
void AppendText(const TCHAR *newText)
{
int outLength = GetWindowTextLength(GetDlgItem(hwndDlg, IDC_EDIT_RECORD));
TCHAR * buf = new TCHAR[outLength+1];
GetDlgItemText(hwndDlg, IDC_EDIT_RECORD, buf, outLength + 1);
wstring text(buf);
text.append(newText).append(L"\r\n");
//_tcscat_s(buf, outLength + 1, newText);
SetDlgItemText(hwndDlg, IDC_EDIT_RECORD, text.c_str());
}
//回调刷新即时聊天记录
void CALLBACK TimerProc(HWND hWnd, UINT nMsg, UINT nTimerid, DWORD dwTime)
{
HttpHelper* http = new HttpHelper();
string data = http->RequestData("http://127.0.0.1/?cmdid=7");
RecordObject *recordObj = new RecordObject(data.c_str());
for (auto record : recordObj->data)
{
AppendText(UTF8ToUnicode(record.StrContent));
}
delete http;
delete recordObj;
}
//窗口消息回调
INT_PTR CALLBACK DialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(lParam);
switch (message)
{
//窗口初始化
case WM_INITDIALOG:
{
hwndDlg = hDlg;
//定时器
SetTimer(hDlg, 1, 3000, TimerProc);
SendMessage(GetDlgItem(hDlg,IDC_COMBO_TYPE), CB_ADDSTRING, 0, (LPARAM)(L"GET"));
SendMessage(GetDlgItem(hDlg, IDC_COMBO_TYPE), CB_ADDSTRING, 0, (LPARAM)(L"POST"));
SendMessage(GetDlgItem(hDlg, IDC_COMBO_TYPE), CB_SETCURSEL, 0, 0);
SetDlgItemText(hwndDlg, IDC_EDIT1, TEXT("http://127.0.0.1/"));
break;
}
case WM_COMMAND:
{
int wmId = LOWORD(wParam);
// 分析菜单选择:
switch (wmId)
{
case IDC_BUTTON_REQUEST:
{
int i = SendMessage(GetDlgItem(hDlg, IDC_COMBO_TYPE), CB_GETCURSEL,0, 0);
string data;
WCHAR wurl[500] = { 0 };
GetDlgItemText(hwndDlg, IDC_EDIT1, wurl, sizeof(wurl));
string url =UnicodeToUtf8(wurl);
WCHAR wparam[2000] = { 0 };
GetDlgItemText(hwndDlg, IDC_EDIT_PARAM, wparam, sizeof(wparam));
string param = UnicodeToUtf8(wparam);
HttpHelper* http = new HttpHelper();
if (i == 0)
{
data = http->RequestData(url);
}
else
{
data = http->RequestData(url,HttpRequest::Hr_Post,"content-type:application/json;charset:utf-8;", param);
}
SetDlgItemText(hwndDlg, IDC_EDIT_RESPOSE,UTF8ToUnicode(data));
delete http;
break;
}
case IDM_EXIT:
DestroyWindow(hDlg);
break;
default:
return DefWindowProc(hDlg, message, wParam, lParam);
}
}
break;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hDlg, &ps);
// TODO: 在此处添加使用 hdc 的任何绘图代码...
EndPaint(hDlg, &ps);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
}
return DefWindowProc(hDlg, message, wParam, lParam);
}
现在看看效果:
到时,vc++的调用方法结束,对其它的数据结构依照这个实例都能写出来!
源码下载地址(vs2017):https://download.csdn.net/download/keepmoving0407/14017024
也可以进群交流下载:(交流群:561112477)
来源:oschina
链接:https://my.oschina.net/u/4408067/blog/4881156