VC 中用 IPicture 在窗口中显示图片文件

想做个东西,用VC,希望是在程序界面上能把指定 URL 的图片显示出来,还没找到如何显示 HTTP 资源的图片,先还是让本地图片能在 VC 的窗口(包括各种控件)中显示出来吧。在网上搜索了一番,大家代码都如出一辙,我也不能生搬硬载,不由又亲手实践了一下,并连同原来学过的 Window SDK 做窗口程序也复习了一遍,没有使用到 MFC,只需存成单个源文件,甚至用 CL 命令来编译都无须打开 VC 的,在 VC6 中测试通过。

效果图如下:



代码如下:(仅需关键最后一个方法 ShowPic(char *lpstrFile, HWND hWnd))
  1#include <windows.h>  // Windows SDK 要用到的
  2#include <commdlg.h>  // 打开文件的窗口要包含这个头文件
  3
  4//用到了 IPicture 接口,要包含下面两个 头文件
  5#include <ocidl.h>
  6#include <olectl.h>
  7
  8// 声明窗口函数的原型
  9LRESULT CALLBACK MainWndProc (HWND, UINT, WPARAM, LPARAM);
 10
 11// 声明显示图片的函数原型
 12HRESULT ShowPic(char *lpstrFile,HWND hWnd) ;
 13
 14//图片文件名
 15char lpstrFile[MAX_PATH]="";
 16
 17
 18int WINAPI WinMain (HINSTANCE hThisInstance,
 19                    HINSTANCE hPrevInstance,
 20                    LPSTR lpszArgument,
 21                    int nFunsterStil)
 22
 23{
 24    // 窗口类名
 25    char szClassName[ ] = "ShowPicture";
 26
 27    WNDCLASSEX windclass;
 28
 29    //用描述主窗口的参数填充 WNDCLASSEX 结构
 30    windclass.cbSize = sizeof(windclass);  //结构大小
 31    windclass.hInstance = hThisInstance;   //实例句酚
 32    windclass.lpszClassName = szClassName;
 33    windclass.lpfnWndProc = MainWndProc;   //窗口函数指针
 34    windclass.style = CS_DBLCLKS;          //捕获双击事件
 35    windclass.cbSize = sizeof (WNDCLASSEX);
 36
 37    windclass.hIcon = LoadIcon (NULL, IDI_APPLICATION); //使用预定义图标
 38    windclass.hIconSm = LoadIcon (NULL, IDI_APPLICATION); //使用预定义类的小图标
 39    windclass.hCursor = LoadCursor (NULL, IDC_ARROW); //使用预定义光标
 40    windclass.lpszMenuName = NULL;                 //不指定主菜单
 41    windclass.cbClsExtra = 0;                      //没有额外的类内存
 42    windclass.cbWndExtra = 0;                      //没有额外的窗口内存
 43
 44    windclass.hbrBackground = (HBRUSH) COLOR_BACKGROUND; //使用预定义背景画刷
 45
 46    //注册这个窗口类,如果失败直接退出程序
 47    if (!RegisterClassEx (&windclass))
 48        return 0;
 49
 50    //创建主窗口
 51    HWND hWnd = CreateWindowEx (
 52        0,                   //dwExStyle, 扩展样式
 53        szClassName,         //lpClassName, 类名
 54        "显示图片 -- 鼠标双击浏览图片文件      -- Author:Unmi", //lpWindowName, 标题
 55        WS_OVERLAPPEDWINDOW, //dwStyle, 窗口风格
 56        CW_USEDEFAULT,       //X, 初始 X 坐标
 57        CW_USEDEFAULT,       //Y, 初始 YX 坐标
 58        640,                 //nWidth, 宽度
 59        480,                 //nHeight, 高度
 60        HWND_DESKTOP,        //hWndParent, 父窗口句柄
 61        NULL,                //hMenu, 菜单句柄
 62        hThisInstance,       //hInstance, 程序实例句柄
 63        NULL                 //lpParam, 用户数据
 64        );
 65
 66    //显示窗口,刷新窗口客户区
 67    ShowWindow (hWnd, nFunsterStil);
 68
 69    // 从消息队列中取出消息,交给窗口函数处理
 70    // 直到 GetMessage 取出的消息是 WM_QUIT,即FALSE,则结束消息循环
 71    MSG messages;
 72    while (GetMessage (&messages, NULL, 0, 0))
 73    {
 74        //转换键盘消息,把虚拟键转换为字符消息
 75        TranslateMessage(&messages);
 76
 77        //将消息发送到相应的窗口函数
 78        DispatchMessage(&messages);
 79    }
 80
 81    // GetMessage 返回 FALSE,程序结束 PostQuitMessage() 发出这一消息
 82    return messages.wParam;
 83}
 84
 85
 86// 窗口函数,消息由 DispatchMessage 派发
 87LRESULT CALLBACK MainWndProc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
 88{
 89    switch (message) //处理消息
 90    {
 91    case WM_DESTROY:
 92        //向消息队列投递一个 WM_QUIT 消息,促使 GetMessage 函数返回0,结束消息循环
 93        PostQuitMessage (0);
 94        break;
 95    case WM_LBUTTONDBLCLK:
 96        {
 97            OPENFILENAME file = {0};
 98            file.lStructSize = sizeof(file);
 99            file.lpstrTitle = "请选择一个图片文件";
100            file.lpstrFile = lpstrFile;
101            file.nMaxFile = MAX_PATH;
102            file.lpstrFilter = "Pictures(*.jpg,*.gif,*.bmp)\0*.jpg;*.gif;*.bmp\0\0";
103            if(::GetOpenFileName(&file))
104            {
105                //清除原来的图像
106                HDC hdc=GetDC(hWnd);
107                RECT rect;
108                ::GetWindowRect(hWnd,&rect);
109                ::FillRect(hdc,&rect,(HBRUSH) COLOR_BACKGROUND);
110
111                ::SendMessage(hWnd,WM_PAINT,NULL,NULL);
112            }
113        }
114        break;
115    case WM_PAINT:
116        ShowPic(lpstrFile,hWnd);
117        break;
118    default:
119
120        //将我们不处理的消息交给系统 做默认处理
121        return DefWindowProc (hWnd, message, wParam, lParam);
122    }
123
124    return 0;
125}
126
127// 显示图片,此文的重点之所在了, lpstrFile 为图片文件名,hWnd 为窗口句柄
128HRESULT ShowPic(char *lpstrFile,HWND hWnd)
129{
130    HDC hDC_Temp=GetDC(hWnd);
131
132    IPicture *pPic;
133    IStream *pStm;
134
135    BOOL bResult;
136
137    HANDLE hFile=NULL;
138    DWORD dwFileSize,dwByteRead;
139
140    //打开图形文件
141    hFile=CreateFile(lpstrFile,GENERIC_READ,
142        FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
143
144    if (hFile!=INVALID_HANDLE_VALUE)
145    {
146        dwFileSize=GetFileSize(hFile,NULL);//获取文件字节数
147
148        if (dwFileSize==0xFFFFFFFF)
149            return E_FAIL;
150    }
151    else
152    {
153        return E_FAIL;
154    }
155
156
157    //分配全局存储空间
158    HGLOBAL hGlobal = GlobalAlloc(GMEM_MOVEABLE, dwFileSize);
159    LPVOID pvData = NULL;
160
161    if (hGlobal == NULL)
162        return E_FAIL;
163
164    if ((pvData = GlobalLock(hGlobal)) == NULL)//锁定分配内存块
165        return E_FAIL;
166
167    ReadFile(hFile,pvData,dwFileSize,&dwByteRead,NULL);//把文件读入内存缓冲区
168
169    GlobalUnlock(hGlobal);
170
171    CreateStreamOnHGlobal(hGlobal, TRUE, &pStm);
172
173    //装入图形文件
174    bResult=OleLoadPicture(pStm,dwFileSize,TRUE,IID_IPicture,(LPVOID*)&pPic);
175
176    if(FAILED(bResult))
177        return E_FAIL;
178
179    OLE_XSIZE_HIMETRIC hmWidth; //图片的真实宽度, 单位为英寸
180    OLE_YSIZE_HIMETRIC hmHeight; //图片的真实高度, 单位为英寸
181    pPic->get_Width(&hmWidth);
182    pPic->get_Height(&hmHeight);
183
184    //转换hmWidth和hmHeight为pixels距离,1英寸=25.4毫米
185    int nWidth = MulDiv(hmWidth,GetDeviceCaps(hDC_Temp,LOGPIXELSX),2540);
186    int nHeight = MulDiv(hmHeight,GetDeviceCaps(hDC_Temp,LOGPIXELSY),2540);
187
188    //将图形输出到屏幕上(有点像BitBlt)
189    bResult=pPic->Render(hDC_Temp,0,0,nWidth,nHeight,
190        0,hmHeight,hmWidth,-hmHeight,NULL);
191
192    pPic->Release();
193       
194    CloseHandle(hFile);//关闭打开的文件    
195       
196    if (SUCCEEDED(bResult))    
197    {    
198        return S_OK;    
199    }    
200    else    
201    {    
202        return E_FAIL;    
203    }    
204}


函数 HRESULT
 ShowPic(char *lpstrFile,HWND hWnd) 是本日志要义之所在,传入文件名和窗口句柄(比如按或者静态文本框的句柄亦可),然后会在指定窗口中按图片实际尺寸显示出来。

注意,必须包含头文件 ocidl.h 和 olectl.h 。关键的方法只有 ShowPic,其余的代码只不过为我练手之用,权可当是累赘。以上实现的是在主窗口中显示图片,图片格式暂定为允许选择JPG/GIF/BMP三种,其他没一一尝试,不过我想只要 IE 中能支持的图片式在此应无问题。

操作方法是:在窗口中双击鼠标左键就会弹出图片选择窗口,选择适当的图片就会在窗口中显示。目前只是读取图片文件显示,期待中的是下一步显示内存中的图片数据字节数组或流,进而给个 HTTP 协议的图片 URL 就能正确显示,那么在这个小范围内的研究对我说算是一个小突破。

现在想来,好像都不是问题,只是不同的 File HANDLE 而已,通过 CInternetSession 读取图片 URL 也会是生成一个 FILE HANDLE,交给 ReadFile 去读取就行了,晚上回去试一试。

参考:1. 在MFC程序中显示JPG/GIF图像
        2. 用IPicture显示图片
        3. 利用IPicture接口加载、显示图片
        4. VC下显示JPEG、GIF图像的简便方法
        5. cool !!!! 在VC中显示JPEG和GIF图像 永久链接 https://yanbin.blog/vc-ipicture-show-picture/, 来自 隔叶黄莺 Yanbin's Blog
[版权声明] 本文采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可。