I'm trying to modify some code that dumps a specific file and I want to modify the first bytes of the dumped file.
I've already done it in a C equivalent program like this :
[...]
SetFilePointer(hDmpFile, 0, 0, FILE_BEGIN);
WriteFile(hDmpFile, "AA", 2, NULL, NULL);
I want to do the same thing in C# but I got some error regarding the WriteFile() function.
Here's what I've done so far :
[...]
public enum EMoveMethod : uint
{
Begin = 0,
Current = 1,
End = 2
}
[DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern uint SetFilePointer(
[In] SafeFileHandle hFile,
[In] int lDistanceToMove,
[Out] out int lpDistanceToMoveHigh,
[In] EMoveMethod dwMoveMethod);
[DllImport("kernel32.dll", BestFitMapping = true, CharSet = CharSet.Ansi)]
public static extern bool WriteFile(
IntPtr hFile,
System.Text.StringBuilder lpBuffer,
uint nNumberOfBytesToWrite,
out uint lpNumberOfBytesWritten,
[In] ref System.Threading.NativeOverlapped lpOverlapped);
[...]
int moveDistanceHighBits = 0;
uint test = SetFilePointer(hDmpFile, 0, out moveDistanceHighBits, EMoveMethod.Begin);
uint test2 = WriteFile(hDmpFile, "AA", 2, null, null);
hDmpFile.Dispose();
[...]
Here's the error I got when executing the file :
Unhandled Exception: System.NotImplementedException: The method or operation is not implemented.
at Foo.Program.WriteFile(SafeFileHandle hDmpFile, String v1, Int32 v2, Object value1, Object value2)
at Foo.Program.Execute(String[] args)
at Foo.Program.Main(String[] args)
For the record, I'm an absolute disaster when it comes to coding and I've never done C#...
Related
I have a simple C driver that accepts a ULONG parameter. In C language I use:
unsigned long ip = atoll(argv[1]);
if (!DeviceIoControl(
DeviceHandle,
0x10,
&ip,
sizeof(ip),
NULL,
0,
&BytesReturned,
NULL
))
It works well, but when I do it in C#,
[return: MarshalAs(UnmanagedType.Bool)]
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool DeviceIoControl([In] IntPtr hDevice,
[In] int dwIoControlCode, [In] IntPtr lpInBuffer,
[In] int nInBufferSize, [Out] IntPtr lpOutBuffer,
[In] int nOutBufferSize, out int lpBytesReturned,
[In] IntPtr lpOverlapped);
...
Int64 ip = ip2long(ipstr);
if (! Win32.DeviceIoControl(
hDevice, 0x10,
(IntPtr) ip, Marshal.SizeOf(ip),
IntPtr.Zero, 0, out bytesReturned, IntPtr.Zero))
{
throw new Exception("DeviceIoControl(): " + Marshal.GetLastWin32Error());
}
It always result in 998 error: Invalid memory access.
From DbgView, the call never reached the driver. What was wrong here?
Interesting, you can't convert uint to IntPtr directly. You must allocate memory to do that:
IntPtr ipPtr = Marshal.AllocHGlobal(Marshal.SizeOf(ip));
Marshal.WriteInt64(ipPtr, ip);
I am receiving an AccessViolationException when calling the ReadFile API. I have looked at all of the entries on StackOverflow dealing with similar issues and have not had any success deciphering what is wrong.
Here is the relevant code in question:
INVOKES
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr CreateFile([MarshalAs(UnmanagedType.LPTStr)] string filename, UInt32 desiredaccess, UInt32 sharemode, IntPtr securityattributes, UInt32 creationdisposition, UInt32 flagsandattributes, IntPtr templatefile);
[DllImport("kernel32.dll", BestFitMapping = true, CharSet = CharSet.Ansi, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool ReadFile(IntPtr hFile, out byte[] lpbuffer, UInt32 nNumberofBytesToRead, out UInt32 lpNumberofBytesRead, IntPtr lpOverlapped);
[DllImport("kernel32.dll", BestFitMapping = true, CharSet = CharSet.Ansi, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool WriteFile(IntPtr hFile, byte[] lpBuffer, UInt32 nNumberOfBytesToWrite, out UInt32 lpNumberOfBytesWritten, IntPtr lpOverlapped);
CREATE FILE FUNCTION CALL
private IntPtr OpenPort(string port)
{
IntPtr printerhandle = IntPtr.Zero;
printerhandle = CreateFile(port,
(UInt32)(FileAccess.GENERIC_READ | FileAccess.GENERIC_WRITE),
(UInt32)(FileShare.FILE_SHARE_READ | FileShare.FILE_SHARE_WRITE),
IntPtr.Zero,
(UInt32)FileMode.OPEN_EXISTING,
(UInt32)(FileAttribute.FILE_ATTRIBUTE_NORMAL),
IntPtr.Zero);
return (printerhandle);
}
READFILE FUNCTION CALL
private bool WriteToPort(IntPtr printer, ref byte[] data)
{
bool success = true;
byte[] data2 = new byte[64];
byte[] dataread = new byte[64];
int index = 0;
int length = 64;
uint written = 0;
uint read = 0;
while ((index + length) <= data.Length)
{
Array.Copy(data, index, data2, 0, length);
success &= WriteFile(printer, data2, (uint)length, out written, IntPtr.Zero);
index += 64;
}
if ((index < data.Length) &&
((index + length) > data.Length))
{
length = data.Length - index;
Array.Copy(data, index, data2, 0, length);
success &= WriteFile(printer, data2, (uint)length, out written, IntPtr.Zero);
}
success &= ReadFile(printer, out dataread, 64, out read, IntPtr.Zero);
return success;
}
The exception occurs when the success &= ReadFile(printer, out dataread, 64, out read, IntPtr.Zero); line is executed.
Here is the relevant stack trace as reported by VS2013:
StackTrace:
at System.StubHelpers.MngdNativeArrayMarshaler.ClearNative(IntPtr pMarshalState, IntPtr pNativeHome, Int32 cElements)
at APP_NET.Main.ReadFile(IntPtr hFile, Byte[]& lpbuffer, UInt32 nNumberofBytesToRead, UInt32& lpNumberofBytesRead, IntPtr lpOverlapped)
at APP_NET.Main.WriteToPort(IntPtr printer, Byte[]& data) in Main.cs:line 770
Any thoughts on what might be happening?
Because you have a wrong declaration of the ReadFile function. Remove out from the lpbuffer argument.
Import of the ReadFile:
[DllImport("kernel32.dll", BestFitMapping = true, CharSet = CharSet.Ansi, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool ReadFile(IntPtr hFile, byte[] lpbuffer, UInt32 nNumberofBytesToRead, out UInt32 lpNumberofBytesRead, IntPtr lpOverlapped);
Reading a file:
success &= ReadFile(printer, dataread, 64, out read, IntPtr.Zero);
I have an unmanaged C++ Windows console app that works fine. I want it in C#. I have done DllImport statements for the necessary Kernel32.dll symbols:
[StructLayout(LayoutKind.Sequential)]
internal struct DiskGeometry
{
public long Cylinders;
public int MediaType;
public int TracksPerCylinder;
public int SectorsPerTrack;
public int BytesPerSector;
}
internal static class NativeMethods
{
internal const uint FileAccessGenericRead = 0x80000000;
internal const uint FileShareWrite = 0x2;
internal const uint FileShareRead = 0x1;
internal const uint CreationDispositionOpenExisting = 0x3;
internal const uint IoCtlDiskGetDriveGeometry = 0x70000;
[DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern SafeFileHandle CreateFile(
string fileName,
uint fileAccess,
uint fileShare,
IntPtr securityAttributes,
uint creationDisposition,
uint flags,
IntPtr template);
[DllImport("Kernel32.dll", SetLastError = false, CharSet = CharSet.Auto)]
public static extern int DeviceIoControl(
SafeFileHandle device,
uint controlCode,
IntPtr inBuffer,
uint inBufferSize,
IntPtr outBuffer,
uint outBufferSize,
ref uint bytesReturned,
IntPtr overlapped);
}
I then have the following application code:
public static void Main()
{
SafeFileHandle diskHandle = NativeMethods.CreateFile(
"\\\\.\\PhysicalDrive0",
NativeMethods.FileAccessGenericRead,
NativeMethods.FileShareWrite | NativeMethods.FileShareRead,
IntPtr.Zero,
NativeMethods.CreationDispositionOpenExisting,
0,
IntPtr.Zero);
if (diskHandle.IsInvalid)
{
Console.WriteLine("CreateFile failed with error: {0}", Marshal.GetLastWin32Error());
return;
}
int geometrySize = Marshal.SizeOf(typeof(DiskGeometry));
Console.WriteLine("geometry size = {0}", geometrySize);
IntPtr geometryBlob = Marshal.AllocHGlobal(geometrySize);
uint numBytesRead = 0;
if (0 == NativeMethods.DeviceIoControl(
diskHandle,
NativeMethods.IoCtlDiskGetDriveGeometry,
IntPtr.Zero,
0,
geometryBlob,
(uint)geometrySize,
ref numBytesRead,
IntPtr.Zero))
{
Console.WriteLine("DeviceIoControl failed with error: {0}", Marshal.GetLastWin32Error());
return;
}
Console.WriteLine("Bytes read = {0}", numBytesRead);
DiskGeometry geometry = (DiskGeometry)Marshal.PtrToStructure(geometryBlob, typeof(DiskGeometry));
Marshal.FreeHGlobal(geometryBlob);
long bytesPerCylinder = (long)geometry.TracksPerCylinder * (long)geometry.SectorsPerTrack * (long)geometry.BytesPerSector;
long totalSize = geometry.Cylinders * bytesPerCylinder;
Console.WriteLine("Media Type: {0}", geometry.MediaType);
Console.WriteLine("Cylinders: {0}", geometry.Cylinders);
Console.WriteLine("Tracks per Cylinder: {0}", geometry.TracksPerCylinder);
Console.WriteLine("Sectors per Track: {0}", geometry.SectorsPerTrack);
Console.WriteLine("Bytes per Sector: {0}", geometry.BytesPerSector);
Console.WriteLine("Bytes per Cylinder: {0}", bytesPerCylinder);
Console.WriteLine("Total disk space: {0}", totalSize);
}
My C# app prints "Bytes read = 0" and the geometry member values are garbage. I am certainly no expert on DllImport and marshaling. Please help me understand what I am doing wrong. If I change the fifth parameter to DeviceIoControl to be "ref DiskGeometry" and just pass in one created right before the call (instead of IntPtr and alloc), all printed geometry member values are 0.
You have mistype, try this:
internal const uint IoCtlDiskGetDriveGeometry = 0x70000;
Try using one of those signatures for the DllImport: http://pinvoke.net/default.aspx/kernel32.DeviceIoControl
DWORD OREnumKey(
__in ORHKEY Handle,
__in DWORD dwIndex,
__out PWSTR lpName,
__inout PDWORD lpcName,
__out_opt PWSTR lpClass,
__inout_opt PDWORD lpcClass,
__out_opt PFILETIME lpftLastWriteTime
);
My code
[DllImport("offreg.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern uint OREnumKey(IntPtr Handle, IntPtr dwIndex, [MarshalAs(UnmanagedType.LPWStr)]out StringBuilder lpName, ref IntPtr lpcName, [MarshalAs(UnmanagedType.LPWStr)]out StringBuilder lpClass, ref IntPtr lpcClass, out System.Runtime.InteropServices.ComTypes.FILETIME lpftLastWriteTime);
IntPtr myKey = hiveid;
IntPtr dwindex=(IntPtr)0;
StringBuilder lpName=new StringBuilder("",255);
IntPtr lpcName = (IntPtr)0;
StringBuilder lpClass=new StringBuilder("",255);
IntPtr lpcClass = (IntPtr)11;
System.Runtime.InteropServices.ComTypes.FILETIME lpftLastWriteTime;
uint ret3 = OREnumKey(myKey, dwindex, out lpName, ref lpcName, out lpClass, ref lpcClass, out lpftLastWriteTime);
ret3=ERROR_MORE_DATA 234
Problem can be in wrong StringBuilder Size, or FILETIME
2nd How i should call PWSTR param from C#?
[MarshalAs(UnmanagedType.LPWStr)]out StringBuilder lpName is it correct?
This is a pretty standard Windows error code, it means that you called a winapi function and you didn't pass a big enough buffer. The only way to fix the problem is to pass a bigger buffer.
This looks a lot like a wrapper for RegQueryKeyEx(), which makes it very likely that you are passing bad data to the function. The lpcName argument is actually ref int, not IntPtr. And you are supposed to pass a variable that stores the size of the buffer you passed, 255 in your case. The lpcClass argument is similarly borked. This ought to fix it:
[DllImport("offreg.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern uint OREnumKey(
IntPtr Handle,
int dwIndex,
StringBuilder lpName,
ref int lpcName,
StringBuilder lpClass,
ref int lpcClass,
out System.Runtime.InteropServices.ComTypes.FILETIME lpftLastWriteTime);
...
StringBuilder lpName=new StringBuilder("",255);
int nameSize = lpName.Capacity;
StringBuilder lpClass=new StringBuilder("",255);
int classSize = lpClass.Capacity;
System.Runtime.InteropServices.ComTypes.FILETIME lpftLastWriteTime;
uint ret3 = OREnumKey(hiveid, 0, lpName, ref nameSize, lpClass, ref classSize, out lpftLastWriteTime);
if (ret3 != 0) throw new Exception("kaboom");
string name = lpName.ToString();
string className = lpClass.ToString();
I am new to .NET compact framework. I need to call a DeviceIoControl function and pass structures as input and output parameters to the IOControl function.
In PInvoke/DeviceIoControl I found how to get access to the function itself. But how can I pass a pointer the structure as InBuf and OutBuf parameter?
The DeviceIoControl is defined as P/Invoke:
[DllImport("coredll", EntryPoint = "DeviceIoControl", SetLastError = true)]
internal static extern int DeviceIoControlCE(
int hDevice, int dwIoControlCode,
byte[] lpInBuffer, int nInBufferSize,
byte[] lpOutBuffer, int nOutBufferSize,
ref int lpBytesReturned, IntPtr lpOverlapped);
The structures in question have this layout:
struct Query
{
int a;
int b;
char x[8];
}
struct Response
{
int result;
uint32 success;
}
void DoIoControl ()
{
Query q = new Query();
Response r = new Response();
int inSize = System.Runtime.InteropServices.Marshal.SizeOf(q);
int outSize = System.Runtime.InteropServices.Marshal.SizeOf(r);
NativeMethods.DeviceIoControlCE((int)handle, (int)IOCTL_MY.CODE,
ref q, inSize, ref r, outSize, ref bytesReturned, IntPtr.Zero);
}
Edit:
When I try to compile this code I get the error:
cannot convert from 'ref MyNamespace.Response' to 'byte[]'
How can I pass the address of the struct to the DeviceIoControl function what expects a pointer to byte instead of struct ref?
The issue is that your P/Invoke declaration doesn't match your call. DeviceIoControl takes in pointers for the in/out paramters:
BOOL DeviceIoControl(
HANDLE hDevice,
DWORD dwIoControlCode,
LPVOID lpInBuffer,
DWORD nInBufferSize,
LPVOID lpOutBuffer,
DWORD nOutBufferSize,
LPDWORD lpBytesReturned,
LPOVERLAPPED lpOverlapped
);
So you can "adjust" your declaration in a lot of ways. The one in the link you provide uses a byte[] probably for convenience where they were using it. In your case, since you're passing simple structs (i.e. no internal pointers to other data), then the easiest "fix" is to just change you P/Invoke declaration:
[DllImport("coredll", SetLastError = true)]
internal static extern int DeviceIoControl(
IntPtr hDevice,
IOCTL.MY dwIoControlCode,
ref Query lpInBuffer,
int nInBufferSize,
ref Response lpOutBuffer,
int nOutBufferSize,
ref int lpBytesReturned,
IntPtr lpOverlapped);
And you code should work. Note I also changed the types of the first two parameters to allow making your calling code more clear without casts.
EDIT 2
If you find you need different signatures, simply overload the P/Invoke. For example, the Smart Device Framework code has at least 11 overloads for DeviceIoControl. Here are just some of them to give you a flavor:
[DllImport("coredll.dll", EntryPoint = "DeviceIoControl", SetLastError = true)]
internal static extern int DeviceIoControl<TInput, TOutput>(
IntPtr hDevice,
uint dwIoControlCode,
ref TInput lpInBuffer,
int nInBufferSize,
ref TOutput lpOutBuffer,
int nOutBufferSize,
out int lpBytesReturned,
IntPtr lpOverlapped)
where TInput : struct
where TOutput : struct;
[DllImport("coredll.dll", EntryPoint = "DeviceIoControl", SetLastError = true)]
internal unsafe static extern int DeviceIoControl(
IntPtr hDevice,
uint dwIoControlCode,
void* lpInBuffer,
int nInBufferSize,
void* lpOutBuffer,
int nOutBufferSize,
out int lpBytesReturned,
IntPtr lpOverlapped);
[DllImport("coredll.dll", EntryPoint = "DeviceIoControl", SetLastError = true)]
internal static extern int DeviceIoControl(
IntPtr hDevice,
uint dwIoControlCode,
IntPtr lpInBuffer,
uint nInBufferSize,
IntPtr lpOutBuffer,
uint nOutBufferSize,
out int lpBytesReturned,
IntPtr lpOverlapped);
[DllImport("coredll.dll", EntryPoint = "DeviceIoControl", SetLastError = true)]
internal static extern int DeviceIoControl(
IntPtr hDevice,
uint dwIoControlCode,
byte[] lpInBuffer,
int nInBufferSize,
IntPtr lpOutBuffer,
int nOutBufferSize,
out int lpBytesReturned,
IntPtr lpOverlapped);