2009-03-03 4 views
42

.NET에서 NTFS 대체 데이터 스트림을 만들거나 삭제/읽기/쓰기/삭제하는 방법은 무엇입니까?NTFS 대체 데이터 스트림 - .NET

네이티브. NET 지원이 없다면 어떤 Win32 API를 사용합니까? 또한, 문서화 된 것으로 생각하지 않기 때문에, 어떻게 사용합니까? .NET에서

+0

표준 파일 복사 진행 대화 상자에서 파일을 복사하려면 :: SHFileOperation()을 사용할 수 없습니다. AltDataStreams에서는 전혀 작동하지 않습니다 (Windows 7에서 확인). :: CopyFileEx()의 경우, 일부 경우에 작동합니다 (예 : 진행률 콜백을 호출하는 동안 파일을 AltDataStream에 복사 할 수 있음). 그러나 다른 경우에는 작동하지 않습니다. – Nishi

답변

4

하지 : 여기

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

두 개의 누락 된 CloseHandle 호출 ... OS가 정리되지만 실제 응용 프로그램에서는 문제가됩니다. – Richard

+3

@ 리차드 - MS의 지원 사이트에서 복사 ... –

+1

ávio C#에서 해당 기능을 P/Invoke 할 수 있습니다. –

30

그들에 대한 기본 .NET 지원이 없습니다 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

SafeHandle에서 파생 된 유형을 사용하여이 파일 핸들을 정리해야합니다. – Richard

+7

네이티브 API를 사용하는 방법을 보여 줬지만'CreateFileW'에서 반환 된 포인터를 사용하는 방법은 제시하지 않았습니다. Windows Explorer에서 파일 속성의 요약 탭에있는 일반 속성에 쓸 수있는보다 완벽한 샘플을보고 싶습니다. –

13

을위한 버전입니다. 네이티브 Win32 메서드를 호출하려면 P/Invoke를 사용해야합니다.

filename.txt:streamname과 같은 경로로 CreateFile을 호출하십시오. SafeFileHandle을 반환하는 interop 호출을 사용하는 경우이를 사용하여 & 쓸 수있는 FileStream을 생성 할 수 있습니다.

파일에있는 스트림을 나열하려면 FindFirstStreamWFindNextStreamW (Server 2003 이상에만 존재 - XP가 아닌)을 사용하십시오.

파일의 나머지 부분을 복사하고 스트림 중 하나를 떠나면 스트림을 삭제할 수 있다고 생각하지 않습니다. 길이를 0으로 설정하는 것도 효과가있을 수 있지만 시도하지는 않았습니다.

디렉토리에 대체 데이터 스트림이있을 수도 있습니다. 파일들 - C:\some\directory:streamname과 같은 방식으로 액세스합니다.

스트림에는 기본 스트림과 관계없이 압축, 암호화 및 희소가 설정 될 수 있습니다.

+7

* * 스트림을 삭제할 수 있습니다. DeleteFile API를 "filename : streamname"과 함께 호출하면됩니다. 외관상으로는, 당신은 ADS로 당신이 정상적인 파일로 할 수있는 무엇이든에 관하여 다만 할 수있다. FileStream이 처리하지 않는 유일한 이유는 경로의 유효성을 검사하고 ":"이 포함되면 실패하기 때문입니다 ... –

6

먼저 Microsoft® .NET Framework에서는이 기능을 제공하지 않습니다. 원하는 경우 간단하고 간단하게 직접 또는 타사 라이브러리를 사용하여 일종의 interop을 수행해야합니다.

Windows Server ™ 2003 이상을 사용하는 경우 Kernel32.dll은 찾고자하는 기능을 정확하게 제공하는 FindFirstFile 및 FindNextFile에 대한 대응을 제공합니다. FindFirstStreamW 및 FindNextStreamW를 사용하면 특정 파일 내의 대체 데이터 스트림을 모두 찾아서 열거 할 수 있으며 이름 및 길이를 포함하여 각 파일에 대한 정보를 검색 할 수 있습니다. 관리 코드에서 이러한 기능을 사용하기위한 코드는 제가 12 월 칼럼에서 보여하는 것과 매우 유사하며, 단순히 당신

FindFirstStreamW 및 FindNextStreamW

사용 1.

그림 1

