在窗口中显示一幅 JPG 图象 Mr

本实例是《杨老师之Blog——COM组件设计与应用(四)》中的实例三,本人实现后并加以注释。

void CShowJPGView::OnDraw(CDC* pDC)
{
  CShowJPGDoc* pDoc = GetDocument();
  ASSERT_VALID(pDoc);
  if (!pDoc)
    return;

  // TODO: 在此处为本机数据添加绘制代码
  ::CoInitialize(NULL);    //初始化COM
  HRESULT hr;
  CFile file;

  file.Open("D:\\test.jpg",CFile::modeRead | CFile::shareDenyNone);   //读入文件内容
  DWORD dwSize = file.GetLength();  //获取图片文件的大小,用来分配全局内存
  HGLOBAL hMem = ::GlobalAlloc(GMEM_MOVEABLE,dwSize);   //给图片分配全局内存
  LPVOID lpBuf = ::GlobalLock(hMem);   //锁定内存
  file.Read(lpBuf,dwSize);   //读取图片到全局内存当中
  file.Close();
  ::GlobalUnlock(hMem);   //解锁内存

  IStream *pStream = NULL;   //创建一个IStream接口指针,用来保存图片流
  IPicture *pPicture = NULL;   //创建一个IPicture接口指针,表示图片对象

  //由HGLOBAL得到IStream,参数TRUE表示释放IStream的同时,释放内存
  hr = ::CreateStreamOnHGlobal(hMem,TRUE,&pStream);   //用全局内存初始化IStream接口指针
  ASSERT(SUCCEEDED(hr));

  hr = ::OleLoadPicture(pStream,dwSize,TRUE,IID_IPicture,(LPVOID*)&pPicture);   //用OleLoadPicture获得IPicture接口指针
  ASSERT(hr == S_OK);

  //得到IPicture COM接口对象后,你就可以进行获得图片信息、显示图片等操作
  long nWidth,nHeight;   //宽高,MM_HIMETRIC模式,单位是0.01毫米
  pPicture->get_Width(&nWidth);    //宽
  pPicture->get_Height(&nHeight);    //高

  //原始大小显示
  CSize sz(nWidth,nHeight);
  pDC->HIMETRICtoDP(&sz);  //转换MM_HIMETRIC模式单位为MM_TEXT像素单位
  pPicture->Render(pDC->m_hDC,0,0,sz.cx,sz.cy,0,nHeight,nWidth,-nHeight,NULL);  //在指定的DC上绘出图片
 
  //按窗口尺寸显示
//   CRect rect;
//   GetClientRect(&rect);
//   pPicture->Render(pDC->m_hDC,0,0,rect.Width(),rect.Height(),0,nHeight,nWidth,-nHeight,NULL);

  if(pPicture)
    pPicture->Release();   //释放IPicture指针
  if(pStream)
    pStream->Release();    //释放IStream指针,同时释放了hMem

  ::CoUninitialize();
}
运行结果:
原始大小显示:

按窗口尺寸显示:

注释1:
CFile:: Open

  virtual BOOL Open( LPCTSTR lpszFileName, UINT nOpenFlags, CFileException* pError = NULL );

返回值:
  如果成功打开,则返回非零值,否则为0。pError参数仅在返回0时才有意义。

参数:
  lpszFileName:待打开文件的路径,路径可为绝对、相对或网络名(UNC)。 
  nOpenFlags:一个定义了文件的共享和访问模式的UINT。它指定了打开文件后的动作,可以用OR(|)操作符将选项组合起来,至少应有一个访问权限和一个共享选项,modeCreate和modeNoInherit模式是可选的。可参阅CFile 构造函数中模式选项的列表。 
  具体如下:
    CFile::modeCreate  让构造器创建一个新文件,如果那个文件已经存在,把那个文件的长度重设为
    CFile::modeNoTruncate  可以同modeCreate. 一起用,如果要创建的文件已经存在,并不把它长度设置为0,因而这个文件获取或者作为一个新建文件或者作为一个已存在文件打开。这个功能往往很好用,比如说,当你需要打开一个设置文件,但是你并不清楚这个文件是否已经存在。
    CFile::modeRead  打开文件仅仅供读
    CFile::modeReadWrite  打开文件供读写
    CFile::modeWrite  打开文件只供写
    CFile::modeNoInherit  阻止这个文件被子进程继承
    CFile::shareDenyNone  打开这个文件同时允许其它进程读写这个文件。如果文件被其它进程以incompatibility模式打开,这是create操作会失败。
    CFile::shareDenyRead  打开文件拒绝其它任何进程读这个文件。如果文件被其它进程用compatibility模式或者是读方式打开,create操作失败。
    CFile::shareDenyWrite  打开文件拒绝其它任何进程写这个文件。如果文件被其它进程用compatibility模式或者是写方式打开,create操作失败。
    CFile::shareExclusive  以独占方式打开这个文件,不允许其它进程读写这个文件。 Construction fails if the file has been opened in any other mode for read or write access, even by the current process.
    CFile::shareCompat  这个标志在32位的MFC中无效。 This flag maps to CFile::shareExclusive when used in CFile::Open.
    CFile::typeText  设置成对回车换行对有特殊处理的文本模式(仅用在派生类中)
    CFile::typeBinary  设置二进制模式(仅用在派生类中)
  pError:指向一个存在的文件异常对象,获取失败操作的状态。

注释2:
调用GlobalAlloc函数分配一块内存,该函数会返回分配的内存句柄。
调用GlobalLock函数锁定内存块,该函数接受一个内存句柄作为参数,然后返回一个指向被锁定的内存块的指针。 您可以用该指针来读写内存。
调用GlobalUnlock函数来解锁先前被锁定的内存,该函数使得指向内存块的指针无效。
调用GlobalFree函数来释放内存块。您必须传给该函数一个内存句柄。

GlobalAlloc
  该函数从堆中分配一定数目的字节数。Win32内存管理器并不提供相互分开的局部和全局堆。提供这个函数只是为了与16位的Windows相兼容。
 
  HGLOBAL GlobalAlloc(
    UINT uFlags, // 分配属性(方式)   
    SIZE_T dwBytes // 分配的字节数   
  );
      
返回值:
  若函数调用成功,则返回一个新分配的内存对象的句柄。  
  若函数调用失败,则返回 NULL。可调用 GetLastError 以获得更多错误信息。

参数:
  uFlags:指定如何分配内存,若指定为0,则是默认的GMEM_FIXED.这个值可以是下面其中一个或几个位标识(那些指明不兼容的组合除外)
  具体如下:
    GHND  GMEM_MOVEABLE 和 GMEM_ZEROINIT的组合。
    GMEM_FIXED  分配固定的内存,返回值是一个指针。   
    GMEM_MOVEABLE  分配可移动的内存,在Win32中内存块在物理内存中是不可移动的,但在缺省堆中可以。返回值是该内存对象的句柄,可使用函数 GlobalLock 将该句柄转换为一个指针。这个标识不能与 GMEM_FIXED 组合使用。  
    GMEM_ZEROINIT  将所申请内存初始化为0。
    GPTR  GMEM_FIXED和GMEM_ZEROINIT组合。    
    GMEM_DDESHARE、GMEM_DISCARDABLE、GMEM_LOWER、GMEM_NOCOMPACT、GMEM_NODISCARD、GMEM_NOT_BANKED、GMEM_NOTIFY、GMEM_SHARE 均被忽略,这些标识只是为与 16 位 Windows 相兼容而提供的。
  dwBytes:指定要申请的字节数。若该参数为 0 且参数 uFlags 指定为 GMEM_MOVEABLE 则该函数返回一个内存对象的句柄,该内存对象被标识为discarded(可抛弃的)。

