VC++文件監(jiān)控之ReadDirectoryChangesW
我這里只介紹采用ReadDirectoryChangesW對文件目錄實施監(jiān)控
關鍵代碼
CfgdsgDlg * dlg = (CfgdsgDlg*)lparam;
HANDLE hDir;
char notify[1024];
DWORD cbBytes,i;
char AnsiChar[3];
wchar_t UnicodeChar[2];
CString path;
FILE_NOTIFY_INFORMATION *pnotify=(FILE_NOTIFY_INFORMATION *)notify;
FILE_NOTIFY_INFORMATION *tmp;
GetCurrentDirectory(MAX_PATH,path.GetBuffer(MAX_PATH+1));
hDir = CreateFile( path, FILE_LIST_DIRECTORY,
FILE_SHARE_READ |
FILE_SHARE_WRITE |
FILE_SHARE_DELETE, NULL,
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS |
FILE_FLAG_OVERLAPPED, NULL);
if (hDir == INVALID_HANDLE_VALUE)
{
dlg->m_edit.ReplaceSel("hDir:INVALID_HANDLE_VALUE\r\n");
return 0;
}
while (TRUE)
{
if(ReadDirectoryChangesW(hDir, ¬ify, sizeof(notify),
FALSE, FILE_NOTIFY_CHANGE_FILE_NAME| FILE_NOTIFY_CHANGE_LAST_WRITE,
&cbBytes, NULL, NULL))
{
tmp = pnotify;
switch(tmp->Action)
{
case FILE_ACTION_ADDED:
dlg->m_edit.ReplaceSel("Directory/File added (添加文件)- \r\n");
break;
case FILE_ACTION_REMOVED:
dlg->m_edit.ReplaceSel("Directory/File removed (刪除文件)- \r\n");
break;
case FILE_ACTION_MODIFIED:
dlg->m_edit.ReplaceSel("Directory/File modified (修改文件內(nèi)容)- \r\n");
break;
case FILE_ACTION_RENAMED_OLD_NAME:
dlg->m_edit.ReplaceSel("Directory/File old name (修改文件名字)- \r\n");
break;
case FILE_ACTION_RENAMED_NEW_NAME:
dlg->m_edit.ReplaceSel("Directory/File new name - \r\n");
break;
default:
break;
}
}
}
FILE_NOTIFY_INFORMATION //可以確定是那個文件進行的修改
typedef struct _FILE_NOTIFY_INFORMATION {
DWORD NextEntryOffset;
DWORD Action;//動作
DWORD FileNameLength;//文件名字的長度
WCHAR FileName[1];//文件名字
} FILE_NOTIFY_INFORMATION,
*PFILE_NOTIFY_INFORMATION;
ReadDirectoryChangesW 返回類型(見MSDN)
| Value | Meaning |
|---|---|
|
FILE_ACTION_ADDED |
The file was added to the directory. |
|
FILE_ACTION_REMOVED |
The file was removed from the directory. |
|
FILE_ACTION_MODIFIED |
The file was modified. This can be a change in the time stamp or attributes. |
|
FILE_ACTION_RENAMED_OLD_NAME |
The file was renamed and this is the old name. |
|
FILE_ACTION_RENAMED_NEW_NAME |
The file was renamed and this is the new name. |
效果如下:
不足的地方:
只能檢測到指定目錄和下一級目錄,超過目錄級數(shù),該函數(shù)檢測不到。
ReadDirectoryChangesW 監(jiān)控文件夾 (一個簡單的監(jiān)控示例程序)
.h文件
// .h文件
#pragma once
typedef void (*PFN_NotifyAction)(DWORD dwAction, LPWSTR szFile, DWORD dwLength);
class CDirectoryWatch
{
public:
CDirectoryWatch(void);
virtual ~CDirectoryWatch(void);
public:
BOOL StartDirectoryWatch(LPCTSTR lpszDirectory, PFN_NotifyAction pFn_NotifyAction);
BOOL StopDirectoryWatch(void);
private:
static UINT __cdecl ThreadProc(LPVOID lParam);
static UINT __cdecl DirectoryWatch(LPVOID lParam);
private:
HANDLE m_hFile;
CWinThread* m_pThread;
TCHAR m_szDirectory[MAX_PATH];
};
.cpp文件
// .cpp文件
#include "StdAfx.h"
#include "DirectoryWatch.h"
#include <strsafe.h>
typedef enum
{
MSG_STARTWATCH = (WM_USER + 0x11),
MSG_STOPWATCH,
MSG_EXITTHREAD
};
#define MAX_BUFFER_SIZE (1024)
typedef struct _tagWATCHPARAMETERS
{
_tagWATCHPARAMETERS()
{
hFile = INVALID_HANDLE_VALUE;
hEvent = NULL;
memset(&ol, 0, sizeof(OVERLAPPED));
pBuffer = NULL;
dwBufferSize = 0;
bExit = FALSE;
pFn_NotifyAction = NULL;
}
HANDLE hFile;
HANDLE hEvent;
OVERLAPPED ol;
BYTE* pBuffer;
DWORD dwBufferSize;
BOOL bExit;
PFN_NotifyAction pFn_NotifyAction;
}WATCH_PARAMETERS, *PWATCH_PARAMETERS;
CDirectoryWatch::CDirectoryWatch() : m_hFile(INVALID_HANDLE_VALUE), m_pThread(NULL)
{
memset(m_szDirectory, 0, sizeof(m_szDirectory));
m_pThread = AfxBeginThread(ThreadProc, NULL, 0, CREATE_SUSPENDED, 0, NULL);
if(NULL == m_pThread)
{
TRACE("Error Code : %d\n", GetLastError());
return ;
}
m_pThread->m_bAutoDelete = FALSE;
m_pThread->ResumeThread();
}
CDirectoryWatch::~CDirectoryWatch()
{
if(INVALID_HANDLE_VALUE != m_hFile)
{
CloseHandle(m_hFile);
m_hFile = INVALID_HANDLE_VALUE;
}
if((NULL != m_pThread) && (NULL != m_pThread->m_hThread))
{
m_pThread->PostThreadMessage(MSG_EXITTHREAD, 0, 0);
WaitForSingleObject(m_pThread->m_hThread, INFINITE);
delete m_pThread;
m_pThread = NULL;
}
}
BOOL CDirectoryWatch::StartDirectoryWatch(LPCTSTR lpszDirectory, PFN_NotifyAction pFn_NotifyAction)
{
if(NULL == m_pThread)
{
return FALSE;
}
if(NULL == lpszDirectory)
{
return FALSE;
}
if(NULL == pFn_NotifyAction)
{
return FALSE;
}
if(!PathFileExists(lpszDirectory))
{
TRACE("Error Code : %d\n", GetLastError());
return FALSE;
}
if(!PathIsDirectory(lpszDirectory))
{
TRACE("Error Code : %d\n", GetLastError());
return FALSE;
}
if(0 == _tcslen(m_szDirectory))
{
StringCchPrintf(m_szDirectory, _countof(m_szDirectory), _T("%s"), lpszDirectory);
}
else if(CSTR_EQUAL != CompareStringOrdinal(m_szDirectory, -1, lpszDirectory, -1, TRUE))
{
TRACE("Not Change Directory.\n");
return FALSE;
}
if(INVALID_HANDLE_VALUE == m_hFile)
{
m_hFile = CreateFile(lpszDirectory, FILE_LIST_DIRECTORY, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, NULL);
if(INVALID_HANDLE_VALUE == m_hFile)
{
TRACE("Error Code : %d\n", GetLastError());
return FALSE;
}
}
return m_pThread->PostThreadMessage(MSG_STARTWATCH, (WPARAM)m_hFile, (LPARAM)pFn_NotifyAction);
}
BOOL CDirectoryWatch::StopDirectoryWatch()
{
if(NULL != m_pThread)
{
return m_pThread->PostThreadMessage(MSG_STOPWATCH, 0, 0);
}
return FALSE;
}
UINT __cdecl CDirectoryWatch::DirectoryWatch(LPVOID lParam)
{
WATCH_PARAMETERS* pParam = (WATCH_PARAMETERS*)lParam;
if(NULL == pParam)
{
return 0;
}
HANDLE& hFile = pParam->hFile;
BYTE* pBuffer = pParam->pBuffer;
DWORD dwBufferSize = pParam->dwBufferSize;
OVERLAPPED& ol = pParam->ol;
HANDLE& hEvent = pParam->hEvent;
BOOL& bExit = pParam->bExit;
PFN_NotifyAction pFn_NotifyAction = pParam->pFn_NotifyAction;
DWORD dwBytesReturn = 0;
DWORD dwRet = WAIT_FAILED;
DWORD dwOffSet = 0;
TCHAR szFile[MAX_PATH] = {0};
while(TRUE)
{
if(WAIT_OBJECT_0 != WaitForSingleObject(hEvent, INFINITE))
{
TRACE("Error Code : %d\n", GetLastError());
break;
}
if(bExit)
{
break;
}
if(!ReadDirectoryChangesW(hFile, pBuffer, dwBufferSize, TRUE,
FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_ATTRIBUTES
| FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_LAST_ACCESS
| FILE_NOTIFY_CHANGE_CREATION | FILE_NOTIFY_CHANGE_SECURITY, &dwBytesReturn, &ol, NULL))
{
TRACE("Error Code : %d\n", GetLastError());
break;
}
if(!GetOverlappedResult(hFile, &ol, &dwBytesReturn, TRUE))
{
TRACE("Error Code : %d\n", GetLastError());
break;
}
FILE_NOTIFY_INFORMATION* pFileNotify = (FILE_NOTIFY_INFORMATION*)pBuffer;
do
{
if(pFn_NotifyAction && (WAIT_OBJECT_0 == WaitForSingleObject(hEvent, 0)))
{
pFn_NotifyAction(pFileNotify->Action, pFileNotify->FileName, (pFileNotify->FileNameLength) / sizeof(WCHAR));
}
dwOffSet = pFileNotify->NextEntryOffset;
pFileNotify = (FILE_NOTIFY_INFORMATION*)((BYTE*)pFileNotify + dwOffSet);
} while (dwOffSet);
}
TRACE0("DirectoryWatch Thread Exit ... \n");
return 0;
}
UINT __cdecl CDirectoryWatch::ThreadProc(LPVOID lParam)
{
WATCH_PARAMETERS* pParam = new WATCH_PARAMETERS;
if(NULL == pParam)
{
goto __CLEANUP__;
}
BYTE* pBuffer = new BYTE[MAX_BUFFER_SIZE];
if(NULL == pBuffer)
{
goto __CLEANUP__;
}
memset(pBuffer, 0, MAX_BUFFER_SIZE);
pParam->pBuffer = pBuffer;
pParam->dwBufferSize = MAX_BUFFER_SIZE;
HANDLE hWatchEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if(NULL == hWatchEvent)
{
goto __CLEANUP__;
}
pParam->ol.hEvent = hWatchEvent;
CWinThread* pThread = NULL;
HANDLE hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if(NULL == hEvent)
{
goto __CLEANUP__;
}
pParam->hEvent = hEvent;
MSG msg;
while(GetMessage(&msg, NULL, 0, 0))
{
switch(msg.message)
{
case MSG_STARTWATCH:
{
HANDLE hFile = (HANDLE)(msg.wParam);
PFN_NotifyAction pFn_NotifyAction = (PFN_NotifyAction)(msg.lParam);
if((INVALID_HANDLE_VALUE == hFile) && (NULL == pFn_NotifyAction))
{
break;
}
if(NULL == pThread)
{
pParam->hFile = hFile;
pParam->pFn_NotifyAction = pFn_NotifyAction;
pThread = AfxBeginThread(DirectoryWatch, (LPVOID)pParam, 0, CREATE_SUSPENDED, NULL);
if(NULL == pThread)
{
goto __CLEANUP__;
}
pThread->m_bAutoDelete = FALSE;
pThread->ResumeThread();
}
SetEvent(hEvent);
}
break;
case MSG_STOPWATCH:
{
ResetEvent(hEvent);
}
break;
case MSG_EXITTHREAD:
{
SetEvent(hEvent);
pParam->bExit = FALSE;
if((NULL != pThread) && (NULL != pThread->m_hThread))
{
WaitForSingleObject(pThread->m_hThread, INFINITE);
delete pThread;
pThread = NULL;
}
goto __CLEANUP__;
}
default:
break;
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
__CLEANUP__:
if(NULL != hWatchEvent)
{
CloseHandle(hWatchEvent);
hWatchEvent = NULL;
}
if(NULL != pBuffer)
{
delete[] pBuffer;
pBuffer = NULL;
}
if(NULL != pParam)
{
delete pParam;
pParam = NULL;
}
TRACE0("ThreadProc Thread Exit ...\n");
return 0;
}
測試代碼
// 測試代碼
#include "stdafx.h"
#include "DirectoryWatch.h"
void NotifyAction(DWORD dwAction, LPWSTR szFile, DWORD dwLength)
{
switch(dwAction)
{
case FILE_ACTION_ADDED:
wprintf(L"FILE_ACTION_ADDED: \n\t");
break;
case FILE_ACTION_REMOVED:
wprintf(L"FILE_ACTION_REMOVED: \n\t");
break;
case FILE_ACTION_MODIFIED:
wprintf(L"FILE_ACTION_MODIFIED: \n\t");
break;
case FILE_ACTION_RENAMED_OLD_NAME:
wprintf(L"FILE_ACTION_RENAMED_OLD_NAME: \n\t");
break;
case FILE_ACTION_RENAMED_NEW_NAME:
wprintf(L"FILE_ACTION_RENAMED_NEW_NAME: \n\t");
break;
default:
break;
}
WCHAR szPath[MAX_PATH] = {0};
wmemcpy(szPath, szFile, min(dwLength, MAX_PATH));
wprintf(L"%s\n", szPath);
}
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
CDirectoryWatch watch;
wprintf(L"Start Directory Watch ...\n");
watch.StartDirectoryWatch(_T("F:\\11"), NotifyAction);
Sleep(30 * 1000);
watch.StopDirectoryWatch();
wprintf(L"Stop Directory Watch ...\n");
Sleep(10 * 1000);
wprintf(L"Start Directory Watch ...\n");
watch.StartDirectoryWatch(_T("F:\\11"), NotifyAction);
Sleep(30 * 1000);
watch.StopDirectoryWatch();
wprintf(L"Stop Directory Watch ...\n");
Sleep(30 * 1000);
wprintf(L"Process Exit ...\n");
return 0;
}
效果如下圖所示:
使用ReadDirectoryChangesW API監(jiān)控文件系統(tǒng)的改變
在C++中若想要監(jiān)控檔案系統(tǒng)改變有很多方法,可以用FindFirstChangeNotification取得檔案變更、或是Hook底層的API等方法來實現(xiàn),這邊使用ReadDirectoryChangesW API來實現(xiàn),該API使用前必須先加入Kernel32.lib。
并加入Windows.h的標頭檔
#include "Windows.h"
這些步驟做完后在程式中就可以看到ReadDirectoryChangesW API了,其函式原型如下:
BOOL WINAPI ReadDirectoryChangesW( __in HANDLE hDirectory, __out LPVOID lpBuffer, __in DWORD nBufferLength, __in BOOL bWatchSubtree, __in DWORD dwNotifyFilter, __out_opt LPDWORD lpBytesReturned, __inout_opt LPOVERLAPPED lpOverlapped, __in_opt LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine );
該API必須帶入八個參數(shù),hDirectory帶入的是要監(jiān)控的目錄Handle、lpBuffer帶入的是用來回傳變動資料的空間、nBufferLength是lpBuffer空間的大小、bWatchSubtree是指定是否偵測子目錄、dwNotifyFilter是指定監(jiān)控的目錄有哪些動作時需要通知、lpBytesReturned是用來回傳變動資料內(nèi)含的長度、lpOverlapped可用來在非同步環(huán)境下使用重疊IO用、lpCompletionRoutine則是當監(jiān)控完成或取消時所呼叫的回調(diào)函式。
其中dwNotifyFilter的值可設定的有FILE_NOTIFY_CHANGE_FILE_NAME、FILE_NOTIFY_CHANGE_DIR_NAME、FILE_NOTIFY_CHANGE_ATTRIBUTES、FILE_NOTIFY_CHANGE_SIZE、FILE_NOTIFY_CHANGE_LAST_WRITE、FILE_NOTIFY_CHANGE_LAST_ACCESS、FILE_NOTIFY_CHANGE_CREATION、與FILE_NOTIFY_CHANGE_SECURITY,詳細所代表的意義可參閱ReadDirectoryChangesW function
了解了函式原型后,就可以開始進入實際的使用。剛有提到說在ReadDirectoryChangesW API函式必須要帶入的第一個參數(shù)是要監(jiān)控的目錄Handle,所以我們必須透過CreateFile API取得要監(jiān)控的目錄Handle,像是下面這樣:
HANDLE hDirectoryHandle = NULL; hDirectoryHandle = ::CreateFileA( file, FILE_LIST_DIRECTORY, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, NULL); if(hDirectoryHandle == INVALID_HANDLE_VALUE) return;
取得監(jiān)控的目錄Handle后,將其帶入ReadDirectoryChangesw API,順帶帶入像是回傳變動資料的Buffer空間、與要監(jiān)控的變動類型等必要參數(shù)。像是下面這樣:
int nBufferSize = 1024;
char* buffer = new char[nBufferSize];
DWORD dwBytes = 0;
memset(buffer, 0, nBufferSize);
if(!::ReadDirectoryChangesW(
hDirectoryHandle,
buffer,
nBufferSize,
bIncludeSubdirectories,
FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_CREATION | FILE_NOTIFY_CHANGE_FILE_NAME,
&dwBytes,
NULL,
NULL) || GetLastError() == ERROR_INVALID_HANDLE)
{
break;
}
if(!dwBytes)
{
printf("Buffer overflow~~\r\n");
}
這邊需注意到的是,若是變動的資料太多,提供的存儲空間不足以存放時,回傳的變動資料長度會是0,此時所有變動資料都會丟失。這樣的情況多半只會出在一瞬間大量的變動,可以增大存儲空間或是減少監(jiān)控的變動類型,以減少回傳的資料量,避免溢位的發(fā)生。
若是運行沒發(fā)生問題,變動的資料會存放在當初塞進去的存儲空間,該空間的資料其實是FILE_NOTIFY_INFORMATION structure的型態(tài)存在,因此我們可將存儲空間的資料轉(zhuǎn)換成PFILE_NOTIFY_INFORMATION。裡面的Action是我們所關注的變動類型,F(xiàn)ileName是變動的檔案名稱,檔案名稱的部分是沒有結尾符號的,必須要搭配FileNameLength去截取。另外變動的資料有時候不止一筆,因此我們必須在這邊用迴圈搭配NextEntryOffset去重覆運行處理流程,處理所有變動的資料。
PFILE_NOTIFY_INFORMATION record = (PFILE_NOTIFY_INFORMATION)buffer;
DWORD cbOffset = 0;
do
{
switch (record->Action)
{
case FILE_ACTION_ADDED:
printf("FILE_ACTION_ADDED:");
break;
case FILE_ACTION_REMOVED:
printf("FILE_ACTION_REMOVED:");
break;
case FILE_ACTION_MODIFIED:
printf("FILE_ACTION_MODIFIED:");
break;
case FILE_ACTION_RENAMED_OLD_NAME:
printf("FILE_ACTION_RENAMED_OLD_NAME:");
break;
case FILE_ACTION_RENAMED_NEW_NAME:
printf("FILE_ACTION_RENAMED_NEW_NAME:");
break;
default:
break;
}
char fileBuffer[512];
WideCharToMultiByte(CP_ACP, 0, record->FileName, record->FileNameLength, fileBuffer, record->FileNameLength, NULL, NULL);
printf(fileBuffer);
printf("\r\n");
cbOffset = record->NextEntryOffset;
record = (PFILE_NOTIFY_INFORMATION)((LPBYTE) record + cbOffset);
}while(cbOffset);
這邊示范一個簡易的使用范例,實際使用時最好還是搭配執(zhí)行緒處理:
// ConsoleApplication10.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include "Windows.h"
void MonitorDir(char* file, bool bIncludeSubdirectories = false)
{
int nBufferSize = 1024;
char* buffer = new char[nBufferSize];
HANDLE hDirectoryHandle = NULL;
hDirectoryHandle = ::CreateFileA(
file,
FILE_LIST_DIRECTORY,
FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE,
NULL,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS
| FILE_FLAG_OVERLAPPED,
NULL);
if(hDirectoryHandle == INVALID_HANDLE_VALUE)
return;
while(1)
{
DWORD dwBytes = 0;
memset(buffer, 0, nBufferSize);
if(!::ReadDirectoryChangesW(
hDirectoryHandle,
buffer,
nBufferSize,
bIncludeSubdirectories,
FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_CREATION | FILE_NOTIFY_CHANGE_FILE_NAME,
&dwBytes,
NULL,
NULL) || GetLastError() == ERROR_INVALID_HANDLE)
{
break;
}
if(!dwBytes)
{
printf("Buffer overflow~~\r\n");
}
PFILE_NOTIFY_INFORMATION record = (PFILE_NOTIFY_INFORMATION)buffer;
DWORD cbOffset = 0;
do
{
switch (record->Action)
{
case FILE_ACTION_ADDED:
printf("FILE_ACTION_ADDED:");
break;
case FILE_ACTION_REMOVED:
printf("FILE_ACTION_REMOVED:");
break;
case FILE_ACTION_MODIFIED:
printf("FILE_ACTION_MODIFIED:");
break;
case FILE_ACTION_RENAMED_OLD_NAME:
printf("FILE_ACTION_RENAMED_OLD_NAME:");
break;
case FILE_ACTION_RENAMED_NEW_NAME:
printf("FILE_ACTION_RENAMED_NEW_NAME:");
break;
default:
break;
}
char fileBuffer[512];
WideCharToMultiByte(CP_ACP, 0, record->FileName, record->FileNameLength, fileBuffer, record->FileNameLength, NULL, NULL);
printf(fileBuffer);
printf("\r\n");
cbOffset = record->NextEntryOffset;
record = (PFILE_NOTIFY_INFORMATION)((LPBYTE) record + cbOffset);
}while(cbOffset);
}
delete buffer;
if(hDirectoryHandle)
CloseHandle(hDirectoryHandle);
}
int _tmain(int argc, _TCHAR* argv[])
{
MonitorDir("C:\\Users\\larry\\Desktop\\新增資料夾");
運行后去對監(jiān)控的目錄操作~可得到類似如下的結果:
欄 目:C語言
下一篇:C++中map和vector作形參時如何給定默認參數(shù)?
本文標題:VC++文件監(jiān)控之ReadDirectoryChangesW
本文地址:http://www.jygsgssxh.com/a1/Cyuyan/370.html
您可能感興趣的文章
- 01-10fatal error LNK1104: 無法打開文件“l(fā)ibc.lib”的解決方法
- 01-10顯示任何進程加載的DLL文件的代碼
- 01-10深入探討linux下進程的最大線程數(shù)、進程最大數(shù)、進程打開的文
- 01-10使用Inotify 監(jiān)控目錄與文件的方法詳解
- 01-10用c 獲取文件MD5值的實現(xiàn)方法
- 01-10用C實現(xiàn)添加和讀取配置文件函數(shù)
- 01-10在vs2010中,輸出當前文件路徑與源文件當前行號的解決方法
- 01-10C++中簡單讀寫文本文件的實現(xiàn)方法
- 01-10C語言文件操作函數(shù)大全(超詳細)
- 01-10深入探討:linux中遍歷文件夾下的所有文件


閱讀排行
本欄相關
- 04-02c語言函數(shù)調(diào)用后清空內(nèi)存 c語言調(diào)用
- 04-02func函數(shù)+在C語言 func函數(shù)在c語言中
- 04-02c語言的正則匹配函數(shù) c語言正則表達
- 04-02c語言用函數(shù)寫分段 用c語言表示分段
- 04-02c語言中對數(shù)函數(shù)的表達式 c語言中對
- 04-02c語言編寫函數(shù)冒泡排序 c語言冒泡排
- 04-02c語言沒有round函數(shù) round c語言
- 04-02c語言分段函數(shù)怎么求 用c語言求分段
- 04-02C語言中怎么打出三角函數(shù) c語言中怎
- 04-02c語言調(diào)用函數(shù)求fibo C語言調(diào)用函數(shù)求
隨機閱讀
- 01-10delphi制作wav文件的方法
- 08-05DEDE織夢data目錄下的sessions文件夾有什
- 01-10C#中split用法實例總結
- 04-02jquery與jsp,用jquery
- 01-11ajax實現(xiàn)頁面的局部加載
- 01-10使用C語言求解撲克牌的順子及n個骰子
- 01-11Mac OSX 打開原生自帶讀寫NTFS功能(圖文
- 01-10SublimeText編譯C開發(fā)環(huán)境設置
- 08-05織夢dedecms什么時候用欄目交叉功能?
- 08-05dedecms(織夢)副欄目數(shù)量限制代碼修改