[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(); 
     } 
    } 
} 
그림에 표시됩니다 FindFirstStreamW를 호출하여 대상 파일의 전체 경로를 전달합니다. FindFirstStreamW에 대한 두 번째 매개 변수는 반환 된 데이터에서 원하는 수준을 지정합니다. 현재 숫자 값이 0 인 단 하나의 수준 (FindStreamInfoStandard) 만 있습니다. 함수의 세 번째 매개 변수는 WIN32_FIND_STREAM_DATA 구조체에 대한 포인터입니다 (기술적으로 세 번째 매개 변수가 가리키는 포인터는 두 번째 매개 변수의 값으로 지정됩니다 정보 수준에 대해 자세히 설명하고 있지만 현재는 단 하나의 수준이므로 모든 의도와 목적을 위해 이것은 WIN32_FIND_STREAM_DATA입니다. 저는 구조체의 관리되는 부분을 클래스로 선언했으며, interop 시그니처에서는 구조체에 대한 포인터로 마샬링되도록 표시했습니다. 마지막 매개 변수는 나중에 사용하기 위해 예약되어 있으므로 0이어야합니다. FindFirstStreamW에서 유효한 핸들이 반환되면 WIN32_FIND_STREAM_DATA 인스턴스에 발견 된 스트림에 대한 정보가 들어 있으며 해당 cStreamName 값을 사용 가능한 첫 번째 스트림 이름으로 호출자에게 반환 할 수 있습니다. FindNextStreamW는 FindFirstStreamW에서 반환 된 핸들을 받아들이고 제공된 WIN32_FIND_STREAM_DATA가있을 경우 다음 스트림에 대한 정보로 채 웁니다. FindNextStreamW는 다른 스트림을 사용할 수 있으면 true를 반환하고 그렇지 않으면 false를 반환합니다. 결과적으로 FindNextStreamW를 계속 호출하고 FindNextStreamW가 false를 반환 할 때까지 결과 스트림 이름을 반환합니다. 이런 일이 발생하면 FindNextStreamW가 스트림을 다 썼기 때문에 반복되지 않도록 마지막 오류 값을 두 번 확인합니다. 예기치 않은 이유가 없습니다. Windows® XP 또는 Windows 2000 Server를 사용하는 경우 불행히도 이러한 기능을 사용할 수 없지만 몇 가지 대안이 있습니다. 첫 번째 해결 방법은 현재 Kernel32.dll, NTQueryInformationFile에서 내 보낸 문서화되지 않은 함수를 포함합니다. 그러나 문서화되지 않은 함수는 이유가있어 문서화되지 않았으므로 나중에 언제든지 변경하거나 제거 할 수 있습니다. 그것들을 사용하지 않는 것이 가장 좋습니다. 이 기능을 사용하려면 웹을 검색하면 많은 참고 자료와 샘플 소스 코드를 찾을 수 있습니다. 그러나 당신의 자신의 위험에 그렇게하십시오. 다른 솔루션과 내가 그림 2에서 보여준 것 중 하나는 Kernel32.dll에서 내 보낸 두 가지 함수에 의존하며 이것들이 문서화되어 있습니다. BackupRead 및 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(); 
      } 
     } 
    } 
} 
BackupRead 뒤에 아이디어가 될 수 있다는 것입니다

를 사용

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); 

그림 2 : 이름에서 알 수있는 바와 같이, BackupRead 및 BackupSeek 백업 지원을위한 Win32®의 API의 일부입니다 파일에서 버퍼로 데이터를 읽는 데 사용되며 그런 다음 백업 저장 매체에 기록 될 수 있습니다. 그러나 BackupRead는 또한 대상 파일을 구성하는 각 대체 데이터 스트림에 대한 정보를 찾는 데 매우 편리합니다. 파일의 모든 데이터를 일련의 이산 바이트 스트림 (각 대체 데이터 스트림은 이러한 바이트 스트림 중 하나임)으로 처리하고 각 스트림 앞에 WIN32_STREAM_ID 구조가옵니다. 따라서 모든 스트림을 열거하려면 각 스트림의 시작 부분에서 모든 WIN32_STREAM_ID 구조체를 읽을 필요가 있습니다 (이 경우 BackupSeek는 매우 유용하게 사용됩니다.이 기능을 사용하면 스트림에서 스트림으로 이동할 때 사용할 수 있으므로 파일의 모든 데이터를 읽는 것). 먼저 관리되지 않는 WIN32_STREAM_ID 구조에 대한 관리 대응을 만들어야합니다, 시작하려면 :