注解:
  如果堆内没有足够的空间满足请求,函数将返回 NULL。因为NULL是用于标明错误的,所以不会分配虚拟0地址。   
  因此很容易检测出是否在使用一个NULL指针。
  使用此函数分配内存可以保证8字节的边界。所有的内存均在执行访问时创建;不需要特别的函数来动态执行所产生的代码。
  若函数调用成功,将至少分配所需内存。若实际分配量超过所需,则内存仍然能够充分利用之。可用函数 GlobalSize 来确定实际所分配的字节数。   
  可使用 GlobalFree 来释放内存。
 
GlobalLock
  锁定内存中指定的内存块,并返回一个地址值,令其指向内存块的起始处。除非用 GlobalUnlock 函数将内存块解锁,否则地址会一直保持有效。Windows 为每个内存对象都维持着一个锁定计数。对这个函数的每次调用都应有一个对应的 GlobalUnlock 调用。
  
  LPVOID GlobalLock(
    HGLOBAL hMem   // handle to global memory object
  );
 
  一般情况下我们在编程的时候,给应用程序分配的内存都是可以移动的或者是可以丢弃的,这样能使有限的内存资源充分利用,所以,在某一个时候我们分配的那块内存的地址是不确定的,因为他是可以移动的,所以得先锁定那块内存块,这儿应用程序需要调用API函数GlobalLock函数来锁定句柄。如下: lpMem=GlobalLock(hMem); 这样应用程序才能存取这块内存。
参数:
  hMem:全局内存对象的句柄。这个句柄是通过GlobalAlloc或GlobalReAlloc来得到的
返回值:
  调用成功,返回指向该对象的第一个字节的指针
  调用失败,返回NULL,可以用GetLastError来获得出错信息
注意:
  调用过GlobalLock锁定一块内存区后,一定要调用GlobalUnlock来解锁。
 
GlobalUnlock
  GlobalUnlock函数解除锁定的内存块,使指向该内存块的指针无效,GlobalLock锁定的内存,一定要用GlobalUnlock解锁。
 
  BOOL GlobalUnlock(
    HGLOBAL hMem   // handle to global memory object
  );
参数:
  hMem:全局内存对象的句柄
返回值:
  非零值,指定的内存对象仍处于被锁定状态
  0,函数执行出错,可以用GetLastError来获得出错信息,如果返回NO_ERROR,则表示内存对象已经解锁了
注意:    
  这个函数实际上是将内存对象的锁定计数器减一,如果计数器不为0,则表示执行过多个GlobalLock函数来对这个内存对象加锁,需要对应数目的GlobalUnlock函数来解锁。
  如果通过GetLastError函数返回错误码为ERROR_NOT_LOCKED,则表示未加锁或已经解锁。
 
注释3:
CreateStreamOnHGlobal
  CreateStreamOnHGlobal函数从指定内存创建流对象。
 
  WINOLEAPI CreateStreamOnHGlobal(
    HGLOBAL hGlobal,              //Memory handle for the stream object
    BOOL fDeleteOnRelease,     //Whether to free memory when the
                                     // object is released
    LPSTREAM *ppstm             //Address of output variable that
                                    // receives the IStream interface pointer
 );

参数:
  hGlobal:由GlobalAlloc函数分配的内存句柄。   
  fDeleteOnRelease:该参数指明上一个参数制定的内存在该对象被释放后是否也自动释放。如果该参数设定为FALSE,那么调用者必须显示的释放hGlobal。如果该参数设置为TRUE,则hGlobal最终会自动释放。   
  ppstm:IStream指针的地址,该指针在该函数执行后指向新创建的流对象。该参数不能未NULL。   如果函数创建流对象成功则返回S_OK。

注释4:
OleLoadPicture
  用API OleLoadPicture来加载JPG、GIF格式的图片(注:不支持PNG格式,另外GIF只能加载第一帧,且不支持透明)
 OleLoadPicture 函数实际上创建了一个IPicture类型的COM接口对象,然后我们可以通过这个COM接口来操作图片(实际上你也可以用API OleCreatePictureIndirect来加载图片,不过相比而言OleLoadPicture函数简化了基于流的IPicture对象的创 建)。

原文地址:https://www.cnblogs.com/miaohw/p/2147541.html