14. juni 2009 - 01:04Der er
29 kommentarer og 1 løsning
Adgang til mappe nægtet
Hej.
Jeg anvender en mindre søgefunktion i min applikation, for at lede efter nogle filer, men der opstår et problem en gang imellem.
Problemet er, at når der bliver så søgt, så stødes der pludselig på en mappe (F.eks en nødvendig en fra Microsoft), hvorefter adgangen bliver nægtet (UnauthorizedAccessException).
Til at søge, bruger jeg en DirectoryInfo klasse, samt en FileInfo klasse, begge fra System.IO. Og så nedenstående:
GetFiles("*.*", SearchOption.AllDirectories);
Jeg har så prøvet, at give adgang, ved at oprette en DirectorySecurity klasse (Så indsætte de nuvæende rettigheder til klassen), så tilføje en ny rettighed (Har prøvet med lidt forskellige, og gik til sidst over til FullControl, for at se om det gjorde nogen forskel):
Nej, jeg har ikke adgang til mappen, hvis jeg selv prøver at tilgå den.
Som jeg kan høre på dig thesurfer, så vil det måske ikke kunne lade sig gøre, at skabe adgang til mappen.
Ligger en af jer, så muligvis inde med en ide til, hvordan man kan springe alle de mapper over, som man ikke har adgang til?
// Lige et side spørgsmål, som jeg gerne giver ekstra point for: Når der nu søges, med den metode jeg har skrevet ovenfor, så foregår det hele i et stort (Alt efter søgeplaceringens antal filer) "lag", hvorefter resultaterne kommer frem. Det må være muligt, at søge gennem en fil ad gangen (Om det tager lidt længere tid generer mig ikke), hvorved dette lag ikke vil opstå? Det ses f.eks. ved antivira når der scannes (Så har man mulighed for at følge med i, hvilken fil den er nået til)
Hvis du f.eks. bruger Try/Catch, kan du undgå at dit program crasher.
Eksempel med C#, som også findes til VB.NET, hvor jeg ikke har gjort så meget ud af case (store/små bogstaver, hvilket man skal huske):
try { int a = int.parse("dette er en streng som vil fejle da den forventer et tal"); } catch(Exception ex) { messagebox.show("Der opstod en fejl: " + ex.message); }
I det eksempel vil du få at vide, at "Der opstod en fejl: " samt selve fejlmeddelelsen, som er noget i stil med "et tal var forventet men den angivne værdi er en streng"..
Angående "lag" / programmet fryser:
Problemet er at du afvikler din søgefunktion direkte fra din form. Det vil automatisk optage hele formen/applikationen. Løsningen er at smide søgefunktionen i en separat tråd.
Noget i stil med
public void soeg() { ... din søge kode her }
og i din søge-knap:
publi... { Thread t = new Thread(new ThreadStarter(soeg); t.isBackground = true; t.start(); }
Jeg har nu prøvet med try og catch, hvilket umiddelbart virker korrekt, men pga. der altid stødes på en eller flere mapper, der ikke kan skabes adgang til, så springes hele GetFiles... funktionen over, således at ingen filer bliver hentet.
Mht. til Threading, for at programmet ikke giver et "lag", når der søges, så får jeg nu følgende fejlmeddelse:
Handling på tværs af tråde er ugyldig: Objektet 'searchProgress' blev åbnet fra en anden tråd end den tråd, det blev oprettet i.
Pga. af det indenfor try { } fejler, så får den ikke fat i nogle filer. Meningen er jo, at den skal få fat i alle de filer, hvor den ikke fejler, men her får den slet ikke fat i nogle.
Jeg prøver lige at søge lidt rundt og kigge på Invoke :)
zuran> Hvis GetFiles er den funktion/metode der henter alle filerne, så har du "smidt hele koden inde i TRY"..
Det er ikke GetFiles der skal i TRY. Det er det inde i GetFiles, der kan gå galt, der skal inde i TRY..
Eksempel
public double GetFiles(double a, double b) {
double r = 0.0;
try {
r = a / b;
} catch (Exception ex) {
messagebox.show("Der opstod en fejl.. du dividerer vel ikke med 0??");
}
return r;
}
Det her går godt: double resultat = GetFiles(10.0, 5.0);
Det her går skidt, da man ikke må dividere med 0: double resultat = GetFiles(10.0, 0.0);
Læg mærke til at det kun er det kritiske, der skal i TRY, og ikke kaldet, da hele koden så ender i TRY.
Det var godt nok et meget simpelt eksempel, men jeg håber at du forstår det..
Pointen er den linie der opretter en forbindelse til mappen, skal i TRY. Det samme skal alt andet der er kritisk, for at man kan oprette forbindelse til mappen.
Hvis det stadigvæk ikke giver mening, vis mig din kode fra GetFiles, og jeg skal smide den korrekte kode ind i TRY. Skriv evt hvilken linie den normalt crasher ved.. det vil hjælpe.
windcape> Fra VS 2005 og opefter, skal man invoke den grafiske brugergrænseflade (formens controls), fra den seperate tråd, hvis tråden skal opdaterer controlen.
Dette var (som standard) ikke påkrævet i VS 2003 (men kunne slåes til), da jeg arbejde med VS 2003.
Jeg manglede ordet "direkte" i min tidligere forklaring.. så giver det mere mening.
Jeg forstår godt konceptet i at anvende TRY osv, men jeg kan ikke se, hvordan man skulle kunne gøre det, i netop det her tilfælde.
// Get all files FileInfo[] fileList = folder.GetFiles("*.*", SearchOption.AllDirectories);
Overstående er den linje, der enter alle filerne, og også den linje der crasher.
Jeg har prøvet at smide den i en TRY, men det vil ikke hjælpe, da linjen så blot springes over, hvis den vil crashe, hvorved der ingen filer bliver hentet.
Du må hellere end gerne, vise mig hvordan det lige gøres i dette tilfælde, tak :)
Hm, så tror jeg umiddelbart ikke, at det ville kunne lade sig gøre med GetFiles metoden.
Fra start ledte jeg dog også efter en metode, til at få fat i en enkel fil i en mappe, og så kunne der blot smides en løkke på en lign, men jeg fandt aldrig andet en GetFiles metoden.
Måske du kender en metode, til at hente en enkel fil, og så via et loop kunne gå videre?
Ellers har jeg følgende kode:
// Get location DirectoryInfo folder = new DirectoryInfo(location.Text);
// Set the maximum of searchProgress to the total number of files in the specified directory searchProgress.Maximum = folder.GetFiles("*.*", SearchOption.AllDirectories).Length;
// Get all files FileInfo[] fileList = folder.GetFiles("*.*", SearchOption.AllDirectories);
Og så en foreach, der gør det muligt for mig at håndtere hver enkel fil, men det hjælper ikke meget mht. crash, når det er den anden linje der fejler.
"windcape> Fra VS 2005 og opefter, skal man invoke den grafiske brugergrænseflade (formens controls), fra den seperate tråd, hvis tråden skal opdaterer controlen.
Dette var (som standard) ikke påkrævet i VS 2003 (men kunne slåes til), da jeg arbejde med VS 2003."
Det har stadig intet med dit IDE at gøre. Og Visual Studio kan bruges til mange andre ting end Winforms ! Din forståelse af multi-threaded GUI lader til at være ikke eksisterende.
Derudover bør problemet løses ved enten ikke at benytte et AllDirectories flag, eller have en catch på UnauthorizedAccessException (catche alle exceptions for en explicit case er meget dumt).
Manuel iteration giver ikke særlig meget mening, når der er et API til formålet.
windcape> Meningen er jo at man skal søge i nuværende mappe, og underliggende mapper. Den skal hente filerne ud af mapperne.
Men hvis du bruger .GetFiles (eller .GetDirectories) på "system volume information" mappen (eller en lignende mappe hvor man ikke kan få adgang), vil den crashe.
Man kommer ikke uden om dette problem, ved at bruge koden på det link du lige har smidt.
Der har man mulighederne "TopDirectoryOnly" eller "AllDirectories". Ingen af de to kan bruges i denne her situation.
Har jeg overset noget på dit link?
PS: Jeg har arbejdet på multi-threaded GUI før. Hvad mener at du at jeg "mangler" / ikke forstår?
Jeg har lavet et eksempel.. for at gøre koden så simpel som muligt, er koden ikke trådet.
Med andre ord: den låser formen indtil den er færdig med at læse/tælle filerne.
Du kan få fat i listen med filerne via "allfiles", som er et "List" fyldt med "FileInfo".
Så kan du f.eks. bruge:
foreach(FileInfo fi in allfiles) { noget her }
Prøv lige det her:
1) tilføj denne linie i toppen:
using System.Collections;
2) Tilføj denne linie over din "public Form1()"-linie:
private List<FileInfo> allfiles = new List<FileInfo>();
3) Tilføj denne metode (håber ikke at Eksperten fjerner indrykningerne):
private void dosearch(DirectoryInfo dir) { try { foreach (FileInfo fi in dir.GetFiles()) { allfiles.Add(fi); } } catch (Exception ex) { // skal ikke rigtigt bruges til noget }
try { DirectoryInfo[] dirs = dir.GetDirectories(); foreach (DirectoryInfo di in dirs) { dosearch(di); } } catch (Exception ex) { // skal ikke rigtigt bruges til noget } }
4) Kald metoden med et DirectoryInfo som parameter:
5) Da metoden pt ikke kører i en tråd, vil den låse din form. Vent til den er færdig og den vil fortælle dig hvor mange filer der er, den angivne mappe inkl. undermapper.
Filerne er tilføjet til listen "allfiles", som er en liste af FileInfo.
private void btnSearch_Click(object sender, EventArgs e) { DirectoryInfo mydir = new DirectoryInfo(location.Text); Thread t = new Thread(new ParameterizedThreadStart(dosearch)); t.IsBackground = true; t.Start(mydir); }
private void dosearch(object threadDir) { Thread.Sleep(1); DirectoryInfo dir = (DirectoryInfo)threadDir; try { foreach (FileInfo fi in dir.GetFiles()) { allfiles.Add(fi); updateLabelCount(allfiles.Count); } } catch (Exception ex) { // skal ikke rigtigt bruges til noget }
try { DirectoryInfo[] dirs = dir.GetDirectories(); foreach (DirectoryInfo di in dirs) { updateLabelPath(di.FullName); dosearch(di); } } catch (Exception ex) { // skal ikke rigtigt bruges til noget } }
thesurfer > At du bliver ved med at skrive fjollede ting som at ens version af et IDE har noget som helst med multi-threading af Managed Winforms at gøre.
Et IDE er et værktøj, en fornem editor. Det afhænger af hvilken version af C# og .NET du benytter, hvis der er forskel i opførsel på det samme stykke kode.
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms;
using System.IO; using System.Threading;
namespace _878163_W { public partial class Form1 : Form { private Delegate UIDelegate;
public Form1() { InitializeComponent(); }
private void StartButton_Click(object sender, EventArgs e) { Thread t = new Thread(new ParameterizedThreadStart( (object s) => GetAllFiles() )); t.Start(); }
private void GetAllFiles() { var files = GetAllFiles(new DirectoryInfo(@"D:\Test\"));
Invoke((Action)(() => { resultTextBox.Text = string.Format( @"{0} filer i D:\Test\ samt undermapper", files.Count() ); })); }
Jeg har afprøvet din seneste post, thesurfer, og den virker, så vil du smide et svar ;) ?
Jeg har også afprøvet din kode (Windcape), hvilken tilsyneladende også virker. _Jeg_ kan dog ikke konkludere hvem der har mest ret af jer, men jeg holder gerne øje med tråden, og så går jeg udfra i må kunne finde ud af sammen, hvis kode jeg bør benytte.
Det er jo netop pointen. Du kan jo ikke debugge din kode hvis du fanger samtlige exceptions over det hele!
Og du bør ikke fange ukendte fejl mere end eet sted, og da slet ikke i en funktion som retunere data!
@zuran:
Min version med iterators er nok mest politisk korrekt, og bedst fra et performance synspunkt. Du bør ihvertfald lære at forstå den kode jeg har postet, hvis du agter at udbygge dit program.
Og derudover glemte thesurfer at vise hvordan man håndterer cross-thread GUI invokes, da han ikke selv har forstået konceptet i det endnu ;-)
Synes godt om
Ny brugerNybegynder
Din løsning...
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.