2009-03-03 8 views
42

Làm cách nào để tạo/xóa/đọc/ghi/NTFS luồng dữ liệu thay thế từ .NET?Các luồng dữ liệu thay thế NTFS - .NET

Nếu không có hỗ trợ .NET gốc, tôi sẽ sử dụng API của Win32 nào? Ngoài ra, làm thế nào tôi sẽ sử dụng chúng, vì tôi không nghĩ rằng đây là tài liệu?

+0

BTW, nếu bạn muốn sao chép (các) tệp với hộp thoại tiến trình sao chép tệp chuẩn, bạn không thể sử dụng :: SHFileOperation() - nó không hoạt động với AltDataStreams (kiểm tra trên Windows 7). Đối với :: CopyFileEx(), nó hoạt động trong một số trường hợp (ví dụ: nó có thể sao chép một tệp vào AltDataStream trong khi gọi tiến trình gọi lại), nhưng nó không hoạt động ở những người khác. – Nishi

Trả lời

4

Không có trong .NET:

http://support.microsoft.com/kb/105763

#include <windows.h> 
    #include <stdio.h> 

    void main() 
    { 
     HANDLE hFile, hStream; 
     DWORD dwRet; 

     hFile = CreateFile("testfile", 
         GENERIC_WRITE, 
        FILE_SHARE_WRITE, 
           NULL, 
         OPEN_ALWAYS, 
            0, 
           NULL); 
     if(hFile == INVALID_HANDLE_VALUE) 
     printf("Cannot open testfile\n"); 
     else 
      WriteFile(hFile, "This is testfile", 16, &dwRet, NULL); 

     hStream = CreateFile("testfile:stream", 
           GENERIC_WRITE, 
          FILE_SHARE_WRITE, 
             NULL, 
            OPEN_ALWAYS, 
              0, 
             NULL); 
     if(hStream == INVALID_HANDLE_VALUE) 
     printf("Cannot open testfile:stream\n"); 
     else 
     WriteFile(hStream, "This is testfile:stream", 23, &dwRet, NULL); 
    } 
+8

Hai cuộc gọi CloseHandle bị thiếu ... Hệ điều hành sẽ dọn dẹp, nhưng sẽ là một vấn đề trong một ứng dụng thực tế. – Richard

+3

@Richard - chỉ cần sao chép từ trang web hỗ trợ của MS ... –

+1

ávio Bạn có thể P/Gọi các hàm đó từ C#. –

30

Đây là một phiên bản dành cho C#

using System.Runtime.InteropServices; 

class Program 
{ 
    static void Main(string[] args) 
    { 
     var mainStream = NativeMethods.CreateFileW(
      "testfile", 
      NativeConstants.GENERIC_WRITE, 
      NativeConstants.FILE_SHARE_WRITE, 
      IntPtr.Zero, 
      NativeConstants.OPEN_ALWAYS, 
      0, 
      IntPtr.Zero); 

     var stream = NativeMethods.CreateFileW(
      "testfile:stream", 
      NativeConstants.GENERIC_WRITE, 
      NativeConstants.FILE_SHARE_WRITE, 
      IntPtr.Zero, 
      NativeConstants.OPEN_ALWAYS, 
      0, 
      IntPtr.Zero); 
    } 
} 

public partial class NativeMethods 
{ 

    /// Return Type: HANDLE->void* 
    ///lpFileName: LPCWSTR->WCHAR* 
    ///dwDesiredAccess: DWORD->unsigned int 
    ///dwShareMode: DWORD->unsigned int 
    ///lpSecurityAttributes: LPSECURITY_ATTRIBUTES->_SECURITY_ATTRIBUTES* 
    ///dwCreationDisposition: DWORD->unsigned int 
    ///dwFlagsAndAttributes: DWORD->unsigned int 
    ///hTemplateFile: HANDLE->void* 
    [DllImportAttribute("kernel32.dll", EntryPoint = "CreateFileW")] 
    public static extern System.IntPtr CreateFileW(
     [InAttribute()] [MarshalAsAttribute(UnmanagedType.LPWStr)] string lpFileName, 
     uint dwDesiredAccess, 
     uint dwShareMode, 
     [InAttribute()] System.IntPtr lpSecurityAttributes, 
     uint dwCreationDisposition, 
     uint dwFlagsAndAttributes, 
     [InAttribute()] System.IntPtr hTemplateFile 
    ); 

} 