대부분의 경우
typedef struct _WIN32_STREAM_ID { 
    DWORD dwStreamId; DWORD dwStreamAttributes; 
    LARGE_INTEGER Size; 
    DWORD dwStreamNameSize; 
    WCHAR cStreamName[ANYSIZE_ARRAY]; 
} WIN32_STREAM_ID; 

, 이것은 당신이 P/호출을 마샬링 것 다른 구조와 같다. 그러나 몇 가지 합병증이 있습니다. 맨 먼저 WIN32_STREAM_ID는 가변 크기 구조입니다. 마지막 멤버 인 cStreamName은 길이가 ANYSIZE_ARRAY 인 배열입니다. ANYSIZE_ARRAY가 1로 정의되어있는 동안 cStreamName은 이전 네 개의 필드 다음에 구조의 나머지 데이터 주소 일 뿐이므로 구조가 sizeof (WIN32_STREAM_ID) 바이트보다 크게 할당되면 추가 공간이 실제로 cStreamName 배열의 일부가됩니다. 이전 필드 인 dwStreamNameSize는 배열의 길이를 정확하게 지정합니다. 이 기능은 Win32 개발에 적합하지만 마샬 러에서는 백업 판독기에 대한 interop 호출의 일부로서 관리되지 않는 메모리에서 관리되는 메모리로이 데이터를 복사해야합니다. 마샬 러는 WIN32_STREAM_ID 구조체가 실제로 크기가 얼마나 큰지를 어떻게 알 수 있습니까? 그렇지 않습니다. 두 번째 문제는 패킹과 정렬과 관련이 있습니다. 잠시 cStreamName을 무시하고, 관리되는 WIN32_STREAM_ID 대응을 위해 다음과 같은 가능성을 고려

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

Int32입니다 크기는 4 바이트이며 INT64 8 바이트입니다. 따라서이 구조체는 20 바이트가됩니다.다음 코드를 실행하는 경우에는, 당신은 두 값은 24이 아니라 20 것을 확인할 수있는 것들 :

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

문제는 컴파일러가 이러한 구조 내에서 값이 항상 정렬되어 있는지 확인하고 싶어한다는 것입니다 적절한 경계. 4 바이트 값은 4로 나눌 수있는 주소에 있어야하며 8 바이트 값은 8로 나눌 수있는 경계에 있어야합니다. 이제 Win32StreamID 구조체의 배열을 만들면 어떻게 될지 상상해보십시오. 배열의 첫 번째 인스턴스에있는 모든 필드가 올바르게 정렬됩니다. 예를 들어 크기 필드는 두 개의 32 비트 정수 다음에 나올 수 있으므로 배열 시작 부분부터 8 바이트가되며 8 바이트 값으로 적합합니다. 그러나 구조의 크기가 20 바이트 인 경우 배열의 두 번째 인스턴스가 해당 멤버를 모두 올바르게 정렬하지는 못합니다. 정수 값은 모두 괜찮을 것이지만 long 값은 배열의 시작부터 28 바이트, 즉 8로 나눌 수없는 값입니다.이 문제를 해결하기 위해 컴파일러는 구조를 24 크기로 채 웁니다. 필드는 항상 올바르게 정렬됩니다 (배열 자체가 있다고 가정). 컴파일러가 올바른 일을한다면, 왜 내가 이것을 염려하는지 궁금해 할 것입니다. 그림 2의 코드를 보면 왜 그런지 알게 될 것입니다. 제가 설명한 첫 번째 마샬링 문제를 해결하기 위해 실제로 cStreamName을 Win32StreamID 구조 밖으로 둡니다. BackupRead를 사용하여 Win32StreamID 구조체를 채우기에 충분한 바이트를 읽은 다음 구조체의 dwStreamNameSize 필드를 검사합니다. 이제 이름의 길이를 알았으므로 BackupRead를 다시 사용하여 파일의 문자열 값을 읽을 수 있습니다. 그게 전부 잘하고 멋지 네요.하지만 Marshal.SizeOf가 20 대신에 Win32StreamID 구조체에 대해 24를 반환한다면, 너무 많은 데이터를 읽으려고 할 것입니다. 이 문제를 피하려면 Win32StreamID의 크기가 실제로 20이 아니라 24가되는지 확인해야합니다. 구조를 장식하는 StructLayoutAttribute의 필드를 사용하여 두 가지 방법으로 수행 할 수 있습니다.

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

