现在的位置: 首页 > 综合 > 正文

从内存字节流到文件—GlobalAlloc,GlobalLock,LockResource等用法

2019年03月10日 ⁄ 综合 ⁄ 共 2996字 ⁄ 字号 评论关闭

图片可以作为程序的资源,有时候我们想把图片资源编译为程序的一部分,这样资源就可以避免直接不查看利用。或者直接使用资源的二进制字节。那么怎么做到呢?windows是这么做的:首先申请一块全局内存块,将资源字节流拷贝到申请的内存中,然后就可以对该内存操作(比如转换为一个windows可以用的图片资源),最后释放掉内存块。下面是用到的几个函数:

GlobalAlloc 

函数原型
GlobalAlloc(UINT uFLAG,DWORD dwBytes);
说明 
分配一个全局内存块 
返回值 
Long,返回全局内存句柄。零表示失败。会设置GetLastError 
参数表 
参数 类型及说明 
uFlags Long,对分配的内存类型进行定义的常数标志,如下所示: 
             GMEM_FIXED 分配一个固定内存块 
             GMEM_MOVEABLE 分配一个可移动内存块 
             GMEM_DISCARDABLE 分配一个可丢弃内存块 
             GMEM_NOCOMPACT 堆在这个函数调用期间不进行累积 
             GMEM_NODISCARD 函数调用期间不丢弃任何内存块 
             GMEM_ZEROINIT 新分配的内存块全部初始化成零 
dwBytes Long,要分配的字符数 
注解  
如指定了 GMEM_FIXED,那么返回值就是要使用的实际内存地址即指针(GlobalLock 会返回同样的值)——所以在使用固定内存块的时候不需要执行一个 GlobalLock/GlobalUnlock 操作
由于 Win32 采用了高级的内存管理方案,所以使用可移动的内存块并没有什么好处
用这个函数分配的内存块允许在8位边界以内

GlobalLock  
函数功能描述
:锁定一个全局的内存对象,返回指向该对象的第一个字节的指针
函数原型
LPVOID GlobalLock( HGLOBAL hMem )
参数
hMem:全局内存对象的句柄。这个句柄是通过GlobalAlloc或GlobalReAlloc来得到的
返回值:
调用成功,返回指向该对象的第一个字节的指针
调用失败,返回NULL,可以用GetLastError来获得出错信息
注意:
调用过GlobalLock锁定一块内存区后,一定要调用GlobalUnlock来解锁。

GlobalUnlock
函数功能描述:
解除被锁定的全局内存对象
函数原型:BOOL GlobalUnlock( HGLOBAL hMem );
参数:hMem:全局内存对象的句柄
返回值:
非零值,指定的内存对象仍处于被锁定状态
0,函数执行出错,可以用GetLastError来获得出错信息,如果返回NO_ERROR,则表示内存对象已经解锁了
注意:    这个函数实际上是将内存对象的锁定计数器减一,如果计数器不为0,则表示执行过多个GlobalLock函数来对这个内存对象加锁,需要对应数目的GlobalUnlock函数来解锁。
    如果通过GetLastError函数返回错误码为ERROR_NOT_LOCKED,则表示未加锁或已经解锁。

调用GlobalAlloc函数分配一块内存,该函数会返回分配的内存句柄。  GlobalAlloc申请的内存分两种,一种是GMEM_FIXED,另一种是GMEM_MOVEABLE。两者的差别只要在于GMEM_MOVEABLE类型的内存操作系统是可以移动的,比如堆中有好几块小内存,当再申请一大块内存时,操作系统会移动GMEM_MOVEABLE类型的内存来合并出一大块。正因为GMEM_MOVEABLE是可移动的,所以要用句柄标识,不能用内存地址标识,在使用时通过GlobalLock由句柄得到内存地址。对于GMEM_FIXED类型的,该函数返回的句柄就是内存指针,可以直接当内存指针使用。
调用GlobalLock函数锁定内存块,该函数接受一个内存句柄作为参数,然后返回一个指向被锁定的内存块的指针。 您可以用该指针来读写内存。
调用GlobalUnlock函数来解锁先前被锁定的内存,该函数使得指向内存块的指针无效。 
调用GlobalFree函数来释放内存块。您必须传给该函数一个内存句柄。

比如将一个图片(位图、png什么的都可以)的二进制转换为程序可以用的资源:

//参数分别是:资源二进制流(把一张图片用可以查看16进制文本打开,拷贝就行);字节流大小;要转换为可以用的资源
BOOL ImageFromIDResource( unsigned char * lpResStreamName,unsigned int len,Gdiplus::Image * &pImg )
{
	// Allocate global memory on which to create stream
	HGLOBAL hMem = GlobalAlloc(GMEM_FIXED, len) ;
	//BYTE* pmem = (BYTE*)GlobalLock(hMem);          //根据前面解释,由于参数是GMEM_FIXED可以不用调用该函数
	//memcpy(pmem, lpResStreamName,len);             //同上
	memcpy(hMem, lpResStreamName, len) ;
	IStream* pstm;
	CreateStreamOnHGlobal(hMem,FALSE,&pstm) ;
	// load from stream
	pImg = Gdiplus::Image::FromStream(pstm);
	// free/release stuff
	pstm->Release();
	//GlobalUnlock(m_hMem);                          //对应的也不需啊
	return TRUE;
}


将VC导入的资源又如何转为临时文件呢?需要用到LockResource函数,下面给出代码,相信不难分析。

BOOL ReleaseResourceToTempPath(int iResId, LPTSTR lpType, LPTSTR lpFileName)
{
 DWORD dwSize;
 HGLOBAL hGlobal;
 LPVOID lpBuffer;
 HRSRC hRes = ::FindResource(NULL, MAKEINTRESOURCE(iResId), lpType);
 if (hRes){
  hGlobal = ::LoadResource(NULL, hRes);
  dwSize = ::SizeofResource(NULL, hRes);
  if (hGlobal && dwSize){
   lpBuffer = ::LockResource(hGlobal);
   TCHAR pTempFileName[256] ;
   if (lpBuffer){
    GetTempPath(MAX_PATH, pTempFileName);
    strcat_s(pTempFileName, sizeof(pTempFileName), lpFileName);
    FILE* fp;
    fopen_s(&fp, pTempFileName, _T("wb"));
    if (dwSize == fwrite(lpBuffer, sizeof(char), dwSize, fp)){
     fclose(fp);
     return TRUE;
    }
    else{
     fclose(fp);
    }
   }
  }
 }
 return FALSE;
}

抱歉!评论已关闭.