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

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

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.

How to use SQLInstallDriverEx in C#?

How would I rewrite the following function in order to use it in C#? The function is part of odbccp32.dll and is used to install an ODBC driver.
BOOL SQLInstallDriverEx(
LPCSTR lpszDriver,
LPCSTR lpszPathIn,
LPSTR lpszPathOut,
WORD cbPathOutMax,
WORD * pcbPathOut,
WORD fRequest,
LPDWORD lpdwUsageCount);
SQLInstallDriverEx Reference
This is my attempt:
[DllImport("odbccp32")]
private static extern bool SQLInstallDriverEx(
string lpszDriver,
string lpszPathIn,
out string lpszPathOut,
ushort cbPathOutMax,
out uint pcbPathOut,
ushort fRequest,
out uint lpdwUsageCount);
A sample usage would be deeply appreciated as well.
p/Invoke Toolkit generates the following:
[DllImport("odbccp32.dll")]
public static extern bool SQLInstallDriverEx(
[In, MarshalAs(UnmanagedType.LPStr)] string lpszDriver,
[In, MarshalAs(UnmanagedType.LPStr)] string lpszPathIn,
[MarshalAs(UnmanagedType.LPStr)] System.Text.StringBuilder lpszPathOut,
ushort cbPathOutMax, ref ushort pcbPathOut, ushort fRequest, ref uint lpdwUsageCount);
public const ushort ODBC_INSTALL_INQUIRY = 1;
public const ushort ODBC_INSTALL_COMPLETE = 2;
The In/MarshalAs attributes may not be necessary. StringBuilders work for "out" strings as the marshaller knows how to convert under the hood. You need to call the method twice, once to resolve the path/path length and the second to pass those values in. That step can be skipped if you know these before hand, but the path can change from what you provide if the driver already exists or the path is too long. The second call requires that the lpszPathOut be non-null and cbPathOutMax be non-zero.
The driver description that you pass to the method is made up of a list of null-byte terminated strings. The list is terminated with a double null.
// if target path is null, it should default to the system directory
var targetPath = #"c:\windows\system32";
ushort len = 0; uint usageCount=0;
// replace this with your actual driver description, note the double \0 at the end
var driver = "Text\0Driver=TEXT.DLL\0Setup=TXTSETUP.DLL\0FileUsage=1\0"
+ "FileExtns=*.txt,*.csv\0\0";
var ret = SQLInstallDriverEx(driver, targetPath, null, 0,
ref len, ODBC_INSTALL_INQUIRY, ref usageCount);
if (ret) {
var sbPath = new StringBuilder(len);
if (ret = SQLInstallDriverEx(driver, targetPath, sbPath, len,
ref len, ODBC_INSTALL_COMPLETE, ref usageCount)) {
// sbPath will now contain the actual path, which may be different
// if there is a previous installation or the path was truncated
// perhaps check here if that was intended
// usageCount will be updated by the system
}
}
if (!ret) {
// query SqlInstallerError(...) here
}
If the return value is false, you will need to call the SqlInstallerError method which you will have to import as well. Fortunately there is a p/invoke page for this method and how to call it available at http://www.pinvoke.net/default.aspx/odbccp32/SQLInstallerError.html. The error codes and what they mean are already outlined on the MSDN page you linked.
Note that you can skip the two-phase method call and call with ODBC_INSTALL_COMPLETE once, provided you have provided correct buffer/sizes.

Marshalling Variable Length Array in 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?

PInvoke ReadFile kernel32: Attempted to read or write protected memory. This is often an indication that other memory is corrupt