두 번째 옵션은 팩 필드를 사용하는 것입니다 : 첫 번째는 구조가해야 정확히 얼마나 큰 런타임에 규정하는 크기 필드를 사용하는 것입니다. Pack은 LayoutKind.Sequential 값을 지정하고 구조 내의 필드 정렬을 제어 할 때 사용해야하는 패킹 크기를 나타냅니다. 관리 구조체의 기본 패킹 크기는 8입니다. 4로 변경하면 20 바이트 구조를 가져옵니다. (배열에서 실제로 사용하지 않기 때문에 효율이 떨어지지 않습니다. 이러한 포장 변화의 결과 수 또는 안정성) : 다음과 같이

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

장소에서이 코드는, 지금, 파일에 스트림을 모두 열거 할 수 있습니다 :

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); 
     } 
    } 
} 

당신에게 ' 이 버전의 FileStreamSearcher는 FindFirstStreamW 및 FindNextStreamW를 사용하는 버전보다 많은 정보를 반환합니다. BackupRead는 보안 정보, 재분석 데이터 등이 포함 된 스트림에서 작동하는 기본 스트림 및 대체 데이터 스트림 이상의 데이터를 제공 할 수 있습니다. 대체 데이터 스트림 만 보려는 경우 대체 데이터 스트림의 StreamType.AlternateData가 될 StreamInfo의 Type 속성을 기준으로 필터링 할 수 있습니다.

> 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" 

그래서, 지금 당신이에 저장된 모든 대체 데이터 스트림의 이름을 검색 할 수있어 : 이 코드를 테스트하려면 명령 프롬프트에서 echo 명령을 사용하여 대체 데이터 스트림이있는 파일을 만들 수 있습니다 파일. 큰. 그러나 실제로 해당 스트림 중 하나에서 데이터를 조작하고 싶다면 어떻게해야할까요? 불행하게도 대체 데이터 스트림의 경로를 FileStream 생성자 중 하나에 전달하려고하면 NotSupportedException이 throw됩니다. "지정된 경로 형식이 지원되지 않습니다." 이 문제를 해결하기 위해 kernel32에서 노출 된 CreateFile 함수에 직접 액세스하여 FileStream의 경로 정규화 검사를 무시할 수 있습니다.dll (그림 3 참조). 필자는 CreateFile 함수에 P/Invoke를 사용하여 경로에서 관리 권한 검사를 수행하지 않고 지정된 경로에 대한 SafeFileHandle을 열고 검색하므로 대체 데이터 스트림 식별자를 포함 할 수 있습니다. 이 SafeFileHandle은 새 관리되는 FileStream을 생성하는 데 사용되어 필요한 액세스를 제공합니다. 이를 통해 System.IO 네임 스페이스의 기능을 사용하여 대체 데이터 스트림의 내용을 쉽게 조작 할 수 있습니다. 다음의 예는 판독 및 C의 내용을 출력한다 : \ TEST.TXT : 이전 예에서 작성한 magStream :

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

도 3

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 CreateFile에 대해 P/호출 사용 MSDN Magazine from January 2006에 있습니다.

+4

링크 전용 답변이 잘못된 이유의 좋은 예입니다. –

+0

MSDN 매거진에 대한 모든 링크가 끊어지고 MSDN 웹 사이트 링크도 곧 중단됩니다. 대답에 대한 자세한 내용을 포함 시키십시오. – AaA

14

이 넛츠 패키지 CodeFluent Runtime Client은 (다른 유틸리티 중에서) 생성/읽기/업데이트/삭제/열거 연산을 지원하는 NtfsAlternateStream Class을 가지고 있습니다.

+7

그리고 사용 방법에 대한 몇 가지 예가 있습니다. http://blog.codefluententities.com/2013/03/14/manipulating-ntfs-alternate-data-streams-in-c-with-the-codefluent-runtime- 고객/ – polkduran

관련 문제