nguyendang95
Thành viên thường trực




- Tham gia
- 25/5/22
- Bài viết
- 202
- Được thích
- 176
Không biết mọi người có hay gặp trường hợp này không: đọc chuỗi Unicode xong, khi xuất ra kết quả thì chuỗi bị vỡ hoặc bị chèn thêm ký tự ngoằn ngoèo vô nghĩa. Tình trạng này rất hay gặp khi làm việc với đối tượng COM.
VD: Giả sử cần gọi web API để lấy kết quả là JSON nhờ sử dụng thư viện COM của WinHTTP.
Với VBA,
kết quả là nội dung JSON cứ chỗ nào có ký tự Unicode là chỗ đó bị lỗi.
Tương tự, nhưng lần này là C++.
Kết quả trả về thì rất tốt, không vấn đề gì.
Nhiều khả năng đây là lỗi (bug) của VBA rồi.
VD: Giả sử cần gọi web API để lấy kết quả là JSON nhờ sử dụng thư viện COM của WinHTTP.
Với VBA,
Mã:
Private Sub TestAPI()
Dim objWinHttp As WinHttp.WinHttpRequest
Set objWinHttp = New WinHttp.WinHttpRequest
With objWinHttp
.Open "GET", "https://iq.vietcap.com.vn/api/iq-insight-service/v1/events?ticker=GMD&fromDate=20160328&toDate=20260328&eventCode=DIV,ISS&page=0&size=250", True
.SetRequestHeader "Accept", "application/json"
.SetRequestHeader "Referer", "iq.vietcap.com.vn"
.Send
If .WaitForResponse(5) Then
Debug.Print .ResponseText
Dim objFSO As Scripting.FileSystemObject
Set objFSO = New Scripting.FileSystemObject
Dim objFile As Scripting.TextStream
Set objFile = objFSO.CreateTextFile("Z:\response.json", True, True)
objFile.Write .ResponseText
objFile.Close
Debug.Print "Response has been written to disk"
Else
Debug.Print "Request timed out"
End If
End With
End Sub
JSON:
{
"id": "68c0c56c6a7bab4d8b740a07",
"organCode": "GMD",
"eventNameVi": "Phát hà nh cỠphiếu",
"eventNameEn": "Share Issue",
"organNameEn": "Gemadept Corporation",
"organNameVi": "Công ty CỠphần Gemadept",
"ticker": "GMD",
"eventCode": "ISS",
"eventTitleVi": "Phát hà nh cỠphiếu - Phát hà nh cho CBCNV tỠlỠ1.5%",
"eventTitleEn": "Share Issue - ESOP ratio 1.5%",
"displayDate1": "2025-10-02T00:00:00",
"displayDate2": "2025-10-06T00:00:00",
"publicDate": "2025-10-06T00:00:00",
"recordDate": "2025-10-02T00:00:00",
"exrightDate": "2025-10-02T00:00:00",
"listingDate": "2028-10-02T00:00:00",
"exerciseRatio": 0.015,
"category": "DIVIDEND"
}
C++:
#include <stdio.h>
#import <winhttpcom.dll> raw_interfaces_only
#pragma comment(lib, "winhttp.lib")
using namespace WinHttp;
int main()
{
HRESULT hr = CoInitialize(NULL);
if (FAILED(hr)) {
wprintf(L"CoInitialize failed: %i\n", hr);
return hr;
}
CLSID clsid = {};
hr = CLSIDFromProgID(OLESTR("WinHttp.WinHttpRequest.5.1"), &clsid);
if (FAILED(hr)) {
CoUninitialize();
wprintf(L"CLSIDFromProgID failed: %i\n", hr);
return hr;
}
IWinHttpRequest* pRequest = NULL;
hr = CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER, __uuidof(IWinHttpRequest), reinterpret_cast<LPVOID*>(&pRequest));
if (FAILED(hr)) {
CoUninitialize();
wprintf(L"CoCreateInstance failed: %i\n", hr);
return hr;
}
BSTR bstrUrl = SysAllocString(OLESTR("https://iq.vietcap.com.vn/api/iq-insight-service/v1/events?ticker=GMD&fromDate=20160328&toDate=20260328&eventCode=DIV,ISS&page=0&size=250"));
if (!bstrUrl) {
CoUninitialize();
wprintf(L"Out of memory\n");
pRequest->Release();
return 1;
}
BSTR bstrMethod = SysAllocString(OLESTR("GET"));
if (!bstrMethod) {
CoUninitialize();
wprintf(L"Out of memory\n");
SysFreeString(bstrUrl);
pRequest->Release();
return 1;
}
VARIANT varAsync = {};
VariantInit(&varAsync);
varAsync.vt = VT_BOOL;
varAsync.boolVal = VARIANT_TRUE;
hr = pRequest->Open(bstrMethod, bstrUrl, varAsync);
VariantClear(&varAsync);
SysFreeString(bstrMethod);
bstrMethod = NULL;
SysFreeString(bstrUrl);
bstrUrl = NULL;
if (FAILED(hr)) {
CoUninitialize();
pRequest->Release();
wprintf(L"'Open' method failed: %i\n", hr);
return hr;
}
BSTR bstrAcceptHeader = SysAllocString(OLESTR("Accept"));
if (!bstrAcceptHeader) {
wprintf(L"Out of memory\n");
pRequest->Release();
CoUninitialize();
return 1;
}
BSTR bstrAcceptHeaderValue = SysAllocString(OLESTR("application/json"));
if (!bstrAcceptHeaderValue) {
wprintf(L"Out of memory\n");
SysFreeString(bstrAcceptHeader);
pRequest->Release();
CoUninitialize();
return 1;
}
hr = pRequest->SetRequestHeader(bstrAcceptHeader, bstrAcceptHeaderValue);
SysFreeString(bstrAcceptHeader);
bstrAcceptHeader = NULL;
SysFreeString(bstrAcceptHeaderValue);
bstrAcceptHeaderValue = NULL;
if (FAILED(hr)) {
wprintf(L"'SetRequestHeader' method failed: %i\n", hr);
pRequest->Release();
CoUninitialize();
return hr;
}
BSTR bstrRefererHeader = SysAllocString(OLESTR("Referer"));
if (!bstrRefererHeader) {
wprintf(L"Out of memory\n");
pRequest->Release();
CoUninitialize();
return 1;
}
BSTR bstrRefererHeaderValue = SysAllocString(OLESTR("iq.vietcap.com.vn"));
if (!bstrRefererHeaderValue) {
wprintf(L"Out of memory\n");
SysFreeString(bstrRefererHeader);
pRequest->Release();
CoUninitialize();
return 1;
}
hr = pRequest->SetRequestHeader(bstrRefererHeader, bstrRefererHeaderValue);
SysFreeString(bstrRefererHeader);
bstrRefererHeader = NULL;
SysFreeString(bstrRefererHeaderValue);
bstrRefererHeaderValue = NULL;
if (FAILED(hr)) {
wprintf(L"'SetRequestHeader' method failed: %i\n", hr);
pRequest->Release();
CoUninitialize();
return hr;
}
hr = pRequest->Send();
if (FAILED(hr)) {
wprintf(L"'Send' method failed: %i\n", hr);
pRequest->Release();
CoUninitialize();
return hr;
}
VARIANT varTimeOut = {};
VariantInit(&varTimeOut);
varTimeOut.vt = VT_I4;
varTimeOut.lVal = 5;
VARIANT_BOOL varfWaitSucceeded = VARIANT_FALSE;
hr = pRequest->WaitForResponse(varTimeOut, &varfWaitSucceeded);
VariantClear(&varTimeOut);
if (FAILED(hr)) {
wprintf(L"'WaitForResponse' method failed: %i\n", hr);
pRequest->Release();
CoUninitialize();
return hr;
}
if (varfWaitSucceeded == VARIANT_FALSE) {
wprintf(L"Request timed out\n");
pRequest->Release();
CoUninitialize();
return 2;
}
long lStatus = 0;
hr = pRequest->get_Status(&lStatus);
if (FAILED(hr)) {
wprintf(L"'get_Status' method failed: %i\n", hr);
pRequest->Release();
CoUninitialize();
return hr;
}
if (lStatus != 200) {
wprintf(L"Status code: %i\nThe server sent an invalid response\n", lStatus);
pRequest->Release();
CoUninitialize();
return 0;
}
BSTR bstrResponse = NULL;
hr = pRequest->get_ResponseText(&bstrResponse);
if (FAILED(hr)) {
wprintf(L"'get_ResponseText' method failed: %i\n", hr);
pRequest->Release();
CoUninitialize();
return hr;
}
wprintf(L"Response from the server:\n%s\n", bstrResponse);
SysFreeString(bstrResponse);
bstrResponse = NULL;
IStream* pStream = NULL;
VARIANT varResponseBody = {};
VariantInit(&varResponseBody);
hr = pRequest->get_ResponseStream(&varResponseBody);
if (FAILED(hr)) {
wprintf(L"'get_ResponseStream' method failed: %i\n", hr);
pRequest->Release();
CoUninitialize();
return hr;
}
if (varResponseBody.vt & VT_STREAM || varResponseBody.vt & VT_UNKNOWN) {
hr = varResponseBody.punkVal->QueryInterface(IID_IStream, reinterpret_cast<LPVOID*>(&pStream));
VariantClear(&varResponseBody);
if (FAILED(hr)) {
wprintf(L"'QueryInterface' method failed: %i\n", hr);
pRequest->Release();
CoUninitialize();
return hr;
}
HANDLE hFile = CreateFile(L"Z:\\response_c++.json", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
DWORD dwError = GetLastError();
if (INVALID_HANDLE_VALUE == hFile) {
wprintf(L"CreateFile failed: %lu\n", dwError);
pStream->Release();
pRequest->Release();
CoUninitialize();
return (int)dwError;
}
DWORD dwBytesRead = 0;
LPVOID lpvBuffer[8192] = {};
do {
hr = pStream->Read(lpvBuffer, 8192, &dwBytesRead);
if (FAILED(hr)) {
wprintf(L"'Read' method of IStream failed: %i\n", hr);
CloseHandle(hFile);
pStream->Release();
pRequest->Release();
CoUninitialize();
return (int)dwError;
}
if (!WriteFile(hFile, lpvBuffer, dwBytesRead, NULL, NULL)) {
dwError = GetLastError();
wprintf(L"WriteFile failed %lu\n", dwError);
CloseHandle(hFile);
pStream->Release();
pRequest->Release();
CoUninitialize();
return (int)dwError;
}
memset(lpvBuffer, 0, 8192);
} while (dwBytesRead);
pStream->Release();
CloseHandle(hFile);
wprintf(L"Response has been written to disk\n");
}
pRequest->Release();
CoUninitialize();
return 0;
}
JSON:
{
"id": "68c0c56c6a7bab4d8b740a07",
"organCode": "GMD",
"eventNameVi": "Phát hành cổ phiếu",
"eventNameEn": "Share Issue",
"organNameEn": "Gemadept Corporation",
"organNameVi": "Công ty Cổ phần Gemadept",
"ticker": "GMD",
"eventCode": "ISS",
"eventTitleVi": "Phát hành cổ phiếu - Phát hành cho CBCNV tỉ lệ 1.5%",
"eventTitleEn": "Share Issue - ESOP ratio 1.5%",
"displayDate1": "2025-10-02T00:00:00",
"displayDate2": "2025-10-06T00:00:00",
"publicDate": "2025-10-06T00:00:00",
"recordDate": "2025-10-02T00:00:00",
"exrightDate": "2025-10-02T00:00:00",
"listingDate": "2028-10-02T00:00:00",
"exerciseRatio": 0.015,
"category": "DIVIDEND"
}

