- Visual C++程序开发范例宝典(软件工程师典藏版)
- 刘志铭 李贺 高茹编著
- 5字
- 2020-06-27 11:09:48
3.8 图像管理
实例122 管理计算机内图片文件的程序
本实例可以方便操作、提高效率
实例位置:光盘\mingrisoft\03\122
实例说明
如今的应用软件在满足用户需求的同时,通常还增加许多额外的功能。例如,添加媒体播放工具、磁盘管理工具等。本实例提供了一个图片管理工具,能够将用户某一路径下的位图文件提取出来,并保存到指定的路径下,效果如图3.45所示。
图3.45 管理计算机内图片文件的程序
技术要点
本实例中需要解决如下问题。
(1)列举磁盘目录。
(2)遍历某一个磁盘下的所有文件夹。
(3)获取某一文件夹下的所有位图文件。
(4)复制位图文件。
对于问题(1)列举磁盘目录,可以通过GetLogicalDriveStrings API函数实现,该函数语法如下:
DWORD GetLogicalDriveStrings(DWORD nBufferLength, LPTSTR lpBuffer );
参数说明:
● nBufferLength:标识缓冲区的大小。
● lpBuffer:标识一个缓冲区。函数会将磁盘信息返回到lpBuffer中。
● 返回值:实际复制到缓冲区中的字符长度。
在实际应用中,通常用户并不知道盘符共占用多少空间,因此可以按如下方式调用GetLogicalDriveStrings函数获取缓冲区占用的空间。
DWORD dirlen = GetLogicalDriveStrings(0,NULL);
根据函数的返回值确定缓冲区大小,再次调用GetLogicalDriveStrings函数获取磁盘信息,代码如下:
LPSTR pdir ,phead; pdir = new char [dirlen+1]; GetLogicalDriveStrings(dirlen,pdir); phead = pdir; while (*pdir != 0 ) { m_disk.AddString(pdir); pdir= _tcschr(pdir,0)+1; } delete[]phead;
对于问题(2)遍历某一个磁盘下的所有文件夹,实现起来较为复杂,需要使用FindFirstFile函数和FindNextFile函数。
(1)FindFirstFile。该函数用于查找某一个目录下的第一个文件。语法如下:
HANDLE FindFirstFile(LPCTSTR lpFileName, LPWIN32_FIND_DATA lpFindFileData );
参数说明:
● lpFileName:标识查找的文件名,可以包含通配符。
● lpFindFileData:是WIN32_FIND_DATA结构指针,用于存储找到的文件信息。
● 返回值:函数返回一个查找句柄,用于FindFirstFile函数。
(2)FindNextFile。该函数根据查找句柄查找下一个文件。语法如下:
BOOL FindNextFile(HANDLE hFindFile, LPWIN32_FIND_DATA lpFindFileData );
参数说明:
● hFindFile:是查找句柄,通常为FindFirstFile函数的返回值。
● lpFindFileData:是WIN32_FIND_DATA结构指针,用于存储找到的文件信息。
使用FindFirstFile函数和FindNextFile函数只能实现一次查找,要遍历整个磁盘,需要编写递归函数实现,详细代码可参考实现过程。
问题(2)解决了,问题(3)获取某一文件夹下的所有位图文件也就迎刃而解了,只是在查找文件时,需要明确指定文件扩展名。详细代码可参考实现过程。
对于问题(4)复制位图文件,利用CopyFile API函数就可实现了。语法如下:
BOOL CopyFile(LPCTSTR lpExistingFileName,LPCTSTR lpNewFileName, BOOL bFailIfExists);
参数说明:
● lpExistingFileName:标识预复制的文件。
● lpNewFileName:标识新的文件名称。
● bFailIfExists:确定如果目标文件存在是否进行替换。
实现过程
(1)新建一个基于对话框的应用程序。
(2)在对话框中添加树控件、组合框控件、列表视图控件和按钮控件。
(3)向对话框类中添加EnumDIR方法,用于遍历指定磁盘下的目录,代码如下:
void CManageImageDlg::EnumDIR(CString dirname,HTREEITEM hparentitem) { WIN32_FIND_DATA m_fileinfo; //记录查找到的文件信息 HANDLE hfile; //查找句柄 HTREEITEM hnode; //树节点句柄 CString temp = dirname; CString tempfile; dirname+="\\*.*"; //查找所有文件 hfile = FindFirstFile(dirname,&m_fileinfo); //如果是目录,继续查找 if (m_fileinfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { tempfile = m_fileinfo.cFileName; tempfile.TrimLeft(); tempfile.TrimRight(); if ((tempfile!= ".") && (tempfile != "..") ) { hnode=m_tree.InsertItem(m_fileinfo.cFileName,0,0,hparentitem); //将查找的目录插入到树控件中 } if (m_fileinfo.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY) if ((tempfile!= ".") && (tempfile != "..") ) { EnumDIR(temp+"\\"+m_fileinfo.cFileName+"\\",hnode); } } while (FindNextFile(hfile,&m_fileinfo)) { if (m_fileinfo.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY) { tempfile = m_fileinfo.cFileName; tempfile.TrimLeft(); tempfile.TrimRight(); if ((tempfile!= ".") && (tempfile != "..") ) { hnode = m_tree.InsertItem(tempfile,0,0,hparentitem); } if (m_fileinfo.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY) if ((tempfile!= ".") && (tempfile != "..") ) { EnumDIR(temp+"\\"+m_fileinfo.cFileName,hnode); } } } }
(4)向对话框类中添加EnumFiles方法,列举指定目录下的所有位图文件,代码如下:
void CManageImageDlg::EnumFiles(LPSTR filepath) { WIN32_FIND_DATA m_fileinfo; //记录查找到的文件信息 HANDLE hfile; //文件查找句柄 m_dir.ResetContent(); CString tempfile; tempfile = filepath; tempfile = tempfile.Left(tempfile.GetLength()-3); CString c_temp,c_fileext; m_disk.GetWindowText(c_temp); //查找文件 hfile = FindFirstFile(filepath,&m_fileinfo); if ( !(m_fileinfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { c_temp = m_fileinfo.cFileName; c_fileext = c_temp.Right(3); if (c_fileext=="bmp") m_dir.AddString( tempfile+m_fileinfo.cFileName); } while (FindNextFile(hfile,&m_fileinfo)) { //如果找到的文件是一个目录,继续查找子目录 if (!( m_fileinfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) c_temp = m_fileinfo.cFileName; c_fileext = c_temp.Right(3); if (c_fileext=="bmp") m_dir.AddString(tempfile+m_fileinfo.cFileName); } }
(5)处理“保存”按钮的单击事件,将所有位图文件保存到某一目录下,代码如下:
void CManageImageDlg::OnSave() { int m_count = m_dir.GetCount(); if (m_count>0) { //构造另存为对话框 CFileDialog m_fdlg(FALSE,"bmp",NULL, OFN_HIDEREADONLY|OFN_OVERWRITEPROMPT,NULL,this); if (m_fdlg.DoModal()==IDOK) { CString m_savepath=m_fdlg.GetPathName(); //获得文件路径 CString m_curfile; for (int i = 0;i <m_count;i++) { m_dir.GetText(i,m_curfile); //复制文件 CopyFile(m_curfile,ExtractFileName(m_savepath)+"\\"+ ExtractFilePath(m_curfile),FALSE); } MessageBox("保存成功"); } } }
举一反三
根据本实例,读者可以:
实现文件查找。
实例123 提取并保存应用程序图标
这是一个可以提高基础技能的实例
实例位置:光盘\mingrisoft\03\123
实例说明
许多应用软件都具有漂亮的应用程序图标。本实例实现了提取并保存应用程序图标的功能,效果如图3.46所示。
图3.46 提取并保存应用程序图标
技术要点
提取应用程序的图标比较简单,可以利用ExtractIcon API函数实现,该函数语法如下:
HICON ExtractIcon(HINSTANCE hInst, LPCTSTR lpszExeFileName, UINT nIconIndex );
参数说明:
● hInst:是当前应用程序的实例句柄。
● lpszExeFileName:标识可执行文件的名称。
● nIconIndex:标识返回的图标索引,如果为零,将返回所标识文件的第一个图标句柄。
● 返回值:提取的图标句柄。
保存应用程序图标略微复杂,需要了解图标的结构。图标文件由3部分组成,第1部分是图标文件头,结构如下:
typedef struct { WORD idReserved; // 保留 (必须是 0) WORD idType; // 资源类型 (1标识ICON) WORD idCount; // 文件所含图片的数量 ICONDIRENTRY idEntries[1]; // 每一个图片的入口 } ICONDIR, *LPICONDIR;
一个图标文件可以包含多个图标,成员idCount用于确定文件包含的图标数量。
第2部分是图标资源索引目录,结构如下:
typedef struct { BYTE bWidth; // 图像宽度(单位是像素) BYTE bHeight; // 图像高度(单位是像素) BYTE bColorCount; // 图像中的颜色数目 (0 if>=8bpp) BYTE bReserved; // 保留(必须是0) WORD wPlanes; // 颜色平面 WORD wBitCount; // 位深 DWORD dwBytesInRes; // 这副图片所占用的字节数目 DWORD dwImageOffset; // 图片在文件中的位置 } ICONDIRENTRY, *LPICONDIRENTRY;
其中dwBytesInRes成员用于确定图标的大小,dwImageOffset确定图标数据在文件中的位置。如果图标文件中包含多个图标,则每一个图标都对应一个图标资源索引目录。
第3部分是图标数据,结构如下:
typdef struct { BITMAPINFOHEADER icHeader; //DIB信息头 RGBQUAD icColors[1]; //颜色表 BYTE icXOR[1]; //XOR掩码 BYTE icAND[1]; //AND掩码 } ICONIMAGE,*LPICONIMAGE;
其中icHeader成员是位图信息头,icColors[1]成员标识颜色表,大小由icHeader成员确定。icXOR[1] 成员标识图标XOR掩码的DIB位,icAND[1]成员标识图标AND单色掩码。
实现过程
(1)新建一个基于对话框的应用程序。
(2)在对话框中放置编辑框控件、按钮控件、静态文本控件和图片控件。
(3)在对话框类的头文件中定义图标文件结构。
(4)处理“提取”按钮的单击事件,提取应用程序图标,代码如下:
//提取应用程序图标 void CFetchAndSaveIconDlg::OnFetch() { CString str; m_filename.GetWindowText(str); if (!str.IsEmpty()) { HICON m_hicon; m_hicon=::ExtractIcon(AfxGetInstanceHandle(),str,0); //获得图标句柄 if (m_hicon != NULL) { m_demoicon.SetIcon(m_hicon); //显示图标 } } }
(5)处理“保存”按钮单击事件,将提取的图标资源保存为图标文件,代码如下:
void CFetchAndSaveIconDlg::OnSave() { CFileDialog m_savedlg(FALSE,"ico",NULL,NULL,"图标(.ico)|*.ico",this); //构造另存为对话框 if (m_savedlg.DoModal()==IDOK) { CString str=m_savedlg.GetPathName(); //获得文件路径 if(!str.IsEmpty()) { CFile m_file(str,CFile::modeCreate|CFile::typeBinary|CFile::modeWrite); //构造文件对象 HICON hicon; CString name; m_filename.GetWindowText(name); //获得文件名 HMODULE hmodule = LoadLibraryEx(name, NULL, LOAD_LIBRARY_AS_DATAFILE); EnumResourceNames(hmodule,RT_GROUP_ICON, ( ENUMRESNAMEPROC)EnumResNameProc,LONG(GetSafeHwnd())); hicon =(HICON)FindResource(hmodule,m_iconname,RT_GROUP_ICON); //查找图标资源 HGLOBAL global=LoadResource(hmodule,(HRSRC)hicon); //加载图标资源 if (global!= NULL) { m_lpMemDir=(LPMEMICONDIR)LockResource(global); //锁定资源 } lpicondir temp = (lpicondir)m_lpMemDir; m_lpdir = (lpicondir)m_lpMemDir; DWORD factsize; //写入文件头 WORD a = m_lpdir->idreserved; m_file.Write(&a,sizeof(WORD)); a = m_lpdir->idtype; m_file.Write(&a,sizeof(WORD)); a = m_lpdir->idcount; m_file.Write(&a,sizeof(WORD)); m_lpdir = NULL; //写入索引目录 icondirentry entry; for (int i = 0; i<temp->idcount;i++) { DWORD size; DWORD imagesize= GetImageOffset(hmodule,i,size); free(m_lpData); entry.bheight = m_lpMemDir->idEntries[i].bHeight; entry.bwidth = m_lpMemDir->idEntries[i].bWidth; entry.breserved = 0; entry.bcolorcount = m_lpMemDir->idEntries[i].bColorCount; entry.dwbytesinres =m_lpMemDir->idEntries[i].dwBytesInRes; entry.dwimageoffset = imagesize; entry.wbitcount = m_lpMemDir->idEntries[i].wBitCount; entry.wplanes = m_lpMemDir->idEntries[i].wPlanes; m_file.Write(&entry,sizeof(entry)); } //写入图像数据 for (int j = 0; j<temp->idcount;j++) { LPBYTE pInfo; DWORD size; DWORD imagesize= GetImageOffset(hmodule,j,size,pInfo); m_file.Write((LPBYTE)m_lpData,size); free(m_lpData); } UnlockResource(global); FreeLibrary(hmodule); m_file.Close(); } } }
举一反三
根据本实例,读者可以:
提取图标文件中包含的所有图标。