Skip to content

共享文件夹 #136

@holdyounger

Description

@holdyounger

共享文件夹

最近由于项目需求,需要编写一个监控本机共享文件夹的变化的模块,经过查询资料,找到并实现了一个较为稳定的方式
项目实现是使用Win32 C++的,测试平台是Win 7 64和Win 10 64,XP测试也是好使的。下面是具体实现

首先要获取并监控系统共享文件夹的路径,相关注册表路径为 HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\LanmanServer\Share ,他的值的格式为如图所示,

共享文件注册表

C++ 获取共享文件夹目录

BOOL GetSharedFoldersList(map<CString, HANDLE> &theList)
{
	BOOL bFlag = FALSE;
	HKEY hKey = NULL;
	do
	{
		//清空列表
		theList.clear();
 
		//HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\LanmanServer\Share
		//通过注册表获取共享文件夹列表
		HKEY rootKey = REG_SHARE_KEY;
		CString szRegPath = REG_SHARE_PATH;
		
		int ErrCode;
		if (ERROR_SUCCESS != (ErrCode = RegOpenKeyEx(rootKey, szRegPath, 0, KEY_READ | KEY_WOW64_64KEY, &hKey)))
		{
			break;
		}
 
		TCHAR szValue[MAX_VALUE_NAME] = { 0 };
		DWORD dwValueSize = MAX_VALUE_NAME;
		int index = 0;
		DWORD dwBufferSize = 255;
		DWORD dwType;
		TCHAR *szValBuffer = NULL;
		DWORD dwSize;
		while (ERROR_NO_MORE_ITEMS != RegEnumValue (
			hKey, index, szValue, &dwValueSize, NULL, &dwType, NULL, &dwBufferSize))
		{
			//判断值类型
			switch(dwType)
			{
				//只找多个串的值
			case REG_MULTI_SZ:
				{
					dwSize = dwBufferSize + 1;
					szValBuffer = new TCHAR[dwSize];
					ZeroMemory(szValBuffer, dwSize);
					if (ERROR_SUCCESS != RegQueryValueEx(hKey, szValue, 0, &dwType, (LPBYTE)szValBuffer, &dwBufferSize))
					{
						break;
					}
					int j = 0;
 
					CString TmpValue;
 
					for(int i = 0;szValBuffer[i] != '\0' ;i += j + 1)
					{	
						for (j = 0;szValBuffer[i + j] != '\0';j ++)
						{
							TmpValue += szValBuffer[i + j];
						}
						
						//如果找到共享路径,则直接放入容器,然后进行下一个路径的查找
						if (TmpValue.Find(_T("Path=")) == 0)
						{
							//m_vSharedList.push_back(TmpValue.Right(TmpValue.GetLength() - 5));
							CString Tmp = TmpValue.Right(TmpValue.GetLength() - 5);
							CString szTmp = Tmp;
							Tmp.MakeLower();
 
							break;
						}
						TmpValue = _T("");
					}
					delete szValBuffer;
					szValBuffer = NULL;
					break;
				}
 
			//其他类型跳过
			default:
				{
					break;
				}
			}
			dwValueSize = MAX_VALUE_NAME;
			dwBufferSize = 255;
			index ++;
		}
		bFlag = TRUE;
	}while(FALSE);
 
	if (hKey != NULL)
	{
		RegCloseKey(hKey);
	}
 
	return bFlag;
}

C++监控共享注册表变化

//监控注册表项以更新共享文件夹列表
DWORD WINAPI SharedMonitor::RefreshThread(LPVOID lpParam)
{
	SharedMonitor *pThis = (SharedMonitor *)lpParam;
 
	//只监控值的改变
	DWORD  dwFilter = REG_NOTIFY_CHANGE_LAST_SET;
 
	HKEY hKey;
	HKEY rootKey = REG_SHARE_KEY;
	CString szRegPath = REG_SHARE_PATH;
 
	//等待10分钟
	int iWaitTime = 10 * 60 * 1000;
	while (TRUE)
	{
		//监控注册表项改变
		//Sleep(iWaitTime);
		HANDLE hEvent;
		BOOL bFlag = FALSE;
		do
		{
			if (ERROR_SUCCESS != RegOpenKeyEx(rootKey, szRegPath, 0, KEY_ALL_ACCESS | KEY_WOW64_64KEY, &hKey))
			{
				MyDebugA ("RegMonitorThread--- RegOpenKeyEx ERR!");
				break;
			}
 
			if (NULL == (hEvent = CreateEvent(NULL, TRUE, FALSE, NULL)))
			{
				MyDebugA ("CreateEvent ERR");
				break;
			}
 
			if (ERROR_SUCCESS != RegNotifyChangeKeyValue (hKey, TRUE, dwFilter, hEvent, TRUE))
			{
				MyDebugA ("RegNotifyChangeKeyValue ERR");
				break;
			}
 
			if (WAIT_FAILED == WaitForSingleObject (hEvent, INFINITE))
			{
				MyDebugA ("WaitForSingleObject ERR");
				break;
			}
 
			bFlag = TRUE;
		}while(FALSE);
		
		RegCloseKey (hKey);
 
		CloseHandle (hEvent);
 
		//如果监控失败,则重新再来
		if (!bFlag)
		{
			continue;
		}
 
		//等待系统将注册表消息发放完毕,否则有时候添加或删除的注册表项无法被枚举到
		Sleep(200);
 
		//进行共享列表枚举和比较
		map<CString, HANDLE> theNewList;
		//获取新列表
		pThis->GetSharedFoldersList(theNewList);
		map<CString, HANDLE> &theOldList = pThis->GetListInstance();
 
		BOOL bFindNew = FALSE;
		//比较两个列表
		//添加旧列表中没有的共享
		for (map<CString, HANDLE>::iterator it = theNewList.begin();
				it != theNewList.end(); ++it)
		{
			if (theOldList.find(it->first) == theOldList.end())
			{
				MyDebug(_T("----------Find New Share----------"));
				MyDebug(it->first);
				theOldList[it->first] = 0;
				bFindNew = TRUE;
			}
		}
 
		//MyDebug(_T("---------Start Delete--------"));
 
		//需要删除的列表,map不支持循环删除多个元素
		vector<CString> theDeleteList;
 
		//删除旧列表中已经取消的共享
		for (map<CString, HANDLE>::iterator it = theOldList.begin();
			it != theOldList.end(); ++it)
		{
			if (theNewList.find(it->first) == theNewList.end())
			{
				//结束监控线程
				if (!TerminateThread(it->second, 0))
				{
					MyDebug(_T("TerminateThread ERR"));
					MyDebug(it->first);
				}
 
				//将线程占用的共享目录句柄释放
				if (gs_mpDirHandleList[it->first])
				{
					CloseHandle(gs_mpDirHandleList[it->first]);
				}
				
				CloseHandle(it->second);
				
				theDeleteList.push_back(it->first);
			}
		}
 
		MyDebug(_T("-----------DeleteList-------------"));
		//删除旧列表中的过期共享
		for (vector<CString>::iterator it = theDeleteList.begin();
				it != theDeleteList.end(); ++it)
		{
			theOldList.erase(*it);
			MyDebug(*it);
		}
 
		//如果有新的共享
		if (bFindNew)
		{
			pThis->SetSharedMonitor();
		}
	}
 
	return 0;
}

共享文件夹内文件监控

最后就是最终的目的,对共享文件夹内文件的变化进行监控操作。这里使用的是 WIN32 API ReadDirectoryChangesW ,这个的具体使用在我之前的一篇文章里有介绍,这里就不多说了,需要注意的一点是 ReadDirectoryChangesW 函数的第一个参数所需的句柄需要 CreateFile 去创建,而且创建后需要一直占用着不能释放,而创建时所需的权限(第三个参数) 必须设置为 "FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE",因为如果少任意一个就有可能导致某些第三方软件的使用出现异常,例如迅雷下载,浏览器下载之类的。

//监控线程
DWORD WINAPI ATSharedMonitor::MonitorThread(LPVOID lpParam)
{
	CString szRootPath = *(CString *)lpParam;
 
 
	HANDLE hRootHandle = CreateFile(
		szRootPath,		//监控路径
		FILE_LIST_DIRECTORY, 
		FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
		NULL,
		OPEN_EXISTING,
		FILE_FLAG_BACKUP_SEMANTICS/* | FILE_FLAG_OVERLAPPED*/,
		NULL);
	if(FAILED_HANDLE(hRootHandle))
	{
		MyDebug(_T("CreateFile Fail"));
		return 0;
	}
 
	//将目录句柄放到线程对应容器中
	gs_mpDirHandleList[szRootPath] = hRootHandle;
 
//	OUTPUT_PARAM_DEBUGA("The CurrentThread:%X", GetCurrentThread());
 
	szRootPath += _T("\\");
 
	wchar_t notify[1024];
	ZeroMemory(notify, 1024);
	DWORD dwBytes;
	FILE_NOTIFY_INFORMATION *pNotify = (FILE_NOTIFY_INFORMATION *)notify;
 
	//过滤同一个文件的操作 
	CString szLastFile = _T("");
 
	while (TRUE)
	{
		if (ReadDirectoryChangesW (hRootHandle, &notify, sizeof(notify), TRUE,
			FILE_NOTIFY_CHANGE_CREATION | FILE_NOTIFY_CHANGE_FILE_NAME | 
			FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_LAST_WRITE,
			&dwBytes, NULL, NULL))
		{
			MyDebug(_T("---------Change Happened!------------"));
			
 
			pNotify->FileName[pNotify->FileNameLength / 2] = '\0';
			//CString tmp = pNotify->FileName;
			//tmp.MakeLower ();
 
			CString TmpPath = szRootPath + pNotify->FileName;
 
			switch(pNotify->Action)
			{
				//重命名获取
 			case FILE_ACTION_RENAMED_OLD_NAME:
 				{
 					MyDebug (TEXT("文件更名 :") + TmpPath);
 					PFILE_NOTIFY_INFORMATION p = (PFILE_NOTIFY_INFORMATION)((char*)pNotify+pNotify->NextEntryOffset);
 					p->FileName[p->FileNameLength/2] = '\0';
 					CString newName = szRootPath + p->FileName;
 					MyDebug(newName);
 
 					break;
 				}
				
 
				//删除
// 			case FILE_ACTION_REMOVED:
// 				{
// 					MyDebug (TEXT("文件删除 :") + TmpPath);
// 				}
 
				//修改..略过文件夹的修改,过滤同一个文件的修改
			case FILE_ACTION_MODIFIED:
				{
 
					if (!PathIsDirectory(TmpPath) /*&& szLastFile.CompareNoCase(pNotify->FileName) != 0*/)
					{
						//szLastFile = pNotify->FileName;
						MyDebug (TEXT("文件修改 :") + TmpPath);
						
					}
					break;
				}
 
				//新建文件获取
			case FILE_ACTION_ADDED:
			 	{
					if (!PathIsDirectory(TmpPath) /*&& szLastFile.CompareNoCase(pNotify->FileName) != 0*/)
					{
			 			MyDebug (TEXT("文件添加 :") + TmpPath);
						
					}
			 		break;
			 	}
 
			default:
				{
					break;
				}
 
			}
		}
	}
	return 0;
}

blog link 共享文件夹

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions