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();
                    }
              }
            }

举一反三

根据本实例,读者可以:

提取图标文件中包含的所有图标。