21. maj 2005 - 10:19Der er
25 kommentarer og 1 løsning
Vente med at åbne en fil før den er lukket.
Et andet program opretter en fil med nogle oplysninger i som jeg skal bruge. Men jeg vil vente med at åbne filen før det andet program er færdig med at skrive i filen og har lukket den dvs, at programmet ikke har fat i den mere. Er det muligt og hvad skal jeg se efter?
Det andet program er det dig eget lavet program? Hvis det er kunne du evt. fra dette program sende en Message med SendMessage når filen er lukket. Hvis ikke så kik evt. på FindFirstChangeNotification, WaitForSingleObject og FindNextChangeNotification.
Det andet program har jeg ikke lavet. Jeg er ved at lave en service som tjekker i et bibliotek, som jeg bestemmer, efter en bestemt extension men skal først åbne den når det andet program er færdig med at skrive i den. Kan du give et eksempel på hvordan man bruger dem du har nævnt?
Du kan åbne filen exclusive, så ingen andre får læse eller skrive adgang. Så får du nemlig en exception, hvis andre programmer stadig har filen åben. Men hvad sker der så, hvis det andet program forsøger, at skrive i filen mens du har låst den? Kan du havne i den situation?
Du har ikke en chance for, at vide hvornår det andet program er færdig med, at skrive - med mindre filen er låst - enten exclusive eller blot for skrivning.
Hvis filen blot bliver oprettet kan du forsøge følgende:
function GetData(const FileName : string) : boolean; var fs : TFileStream; begin try fs := TFileStream(FileName,fmOpenRead or fmShareExclusive); try FileList.LoadFromStream(fs); Result := True; finally FreeAndNil(fs); end; except Result := False; // Filen var låst - prøv igen om lidt end; end;
function GetData(const FileName : string) : boolean; var fs : TFileStream; begin try fs := TFileStream.Create(FileName,fmOpenRead or fmShareExclusive); try FileList.LoadFromStream(fs); // FileList deklareret andetsteds.. Result := True; finally FreeAndNil(fs); end; except Result := False; // Filen var låst - prøv igen om lidt end; end;
Ja, smid en TButton og en TMemo på en form. Når der sker en ændring i en fils attribut i den mappe der er defineret så vil der blive skrevet 'Der er sket en ændring' i memoen. Der er andre muligheder end FILE_NOTIFY_CHANGE_ATTRIBUTES, se hjælpen eller spørg igen. Du kan teste ved at gemme f.eks. en tekst fil med f.eks. Notepad i den aktuelle mappe, bemærk at af en eller anden grund så vil du få 2x'Der er sket en ændring' når du gemmer med Notepad eller Wordpad, dette sker ikke hvis du gemmer med f.eks. Delphi eller MS Word. I stedet for at bruge FILE_NOTIFY_CHANGE_ATTRIBUTES kan du evt. bruge FILE_NOTIFY_CHANGE_LAST_WRITE og så checke dato/tid når du får besked. -------------------
public constructor Create(aWatchHandle : THandle); end;
var Form1: TForm1;
implementation
{$R *.dfm}
{ TWatchThread }
constructor TWatchThread.Create(aWatchHandle : THandle); begin inherited Create(False); WatchHandle:=aWatchHandle; FreeOnTerminate:=true; end;
procedure TWatchThread.SendBesked; begin Form1.Memo1.Lines.Add(Besked); end;
procedure TWatchThread.Execute; var result : integer; begin Besked:='Thread startet'; Synchronize(SendBesked);
while not terminated do begin result:=WaitForSingleObject(WatchHandle,100); if result=WAIT_OBJECT_0 then begin Besked:='Der er sket en ændring'; Synchronize(SendBesked); if not FindNextChangeNotification(WatchHandle) then begin Besked:='Fejl i FindNextChangeNotification'; Synchronize(SendBesked); Terminate; end; end; end; end;
{ end TWatchThread }
procedure TForm1.Button1Click(Sender: TObject); var lpPathName : string; begin lpPathName:='c:\hk'; // mappen der skal overvåges WatchHandle:=FindFirstChangeNotification(PChar(lpPathName),false,FILE_NOTIFY_CHANGE_ATTRIBUTES); if WatchHandle=INVALID_HANDLE_VALUE then Memo1.Lines.Add('fejl i FindFirstChangeNotification');
Doc404, vil lige høre om den function du har lavet om den vil kunne bruges til at tjekke om en fil er lukket for at være klar til at åbne den igen. Jeg kan godt se hvad den vil returnere, men som jeg ser det, vil den åbne filen uanset om den er åben eller lukket. Jeg kan ikke begynde at læse filen før den er lukket. Først der kan jeg være sikker på at der ikke bliver skrevet flere oplysninger i filen. Så mit svar ovenover må så være ja, det kan forekomme ud fra den måde du spørger på. Men hvis du spørger mig det andet program vil åbne den samme fil efter, at den har skrevet i den, vil svaret være nej. Navnet på filen vil være forskelligt, men ikke extension. Den vil være det samme.
Argumentet fmShareExclusive gør at filen skal kunne åbnes UDEN at andre programmer har eller kan få adgang til den. Derfor vil den fejle - og altså ikke åbne filen, hvis et andet program stadig har filen åben.
Filen bliver kun åbnet og data læst, hvis filen er fri.
Denne procedure bliver brugt når der sker en ændring i et bibliotek som jeg bestemmer. Hvordan er det muligt at bruge din function i den? Den skal blive ved med undersøge filen til den er klar også åbne den? Og er der nogen forskel i at bruge FileList.loadfromfile(FileName)? Og er den enten fmOpenRead eller fmShareExclusive man bruger?
procedure TService1.ShellChangeNotifier1Change; var sr: TSearchRec; begin if findfirst(ShellChangeNotifier1.Root + '*.ilp',faAnyFile,sr) = 0 then begin FileName := sr.Name; end; FindClose(sr); end;
procedure TService1.ShellChangeNotifier1Change; var sr: TSearchRec; begin if findfirst(ShellChangeNotifier1.Root + '*.ilp',faAnyFile,sr) = 0 then begin FileName := sr.Name; repeat GetData(FileName) until GetData(FileName) = true; end; FindClose(sr); end;
Jeg tror sku at jeg fundet løsningen. Den ligger i den komponent jeg bruger. ShellChangeNotifier. Jeg så mig ikke for. Der er nogle forskellige filter, bl.a en der hedder nfWriteChange := true;
Nu har I gjort et godt stykke arbejde. Jeg aner ikke om de kan det jeg efterlyser og jeg aner ikke hvordan jeg skal fordele de point. Og kan man overhovedet fordele dem?
Men selvom jeg har har sat det filter og kopiere en fil til det overvåget bibliotek for jeg en "File is being used by another process". Jeg troede at når man satte det filter vil den først se at noget er ændret hvis det filter var sandt.
Kroning: Har du nogen ide til hvordan jeg kan bruge ShellChangeNotifier? Og ellers i det du har lavet, hvordan er det muligt at bruge LogMessage for en service i stedet for at bruge Synchronize(SendBesked);
Description LogMessage sends an error message to the event log when an error or exception occurs. By default the EventType is EVENTLOG_ERROR_TYPE (usually indicating a loss of functionality or data) and the category and ID are zero. For example, if a service cannot be loaded as the system boots, it can log an error event, and the category and ID are zero.
The EventType specifies the type of event being logged. This parameter can be one of the following values: Value Meaning EVENTLOG_WARNING_TYPE Warning event EVENTLOG_INFORMATION_TYPE Information event EVENTLOG_AUDIT_SUCCESS Success Audit event EVENTLOG_AUDIT_FAILURE Failure Audit event
EVENTLOG_ERROR_TYPEError event
The Category parameter specifies the event category, which is source-specific information and can have any value.
The ID parameter specifies the event identifier, which is the message that goes with this event as an entry in the message file associated with the event source.
Hvis det er en event i hoved tråden så skal du bruge Synchronize, se hjælpen for hvordan man bruger den. Hvis det er en event som befinder sig i en anden tråd så skal der lidt mere til, det er ikke noget jeg har brugt jeg vil ikke begynde at forsøge at forklare det. Men kik evt. på http://www.pergolesi.demon.co.uk/prog/threads/ToC.html Hvis du vil kalde en event i en tråd fra hoved tråden så kan du evt. bruge PostThreadMessage.
procedure TWatchThread.Execute; begin while not Terminated do begin Synchronize(ShellChangeNotifier1.OnChange); Sleep(500); end; end;
procedure TService1.ShellChangeNotifier1Change; begin Beep; end;
så bliver synchronize udført hver gang. Korrekt? Nu er det sådan at ShellChangeNotifer har en properties "root" hvor jeg designtime har sat den til c:\test. Hvilket betyder at når der sker noget i c:\test skal den bipper 1 gang, men den gør det også hvis der sker noget i c:\ og det er ikke helt det der er meningen. Jeg havde forstillet mig at den selv holder styr på at noget er sket i "root".
Hvis jeg laver et alm prg hvor jeg i OnChange skriver:
procedure TService1.ShellChangeNotifier1Change; begin Beep; end;
vil den bippe 1 gang når noget er sket i "root".
Har du nogen ide til hvordan jeg kan gøre det i en service? Dit eksempel virker også, men kunne nu godt tænke mig at bruge ShellChangeNotifier. Vil gerne sætte flere point på spil.
Jeg har aldrig brugt ShellChangeNotifier og jeg har aldrig lavet en service så jeg er nok ikke den rette til at svare på det. Men prøv at lave et nyt spørgsmål så er der måske en anden der kan svare.
Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.