c++ 根据文件的最后修改时间判断是否需要覆盖更新文件
2010-11-16 16:40
1506 查看
主要根据获取文件的属性来判断是否需要更新。
一、获取文件的属性
WIN32_FIND_DATA结构
关于文件的全部属性信息,总计有以下以下9
种:文件的标题名、文件的属性(只读、存档,隐藏等)、文件的创建时间、文件的最后访问时间、文件的最后修改时间、文件大小的高位双字、文件大小的低位双
字、保留、保留。在这里只有文件标题名和文件的长度可以通过CFile类比较方便的获得,而对于其他几种属性的获取和设置就无能为力了。
在用findfirst()和findnext()函数去查找磁盘文件时经常使用的一个数据结构WIN32_FIND_DATA的成员变量里包含了以上所有的文件属性,因此可以通过这个结构作为获取和更改文件属性的手段。该结构的内容如下:
typedef struct _WIN32_FIND_DATA {
DWORD dwFileAttributes; //文件属性
FILETIME ftCreationTime; // 文件创建时间
FILETIME ftLastAccessTime; // 文件最后一次访问时间
FILETIME ftLastWriteTime; // 文件最后一次修改时间
DWORD nFileSizeHigh; // 文件长度高32位
DWORD nFileSizeLow; // 文件长度低32位
DWORD dwReserved0; // 系统保留
DWORD dwReserved1; // 系统保留
TCHAR cFileName[ MAX_PATH ]; // 长文件名
TCHAR cAlternateFileName[ 14 ]; // 8.3格式文件名
} WIN32_FIND_DATA, *PWIN32_FIND_DATA;
可以通过FindFirstFile()函数根据当前的文件存放路径查找该文件来把待操作文件的相关属性读取到WIN32_FIND_DATA结构中去:
WIN32_FIND_DATA ffd ;
HANDLE hFind = FindFirstFile("c://test.dat",&ffd);
在使用这个结构时不能手工修改这个结构中的任何数据,结构对于开发人员来说只能作为一个只读数据,其所有的成员变量都会由系统完成填写。
实例
:
//--get the LastWriteTime of folder/files
WIN32_FIND_DATA ffd ;
HANDLE hFind = FindFirstFile(openedSourceDir,&ffd);
SYSTEMTIME stUTC, stLocal;
FileTimeToSystemTime(&(ffd.ftLastWriteTime), &stUTC);
SystemTimeToTzSpecificLocalTime(NULL, &stUTC, &stLocal);
myTime.Format("%d. %d %d, %d:%d", stLocal.wDay,stLocal.wMonth,stLocal.wYear,stLocal.wHour,stLocal.wMinute);
//--
d_ColorStatic.SetWindowText((LPCTSTR)myTime);
二、判断使用该文件的程序是否在运行:
虽然WinMain函数有hPreInstance参数来指示,但是那是在Win16位的前提下,到了32Bit时代,那个参数已经完全成为摆设。
而本文正好探讨了如何防止C++程序重复运行的方法。
PS:因为本人使用MFC,所以为了方便,所有代码均为以MFC为基础。大家可以根据自己需要更改
1.查找窗体
对于存在GUI窗体(CUI暂不讨论)的程序来说,最容易想到的就是利用FindWindow,以标题作为参数进行查找主窗体,然后使其关闭即可。
通常,我们能写出如下代码:
//
Find Window by Caption
//
Add this code in InitInstance function of class
//
you have derived from the CWinApp class
HWND hWnd
=
::FindWindow(NULL,
"
MFCDialog
"
);
if
(hWnd)
{
AfxMessageBox(
"
Has been running
"
);
return
FALSE;
}
以上的代码可以简单的起到防止重复启动的效果,但是局限性很大。
首先,由于在FindWindow中要指定窗体的标题,如果窗体的标题在程序运行中是不断变化的,那么就给搜索带来了一定难度。
而且,如果其他程序也恰好是用相同的标题的话……- -#。当然,你可以通过在FindWindow中指定类名来减少错误。但是如果你看过我前面写的文章的话,你就会发现,MFC注册窗口类并不是那么随意,而是经过N次阴谋筹划之后……
看来这方法的局限性的确很大- -#
2.额外窗体存储
此方法来源于对上面一种方法的补充,因为通过搜索MFC的窗体类比较困难,而且准确度不一定高。所以,我想到了使用额外窗体存储(Extra Widnow Memory)的方法
PS:关于什么是额外窗体存储,请自行google或MSDN或查看我曾经写的The Analyses Of Windows Runnning Principle
如果你使用SDK进行开发,可以在创建窗体时填充这一属性,然后用GetWindowLong获取。
而由于我使用MFC,所以我更关注如何在MFC中使用这一属性。
一般来说,我们可以使用SetWindowLong对额外窗体存储进行填充,然后用GetWindowLong获取,最后配合FindWindow来检验程序是否重复运行。
//
Add this code in InitDialog function
//
and you can specify any number you want
BOOL bRet
=
::SetWindowLong(GetSafeHwnd(), GWL_USERDATA,
256
);
//
Add this code in InitInstance function
//
Find Window by using extra memory
HWND hWnd
=
FindWindow(NULL,
"
MFCDialog
"
);
if
(hWnd)
{
BOOL bRet
=
::GetWindowLong(hWnd, GWL_USERDATA);
if
(
256
==
bRet)
//
compare
{
AfxMessageBox(
"
Has been running
"
);
return
FALSE;
}
}
3.全局原子
你可以使用GlobalAddAtom将某个特定的字符串添加到全局原子列表(Global Atom Table),然后在程序运行时检查该字符串即可。
但是这个方法有一个致命的弱点,程序退出时,Windows不会自动为你删除添加到列表中的Atom,而是需要你自己使用GlobalDeleteAtom进行删除。
这就意味着,如果你的程序意外的退出了,没有删除添加的Atom,那么,你的程序将无法运行。
所以,这并不是一个好方法。
4.枚举进程
这或许是一个毕竟正常,或者说相对稳定的方法。
我们可以使用CreateToolhelp32Snapshot或者EnumProcess来枚举当前的进程,然后检查是否已经运行。如果担心存在同名的进程,还可以检查路径。
枚举进程的代码我之前已经写过,在这里就不再重复了。
PS:在Vista下使用EnumProcess时,要注意权限问题,OpenProcess增加了一个新的权限常数,仅限Vista。如果不增加这个参数,很多进程是无法被枚举出来的(不过MS不印象我们自己的进程- -#)。
5.互斥对象
使用互斥对象来防止程序重复运行是一个很常用的做法,而且M$也推荐使用这种方法。和上面的几种方法相比,需要写的代码少,而且效率比较高。所谓方便易用~
一般我们会使用CreateMutex来创建互斥体,当第二次创建相同的互斥体时,这个API会返回前一个互斥体的Handle,而GetLastError则会返回ERROR_ALREADY_EXITS
//
Mutex Object
//
Add this code in InistInstance function
HANDLE hMutex
=
NULL;
TCHAR
*
lpszName
=
"
TestMutex
"
;
hMutex
=
::CreateMutex(NULL, FALSE, lpszName);
DWORD dwRet
=
::GetLastError();
if
(hMutex)
{
if
(ERROR_ALREADY_EXISTS
==
dwRet)
{
AfxMessageBox(
"
Has been running
"
);
CloseHandle(hMutex);
//
should be closed
return
FALSE;
}
}
else
{
AfxMessageBox(
"
Create Mutex Error
"
);
}
//
Add this code in Destruction function
::CloseHandle(hMutex);
使用互斥体时要注意几个问题:
在CreateMutex之后马上GetLastError,GetLastError是一个很复杂的API,任何牵涉到GetLastError的操作在执行之后,都会覆盖先前的值。
把正常的CloseHandle写到窗体的析构函数或者程序对象的析构函数里。不要在CreateMutex之后立刻CloseHandle,否则互斥对象会被清空。
这也是我当初所犯的错误,(不知道网上那么多错误的代码是不是经过Debug的囧),当互斥对象的最后一个Handle被Close之后,互斥对象将被删除。如果程序在退出时没有清空互斥对象,Windows将会执行这一操作。当然,把次操作交给OS不是一个好习惯。
三、判断时间的大小
四、拷贝文件
一、获取文件的属性
WIN32_FIND_DATA结构
关于文件的全部属性信息,总计有以下以下9
种:文件的标题名、文件的属性(只读、存档,隐藏等)、文件的创建时间、文件的最后访问时间、文件的最后修改时间、文件大小的高位双字、文件大小的低位双
字、保留、保留。在这里只有文件标题名和文件的长度可以通过CFile类比较方便的获得,而对于其他几种属性的获取和设置就无能为力了。
在用findfirst()和findnext()函数去查找磁盘文件时经常使用的一个数据结构WIN32_FIND_DATA的成员变量里包含了以上所有的文件属性,因此可以通过这个结构作为获取和更改文件属性的手段。该结构的内容如下:
typedef struct _WIN32_FIND_DATA {
DWORD dwFileAttributes; //文件属性
FILETIME ftCreationTime; // 文件创建时间
FILETIME ftLastAccessTime; // 文件最后一次访问时间
FILETIME ftLastWriteTime; // 文件最后一次修改时间
DWORD nFileSizeHigh; // 文件长度高32位
DWORD nFileSizeLow; // 文件长度低32位
DWORD dwReserved0; // 系统保留
DWORD dwReserved1; // 系统保留
TCHAR cFileName[ MAX_PATH ]; // 长文件名
TCHAR cAlternateFileName[ 14 ]; // 8.3格式文件名
} WIN32_FIND_DATA, *PWIN32_FIND_DATA;
可以通过FindFirstFile()函数根据当前的文件存放路径查找该文件来把待操作文件的相关属性读取到WIN32_FIND_DATA结构中去:
WIN32_FIND_DATA ffd ;
HANDLE hFind = FindFirstFile("c://test.dat",&ffd);
在使用这个结构时不能手工修改这个结构中的任何数据,结构对于开发人员来说只能作为一个只读数据,其所有的成员变量都会由系统完成填写。
实例
:
//--get the LastWriteTime of folder/files
WIN32_FIND_DATA ffd ;
HANDLE hFind = FindFirstFile(openedSourceDir,&ffd);
SYSTEMTIME stUTC, stLocal;
FileTimeToSystemTime(&(ffd.ftLastWriteTime), &stUTC);
SystemTimeToTzSpecificLocalTime(NULL, &stUTC, &stLocal);
myTime.Format("%d. %d %d, %d:%d", stLocal.wDay,stLocal.wMonth,stLocal.wYear,stLocal.wHour,stLocal.wMinute);
//--
d_ColorStatic.SetWindowText((LPCTSTR)myTime);
二、判断使用该文件的程序是否在运行:
虽然WinMain函数有hPreInstance参数来指示,但是那是在Win16位的前提下,到了32Bit时代,那个参数已经完全成为摆设。
而本文正好探讨了如何防止C++程序重复运行的方法。
PS:因为本人使用MFC,所以为了方便,所有代码均为以MFC为基础。大家可以根据自己需要更改
1.查找窗体
对于存在GUI窗体(CUI暂不讨论)的程序来说,最容易想到的就是利用FindWindow,以标题作为参数进行查找主窗体,然后使其关闭即可。
通常,我们能写出如下代码:
//
Find Window by Caption
//
Add this code in InitInstance function of class
//
you have derived from the CWinApp class
HWND hWnd
=
::FindWindow(NULL,
"
MFCDialog
"
);
if
(hWnd)
{
AfxMessageBox(
"
Has been running
"
);
return
FALSE;
}
以上的代码可以简单的起到防止重复启动的效果,但是局限性很大。
首先,由于在FindWindow中要指定窗体的标题,如果窗体的标题在程序运行中是不断变化的,那么就给搜索带来了一定难度。
而且,如果其他程序也恰好是用相同的标题的话……- -#。当然,你可以通过在FindWindow中指定类名来减少错误。但是如果你看过我前面写的文章的话,你就会发现,MFC注册窗口类并不是那么随意,而是经过N次阴谋筹划之后……
看来这方法的局限性的确很大- -#
2.额外窗体存储
此方法来源于对上面一种方法的补充,因为通过搜索MFC的窗体类比较困难,而且准确度不一定高。所以,我想到了使用额外窗体存储(Extra Widnow Memory)的方法
PS:关于什么是额外窗体存储,请自行google或MSDN或查看我曾经写的The Analyses Of Windows Runnning Principle
如果你使用SDK进行开发,可以在创建窗体时填充这一属性,然后用GetWindowLong获取。
而由于我使用MFC,所以我更关注如何在MFC中使用这一属性。
一般来说,我们可以使用SetWindowLong对额外窗体存储进行填充,然后用GetWindowLong获取,最后配合FindWindow来检验程序是否重复运行。
//
Add this code in InitDialog function
//
and you can specify any number you want
BOOL bRet
=
::SetWindowLong(GetSafeHwnd(), GWL_USERDATA,
256
);
//
Add this code in InitInstance function
//
Find Window by using extra memory
HWND hWnd
=
FindWindow(NULL,
"
MFCDialog
"
);
if
(hWnd)
{
BOOL bRet
=
::GetWindowLong(hWnd, GWL_USERDATA);
if
(
256
==
bRet)
//
compare
{
AfxMessageBox(
"
Has been running
"
);
return
FALSE;
}
}
3.全局原子
你可以使用GlobalAddAtom将某个特定的字符串添加到全局原子列表(Global Atom Table),然后在程序运行时检查该字符串即可。
但是这个方法有一个致命的弱点,程序退出时,Windows不会自动为你删除添加到列表中的Atom,而是需要你自己使用GlobalDeleteAtom进行删除。
这就意味着,如果你的程序意外的退出了,没有删除添加的Atom,那么,你的程序将无法运行。
所以,这并不是一个好方法。
4.枚举进程
这或许是一个毕竟正常,或者说相对稳定的方法。
我们可以使用CreateToolhelp32Snapshot或者EnumProcess来枚举当前的进程,然后检查是否已经运行。如果担心存在同名的进程,还可以检查路径。
枚举进程的代码我之前已经写过,在这里就不再重复了。
PS:在Vista下使用EnumProcess时,要注意权限问题,OpenProcess增加了一个新的权限常数,仅限Vista。如果不增加这个参数,很多进程是无法被枚举出来的(不过MS不印象我们自己的进程- -#)。
5.互斥对象
使用互斥对象来防止程序重复运行是一个很常用的做法,而且M$也推荐使用这种方法。和上面的几种方法相比,需要写的代码少,而且效率比较高。所谓方便易用~
一般我们会使用CreateMutex来创建互斥体,当第二次创建相同的互斥体时,这个API会返回前一个互斥体的Handle,而GetLastError则会返回ERROR_ALREADY_EXITS
//
Mutex Object
//
Add this code in InistInstance function
HANDLE hMutex
=
NULL;
TCHAR
*
lpszName
=
"
TestMutex
"
;
hMutex
=
::CreateMutex(NULL, FALSE, lpszName);
DWORD dwRet
=
::GetLastError();
if
(hMutex)
{
if
(ERROR_ALREADY_EXISTS
==
dwRet)
{
AfxMessageBox(
"
Has been running
"
);
CloseHandle(hMutex);
//
should be closed
return
FALSE;
}
}
else
{
AfxMessageBox(
"
Create Mutex Error
"
);
}
//
Add this code in Destruction function
::CloseHandle(hMutex);
使用互斥体时要注意几个问题:
在CreateMutex之后马上GetLastError,GetLastError是一个很复杂的API,任何牵涉到GetLastError的操作在执行之后,都会覆盖先前的值。
把正常的CloseHandle写到窗体的析构函数或者程序对象的析构函数里。不要在CreateMutex之后立刻CloseHandle,否则互斥对象会被清空。
这也是我当初所犯的错误,(不知道网上那么多错误的代码是不是经过Debug的囧),当互斥对象的最后一个Handle被Close之后,互斥对象将被删除。如果程序在退出时没有清空互斥对象,Windows将会执行这一操作。当然,把次操作交给OS不是一个好习惯。
三、判断时间的大小
WIN32_FIND_DATA ffdUkey ,ffd; HANDLE hFind = FindFirstFile(strDir +"//Plugins//UsbDisk.api",&ffdUkey); hFind = FindFirstFile(lookPath+"//UsbDisk.api",&ffd); LONG lRet = CompareFileTime(&ffdUkey.ftLastWriteTime,&ffd.ftLastWriteTime);
四、拷贝文件
//拷贝文件 BOOL CBmpDlgDlg:: FileCopyTo(CString source, CString destination, CString searchStr, BOOL cover) { CString strSourcePath = source; CString strDesPath = destination; CString strFileName = searchStr; CFileFind filefinder; CString strSearchPath = strSourcePath + "//" + strFileName; CString filename; BOOL bfind = filefinder.FindFile(strSearchPath); CString SourcePath, DisPath; while (bfind) { bfind = filefinder.FindNextFile(); filename = filefinder.GetFileName(); SourcePath = strSourcePath + "//" + filename; DisPath = strDesPath + "//" + filename; DeleteFile((LPCSTR)DisPath); CopyFile((LPCSTR)SourcePath, (LPCSTR)DisPath, cover); } filefinder.Close(); return true; }
相关文章推荐
- (java)根据文件修改时间,判断是否为当天文件,如果不是则删除文件夹内文件
- linux 判断文件最后更新时间 实现监控日志是否有输出功能
- SQL语句二次排序。先根据是否置顶字段。再根据最后更新时间排序。
- mysql中根据插入的记录判断是否需要是插入还是更新
- linux 获取本地文件最后修改时间 c++
- 待续。makefile关于文件修改时间确定是否需要重新编译的正确书写格式
- C++ 读取文件中的数字 并存放到vector中 在由vector存放到 multimap中 最后BFS 判断是否连通
- c++判断硬盘是否连接、修改指定盘符、读取配置文件、获取exe路径
- java实现根据文件最后的修改时间排序
- DOS批处理判断文件修改时间是否相同,精确到秒
- 分享一个excel根据文件超链接获取链接文档的最后更新时间
- 文件创建、删除、重名名文件、判断文件的读写权限以及是否存在,设置和查询文件的最近修改时间
- 【日期时间】批处理判断一个文件在最近5分钟内是否被更新过
- 安卓判断文件的最后修改时间
- C/C++中判断某一文件或目录是否存在 .
- C# 检查网络是否连通 判断远程文件是否存在 C#获取程序路径的方法中需要注意的地方
- 判断网页是真静态还是伪静态的方法:查看网页最后更新时间
- android应用程序安全卫士——2、通过服务器xml文件,判断app是否需要更新
- java 得到文件最后的修改时间
- vbscript获取文件的创建时间、最后修改时间和最后访问时间的方法