9

जब भी कोई परिवर्तन पता चला है तो मैं एक निर्दिष्ट निर्देशिका देखने के लिए ReadDirectoryChangesW का उपयोग करता हूं और इंडेक्सिंग संरचनाओं को अद्यतन करता हूं।ReadDirectoryChangesW घटनाओं को क्यों छोड़ता है?

FDirHandle := CreateFile (PChar (FDirectoryWatch.WatchedDirectory), 
          FILE_LIST_DIRECTORY or GENERIC_READ, 
          FILE_SHARE_READ or FILE_SHARE_WRITE or FILE_SHARE_DELETE, 
          nil, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS or 
          FILE_FLAG_OVERLAPPED, 0);   

मैं एकाधिक फ़ाइलों को हटाने मैं केवल एक घटना मिलता है: मैं निम्नलिखित कोड (मोटे तौर पर)

var 
    InfoPointer : PFileNotifyInformation; 
    NextOffset : DWORD; 
... 
while (not Terminated) do begin 
    if ReadDirectoryChangesW (FDirHandle, FBuffer, FBufferLength, True, 
          FFilter, @BytesRead, @FOverlap, nil) then 
    begin 
    WaitResult := WaitForMultipleObjects (2, @FEventArray, False, INFINITE); 
    if (WaitResult = waitFileChange) then 
     begin 
     InfoPointer := FBuffer; 
     repeat 
     NextOffset := InfoPointer.NextEntryOffset; 
     ... 
     PByte (InfoPointer) := PByte (InfoPointer) + NextOffset; 
     until NextOffset = 0; 
     end; 
    end; 
end; 

फ़िल्टर

FFilter := FILE_NOTIFY_CHANGE_FILE_NAME or 
      FILE_NOTIFY_CHANGE_DIR_NAME or 
      FILE_NOTIFY_CHANGE_SIZE or 
      FILE_NOTIFY_CHANGE_LAST_WRITE; 

है और निर्देशिका संभाल इस तरह प्राप्त किया जाता है का उपयोग करें और नेक्स्टऑफसेट 0 है! और जब मैं एक निर्देशिका हटाता हूं तो मुझे निर्देशिका के लिए केवल एक ईवेंट मिलता है। अगर मैं निर्देशिका में प्रत्येक फाइल के लिए एक घटना चाहता हूं तो क्या होगा?

किसी भी मदद की सराहना की जाएगी।

उत्तर

15

यह है कि आप विभिन्न तरीकों से मिश्रण कर रहे हैं ReadDirectoryChangesW() उपयोग करने के लिए मुझे लगता है, आप दोनों फ़ाइल _ फ्लैग _ ओवरलैप झंडा जब निर्देशिका खोलने निर्दिष्ट करें और करने के लिए lpOverlapped पैरामीटर एक सूचक प्रदान करते हैं , जिसका अर्थ है कि आप संरचना में घटना पर इंतजार करना चाहते हैं और एसिंक्रोनस I/O को संभालना चाहते हैं; और साथ ही आप कार्यकर्ता धागे में एक लूप में ReadDirectoryChangesW() पर कॉल करें। मैं पहले lpOverlappedशून्य पर फिर से प्रयास करता हूं, क्योंकि आपके पास समर्पित थ्रेड है और सिंक्रोनस मोड का उपयोग कर सकता है।

ReadDirectoryChangesW() एपीआई के दस्तावेज में इसका उपयोग करने के विभिन्न तरीकों का वर्णन किया गया है। ध्यान दें कि यह भी संभव है कि बफर ओवरफ्लो हो, इसलिए घटनाओं को बदलना वैसे भी खोया जा सकता है। शायद आपको इस फ़ंक्शन पर पूरी तरह भरोसा करने की अपनी रणनीति पर पुनर्विचार करना चाहिए, निर्देशिका सामग्री के स्नैपशॉट की तुलना करना भी काम कर सकता है।

संपादित करें:

आपके संपादित कोड बेहतर लग रहा है। मेरे परीक्षणों में हालांकि ReadDirectoryChangesW() विज्ञापन के रूप में काम करता था, लौटा बफर में कई डेटा प्रविष्टियां थीं, या प्रक्रिया करने के लिए एक से अधिक बफर थे। यह डेल्फी में ब्रेकपॉइंट मारने के बाद समय पर निर्भर करता है, मुझे एक बफर में कई प्रविष्टियां मिलती हैं।

type 
    TWatcherThread = class(TThread) 
    private 
    fChangeHandle: THandle; 
    fDirHandle: THandle; 
    fShutdownHandle: THandle; 
    protected 
    procedure Execute; override; 
    public 
    constructor Create(ADirectoryToWatch: string); 
    destructor Destroy; override; 

    procedure Shutdown; 
    end; 

constructor TWatcherThread.Create(ADirectoryToWatch: string); 
const 
    FILE_LIST_DIRECTORY = 1; 
begin 
    inherited Create(TRUE); 
    fChangeHandle := CreateEvent(nil, FALSE, FALSE, nil); 
    fDirHandle := CreateFile(PChar(ADirectoryToWatch), 
    FILE_LIST_DIRECTORY or GENERIC_READ, 
    FILE_SHARE_READ or FILE_SHARE_WRITE or FILE_SHARE_DELETE, 
    nil, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS or FILE_FLAG_OVERLAPPED, 0); 
    fShutdownHandle := CreateEvent(nil, FALSE, FALSE, nil); 
    Resume; 
end; 

destructor TWatcherThread.Destroy; 
begin 
    if fDirHandle <> INVALID_HANDLE_VALUE then 
    CloseHandle(fDirHandle); 
    if fChangeHandle <> 0 then 
    CloseHandle(fChangeHandle); 
    if fShutdownHandle <> 0 then 
    CloseHandle(fShutdownHandle); 
    inherited Destroy; 
end; 

procedure TWatcherThread.Execute; 
type 
    PFileNotifyInformation = ^TFileNotifyInformation; 
    TFileNotifyInformation = record 
    NextEntryOffset: DWORD; 
    Action: DWORD; 
    FileNameLength: DWORD; 
    FileName: WideChar; 
    end; 
const 
    BufferLength = 65536; 
var 
    Filter, BytesRead: DWORD; 
    InfoPointer: PFileNotifyInformation; 
    Offset, NextOffset: DWORD; 
    Buffer: array[0..BufferLength - 1] of byte; 
    Overlap: TOverlapped; 
    Events: array[0..1] of THandle; 
    WaitResult: DWORD; 
    FileName, s: string; 
begin 
    if fDirHandle <> INVALID_HANDLE_VALUE then begin 
    Filter := FILE_NOTIFY_CHANGE_FILE_NAME or FILE_NOTIFY_CHANGE_DIR_NAME 
     or FILE_NOTIFY_CHANGE_SIZE or FILE_NOTIFY_CHANGE_LAST_WRITE; 

    FillChar(Overlap, SizeOf(TOverlapped), 0); 
    Overlap.hEvent := fChangeHandle; 

    Events[0] := fChangeHandle; 
    Events[1] := fShutdownHandle; 

    while not Terminated do begin 
     if ReadDirectoryChangesW (fDirHandle, @Buffer[0], BufferLength, TRUE, 
     Filter, @BytesRead, @Overlap, nil) 
     then begin 
     WaitResult := WaitForMultipleObjects(2, @Events[0], FALSE, INFINITE); 
     if WaitResult = WAIT_OBJECT_0 then begin 
      InfoPointer := @Buffer[0]; 
      Offset := 0; 
      repeat 
      NextOffset := InfoPointer.NextEntryOffset; 
      FileName := WideCharLenToString(@InfoPointer.FileName, 
       InfoPointer.FileNameLength); 
      SetLength(FileName, StrLen(PChar(FileName))); 
      s := Format('[%d] Action: %.8xh, File: "%s"', 
       [Offset, InfoPointer.Action, FileName]); 
      OutputDebugString(PChar(s)); 
      PByte(InfoPointer) := PByte(DWORD(InfoPointer) + NextOffset); 
      Offset := Offset + NextOffset; 
      until NextOffset = 0; 
     end; 
     end; 
    end; 
    end; 
