381 Chương 9: File, thư mục, và I/O private System.Windows.Forms.MenuItem mnuSave; private System.Windows.Forms.MenuItem mnuExit; private System.Windows.Forms.RichTextBox rtDoc; // (Bỏ qua phần mã designer.) private void mnuOpen_Click(object sender, System.EventArgs e) { OpenFileDialog dlg = new OpenFileDialog(); dlg.Filter = "Rich Text Files (*.rtf)|*.RTF|" + "All files (*.*)|*.*"; dlg.CheckFileExists = true; dlg.InitialDirectory = Application.StartupPath; if (dlg.ShowDialog() == DialogResult.OK) { rtDoc.LoadFile(dlg.FileName); rtDoc.Enabled = true; } } private void mnuSave_Click(object sender, System.EventArgs e) { SaveFileDialog dlg = new SaveFileDialog(); dlg.Filter = "RichText Files (*.rtf)|*.RTF|Text Files (*.txt)| *.TXT" + "|All files (*.*)|*.*"; dlg.CheckFileExists = true; dlg.InitialDirectory = Application.StartupPath; if (dlg.ShowDialog() == DialogResult.OK) { rtDoc.SaveFile(dlg.FileName); } } private void mnuExit_Click(object sender, System.EventArgs e) { this.Close(); } 382 Chương 9: File, thư mục, và I/O } 18. 18. S d ng không gian l u tr riêngử ụ ư ữ S d ng không gian l u tr riêngử ụ ư ữ Bạn cần lưu dữ liệu vào file, nhưng ứng dụng của bạn không được cấp FileIOPermission để sử dụng ổ đĩa cứng. Sử dụng các lớp IsolatedStorageFile và IsolatedStorageFileStream thuộc không gian tên System.IO.IsolatedStorage . Các lớp này cho phép ứng dụng ghi dữ liệu vào một file trong thư mục của một người dùng cụ thể mà không cần được cấp phép truy xuất trực tiếp ổ đĩa cứng cục bộ. .NET Framework hỗ trợ không gian lưu trữ riêng, tức là cho phép bạn đọc và ghi vào hệ thống file ảo của người dùng cụ thể mà CLR quản lý. Khi bạn tạo các file lưu trữ riêng, dữ liệu tự động được lưu vào một nơi duy nhất trong đường dẫn profile của người dùng (thông thường đường dẫn này có dạng C:\Documents and Settings\[username]\Local Settings\Application Data\IsolatedStorage\ [guid_identifier]). Một lý do để sử dụng không gian lưu trữ riêng là trao cho một ứng dụng có-độ-tin cậy-một- phần có khả năng hạn chế khi lưu trữ dữ liệu (xem mục 13.1 để có thêm thông tin về mã lệnh có-độ-tin-cậy-một-phần). Ví dụ, chính sách bảo mật CLR mặc định cấp cho mã lệnh cục bộ có FileIOPermission không hạn chế, tức là có quyền đọc và ghi bất kỳ file nào. Mã lệnh thực thi từ một máy chủ ở xa trên mạng Intranet cục bộ tự động được cấp ít quyền hơn—thiếu mất FileIOPermission , nhưng có IsolatedStoragePermission , tức là có khả năng sử dụng không gian lưu trữ riêng (chính sách bảo mật cũng hạn chế dung lượng tối đa có thể được sử dụng trong không gian lưu trữ riêng). Một lý do khác để sử dụng không gian lưu trữ riêng là bảo vệ dữ liệu tốt hơn. Ví dụ, đối với dữ liệu trong không gian lưu trữ riêng của một người dùng, những người dùng khác không phải là nhà quản trị sẽ không được quyền truy xuất. Đoạn mã dưới đây minh họa cách truy xuất không gian lưu trữ riêng: using System; using System.IO; using System.IO.IsolatedStorage; public class IsolatedStoreTest { private static void Main() { // Tạo không gian lưu trữ riêng cho người dùng hiện hành. IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForAssembly(); 383 Chương 9: File, thư mục, và I/O // Tạo một thư mục tại gốc của không gian lưu trữ riêng. store.CreateDirectory("MyFolder"); // Tạo một file trong không gian lưu trữ riêng. Stream fs = new IsolatedStorageFileStream( "MyFile.txt", FileMode.Create, store); StreamWriter w = new StreamWriter(fs); // Ghi file như bình thường. w.WriteLine("Test"); w.Flush(); fs.Close(); Console.WriteLine("Current size: " + store.CurrentSize.ToString()); Console.WriteLine("Scope: " + store.Scope.ToString()); Console.WriteLine("Contained files include:"); string [] files = store.GetFileNames("*.*"); foreach (string file in files) { Console.WriteLine(file); } Console.ReadLine(); } } Theo mặc định, mỗi không gian lưu trữ riêng được tách biệt bởi người dùng và assembly. Điều này có nghĩa là khi cùng một người chạy cùng một ứng dụng, ứng dụng này sẽ truy xuất dữ liệu trong cùng không gian lưu trữ riêng. Tuy nhiên, bạn có thể tách biệt thêm bởi miền ứng dụng để nhiều thể hiện của cùng một ứng dụng nhận các không gian lưu trữ riêng khác nhau, như ví dụ sau: // Truy xuất không gian lưu trữ riêng của người dùng // và assembly hiện tại (tương tự ví dụ trên). store = IsolatedStorageFile.GetStore(IsolatedStorageScope.User | IsolatedStorageScope.Assembly, null, null); // Truy xuất không gian lưu trữ riêng của người dùng, assembly, // và miền ứng dụng hiện hành. Nói cách khác, dữ liệu này chỉ được 384 Chương 9: File, thư mục, và I/O // truy xuất bởi thể hiện hiện tại của ứng dụng. store = IsolatedStorageFile.GetStore(IsolatedStorageScope.User | IsolatedStorageScope.Assembly | IsolatedStorageScope.Domain, null, null); File được lưu trữ như một phần profile của người dùng, vì vậy người dùng có thể truy xuất các file này từ bất kỳ máy nào trong mạng LAN nếu roaming profile 3 đã được cấu hình (trong trường hợp này, phải thiết lập cờ IsolatedStorageFile.Roaming khi tạo không gian lưu trữ). Bằng cách để .NET Framework và CLR cung cấp các mức cách ly, bạn không phải duy trì sự tách biệt giữa các file, và không phải lo việc không hiểu rõ cơ chế làm việc sẽ gây mất dữ liệu quan trọng. 19. 19. Theo dõi h th ng file đ phát hi n thay đ iệ ố ể ệ ổ Theo dõi h th ng file đ phát hi n thay đ iệ ố ể ệ ổ Bạn cần phản ứng khi hệ thống file thay đổi tại một đường dẫn cụ thể (chẳng hạn sửa hay tạo file). Sử dụng thành phần System.IO.FileSystemWatcher , chỉ định file hoặc đường dẫn cần theo dõi, và thụ lý các sự kiện Created , Deleted , Renamed , và Changed . Khi liên kết nhiều ứng dụng và các quá trình nghiệp vụ, thường cần tạo một chương trình thụ động và chỉ trở nên tích cực khi một file được tạo ra hoặc bị thay đổi. Bạn có thể tạo kiểu chương trình thế này bằng cách quét định kỳ qua một thư mục, nhưng lại gặp phải vấn đề cân bằng. Quét càng thường xuyên, càng tốn tài nguyên hệ thống. Quét càng ít, càng lâu phát hiện được thay đổi. Cách tiện nhất là sử dụng lớp FileSystemWatcher để phản ứng trực tiếp các sự kiện file của Windows. Để sử dụng FileSystemWatcher , bạn phải tạo một thể hiện và thiết lập các thuộc tính sau: • Path —chỉ định đường dẫn cần theo dõi. • Filter —chỉ định kiểu file cần theo dõi. • NotifyFilter —chỉ định kiểu thay đổi cần theo dõi. FileSystemWatcher sinh ra bốn sự kiện chính: Created , Deleted , Renamed , và Changed . Các sự kiện này cung cấp thông tin qua đối số FileSystemEventArgs , bao gồm tên file ( Name ), đường dẫn đầy đủ ( FullPath ), kiểu thay đổi ( ChangeType ). Sự kiện Renamed cung cấp thể hiện RenamedEventArgs dẫn xuất từ FileSystemEventArgs , và thêm thông tin về tên file ban đầu ( OldName và OldFullPath ). Nếu cần, bạn có thể vô hiệu các sự kiện này bằng cách thiết lập thuộc tính FileSystemWatcher.EnableRaisingEvents là false . Dễ dàng thụ lý các sự kiện Created , Deleted , và Renamed . Nhưng nếu muốn sử dụng sự kiện Changed , bạn cần sử dụng thuộc tính NotifyFilter để chỉ định kiểu thay đổi cần theo dõi. Bằng không, chương trình của bạn có thể bị sa lầy bởi một loạt các sự kiện khi file bị thay đổi. 3 Roaming profile được tạo bởi người quản trị hệ thống và được lưu trữ trên một server. Profile này có sẵn mỗi khi bạn đăng nhập vào bất kỳ máy tính nào trên mạng. Bất cứ thay đổi nào cũng khiến roaming profile được cập nhật lại trên server. 385 Chương 9: File, thư mục, và I/O Thuộc tính NotifyFilter có thể được thiết lập bằng cách kết hợp các giá trị thuộc kiểu liệt kê System.IO.NotifyFilters : Attributes , CreationTime , DirectoryName , FileName , LastWrite , LastAccess , Security , và Size . Ví dụ dưới đây thụ lý sự kiện Created và Deleted . Và thử nghiệm các sự kiện này bằng cách tạo ra một file thử nghiệm. using System; using System.IO; using System.Windows.Forms; public class FileWatcherTest { private static void Main() { // Cấu hình FileSystemWatcher. FileSystemWatcher watch = new FileSystemWatcher(); watch.Path = Application.StartupPath; watch.Filter = "*.*"; watch.IncludeSubdirectories = true; // Đăng ký các phương thức thụ lý sự kiện. watch.Created += new FileSystemEventHandler(OnCreatedOrDeleted); watch.Deleted += new FileSystemEventHandler(OnCreatedOrDeleted); watch.EnableRaisingEvents = true; Console.WriteLine("Press Enter to create a file."); Console.ReadLine(); if (File.Exists("test.bin")) { File.Delete("test.bin"); } FileStream fs = new FileStream("test.bin", FileMode.Create); fs.Close(); Console.WriteLine("Press Enter to terminate the application."); Console.WriteLine(); Console.ReadLine(); } 386 Chương 9: File, thư mục, và I/O // Phát sinh khi một file mới được tạo ra // trong thư mục cần theo dõi. private static void OnCreatedOrDeleted(object sender, FileSystemEventArgs e) { // Hiển thị thông báo. Console.WriteLine("\tNOTIFICATION: " + e.FullPath + "' was " + e.ChangeType.ToString()); Console.WriteLine(); } } 20. 20. Truy xu t c ng COMấ ổ Truy xu t c ng COMấ ổ Bạn cần gửi dữ liệu trực tiếp đến một cổng tuần tự ( serial port ). Win32 API cung cấp các hàm không-được-quản-lý trong thư viện kernell32.dll để trực tiếp đọc và ghi các byte đến cổng tuần tự. Bạn có thể nhập các hàm này vào ứng dụng hoặc sử dụng Microft Communications ActiveX control ( MSComm.ocx —có trong Microsoft Visual Studio 6 ). .NET không cung cấp bất kỳ giao diện được-quản-lý nào để thao tác với các cổng tuần tự. Do đó, những ai cần chức năng này sẽ phải làm việc với các cơ chế tương đối phức tạp. Một hướng giải quyết là tạo một vỏ bọc .NET cho Microsoft Communications Control (MSComm.ocx). Điều kiểm này cung cấp một mô hình đối tượng mức-cao để làm việc với cổng tuần tự. Tuy nhiên, bạn phải thu lấy điều kiểm này thông qua Visual Studio 6 (bạn có thể chỉ chọn cài đặt các thành phần ActiveX khi cài đặt Visual Studio 6, tốn khoảng 5MB). Để có thêm thông tin, bạn hãy tham khảo tài liệu Visual Studio 6. Một hướng giải quyết khác là nhập các hàm API từ thư viện kernell32.dll. Cần cẩn thận khi sử dụng phương pháp này vì bạn phải sử dụng đúng kiểu dữ liệu C# và duy trì layout của các cấu trúc bộ nhớ. May mắn là, vấn đề này đã được Justin Harrell ( jharrell@aciss.com ) giải quyết với một lớp C# tùy biến có tên là ComPort . Mã lệnh của lớp này khá dài, bạn hãy xem trong đĩa CD đính kèm. Bạn có thể thêm lớp ComPort vào ứng dụng của bạn và sử dụng đoạn mã sau để tương tác với một cổng COM. ComPort port = new ComPort(); try { // Cấu hình cho cổng. 387 Chương 9: File, thư mục, và I/O port.BaudRate = 9600; port.Parity = 0; port.PortNum = 1; port.ReadTimeout = 10; port.StopBits = 1; port.ByteSize = 1; // Mở cổng. port.Open(); // Ghi dữ liệu. port.Write(new byte[1]); // Đóng cổng. port.Close(); }catch (ApplicationException err) { Console.WriteLine(err.Message); } 388 Chương 9: File, thư mục, và I/O 10 389 Chương 10:CƠ SỞ DỮ LIỆU 390 . IsolatedStorageFile.Roaming khi tạo không gian lưu trữ). Bằng cách để .NET Framework và CLR cung cấp các mức cách ly, bạn không phải duy trì sự tách biệt giữa các file, và không phải lo việc không hiểu rõ cơ. cần theo dõi, và thụ lý các sự kiện Created , Deleted , Renamed , và Changed . Khi liên kết nhiều ứng dụng và các quá trình nghiệp vụ, thường cần tạo một chương trình thụ động và chỉ trở. thay đổi. Cách tiện nhất là sử dụng lớp FileSystemWatcher để phản ứng trực tiếp các sự kiện file của Windows. Để sử dụng FileSystemWatcher , bạn phải tạo một thể hiện và thiết lập các thuộc