public partial class NativeConstants 
{ 

    /// GENERIC_WRITE -> (0x40000000L) 
    public const int GENERIC_WRITE = 1073741824; 

    /// FILE_SHARE_DELETE -> 0x00000004 
    public const int FILE_SHARE_DELETE = 4; 

    /// FILE_SHARE_WRITE -> 0x00000002 
    public const int FILE_SHARE_WRITE = 2; 

    /// FILE_SHARE_READ -> 0x00000001 
    public const int FILE_SHARE_READ = 1; 

    /// OPEN_ALWAYS -> 4 
    public const int OPEN_ALWAYS = 4; 
} 
+8

Nên sử dụng một loại có nguồn gốc từ SafeHandle ở đây, để đảm bảo bạn dọn dẹp các tệp xử lý đó. – Richard

+7

Bạn đã chỉ ra cách sử dụng các API gốc, nhưng không chỉ cách sử dụng con trỏ được trả về từ 'CreateFileW'. Tôi muốn * thực sự * muốn xem một mẫu hoàn chỉnh hơn để viết cho các thuộc tính phổ biến có sẵn trong tab Tóm tắt các thuộc tính tệp trong Windows Explorer. –

13

Không có .NET hỗ trợ cho họ. Bạn phải sử dụng P/Invoke để gọi các phương thức Win32 gốc.

Để tạo chúng, hãy gọi CreateFile bằng đường dẫn như filename.txt:streamname. Nếu bạn sử dụng cuộc gọi interop trả về SafeFileHandle, bạn có thể sử dụng nó để xây dựng một FileStream mà sau đó bạn có thể đọc & ghi vào.

Để liệt kê các luồng tồn tại trên một tệp, hãy sử dụng FindFirstStreamWFindNextStreamW (chỉ tồn tại trên Server 2003 trở lên - không phải XP).

Tôi không tin bạn có thể xóa luồng, ngoại trừ bằng cách sao chép phần còn lại của tệp và thoát khỏi một trong các luồng. Đặt độ dài thành 0 cũng có thể hoạt động nhưng tôi chưa thử.

Bạn cũng có thể có các luồng dữ liệu thay thế trên một thư mục. Bạn truy cập chúng giống như với các tệp - C:\some\directory:streamname.

Luồng có thể có tính năng nén, mã hóa và không được đặt trên chúng độc lập với luồng mặc định.

+7

Bạn * có thể * xóa luồng: chỉ cần gọi API DeleteFile bằng "filename: streamname". Rõ ràng, bạn có thể làm với một quảng cáo chỉ là về bất cứ điều gì bạn có thể làm với một tập tin bình thường. Lý do duy nhất tại sao FileStream không xử lý nó là bởi vì nó xác nhận đường dẫn và thất bại nếu nó chứa ":" ... –

6

Thứ nhất, không có gì trong Microsoft® .NET Framework cung cấp chức năng này. Nếu bạn muốn nó, đồng bằng và đơn giản, bạn sẽ cần phải làm một số loại interop, hoặc trực tiếp hoặc sử dụng một thư viện của bên thứ ba.

Nếu bạn đang sử dụng Windows Server ™ 2003 trở lên, Kernel32.dll sẽ hiển thị đối sánh với FindFirstFile và FindNextFile cung cấp chức năng chính xác mà bạn đang tìm kiếm. FindFirstStreamW và FindNextStreamW cho phép bạn tìm và liệt kê tất cả các luồng dữ liệu thay thế trong một tệp cụ thể, truy xuất thông tin về mỗi tệp, bao gồm tên và thời lượng của nó. Mã cho việc sử dụng các chức năng này từ mã được quản lý rất giống với điều mà chính tôi đã cho thấy trong cột tháng mười hai của tôi, và được thể hiện trong hình 1.

Hình 1 Sử dụng FindFirstStreamW và FindNextStreamW

[SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode = true)] 
public sealed class SafeFindHandle : SafeHandleZeroOrMinusOneIsInvalid { 

    private SafeFindHandle() : base(true) { } 

    protected override bool ReleaseHandle() { 
     return FindClose(this.handle); 
    } 

    [DllImport("kernel32.dll")] 
    [return: MarshalAs(UnmanagedType.Bool)] 
    [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] 
    private static extern bool FindClose(IntPtr handle); 

} 