end; 

procedure TWatcherThread.Shutdown; 
begin 
    Terminate; 
    if fShutdownHandle <> 0 then 
    SetEvent(fShutdownHandle); 
end; 

//////////////////////////////////////////////////////////////////////////////// 

procedure TForm1.FormCreate(Sender: TObject); 
begin 
    fThread := TWatcherThread.Create('D:\Temp'); 
end; 

procedure TForm1.FormDestroy(Sender: TObject); 
begin 
    if fThread <> nil then begin 
    TWatcherThread(fThread).Shutdown; 
    fThread.Free; 
    end; 
end; 

एक निर्देशिका हटाया जा रहा है वास्तव में केवल इसके लिए एक परिवर्तन, उसमें शामिल फ़ाइलों के लिए कुछ भी नहीं लौट करता है:

पूर्णता के लिए मैं परीक्षण कोड देते हैं, डेल्फी 5 का उपयोग कर कार्यान्वित किया। लेकिन यह समझ में आता है, क्योंकि आप केवल मूल निर्देशिका के हैंडल देख रहे हैं। अगर आपको उपनिर्देशिका के लिए अधिसूचनाओं की आवश्यकता है तो आपको शायद उन्हें भी देखना होगा।

+0

मेरी देरी टिप्पणी के लिए खेद है।मैंने पहले सिंक्रोनस संस्करण का उपयोग किया (ठीक उसी समस्या के साथ) लेकिन एसिंक्रोनस संस्करण में स्विच किया क्योंकि मुझे धागे को साफ करने के लिए कोई रास्ता नहीं मिला। मुझे उदाहरण कोड में एक महत्वपूर्ण पंक्ति याद आई है (हालांकि WaitForMultipleObjects को अवरुद्ध कॉल, जिसे या तो फ़ाइल परिवर्तन ईवेंट द्वारा समाप्त किया जा सकता है या एक समाप्ति घटना द्वारा)। मैं तदनुसार सवाल संपादित करता हूं। (...) – jpfollenius

+0

स्नैपशॉट से आपका क्या मतलब है? यदि आप FindFirst का उपयोग कर सभी फाइलों पर पुनरावृत्ति का मतलब है, FindNext: मैंने पहले इस तरह के एक दृष्टिकोण का उपयोग किया था, लेकिन मैं बड़ी निर्देशिकाओं का उपयोग करते समय (1) बड़ी पहचान का समय और (2) इंडेक्सिंग थ्रेड के लिए स्थिर I/O लोड में देरी से बचने के लिए (1) सभी को धीमा करना चाहता था अन्य I/O संचालन। – jpfollenius

+1

आपकी दूसरी टिप्पणी से सहमत है, लेकिन जैसा कि एमएसडीएन दस्तावेज बताता है कि आपको आंतरिक बफर के अतिप्रवाह के लिए तैयार होने की आवश्यकता है, और उस स्थिति में निर्देशिका का पूर्ण (पुनः) स्कैन आवश्यक है। – mghie

4

हमें घटनाओं को खोने के साथ एक ही समस्या है, खासकर यदि एक ही समय में बहुत से परिवर्तन होते हैं, यानी। निगरानी की गई निर्देशिका में 500 फाइलों की प्रतिलिपि बनाई गई है।

अंत में हमें Cromis मिला और Directory watch का उपयोग करें। हमने कभी वापस नहीं देखा है।

+0

निर्देशिका घड़ी वास्तव में अच्छा है। 64 बिट संगतता के लिए get getWindowLong को getWindowLongPtr (सेट के लिए भी ...) के साथ बदलने की आवश्यकता है – Ampere

संबंधित मुद्दे