最近一个项目,因为用户服务器上没有DotNet Framework,所以就用VC2005来开发非托管程序。其中有个小功能,就是对某个文件夹下的文件进行压缩,产生一个zip文件。
就想到了使用Shell32.dll中的一些对象、方法来做。基本的思路就是先建立一个空的zip文件,然后将文件夹中的文件依次添加到其中。
因为以前VC用得很少,所以陆陆续续遇到了一些问题,我的解决办法如下:
1. 如何引入Shell32.dll。
C#里面,可以在“添加引用”中来添加Microsoft Shell Controls And Automation的COM引用,从而引用了Shell32.dll。可我发现,如果VC没有使用CLR支持,是不能像C#那样操作的。
研究了一下,其实很简单,只要#import <Shell32.dll>就行了。
2.创建空的zip文件。
{
//bytes needed to create a file of zip format.
byte btEmptyzip[] = { 80, 75, 5, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
//create a zip file in the specified path.
CFile objFile;
objFile.Open((LPCTSTR)(m_cstrZipFileFullPath),CFile::modeWrite| CFile::shareExclusive| CFile::modeCreate);
objFile.Write(btEmptyzip, sizeof(btEmptyzip));
objFile.Flush();
objFile.Close();
}
3.将其他文件依次添加到Zip文件当中。使用MoveHere和CopyHere方法都可以。
{
CreateEmptyZip();
Shell32::IShellDispatchPtr ptrShell;
ptrShell.CreateInstance(__uuidof(Shell32::Shell));
Shell32::FolderPtr ptrFolder=ptrShell->NameSpace((COleVariant)m_cstrZipFileFullPath);
//Loop through the directory of Location A and add all files to the output zip file.
CFileFind objFinder;
bool bWorking=objFinder.FindFile(m_cstrSourceFilePath+"*.*");
while(bWorking)
{
bWorking=objFinder.FindNextFile();
//Skip ., .. and subdirectory.
if(objFinder.IsDots()||objFinder.IsDirectory())
continue;
//add the file to the zip file
ptrFolder->MoveHere((COleVariant)(objFinder.GetFilePath()),20);
}
//Code to sleep the main thread until the MoveHere thread is over
}
4. 线程异步的问题。
用MoveHere方法时,其实是开启了另外一个线程,与主线程是不同步的(个人理解)。如果需要压缩的文件很大,很可能文件还没压缩完,主线程就结束了,生成的压缩文件就会出错;所以最好能等到这个MoveHere的线程结束再执行主线程。
使用MoveHere方法时,会跳出压缩的一个窗口,名字是"Compressing...",我的办法就是拿到这个窗口的句柄,做一个循环一直判断。句柄为0则说明压缩完毕了。
代码如下:
HWND hWnd = FindWindow( " #32770 " , " Compressing " );
while (hWnd )
{
Sleep(3000);
hWnd=FindWindow("#32770","Compressing");
}
加到AddtoZip()方法的//Code to sleep the main thread until the MoveHere thread is over.之处。
FindWindow函数返回与指定字符创相匹配的窗口类名或窗口名的最顶层窗口的窗口句柄。
HWND FindWindow ( LPCTSTR lpClassName, LPCTSTR lpWindowName );
类名和窗口名都是通过Spy++查看到的。
不过也有问题,如果用的中文操作系统,lpWindowName就是“压缩中...”,不是“Compressing...”这样如上的代码就不对了。
不知道有没有更好的办法。
5. 在构造函数中最好加上“CoInitialize(NULL);”,析构函数里加上“CoUninitialize();”,另外我在属性中将字符集由默认的Unicode改成多字符字节集。上面的代码应该没问题了。
VC用的很少,不太懂,这些小问题花了偶不少时间。如果有朋友来看,希望能多多指教。