首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> VC >> VC 教學

VC++ 透過 URL 在視窗顯示網路圖片 (IPicture + CInternetSession)

     編輯:VC 教學

前言

承接上篇「VC++ 使用 IPicture 顯示本地圖片」,本文擴展至**從 HTTP URL 下載並直接在視窗/控件繪製圖片**。核心流程:CInternetSession::OpenURL 取得 CHttpFile → 讀取位元組到 HGLOBAL → CreateStreamOnHGlobal 建立 IStream → OleLoadPicture 載入 IPicture → Render 繪製至 DC。完整封裝為 Utils::ShowPic(LPCSTR url, HWND hWnd) 可直接復用。

完整實作程式碼

#include <afxinet.h>
#include <olectl.h>      // IPicture, OleLoadPicture
#include <atlbase.h>     // CComPtr (建議使用智能指標)

#pragma comment(lib, "wininet.lib")
#pragma comment(lib, "oleaut32.lib")
#pragma comment(lib, "olepro32.lib")

class Utils
{
public:
    // 顯示網路圖片於指定視窗
    // lpstrImgUrl: 圖片 HTTP/HTTPS URL
    // hWnd: 目標視窗控制代碼 (可為 Picture Control 的 m_hWnd)
    static HRESULT ShowPic(LPCSTR lpstrImgUrl, HWND hWnd)
    {
        if (!lpstrImgUrl || !hWnd) return E_INVALIDARG;

        HDC hDC = GetDC(hWnd);
        if (!hDC) return E_FAIL;

        CComPtr spPicture;
        CComPtr spStream;
        HRESULT hr = E_FAIL;

        // 1. 下載圖片資料
        CInternetSession session(_T("ImgDownloader/1.0"));
        CFile* pFile = NULL;
        DWORD dwFileSize = 0;

        try {
            pFile = session.OpenURL(lpstrImgUrl, 1, NULL, NULL); // 1 = TRANSFER_ASCII (二進位亦可)
            if (!pFile) return E_FAIL;

            dwFileSize = pFile->GetLength();
            if (dwFileSize == 0 || dwFileSize == 0xFFFFFFFF) {
                pFile->Close(); delete pFile; ReleaseDC(hWnd, hDC); return E_FAIL;
            }

            // 2. 配置全域記憶體並讀入
            HGLOBAL hGlobal = GlobalAlloc(GMEM_MOVEABLE, dwFileSize);
            if (!hGlobal) { pFile->Close(); delete pFile; ReleaseDC(hWnd, hDC); return E_OUTOFMEMORY; }

            LPVOID pvData = GlobalLock(hGlobal);
            if (!pvData) { GlobalFree(hGlobal); pFile->Close(); delete pFile; ReleaseDC(hWnd, hDC); return E_FAIL; }

            DWORD dwRead = pFile->Read(pvData, dwFileSize);
            GlobalUnlock(hGlobal);
            pFile->Close(); delete pFile; pFile = NULL;

            if (dwRead != dwFileSize) { GlobalFree(hGlobal); ReleaseDC(hWnd, hDC); return E_FAIL; }

            // 3. 建立 IStream
            hr = CreateStreamOnHGlobal(hGlobal, TRUE, &spStream);
            if (FAILED(hr)) { ReleaseDC(hWnd, hDC); return hr; }

            // 4. 載入 IPicture
            hr = OleLoadPicture(spStream, dwFileSize, TRUE, IID_IPicture, (LPVOID*)&spPicture);
            if (FAILED(hr) || !spPicture) { ReleaseDC(hWnd, hDC); return hr; }

            // 5. 取得圖片尺寸 (HIMETRIC: 0.01 毫米)
            OLE_XSIZE_HIMETRIC hmWidth;  OLE_YSIZE_HIMETRIC hmHeight;
            spPicture->get_Width(&hmWidth);
            spPicture->get_Height(&hmHeight);

            // 6. 轉換為裝置像素
            int nWidth  = MulDiv(hmWidth,  GetDeviceCaps(hDC, LOGPIXELSX), 2540);
            int nHeight = MulDiv(hmHeight, GetDeviceCaps(hDC, LOGPIXELSY), 2540);

            // 7. 繪製 (左上角 0,0)
            hr = spPicture->Render(hDC, 0, 0, nWidth, nHeight,
                                   0, hmHeight, hmWidth, -hmHeight, NULL);

            ReleaseDC(hWnd, hDC);
            return SUCCEEDED(hr) ? S_OK : E_FAIL;
        }
        catch (CInternetException* e) {
            TCHAR szErr[256]; e->GetErrorMessage(szErr, 255);
            TRACE(_T("下載失敗: %s (0x%08X)\n"), szErr, e->m_dwError);
            e->Delete();
            if (pFile) { pFile->Close(); delete pFile; }
            ReleaseDC(hWnd, hDC);
            return E_FAIL;
        }
        catch (CMemoryException*) {
            if (pFile) { pFile->Close(); delete pFile; }
            ReleaseDC(hWnd, hDC);
            return E_OUTOFMEMORY;
        }
    }
};

呼叫範例

// 在對話框按鈕事件中
void CMyDlg::OnBtnLoadImage()
{
    CString url = _T("https://example.com/image.jpg");
    HRESULT hr = Utils::ShowPic(url, GetDlgItem(IDC_STATIC_PIC)->m_hWnd);
    if (FAILED(hr)) AfxMessageBox(_T("圖片載入失敗"));
}

關鍵差異對照 (本地 vs 網路)

項目本地檔案版網路 URL 版
資料來源CreateFile + ReadFileCInternetSession::OpenURL → CHttpFile::Read
檔案大小GetFileSizeCFile::GetLength
記憶體讀取ReadFileCHttpFile::Read
後續流程同樣:HGLOBAL → IStream → OleLoadPicture → Render同上

限制與注意事項

  • 不支援 GIF 動畫:OleLoadPicture 僅渲染第一幀。需動畫請用 GDI+ Image + ImageAnimator 或 Direct2D。
  • 同步阻塞:下載與解碼在 UI 執行緒,大圖會卡死介面。生產環境請移至工作執行緒,完成後 PostMessage 通知 UI 繪製。
  • HTTPS 憑證:WinInet 自動處理;若為自簽憑證需設定 INTERNET_FLAG_IGNORE_CERT_* 旗標。
  • 記憶體管理:範例使用 CComPtr 自動釋放 COM 介面;HGLOBAL 由 CreateStreamOnHGlobal(..., TRUE) 交由 Stream 釋放。
  • 編譯需求:連結 wininet.lib、oleaut32.lib、olepro32.lib;包含 afxinet.h、olectl.h。

現代化改良建議

  1. 改用 GDI+ Bitmap::FromStream 支援更多格式 (PNG 透明、JPG、GIF 動畫)。
  2. 使用 IStream 配合 SHCreateStreamOnFileEx 或 WinHttp 非同步下載。
  3. UI 框架若為 WPF/UWP/WinUI,直接使用 BitmapImage 的 UriSource 綁定 URL 即可。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved