Marshalling Variable Length Array in C# - c#

I am processing input data from a human interface device using GetRawInputData. There is a RAWINPUT structure which contains a RAWHID structure:
typedef struct tagRAWHID {
DWORD dwSizeHid;
DWORD dwCount;
BYTE bRawData[1];
} RAWHID, *PRAWHID, *LPRAWHID;
From the documentation:
The size of the bRawData array is dwSizeHid * dwCount.
The code for the structs are trivial (save RAWHID) so I will not include them.
The HID is a magnetic stripe reader, so depending on the number of tracks and their length, the size of bRawData changes.
I have a solution for my use case but I would like to do it the right way.
P/Invoke Signature:
[DllImport("user32.dll", SetLastError = true)]
public static extern int GetRawInputData(IntPtr hRawInput, DataCommand command,
[MarshalAs(UnmanagedType.LPArray)] byte[] pData, ref int size, int sizeHeader);
Calling GetInputData:
var dwSize = 0;
GetRawInputData(lParam, DataCommand.RID_INPUT, null, ref dwSize, Marshal.SizeOf(typeof(Rawinputheader)));
//dwSize is now the size of input data since pData was null
var data = new byte[dwSize];
if (dwSize != GetRawInputData(lParam, DataCommand.RID_INPUT, data, ref dwSize, Marshal.SizeOf(typeof(Rawinputheader))))
{
//error occured
}
//turn it into a string
string strData = Encoding.ASCII.GetString(data);
I then regex the entire structure's data and pull out the tracks from the magnetic swipe since they have sentinel characters at the beginning and end. Is there a more elegant way to go about this?

Related

ReadProcessMemory not working with Dolphin emulator

I'm trying to get access to some of the values in the memory of an emulation of Smash Bros Melee running in Dolphin. The Dolphin debug mode says that the address of the value I want is 0x80C6BA10 (I'm assuming that it means 0x00C6BA10, because 0x80C6BA10 goes over the integer limit, and it had previously referred to the same address as 0x00C6BA10). But when I pass this into the ReadProcessMemory function, I get a byte array that is just [0,0,0,0] (I'm using 4 bytes because the value I'm trying to get is an 8 digit hexadecimal number).
I tried to use OllyDbg to check if the address is right, but for some reason Dolphin doesn't work with OllyDbg, maybe because it isn't 64 bit? I've also tried using the unchecked() method in order to pass 0x80C6BA10 instead of 0x00C6BA10.
public class Program
{
//Constant that says we want to just read memory
const int PROCESS_WM_READ = 0x0010;
// **** Imports the functions used to get memory data ****
[DllImport("kernel32.dll")]
public static extern IntPtr OpenProcess(int dwDesiredAccess, bool
bInheritHandle, int dwProcessId);
[DllImport("kernel32.dll")]
public static extern bool ReadProcessMemory(int hProcess, int
lpBaseAddress, byte[] lpBuffer, int dwSize, ref int lpNumberOfBytesRead);
static void Main(string[] args)
{
//Gets process by finding the first process with the name dolphin
Process melee = Process.GetProcessesByName("Dolphin")[0];
IntPtr processHandle = OpenProcess(PROCESS_WM_READ, false,
melee.Id);
int bytesRead = 0;
//The list that will be populated by the data we find in the
memory
byte[] buffer = new byte[4];
//Reads the memory at the specified location
ReadProcessMemory((int)processHandle, 0x00C6BA10, buffer,
buffer.Length, ref bytesRead);
//Converts byte array into floating point number -- not currently
being used
float memoryValue = BitConverter.ToSingle(buffer, 0);
foreach (byte b in buffer)
{
Console.WriteLine(b);
}
Console.ReadLine();
}
}
I expect the output to be anything but 0, but what I get back is an array of 0s. I think part of the issue might be that I'm trying to get memory values from a process that is running as a sub-process of Dolphin.

Get pointer (IntPtr) from a Span<T> staying in safe mode

I would like to use Span and stackalloc to allocate an array of struct and pass it to an interop call. Is it possible to retrieve a pointer (IntPtr) from the Span without being unsafe ?
Here is how i did it without unsafe:
i just changed lpBuffer to ref byte instead of byte[] (for c++ user COULD represent it as uint8_t*)
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool ReadProcessMemory(nint hProcess, nuint lpBaseAddress, ref byte lpBuffer, int dwSize, out int lpNumberOfBytesRead);
Span<byte> bytes = stackalloc byte[8];
// Get reference to first byte in the span (for c++ user we COULD represent it as `uint8_t*`)
ref byte bytesReference = ref MemoryMarshal.AsRef<byte>(bytes);
// You can pass `ref MemoryMarshal.AsRef<byte>(bytes)` to the function directly
bool success = Win32.ReadProcessMemory(_processHandle, address, ref bytesReference, cSize, out int numberOfBytesRead);
lpBuffer could be in byte instead of ref byte as that will allow you to use ReadOnlySpan
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool WriteProcessMemory(nint hProcess, nuint lpBaseAddress, in byte lpBuffer, int dwSize, out int lpNumberOfBytesWritten);
ReadOnlySpan<byte> bytes = stackalloc byte[8];
ref readonly byte bytesReference = ref MemoryMarshal.AsRef<byte>(bytes);
// You can pass `in MemoryMarshal.AsRef<byte>(bytes)` to the function directly
Win32.WriteProcessMemory(_processHandle, address, in bytesReference, bytes.Length, out int numberOfBytesWritten);
Notes:
[+] You could use GetPinnableReference instead of MemoryMarshal.AsRef but it is not intended to be called by user code.
[+] ReadProcessMemory Windows API function.
[+] ReadProcessMemory(kernel32) pinvoke Original implementation.

C# wrapper class for c++ lib dll

I am trying to create a class in c# to access the function in a c++ lib. The function in the c++ dll :
bool WriteReply(const unsigned char *reply, const unsigned long reply_length).
A sample of how its used in c++:-
unsigned short msg_id = 0x0000;
byte msg_body[] = {(byte)(GetTickCount()/0x100)}; // a random value for loopback data
// combine the message id and message body into an big msg
unsigned long msg_length = sizeof(msg_id)+sizeof(msg_body);
byte* big_msg = new byte[msg_length];
big_msg[0] = LOBYTE(msg_id);
big_msg[1] = HIBYTE(msg_id);
memcpy((void*)&big_msg[2], (void*)msg_body, sizeof(msg_body));
// send the big message
if (!big_dev.WriteReply(big_msg, msg_length))
{
//do something here
}
I can't seem to pass the function from c# to the dll (AccessViolationException). This is the command i've tried:-
byte[] bytearray = new byte[3] { 0x01, 0x02, 0x03 };
IntPtr unmanagedPointer = Marshal.AllocHGlobal(bytearray.Length);
Marshal.Copy(bytearray, 0, unmanagedPointer, bytearray.Length);
bool writestatus = (bool)NativeMethods.WriteReply(unmanagedPointer, (uint)bytearray.Length);
and on the import side:-
[DllImport("dllname.dll", EntryPoint = "WriteReply")]
[return: MarshalAs(UnmanagedType.U1)]
internal static extern bool WriteReply(IntPtr msg, uint reply_length);
Please let me know where have i gone wrong?Thanks!
Assuming your C++ method uses the string and does not modify it...
Try this
__declspec(dllexport) bool __cdecl WriteReply(const unsigned char *reply, const unsigned long reply_length);
[DllImport("libfile.dll", EntryPoint = "WriteReply")]
private static extern bool WriteReplyExternal(
[MarshalAs(UnmanagedType.LPStr)] [Out] string replyString,
[Out] UInt32 replyLength);
Or better yet (since C strings are null-terminated and the buffer is readonly, so you don't have to worry about buffer overflow, the length parameter is redudant):
__declspec(dllexport) bool __cdecl WriteReply(const unsigned char *reply);
[DllImport("libfile.dll", EntryPoint = "WriteReply")]
private static extern bool WriteReplyExternal(
[MarshalAs(UnmanagedType.LPStr)] [Out] string replyString);
These will work if the method is not within a class, otherwise you will need to use the C++ mangled name as the entry point.
If your string contains characters outside the 1...127 ASCII range (e.g. non-English letters), you should use wchar_t instead of char in the C++ and LPWStr instead of LPStr in the marshalling.
Edit:
You need to wrap the private method with another method with a signature that is more appropriate for .NET e.g.
public void WriteReply(string message)
{
var result = WriteReplyExternal(message, message.Length);
if (result == false)
throw new ApplicationException("WriteReplay failed ...");
}
I think the latest addition of code provides a clue as to the real problem:
if (!big_dev.WriteReply(big_msg, msg_length))
This cannot work because WriteReply is an member function. You need to be calling a C style function rather than a C++ member function. The latter requires an instance (big_dev in the code sample).

How to initialise an unsafe pointer in C# and convert it to a byte[]?

I put a post up yesterday, How does one create structures for C# originally written in C++.
Thank you for your responses.
I'm trying, without much success, to use DeviceIOControl on an ARM platform running WinCE 6.0 and .NET Compact framework 2.0 All I am trying to achieve is the control of a port pin and it's proving to be a nightmare.
The following is the PInvoke declaration:
[DllImport("coredll.dll", EntryPoint = "DeviceIoControl", SetLastError = true)]
internal static extern bool DeviceIoControlCE(int hDevice,
int dwIoControlCode,
byte[] lpInBuffer,
int nInBufferSize,
byte[] lpOutBuffer,
int nOutBufferSize,
ref int lpBytesReturned,
IntPtr lpOverlapped);
The PInvoke declaration suggests a byte[] may be passed to it simply. Surely it's an easy matter to write the values to each member of a structure, convert it to an array of bytes and pass it to the dll.
I have the following:
[StructLayout(LayoutKind.Sequential)]
public struct pio_desc
{
unsafe byte* pin_name; //Length???
public uint pin_number; //4 bytes
public uint default_value; //4 bytes
public byte attribute; //1 byte
public uint pio_type; //4 bytes
}
and
pio_desc PA13 = new pio_desc();
So surely now it's a matter of doing something like:
PA13.pin_number = AT91_PIN_PA13; //Length 4 bytes
PA13.default_value = 0; //Length 4 bytes
PA13.attribtue = PIO_DEFAULT; //Length 1 byte
PA13.pio_type = PIO_OUTPUT; //Length 4 bytes
and to convert (pin_number for example) to a byte[]:
byte[] temp = BitConverter.GetBytes(PA13.pin_number); //uints are 4 bytes wide
byteArray[++NumberOfChars] = temp[0];
byteArray[++NumberOfChars] = temp[1];
byteArray[++NumberOfChars] = temp[2];
byteArray[++NumberOfChars] = temp[3]; //Will need to check on Endianess
Questions:
In the structure PA13, how do I initialise the unsafe pointer pin_name? The author of the driver notes that this is not used, presumably by his driver. Will Windows need this to be some value?
PA13.pin_name = ??????
Then, how do I convert this pointer to a byte to fit into my byte[] array to be passed to DeviceIOControl?
I've become quite disappointed and frustrated at how difficult it is to change the voltage level of a port pin - I've been struggling with this problem for days now. Because I come from a hardware background, I think it's going to be easier (and less eligant) for me to implement IO control on another controller and to pass control data to it via a COM port.
Thanks again for any (simple) assistance.
You will need to do a few different things here. First, replace this member:
unsafe byte* pin_name; //Length???
with:
[MarshalAs(UnmanagedType.LPStr)] public string pin_name;
Then replace the in/out buffers in the P/Invoke declaration from byte[] to IntPtr. Then you can use this code to convert the data:
pio_desc PA13;
// Set the members of PA13...
IntPtr ptr = IntPtr.Zero;
try {
var size = Marshal.SizeOf(PA13);
ptr = Marshal.AllocHGlobal(size);
Marshal.StructureToPtr(PA13, ptr, false);
// Your P/Invoke call goes here.
// size will be the "nInBufferSize" argument
// ptr will be the "lpInBuffer" argument
} finally {
if (ptr != IntPtr.Zero) {
Marshal.DestroyStructure(ptr, typeof(pio_desc));
Marshal.FreeHGlobal(ptr);
}
}
You can make this a lot easier by lying about the [DllImport] declaration. Just declare the lpInBuffer argument as the structure type, the pinvoke marshaller will convert it to a pointer anyway. Thus:
[DllImport("coredll.dll", EntryPoint = "DeviceIoControl", SetLastError = true)]
internal static extern bool SetOutputPin(IntPtr hDevice,
int dwIoControlCode,
ref pio_desc lpInBuffer,
int nInBufferSize,
IntPtr lpOutBuffer,
int nOutBufferSize,
out int lpBytesReturned,
IntPtr lpOverlapped);
Using IntPtr for lpOutBuffer because the driver probably doesn't return anything. Pass IntPtr.Zero. Same idea with the structure. If the field isn't used then simply declare it as an IntPtr:
[StructLayout(LayoutKind.Sequential)]
public struct pio_desc
{
public IntPtr pin_name; // Leave at IntPtr.Zero
public uint pin_number; //4 bytes
public uint default_value; //4 bytes
public byte attribute; //1 byte
public uint pio_type; //4 bytes
}
Be careful about the Packing property, it makes a difference here because of the byte sized field. You may need 1 but that's just a guess without knowing anything about the driver. If you have working C code then test the value of sizeof(pio_desc) and compare with Marshal.SizeOf(). Pass Marshal.SizeOf(typeof(pio_desc)) as the nInBufferSize argument. If you would have posted the C declarations then this would have been easier to answer accurately.
Declare lpInBuffer and lpOutBuffer as IntPtr. Initialize them using Marshal.AllocHGlobal (don't forget to release them with Marshal.FreeHGlobal in the end). Fill these buffer and read it using different Marshal.Copy overloads.

How to read and modify NTFS Alternate Data Streams using .NET [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 5 years ago.
Improve this question
How can I read and modify "NTFS Alternate Data Streams" using .NET?
It seems there is no native .NET support for it. Which Win32 API's would I use? Also, how would I use them, as I don't think this is documented?
Here is a version for 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;
}
A First, nothing in the Microsoft® .NET Framework provides this functionality. If you want it, plain and simple you'll need to do some sort of interop, either directly or using a third-party library.
If you're using Windows Server™ 2003 or later, Kernel32.dll exposes counterparts to FindFirstFile and FindNextFile that provide the exact functionality you're looking for. FindFirstStreamW and FindNextStreamW allow you to find and enumerate all of the Alternate Data Streams within a particular file, retrieving information about each, including its name and its length. The code for using these functions from managed code is very similar to that which I showed in my December column, and is shown in Figure 1.
Figure 1 Using FindFirstStreamW and 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();
}
}
}
You simply call FindFirstStreamW, passing to it the full path to the target file. The second parameter to FindFirstStreamW dictates the level of detail you want in the returned data; currently, there is only one level (FindStreamInfoStandard), which has a numerical value of 0. The third parameter to the function is a pointer to a WIN32_FIND_STREAM_DATA structure (technically, what the third parameter points to is dictated by the value of the second parameter detailing the information level, but as there's currently only one level, for all intents and purposes this is a WIN32_FIND_STREAM_DATA). I've declared that structure's managed counterpart as a class, and in the interop signature I've marked it to be marshaled as a pointer to a struct. The last parameter is reserved for future use and should be 0.
If a valid handle is returned from FindFirstStreamW, the WIN32_FIND_STREAM_DATA instance contains information about the stream found, and its cStreamName value can be yielded back to the caller as the first stream name available. FindNextStreamW accepts the handle returned from FindFirstStreamW and fills the supplied WIN32_FIND_STREAM_DATA with information about the next stream available, if it exists. FindNextStreamW returns true if another stream is available, or false if not.
As a result, I continually call FindNextStreamW and yield the resulting stream name until FindNextStreamW returns false. When that happens, I double check the last error value to make sure that the iteration stopped because FindNextStreamW ran out of streams, and not for some unexpected reason.
Unfortunately, if you're using Windows® XP or Windows 2000 Server, these functions aren't available to you, but there are a couple of alternatives. The first solution involves an undocumented function currently exported from Kernel32.dll, NTQueryInformationFile. However, undocumented functions are undocumented for a reason, and they can be changed or even removed at any time in the future. It's best not to use them. If you do want to use this function, search the Web and you'll find plenty of references and sample source code. But do so at your own risk.
Another solution, and one which I've demonstrated in Figure 2, relies on two functions exported from Kernel32.dll, and these are documented. As their names imply, BackupRead and BackupSeek are part of the Win32® API for backup support:
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);
Figure 2 Using BackupRead and 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();
}
}
}
}
The idea behind BackupRead is that it can be used to read data from a file into a buffer, which can then be written to the backup storage medium. However, BackupRead is also very handy for finding out information about each of the Alternate Data Streams that make up the target file. It processes all of the data in the file as a series of discrete byte streams (each Alternate Data Stream is one of these byte streams), and each of the streams is preceded by a WIN32_STREAM_ID structure. Thus, in order to enumerate all of the streams, you simply need to read through all of these WIN32_STREAM_ID structures from the beginning of each stream (this is where BackupSeek becomes very handy, as it can be used to jump from stream to stream without having to read through all of the data in the file).
To begin, you first need to create a managed counterpart for the unmanaged WIN32_STREAM_ID structure:
typedef struct _WIN32_STREAM_ID {
DWORD dwStreamId; DWORD dwStreamAttributes;
LARGE_INTEGER Size;
DWORD dwStreamNameSize;
WCHAR cStreamName[ANYSIZE_ARRAY];
} WIN32_STREAM_ID;
For the most part, this is like any other structure you'd marshal through P/Invoke. However, there are a few complications. First and foremost, WIN32_STREAM_ID is a variable-sized structure. Its last member, cStreamName, is an array with length ANYSIZE_ARRAY. While ANYSIZE_ARRAY is defined to be 1, cStreamName is just the address of the rest of the data in the structure after the previous four fields, which means that if the structure is allocated to be larger than sizeof (WIN32_STREAM_ID) bytes, that extra space will in effect be part of the cStreamName array. The previous field, dwStreamNameSize, specifies exactly how long the array is.
While this is great for Win32 development, it wreaks havoc on a marshaler that needs to copy this data from unmanaged memory to managed memory as part of the interop call to BackupRead. How does the marshaler know how big the WIN32_STREAM_ID structure actually is, given that it's variable sized? It doesn't.
The second problem has to do with packing and alignment. Ignoring cStreamName for a moment, consider the following possibility for your managed WIN32_STREAM_ID counterpart:
[StructLayout(LayoutKind.Sequential)]
public struct Win32StreamID {
public int dwStreamId;
public int dwStreamAttributes;
public long Size;
public int dwStreamNameSize;
}
An Int32 is 4 bytes in size and an Int64 is 8 bytes. Thus, you would expect this struct to be 20 bytes. However, if you run the following code, you'll find that both values are 24, not 20:
int size1 = Marshal.SizeOf(typeof(Win32StreamID));
int size2 = sizeof(Win32StreamID); // in an unsafe context
The issue is that the compiler wants to make sure that the values within these structures are always aligned on the proper boundary. Four-byte values should be at addresses divisible by 4, 8-byte values should be at boundaries divisible by 8, and so on. Now imagine what would happen if you were to create an array of Win32StreamID structures. All of the fields in the first instance of the array would be properly aligned. For example, since the Size field follows two 32-bit integers, it would be 8 bytes from the start of the array, perfect for an 8-byte value. However, if the structure were 20-bytes in size, the second instance in the array would not have all of its members properly aligned. The integer values would all be fine, but the long value would be 28 bytes from the start of the array, a value not evenly divisible by 8. To fix this, the compiler pads the structure to a size of 24, such that all of the fields will always be properly aligned (assuming the array itself is).
If the compiler's doing the right thing, you might be wondering why I'm concerned about this. You'll see why if you look at the code in Figure 2. In order to get around the first marshaling issue I described, I do in fact leave the cStreamName out of the Win32StreamID structure. I use BackupRead to read in enough bytes to fill my Win32StreamID structure, and then I examine the structure's dwStreamNameSize field. Now that I know how long the name is, I can use BackupRead again to read in the string's value from the file. That's all well and dandy, but if Marshal.SizeOf returns 24 for my Win32StreamID structure instead of 20, I'll be attempting to read too much data.
To avoid this, I need to make sure that the size of Win32StreamID is in fact 20 and not 24. This can be accomplished in two different ways using fields on the StructLayoutAttribute that adorns the structure. The first is to use the Size field, which dictates to the runtime exactly how big the structure should be:
[StructLayout(LayoutKind.Sequential, Size = 20)]
The second option is to use the Pack field. Pack indicates the packing size that should be used when the LayoutKind.Sequential value is specified and controls the alignment of the fields within the structure. The default packing size for a managed structure is 8. If I change that to 4, I get the 20-byte structure I'm looking for (and as I'm not actually using this in an array, I don't lose efficiency or stability that might result from such a packing change):
[StructLayout(LayoutKind.Sequential, Pack = 4)]
public struct Win32StreamID {
public StreamType dwStreamId;
public int dwStreamAttributes;
public long Size;
public int dwStreamNameSize; // WCHAR cStreamName[1];
}
With this code in place, I can now enumerate all of the streams in a file, as shown here:
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);
}
}
}
You'll notice that this version of FileStreamSearcher returns more information than the version that uses FindFirstStreamW and FindNextStreamW. BackupRead can provide data on more than just the primary stream and Alternate Data Streams, also operating on streams containing security information, reparse data, and more. If you only want to see the Alternate Data Streams, you can filter based on the StreamInfo's Type property, which will be StreamType.AlternateData for Alternate Data Streams.
To test this code, you can create a file that has Alternate Data Streams using the echo command at the command prompt:
> 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"
So, now you're able to retrieve the names of all Alternate Data Streams stored in a file. Great. But what if you want to actually manipulate the data in one of those streams? Unfortunately, if you attempt to pass a path for an Alternate Data Stream to one of the FileStream constructors, a NotSupportedException will be thrown: "The given path's format is not supported."
To get around this, you can bypass FileStream's path canonicalization checks by directly accessing the CreateFile function exposed from kernel32.dll (see Figure 3). I've used a P/Invoke for the CreateFile function to open and retrieve a SafeFileHandle for the specified path, without performing any of the managed permission checks on the path, so it can include Alternate Data Stream identifiers. This SafeFileHandle is then used to create a new managed FileStream, providing the required access. With that in place, it's easy to manipulate the contents of an Alternate Data Stream using the System.IO namespace's functionality. The following example reads and prints out the contents of the C:\test.txt:magStream created in the previous example:
string path = #"C:\test.txt:magStream";
using (StreamReader reader = new StreamReader(CreateFileStream(path, FileAccess.Read, FileMode.Open, FileShare.Read))) {
Console.WriteLine(reader.ReadToEnd());
}
Figure 3 Using P/Invoke for 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 in MSDN Magazine from January 2006.
There is no native .NET support for them. You have to use P/Invoke to call the native Win32 methods.
To create them, call CreateFile with a path like filename.txt:streamname. If you use the interop call that returns a SafeFileHandle, you can use that to construct a FileStream that you can then read & write to.
To list the streams that exist on a file, use FindFirstStreamW and FindNextStreamW (which exist only on Server 2003 and later - not XP).
I don't believe you can delete a stream, except by copying the rest of the file and leaving off one of the streams. Setting the length to 0 may also work, but I haven't tried it.
You can also have alternate data streams on a directory. You access them the same as with files - C:\some\directory:streamname.
Streams can have compression, encryption and sparseness set on them independent of the default stream.
This nuget package CodeFluent Runtime Client has (amongst other utilities) an NtfsAlternateStream Class that supports create/read/update/delete/enumeration operations.
Not in .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);
}

Categories