public class FileStreamSearcher { 
    private const int ERROR_HANDLE_EOF = 38; 
    private enum StreamInfoLevels { FindStreamInfoStandard = 0 } 

    [DllImport("kernel32.dll", ExactSpelling = true, CharSet = CharSet.Auto, SetLastError = true)] 
    private static extern SafeFindHandle FindFirstStreamW(string lpFileName, StreamInfoLevels InfoLevel, [In, Out, MarshalAs(UnmanagedType.LPStruct)] WIN32_FIND_STREAM_DATA lpFindStreamData, uint dwFlags); 

    [DllImport("kernel32.dll", ExactSpelling = true, CharSet = CharSet.Auto, SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool FindNextStreamW(SafeFindHandle hndFindFile, [In, Out, MarshalAs(UnmanagedType.LPStruct)] WIN32_FIND_STREAM_DATA lpFindStreamData); 
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] 
    private class WIN32_FIND_STREAM_DATA { 
     public long StreamSize; 
     [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 296)] 
     public string cStreamName; 
    } 

    public static IEnumerable<string> GetStreams(FileInfo file) { 
     if (file == null) throw new ArgumentNullException("file"); 
     WIN32_FIND_STREAM_DATA findStreamData = new WIN32_FIND_STREAM_DATA(); 
     SafeFindHandle handle = FindFirstStreamW(file.FullName, StreamInfoLevels.FindStreamInfoStandard, findStreamData, 0); 
     if (handle.IsInvalid) throw new Win32Exception(); 
     try { 
      do { 
       yield return findStreamData.cStreamName; 
      } while (FindNextStreamW(handle, findStreamData)); 
      int lastError = Marshal.GetLastWin32Error(); 
      if (lastError != ERROR_HANDLE_EOF) throw new Win32Exception(lastError); 
     } finally { 
      handle.Dispose(); 
     } 
    } 
} 

Bạn chỉ cần gọi FindFirstStreamW, chuyển đến nó đường dẫn đầy đủ tới tệp đích. Tham số thứ hai để FindFirstStreamW quy định mức độ chi tiết bạn muốn trong dữ liệu trả về; hiện tại, chỉ có một mức (FindStreamInfoStandard), có một giá trị bằng 0. Tham số thứ ba cho hàm là một con trỏ tới một cấu trúc WIN32_FIND_STREAM_DATA (về mặt kỹ thuật, thông số thứ ba được chỉ ra bởi giá trị của tham số thứ hai chi tiết mức độ thông tin, nhưng vì hiện tại chỉ có một cấp độ, cho tất cả các ý định và mục đích, đây là một WIN32_FIND_STREAM_DATA). Tôi đã tuyên bố rằng đối tượng được quản lý của cấu trúc như là một lớp, và trong chữ ký interop, tôi đã đánh dấu nó được sắp xếp như một con trỏ tới một cấu trúc. Tham số cuối cùng được dành riêng cho sử dụng trong tương lai và phải là 0. Nếu một trình điều khiển hợp lệ được trả về từ FindFirstStreamW, cá thể WIN32_FIND_STREAM_DATA chứa thông tin về luồng tìm thấy và giá trị cStreamName của nó có thể được trả lại cho người gọi làm tên luồng đầu tiên có sẵn. FindNextStreamW chấp nhận xử lý được trả về từ FindFirstStreamW và điền vào WIN32_FIND_STREAM_DATA được cung cấp kèm theo thông tin về luồng tiếp theo có sẵn, nếu nó tồn tại. FindNextStreamW trả về true nếu một luồng khác có sẵn hoặc false nếu không. Kết quả là, tôi liên tục gọi FindNextStreamW và mang lại tên luồng kết quả cho đến khi FindNextStreamW trả về false. Khi điều đó xảy ra, tôi kiểm tra lại giá trị lỗi cuối cùng để đảm bảo rằng lặp lại dừng lại vì FindNextStreamW đã hết luồng và không phải vì một số lý do không mong muốn. Thật không may, nếu bạn đang sử dụng Windows® XP hoặc Windows 2000 Server, các chức năng này không có sẵn cho bạn, nhưng có một vài lựa chọn thay thế. Giải pháp đầu tiên liên quan đến chức năng không có giấy tờ hiện được xuất từ ​​Kernel32.dll, NTQueryInformationFile. Tuy nhiên, các chức năng không có giấy tờ không có giấy tờ vì lý do, và chúng có thể được thay đổi hoặc thậm chí bị loại bỏ bất cứ lúc nào trong tương lai. Tốt nhất là không nên sử dụng chúng. Nếu bạn muốn sử dụng chức năng này, hãy tìm kiếm trên Web và bạn sẽ tìm thấy nhiều tài liệu tham khảo và mã nguồn mẫu. Nhưng làm như vậy có nguy cơ của riêng bạn. Một giải pháp khác, và một giải pháp mà tôi đã minh họa trong hình Hình 2, dựa trên hai hàm được xuất từ ​​Kernel32.dll và chúng được ghi lại. Như tên của họ ngụ ý, BackupRead và BackupSeek là một phần của API Win32® hỗ trợ sao lưu:

BOOL BackupRead(HANDLE hFile, LPBYTE lpBuffer, DWORD nNumberOfBytesToRead, LPDWORD lpNumberOfBytesRead, BOOL bAbort, BOOL bProcessSecurity, LPVOID* lpContext); 
BOOL BackupSeek(HANDLE hFile, DWORD dwLowBytesToSeek, DWORD dwHighBytesToSeek, LPDWORD lpdwLowByteSeeked, LPDWORD lpdwHighByteSeeked, LPVOID* lpContext); 

Hình 2 Sử dụng BackupRead và BackupSeek

public enum StreamType { 
    Data = 1, 
    ExternalData = 2, 
    SecurityData = 3, 
    AlternateData = 4, 
    Link = 5, 
    PropertyData = 6, 
    ObjectID = 7, 
    ReparseData = 8, 
    SparseDock = 9 
} 

public struct StreamInfo { 
    public StreamInfo(string name, StreamType type, long size) { 
     Name = name; 
     Type = type; 
     Size = size; 
    } 
    readonly string Name; 
    public readonly StreamType Type; 
    public readonly long Size; 
} 

public class FileStreamSearcher { 
    [DllImport("kernel32.dll")] 
    [return: MarshalAs(UnmanagedType.Bool)] 
    private static extern bool BackupRead(SafeFileHandle hFile, IntPtr lpBuffer, uint nNumberOfBytesToRead, out uint lpNumberOfBytesRead, [MarshalAs(UnmanagedType.Bool)] bool bAbort, [MarshalAs(UnmanagedType.Bool)] bool bProcessSecurity, ref IntPtr lpContext);[DllImport("kernel32.dll")] 

    [return: MarshalAs(UnmanagedType.Bool)] 
    private static extern bool BackupSeek(SafeFileHandle hFile, uint dwLowBytesToSeek, uint dwHighBytesToSeek, out uint lpdwLowByteSeeked, out uint lpdwHighByteSeeked, ref IntPtr lpContext); public static IEnumerable<StreamInfo> GetStreams(FileInfo file) { 
     const int bufferSize = 4096; 
     using (FileStream fs = file.OpenRead()) { 
      IntPtr context = IntPtr.Zero; 
      IntPtr buffer = Marshal.AllocHGlobal(bufferSize); 
      try { 
       while (true) { 
        uint numRead; 
        if (!BackupRead(fs.SafeFileHandle, buffer, (uint)Marshal.SizeOf(typeof(Win32StreamID)), out numRead, false, true, ref context)) throw new Win32Exception(); 
        if (numRead > 0) { 
         Win32StreamID streamID = (Win32StreamID)Marshal.PtrToStructure(buffer, typeof(Win32StreamID)); 
         string name = null; 
         if (streamID.dwStreamNameSize > 0) { 
          if (!BackupRead(fs.SafeFileHandle, buffer, (uint)Math.Min(bufferSize, streamID.dwStreamNameSize), out numRead, false, true, ref context)) throw new Win32Exception(); name = Marshal.PtrToStringUni(buffer, (int)numRead/2); 
         } 
         yield return new StreamInfo(name, streamID.dwStreamId, streamID.Size); 
         if (streamID.Size > 0) { 
          uint lo, hi; BackupSeek(fs.SafeFileHandle, uint.MaxValue, int.MaxValue, out lo, out hi, ref context); 
         } 
        } else break; 
       } 
      } finally { 
       Marshal.FreeHGlobal(buffer); 
       uint numRead; 
       if (!BackupRead(fs.SafeFileHandle, IntPtr.Zero, 0, out numRead, true, false, ref context)) throw new Win32Exception(); 
      } 
     } 
    } 
} 

Ý tưởng đằng sau BackupRead là nó có thể được được sử dụng để đọc dữ liệu từ một tệp vào bộ đệm, sau đó có thể được ghi vào phương tiện lưu trữ dự phòng. Tuy nhiên, BackupRead cũng rất tiện lợi cho việc tìm kiếm thông tin về từng luồng dữ liệu thay thế tạo nên tệp đích. Nó xử lý tất cả dữ liệu trong tệp dưới dạng một chuỗi các luồng byte riêng biệt (mỗi luồng dữ liệu thay thế là một trong các luồng byte) và mỗi luồng được đặt trước bởi cấu trúc WIN32_STREAM_ID. Vì vậy, để liệt kê tất cả các luồng, bạn chỉ cần đọc qua tất cả các cấu trúc WIN32_STREAM_ID này từ đầu mỗi luồng (đây là nơi BackupSeek trở nên rất tiện dụng, vì nó có thể được sử dụng để chuyển từ luồng này sang luồng khác mà không cần để đọc qua tất cả dữ liệu trong tệp). Để bắt đầu, trước tiên bạn cần tạo một đối tác quản lý đối với cơ cấu WIN32_STREAM_ID không được quản lý:

typedef struct _WIN32_STREAM_ID { 
    DWORD dwStreamId; DWORD dwStreamAttributes; 
    LARGE_INTEGER Size; 
    DWORD dwStreamNameSize; 
    WCHAR cStreamName[ANYSIZE_ARRAY]; 
} WIN32_STREAM_ID; 

Đối với hầu hết các phần, điều này giống như bất kỳ cấu trúc khác mà bạn muốn sắp xếp thông qua P/Invoke. Tuy nhiên, có một vài biến chứng. Đầu tiên và quan trọng nhất, WIN32_STREAM_ID là một cấu trúc có kích thước thay đổi. Thành viên cuối cùng của nó, cStreamName, là một mảng có độ dài ANYSIZE_ARRAY. Trong khi ANYSIZE_ARRAY được định nghĩa là 1, cStreamName chỉ là địa chỉ của phần còn lại của dữ liệu trong cấu trúc sau bốn trường trước đó, có nghĩa là nếu cấu trúc được phân bổ lớn hơn sizeof (WIN32_STREAM_ID) byte, khoảng trống thừa đó sẽ có hiệu lực là một phần của mảng cStreamName. Trường trước, dwStreamNameSize, chỉ định chính xác khoảng thời gian của mảng. Trong khi điều này là rất tốt cho Win32 phát triển, nó tàn phá trên một marshaler mà cần phải sao chép dữ liệu này từ bộ nhớ không được quản lý để quản lý bộ nhớ như là một phần của cuộc gọi interop để BackupRead. Làm thế nào để marshaler biết làm thế nào lớn các cấu trúc WIN32_STREAM_ID thực sự là, cho rằng nó có kích thước biến? Nó không. Vấn đề thứ hai liên quan đến đóng gói và căn chỉnh. Bỏ qua cStreamName một lúc, hãy xem xét khả năng sau đây cho đối tác WIN32_STREAM_ID được quản lý của bạn:

[StructLayout(LayoutKind.Sequential)] 
public struct Win32StreamID { 
    public int dwStreamId; 
    public int dwStreamAttributes; 
    public long Size; 
    public int dwStreamNameSize; 
} 

Một Int32 có kích thước 4 byte và Int64 là 8 byte. Do đó, bạn mong đợi cấu trúc này là 20 byte.Tuy nhiên, nếu bạn chạy các mã sau đây, bạn sẽ thấy rằng cả hai giá trị là 24, không phải 20:

int size1 = Marshal.SizeOf(typeof(Win32StreamID)); 
int size2 = sizeof(Win32StreamID); // in an unsafe context 

Vấn đề là trình biên dịch muốn đảm bảo rằng các giá trị trong các cấu trúc luôn thẳng hàng trên ranh giới thích hợp. Giá trị bốn byte phải ở địa chỉ chia hết cho 4, giá trị 8 byte phải ở ranh giới chia hết cho 8, v.v. Bây giờ hãy tưởng tượng điều gì sẽ xảy ra nếu bạn tạo một mảng cấu trúc Win32StreamID. Tất cả các trường trong trường hợp đầu tiên của mảng sẽ được căn chỉnh đúng. Ví dụ, vì trường Kích thước theo sau hai số nguyên 32 bit, nó sẽ là 8 byte tính từ đầu của mảng, hoàn hảo cho giá trị 8 byte. Tuy nhiên, nếu cấu trúc có kích thước 20 byte, thể hiện thứ hai trong mảng sẽ không có tất cả các thành viên của nó được căn chỉnh đúng cách. Tất cả các giá trị số nguyên sẽ là tốt, nhưng giá trị dài sẽ là 28 byte từ đầu mảng, giá trị không chia hết cho 8. Để sửa lỗi này, trình biên dịch sẽ cấu trúc thành kích thước 24, sao cho tất cả các trường sẽ luôn được căn chỉnh đúng (giả sử chính mảng đó là). Nếu trình biên dịch làm điều đúng, bạn có thể thắc mắc tại sao tôi quan tâm đến điều này. Bạn sẽ thấy lý do tại sao nếu bạn nhìn vào mã trong Hình 2. Để có được xung quanh vấn đề marshaling đầu tiên mà tôi đã mô tả, tôi thực sự để nguyên cStreamName ra khỏi cấu trúc Win32StreamID. Tôi sử dụng BackupRead để đọc đủ byte để điền vào cấu trúc Win32StreamID của tôi, và sau đó tôi kiểm tra trường dwStreamNameSize của cấu trúc. Bây giờ tôi biết tên là bao lâu, tôi có thể sử dụng BackupRead một lần nữa để đọc trong giá trị của chuỗi từ tệp. Đó là tất cả tốt và dandy, nhưng nếu Marshal.SizeOf trả về 24 cho cấu trúc Win32StreamID của tôi thay vì 20, tôi sẽ cố gắng đọc quá nhiều dữ liệu. Để tránh điều này, tôi cần phải chắc chắn rằng kích thước của Win32StreamID là trên thực tế 20 và không 24. Điều này có thể được thực hiện theo hai cách khác nhau bằng cách sử dụng các trường trên StructLayoutAttribute tô điểm cho cấu trúc. Đầu tiên là sử dụng trường Kích thước, điều này dẫn đến thời gian chạy chính xác cấu trúc lớn như thế nào:

[StructLayout(LayoutKind.Sequential, Size = 20)] 

Tùy chọn thứ hai là sử dụng trường Gói. Gói cho biết kích thước đóng gói sẽ được sử dụng khi giá trị LayoutKind.Sequential được chỉ định và điều khiển căn chỉnh của các trường trong cấu trúc. Kích thước đóng gói mặc định cho cấu trúc được quản lý là 8. Nếu tôi thay đổi thành 4, tôi nhận được cấu trúc 20 byte mà tôi đang tìm kiếm (và vì tôi không thực sự sử dụng cấu trúc này trong một mảng, tôi không mất hiệu quả hoặc ổn định mà có thể là kết quả của một sự thay đổi bao bì như vậy):

[StructLayout(LayoutKind.Sequential, Pack = 4)] 
public struct Win32StreamID { 
    public StreamType dwStreamId; 
    public int dwStreamAttributes; 
    public long Size; 
    public int dwStreamNameSize; // WCHAR cStreamName[1]; 
} 

với mã này tại chỗ, bây giờ tôi có thể liệt kê tất cả các dòng trong một tập tin, như thể hiện ở đây:

static void Main(string[] args) { 
    foreach (string path in args) { 
     Console.WriteLine(path + ":"); 
     foreach (StreamInfo stream in FileStreamSearcher.GetStreams(new FileInfo(path))) { 
      Console.WriteLine("\t{0}\t{1}\t{2}", stream.Name != null ? stream.Name : "(unnamed)", stream.Type, stream.Size); 
     } 
    } 
} 

Bạn' sẽ nhận thấy rằng phiên bản FileStreamSearcher này trả về nhiều thông tin hơn phiên bản sử dụng FindFirstStreamW và FindNextStreamW. BackupRead có thể cung cấp dữ liệu trên không chỉ luồng chính và luồng dữ liệu thay thế, cũng hoạt động trên các luồng có chứa thông tin bảo mật, dữ liệu thô và hơn thế nữa. Nếu bạn chỉ muốn xem Dòng dữ liệu thay thế, bạn có thể lọc dựa trên thuộc tính Loại của StreamInfo, sẽ là StreamType.AlternateData cho luồng dữ liệu thay thế. Để kiểm tra mã này, bạn có thể tạo một tập tin có thay thế dữ liệu Streams sử dụng lệnh echo tại dấu nhắc lệnh:

> echo ".NET Matters" > C:\test.txt 
> echo "MSDN Magazine" > C:\test.txt:magStream 
> StreamEnumerator.exe C:\test.txt 
test.txt: 
     (unnamed)    SecurityData 164 
     (unnamed)    Data   17 
     :magStream:$DATA  AlternateData 18 
> type C:\test.txt 
".NET Matters" 
> more < C:\test.txt:magStream 
"MSDN Magazine" 

Vì vậy, bây giờ bạn có thể lấy tên của tất cả dữ liệu Streams thay thế được lưu trữ trong một tập tin. Tuyệt quá. Nhưng nếu bạn muốn thực sự thao tác dữ liệu ở một trong các luồng đó thì sao? Thật không may, nếu bạn cố gắng chuyển một đường dẫn cho một luồng dữ liệu thay thế tới một trong các nhà xây dựng FileStream, một NotSupportedException sẽ bị ném ra: "Định dạng của đường dẫn đã cho không được hỗ trợ." Để giải quyết vấn đề này, bạn có thể bỏ qua việc kiểm tra chuẩn hóa đường dẫn của FileStream bằng cách truy cập trực tiếp vào hàm CreateFile được hiển thị từ kernel32.dll (xem Hình 3). Tôi đã sử dụng P/Invoke cho hàm CreateFile để mở và truy xuất SafeFileHandle cho đường dẫn đã chỉ định, mà không thực hiện bất kỳ kiểm tra quyền quản lý nào trên đường dẫn, vì vậy nó có thể bao gồm các định danh Dòng dữ liệu thay thế. SafeFileHandle này sau đó được sử dụng để tạo một FileStream được quản lý mới, cung cấp quyền truy cập bắt buộc. Với điều đó tại chỗ, thật dễ dàng để thao tác nội dung của một luồng dữ liệu thay thế bằng cách sử dụng chức năng của không gian tên System.IO. Ví dụ dưới đây đọc và in ra nội dung của thư mục C: \ test.txt: magStream tạo ra trong ví dụ trước:

string path = @"C:\test.txt:magStream"; 
using (StreamReader reader = new StreamReader(CreateFileStream(path, FileAccess.Read, FileMode.Open, FileShare.Read))) { 
    Console.WriteLine(reader.ReadToEnd()); 
} 

Hình 3 Sử dụng P/Invoke cho CreateFile

private static FileStream CreateFileStream(string path, FileAccess access, FileMode mode, FileShare share) { 
    if (mode == FileMode.Append) mode = FileMode.OpenOrCreate; SafeFileHandle handle = CreateFile(path, access, share, IntPtr.Zero, mode, 0, IntPtr.Zero); 
    if (handle.IsInvalid) throw new IOException("Could not open file stream.", new Win32Exception()); 
    return new FileStream(handle, access); 
} 

[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] 
private static extern SafeFileHandle CreateFile(string lpFileName, FileAccess dwDesiredAccess, FileShare dwShareMode, IntPtr lpSecurityAttributes, FileMode dwCreationDisposition, int dwFlagsAndAttributes, IntPtr hTemplateFile); 

Stephen Toub trong MSDN Magazine from January 2006.

+4

Một ví dụ tốt về lý do tại sao các câu trả lời chỉ có liên kết là xấu. –

+0

Tất cả các liên kết đến các tạp chí MSDN đều bị hỏng và các liên kết tới trang web MSDN cũng sẽ sớm bị hỏng. Vui lòng bao gồm thêm chi tiết về câu trả lời của bạn. – AaA

14

Gói nuget này CodeFluent Runtime Client có (trong số các tiện ích khác) NtfsAlternateStream Class hỗ trợ các hoạt động tạo/đọc/cập nhật/xóa/liệt kê.

+7

Và ở đây bạn có một số ví dụ về cách sử dụng nó http://blog.codefluententities.com/2013/03/14/manipulating-ntfs-alternate-data-streams-in-c-with-the-codefluent-runtime- khách hàng / – polkduran