I'm trying to make kernel32's ReadFile work since native c# alternatives are slow as...
The following code was working a couple of times, then suddenly only gives "Attempted to read or write protected memory. This is often an indication that other memory is corrupt."
I figured a restart of the server would make it work a couple times before getting the same error, but it seems I was wrong there, it happens no matter what I do now..
Here's my code:
using System.Runtime.InteropServices;
private WinFileIO.WinFileIO _winFile;
_winFile = new WinFileIO.WinFileIO(new byte[bufferSize]);
_winFile.OpenForReading(_fileInfo.FullName);
public WinFileIO(Array Buffer)
{
PinBuffer(Buffer);
pHandleRead = IntPtr.Zero;
pHandleWrite = IntPtr.Zero;
bufferSize = Buffer.GetLength(0);
}
private void PinBuffer(Array Buffer)
{
UnpinBuffer();
gchBuf = GCHandle.Alloc(Buffer, GCHandleType.Pinned);
IntPtr pAddr = Marshal.UnsafeAddrOfPinnedArrayElement(Buffer, 0);
// pBuffer is the pointer used for all of the I/O functions in this class.
pBuffer = (void*)pAddr.ToPointer();
}
public void UnpinBuffer()
{
if (gchBuf.IsAllocated)
gchBuf.Free();
}
[System.Runtime.InteropServices.DllImport("kernel32", SetLastError = true)]
static extern unsafe System.IntPtr CreateFile
(
string FileName, // file name
uint DesiredAccess, // access mode
uint ShareMode, // share mode
uint SecurityAttributes, // Security Attributes
uint CreationDisposition, // how to create
uint FlagsAndAttributes, // file attributes
int hTemplateFile // handle to template file
);
[System.Runtime.InteropServices.DllImport("kernel32", SetLastError = true)]
private unsafe static extern bool ReadFile
(
int hFile,
byte[] arraypBuffer,
int NumberOfBytesToRead,
ref int lpNumberOfBytesToRead,
int* ptr
);
[System.Runtime.InteropServices.DllImport("kernel32", SetLastError = true)]
static extern unsafe bool SetFilePointer
(
System.IntPtr hObject,
int lDistanceToMove,
ref int lpDistanceToMoveHigh,
EMoveMethod dwMoveMethod
);
public void OpenForReading(string FileName)
{
Close(true, false);
pHandleRead = CreateFile(FileName, 3, 3, 0, OPEN_EXISTING, 0, 0);
if (pHandleRead == System.IntPtr.Zero)
{
Win32Exception WE = new Win32Exception();
ApplicationException AE = new ApplicationException("WinFileIO:OpenForReading - Could not open file " +
FileName + " - " + WE.Message);
throw AE;
}
}
public void MoveOffset(int moveDistance, EMoveMethod moveMethod)
{
if (pHandleRead != System.IntPtr.Zero)
{
int moveDistanceHigh = 0;
SetFilePointer(pHandleRead, moveDistance, ref moveDistanceHigh, moveMethod);
}
}
public unsafe Tuple<int, byte[]> ReadChunk(int bufferSize, int offset)
{
int bytesRead = 0;
byte[] bufBytes = new byte[bufferSize];
MoveOffset(offset, EMoveMethod.Begin);
int pointer = (int)Marshal.PtrToStructure(pHandleRead, typeof(int));
if (ReadFile(pointer, bufBytes, bufferSize, ref bytesRead, null))
{
byte[] outBytes = new byte[bytesRead];
Array.Copy(bufBytes, outBytes, bytesRead);
return new Tuple<int, byte[]>(bytesRead, outBytes);
}
return null;
}
I think that's all the relevant code.
I'm guessing it has something to do with the CreateFile and ReadFile-signatures not being fully compatible (IntPtr vs. int),
the Marshal.PtrToStructure for the IntPtr not pointing where it should, or memory not being freed or something?
The fact that it didn't work for a couple tries after a reboot confuses me though.
Anyone spot anything obvious or have any suggestions I can look into?
Thanks
Edit: As you might notice, this is kind of a mishmash of different approaches, I'm not using the pinned buffer anymore as I struggled to get the contents of the buffer read like I would want to.
Edit2: The stacktrace says this is the problem:
at System.Runtime.InteropServices.Marshal.PtrToStructure(IntPtr ptr, Type structureType)
Your pinvoke declarations are bad, get good ones from pinvoke.net
The 1st argument of ReadFile() is a handle, the one you got from CreateFile(). It is IntPtr. You dug yourself a hole by trying to convert the IntPtr to int, the Marshal.PtrToStructure() call is not correct. And will almost always bomb with an AVE, a handle is not a pointer.
Fix the [DllImport] declaration and use the IntPtr you got from CreateFile() directly. And don't forget that System.IO.FileStream is the .NET wrapper for these winapi functions, you only ever need to pinvoke CreateFile() if you need to open a handle to a device instead of a file. You then still use the FileStream constructor that takes a handle and use its Read() method to call ReadFile().
This code
int pointer = (int)Marshal.PtrToStructure(pHandleRead, typeof(int));
does not do what you think it does. This code attempts to treat pHandleRead as a pointer to int. But that's not what you want at all. The code that does what you want is:
int iHandleRead = pHandleRead.ToInt32();
In fact you only find yourself writing that code because your p/invoke declarations are poor. Your ReadFile declaration should receive the file handle as IntPtr and not as int. If it did so then you would not need the code above at all and would pass pHandleRead.
More general points:
I do not understand why you have chosen to use unsafe code here. Nothing at all here requires that.
I also think it unlikely that this code, if you make it work, will perform better than the managed equivalent.

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.

Categories