📄 dwatchdlg.cpp
字号:
// dwatchDlg.cpp : implementation file
//
#include "stdafx.h"
#include "winsvc.h" // Fuer Admi-Test
#include "dwatch.h"
#include "optionsdialog.h"
#include "dwatchDlg.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
// Undokumentierte Funktion aus ntdll.dll
// Parameters
// ----------
// 1. FileHandle
// 2. EventHandle (OPTIONAL)
// 3. &ApcRoutine (OPTIONAL)
// 4. &ApcContext (OPTIONAL)
// 5. &IO_STATUS_BLOCK (2 DWORDS)
// 6. &buffer (FILE_NOTIFY_INFORMATION)
// 7. BufferLength
// 8. Flags
// 9. BOOL -> Subdirectories ueberwachen oder nicht
extern "C"
DWORD __stdcall NtNotifyChangeDirectoryFile (HANDLE, HANDLE, PVOID, PVOID, PVOID, PVOID, DWORD, DWORD, BOOL);
// Watch-Handles
HANDLE watchhandle[8];
// Watch-Info fuer den jeweiligen Thread
DWORD threadinfo[8];
// MFC-Threads
CWinThread *threads[8];
// Thread-synchronisation
CRITICAL_SECTION csection;
////////////////////////////////////////////////////////////////////////////
// EXTRA THREADS --- not class members
// Wartet die initialisierung dieser App ab
// ... und laesst dann die Options-Box erscheinen
// Ausserdem wird die Groesse des Fensters eingestellt
void WaitFunc (CDwatchDlg *cdw)
{
WaitForInputIdle (GetCurrentProcess(), 10000);
HWND hwnd = cdw->GetSafeHwnd();
SetWindowPos (hwnd, NULL, 0, 0, 600, 300, SWP_NOMOVE | SWP_NOZORDER);
SendMessage (hwnd, WM_CONTEXTMENU, 0, 0L);
}
// Sorgt dafuer, dass das ListCtrl wieder sichtbar wird
void VisibilityFunc (CDwatchDlg *cdw)
{
SetThreadPriority (GetCurrentThread(), THREAD_PRIORITY_IDLE);
Sleep (2000);
for (;;)
{
Sleep (2000);
if (cdw->m_listctrl.GetSafeHwnd() && cdw->m_visibility == FALSE)
{
cdw->m_listctrl.SendMessage (WM_SETREDRAW, TRUE, 0L);
cdw->m_listctrl.InvalidateRect (NULL);
cdw->m_listctrl.UpdateWindow();
cdw->m_visibility = TRUE;
}
}
}
// Jeder einzelne "Watch" wird als Thread ausgef黨rt
void Threadfunc (CDwatchDlg *cdw)
{
// Thread findet sich selbst
int s;
int tnum = -1;
for (s=0; s<8; s++)
{
if (threads[s])
{
if (threads[s]->m_nThreadID == GetCurrentThreadId())
{
tnum = s;
break;
}
}
}
// WatchHandle oeffnen
watchhandle[tnum] = CreateFile (cdw->m_options.m_dirname,
FILE_LIST_DIRECTORY,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS,
NULL);
BYTE buffer[1024];
DWORD io_status_block[2];
for (;;)
{
ZeroMemory (buffer, sizeof (buffer));
NtNotifyChangeDirectoryFile (watchhandle[tnum],
NULL,
NULL,
NULL,
&io_status_block,
&buffer,
sizeof (buffer),
threadinfo[tnum],
TRUE);
WaitForSingleObject (watchhandle[tnum], INFINITE);
EnterCriticalSection (&csection);
cdw->SetEntry (tnum, (FILE_NOTIFY_INFORMATION*)buffer);
LeaveCriticalSection (&csection);
}
}
/////////////////////////////////////////////////////////////////////////////
// HelperFuncs
// Einen Eintrag im ListCtrl setzen.... wird vom jeweiligen Thread gemacht
// ...buffer zeigt auf eine struct -> FILE_NOTIFY_INFORMATION
// TIME-CRITICAL !!!!
void CDwatchDlg::SetEntry (int num, FILE_NOTIFY_INFORMATION *buffer)
{
// Local vars
static SYSTEMTIME st;
static char string[256];
static int numitems;
static char *action;
static int s;
// ListCtrl abschalten, damit die Threads schneller sind...
if (m_visibility)
{
m_visibility = FALSE;
m_listctrl.SendMessage (WM_SETREDRAW, FALSE, 0L);
}
numitems = m_listctrl.GetItemCount();
// Zeit eintragen
GetLocalTime (&st);
sprintf (string, "%02d:%02d:%02d", st.wHour, st.wMinute, st.wSecond);
m_listctrl.InsertItem (numitems, string);
// Thread-info eintragen
m_listctrl.SetItemText (numitems, 1, m_infotxt[num]);
// Aktion eintragen
switch (buffer->Action)
{
case FILE_ACTION_ADDED:
action = "++ created ++";
break;
case FILE_ACTION_REMOVED:
action = "-- removed --";
break;
case FILE_ACTION_MODIFIED:
action = "< modified >";
break;
case FILE_ACTION_RENAMED_OLD_NAME:
action = "renaming...";
break;
case FILE_ACTION_RENAMED_NEW_NAME:
action = "...renamed";
break;
default:
action = "Unknown";
break;
}
m_listctrl.SetItemText (numitems, 2, action);
// File/Directory-Namen eintragen
for (s=0; s<(int)buffer->FileNameLength; s++)
string[s] = (char)buffer->FileName[s];
string[s+1] = (char)0;
m_listctrl.SetItemText (numitems, 3, string);
// Eventuell alles noch einmal
if (buffer->NextEntryOffset)
SetEntry (num, (FILE_NOTIFY_INFORMATION*)((BYTE*)buffer + buffer->NextEntryOffset));
}
// Threads Killen und zugehoerige Watchhandles schliessen
// ... und Critical-Section-Objekt erneuern
void CDwatchDlg::KillAllThreads (void)
{
int s;
for (s=0; s<8; s++)
{
if (threads[s])
{
HANDLE hthread = threads[s]->m_hThread;
TerminateThread (hthread, 0);
delete threads[s];
CloseHandle (hthread);
CloseHandle (watchhandle[s]);
threads[s] = NULL;
}
}
DeleteCriticalSection (&csection);
InitializeCriticalSection (&csection);
}
// Titelzeile aktualisieren
void CDwatchDlg::UpdateTitle (BOOL flag)
{
CString cs = "Directory Watchdog";
if (flag)
cs = cs + " <" + m_options.m_dirname + ">";
else
cs = cs + " <stopped>";
SetWindowText (cs);
}
/////////////////////////////////////////////////////////////////////////////
// CDwatchDlg dialog
CDwatchDlg::CDwatchDlg(CWnd* pParent /*=NULL*/)
: CDialog(CDwatchDlg::IDD, pParent)
{
//{{AFX_DATA_INIT(CDwatchDlg)
// NOTE: the ClassWizard will add member initialization here
//}}AFX_DATA_INIT
// Auf Admi testen
SC_HANDLE sch = OpenSCManager (NULL, NULL, SC_MANAGER_ALL_ACCESS);
if (!sch)
{
MessageBox ("You must log on as an administrator to use this tool...",
"Sorry",
MB_ICONSTOP);
ExitProcess (0);
}
CloseServiceHandle (sch);
// Note that LoadIcon does not require a subsequent DestroyIcon in Win32
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
// CWinThread-Pointers und Watchhandles loeschen
int s;
for (s=0; s<8; s++)
{
threads[s] = NULL;
watchhandle[s] = NULL;
}
// Critical Section anlegen
InitializeCriticalSection (&csection);
// WatchInfo-Flags laden
threadinfo[0] = FILE_NOTIFY_CHANGE_LAST_ACCESS;
threadinfo[1] = FILE_NOTIFY_CHANGE_ATTRIBUTES;
threadinfo[2] = FILE_NOTIFY_CHANGE_DIR_NAME;
threadinfo[3] = FILE_NOTIFY_CHANGE_FILE_NAME;
threadinfo[4] = FILE_NOTIFY_CHANGE_SECURITY;
threadinfo[5] = FILE_NOTIFY_CHANGE_SIZE;
threadinfo[6] = FILE_NOTIFY_CHANGE_CREATION;
threadinfo[7] = FILE_NOTIFY_CHANGE_LAST_WRITE;
// WatchInfo-Texte laden
m_infotxt[0] = "Read";
m_infotxt[1] = "Attributes";
m_infotxt[2] = "Directory";
m_infotxt[3] = "File";
m_infotxt[4] = "Security";
m_infotxt[5] = "Size";
m_infotxt[6] = "TimeStamp";
m_infotxt[7] = "Write";
m_visibility = TRUE;
// Nachtr鋑liche Initialisierung starten
AfxBeginThread ((AFX_THREADPROC)WaitFunc, this);
// ListCtrl-Updater starten
m_updater = AfxBeginThread ((AFX_THREADPROC)VisibilityFunc, this);
}
void CDwatchDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CDwatchDlg)
DDX_Control(pDX, IDC_LIST1, m_listctrl);
//}}AFX_DATA_MAP
}
BEGIN_MESSAGE_MAP(CDwatchDlg, CDialog)
//{{AFX_MSG_MAP(CDwatchDlg)
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_WM_SIZE()
ON_WM_CONTEXTMENU()
ON_WM_CLOSE()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CDwatchDlg message handlers
BOOL CDwatchDlg::OnInitDialog()
{
CDialog::OnInitDialog();
// Set the icon for this dialog. The framework does this automatically
// when the application's main window is not a dialog
SetIcon(m_hIcon, TRUE); // Set big icon
// ListControl vorbereiten
char *ColTxt[] = { "Time", "File Access", "Action", "Filename"};
int i;
LV_COLUMN lvc;
lvc.mask=LVCF_FMT | LVCF_TEXT | LVCF_SUBITEM;
lvc.iSubItem=-1;
for(i=0; i<4; i++)
{
lvc.pszText = ColTxt[i];
lvc.fmt = LVCFMT_LEFT;
m_listctrl.InsertColumn (i,&lvc);
}
return TRUE; // return TRUE unless you set the focus to a control
}
// If you add a minimize button to your dialog, you will need the code below
// to draw the icon. For MFC applications using the document/view model,
// this is automatically done for you by the framework.
void CDwatchDlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // device context for painting
SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);
// Center icon in client rectangle
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
// Draw the icon
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialog::OnPaint();
}
}
// The system calls this to obtain the cursor to display while the user drags
// the minimized window.
HCURSOR CDwatchDlg::OnQueryDragIcon()
{
return (HCURSOR) m_hIcon;
}
// ListCtrl anpassen
void CDwatchDlg::OnSize(UINT nType, int cx, int cy)
{
CDialog::OnSize(nType, cx, cy);
if (m_listctrl.GetSafeHwnd())
{
int ptx = cx/6;
m_listctrl.MoveWindow (0,0, cx, cy);
m_listctrl.SetColumnWidth (0, ptx);
m_listctrl.SetColumnWidth (1, ptx);
m_listctrl.SetColumnWidth (2, ptx);
m_listctrl.SetColumnWidth (3, cx-ptx-ptx-ptx);
}
}
void CDwatchDlg::OnContextMenu(CWnd* pWnd, CPoint point)
{
switch (m_options.DoModal())
{
// Threads starten, alte Killen
// Die komische Prozedur von wegen Thread suspended createn und
// dann zu resumen ist noetig, damit die CWinThread-Pointer gueltige
// Werte haben, wenn die Threads loslegen.....
case 0:
KillAllThreads();
UpdateTitle (TRUE);
if (m_options.m_access)
{
threads[0] = AfxBeginThread ((AFX_THREADPROC)Threadfunc,
this,
THREAD_PRIORITY_TIME_CRITICAL,
0,
CREATE_SUSPENDED);
threads[0]->ResumeThread();
}
if (m_options.m_attributes)
{
threads[1] = AfxBeginThread ((AFX_THREADPROC)Threadfunc,
this,
THREAD_PRIORITY_TIME_CRITICAL,
0,
CREATE_SUSPENDED);
threads[1]->ResumeThread();
}
if (m_options.m_dirnames)
{
threads[2] = AfxBeginThread ((AFX_THREADPROC)Threadfunc,
this,
THREAD_PRIORITY_TIME_CRITICAL,
0,
CREATE_SUSPENDED);
threads[2]->ResumeThread();
}
if (m_options.m_filenames)
{
threads[3] = AfxBeginThread ((AFX_THREADPROC)Threadfunc,
this,
THREAD_PRIORITY_TIME_CRITICAL,
0,
CREATE_SUSPENDED);
threads[3]->ResumeThread();
}
if (m_options.m_security)
{
threads[4] = AfxBeginThread ((AFX_THREADPROC)Threadfunc,
this,
THREAD_PRIORITY_TIME_CRITICAL,
0,
CREATE_SUSPENDED);
threads[4]->ResumeThread();
}
if (m_options.m_size)
{
threads[5] = AfxBeginThread ((AFX_THREADPROC)Threadfunc,
this,
THREAD_PRIORITY_TIME_CRITICAL,
0,
CREATE_SUSPENDED);
threads[5]->ResumeThread();
}
if (m_options.m_tstamp)
{
threads[6] = AfxBeginThread ((AFX_THREADPROC)Threadfunc,
this,
THREAD_PRIORITY_TIME_CRITICAL,
0,
CREATE_SUSPENDED);
threads[6]->ResumeThread();
}
if (m_options.m_write)
{
threads[7] = AfxBeginThread ((AFX_THREADPROC)Threadfunc,
this,
THREAD_PRIORITY_TIME_CRITICAL,
0,
CREATE_SUSPENDED);
threads[7]->ResumeThread();
}
break;
// Threads stoppen/loeschen
case 1:
KillAllThreads();
UpdateTitle (FALSE);
break;
// Display loeschen
case 2:
m_listctrl.DeleteAllItems();
break;
// Absaven
case 3:
{
CFileDialog cf (FALSE, NULL, "*.*");
cf.m_ofn.Flags &= ~OFN_EXPLORER;
if (cf.DoModal() == IDOK)
{
CString cs = cf.GetPathName();
CFile cfile(cs, CFile::modeCreate | CFile::modeWrite);
cs.Format ("Directory scan of: %s\n\n", m_options.m_dirname);
cfile.Write (cs, cs.GetLength());
int s;
for (s=0; s<m_listctrl.GetItemCount(); s++)
{
cs.Format ("%-20s %-20s %-20s %s\n",
m_listctrl.GetItemText (s, 0),
m_listctrl.GetItemText (s, 1),
m_listctrl.GetItemText (s, 2),
m_listctrl.GetItemText (s, 3));
cfile.Write (cs, cs.GetLength());
}
cfile.Close();
}
}
break;
}
}
// Threads killen, sonst Haenger!!!
void CDwatchDlg::OnClose()
{
KillAllThreads();
TerminateThread (m_updater->m_hThread, 0);
delete m_updater;
CDialog::OnClose();
}
// Return-Taste -> Options-Dialog starten
void CDwatchDlg::OnOK()
{
OnContextMenu (NULL, 0);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -