Why raw disk read in C# reads from slightly shifted offset? - c#

I'm trying to read raw disk. I've successfully opened the drive and obtained valid handle (CreateFile), set offset for that handle to zero (SetFilePointerEx) and read the data to a buffer Byte[] (ReadFile). So far so good. But for some unknown reason, when I compare the buffer (took my a while to figure it out actually) to what 3rd party utilities (Disk Investigator) shows. It doesn't contain a boot information (jump instruction), but correct disk data, but starting at offset 85 = 0x55. So somewhere in the middle of volume boot information.
Why is that? Is there something, I'm missing (obviously I am)?
System: Windows 8 (in VmWare Workstation)
Code:
// moves the pointer to a given offset
Int64 offset = 0;
Int64 newOffset;
Int32 bufferSize = (Int32) clusterSizeInBytes;
SetFilePointerEx(driveHandle.Handle, offset, out newOffset, WinMoveMethod.Begin);
Int32 error = Marshal.GetLastWin32Error();
// reads the raw buffer
Int32 numberOfBytesRead;
rawData = new Byte[bufferSize];
Boolean result = ReadFile(driveHandle.Handle, rawData, bufferSize,
out numberOfBytesRead, IntPtr.Zero);
CreateFile (elsewhere)
driveHandle = CreateFile("\\.\PhysicalDrive1", WinFileAccessMode.GenericRead,
WinFileSharedAccess.All, IntPtr.Zero, WinFileMode.OpenExisting,
WinFileAttribute.None, IntPtr.Zero);
PInvoke methods:
[DllImport("kernel32.dll", SetLastError = true)]
public static extern Boolean SetFilePointerEx(
[In] SafeFileHandle fileHandle,
[In] Int64 distanceToMove,
[Out] out Int64 newOffset,
[In] WinMoveMethod moveMethod);
[DllImport("kernel32", SetLastError = true)]
public extern static Boolean ReadFile(
[In] SafeFileHandle handle,
[Out] Byte[] buffer,
[In] Int32 numBytesToRead,
[Out] out Int32 numBytesRead,
[In] IntPtr overlapped);
Enumerations:
public enum WinMoveMethod : uint
{
Begin = 0,
Current = 1,
End = 2
}

Can't find anymore a library using a namespace "DiskLib". I can't test that or completely upload now, even I don't know anymore who wrote that piece of code, but the lines you may find interesting are
public class DiskStream : Stream
{
public const int DEFAULT_SECTOR_SIZE = 512;
private const int BUFFER_SIZE = 4096;
private string diskID;
private DiskInfo diskInfo;
private FileAccess desiredAccess;
private SafeFileHandle fileHandle;
public DiskInfo DiskInfo
{
get { return this.diskInfo; }
}
public uint SectorSize
{
get { return this.diskInfo.BytesPerSector; }
}
public DiskStream(string diskID, FileAccess desiredAccess)
{
this.diskID = diskID;
this.diskInfo = new DiskInfo(diskID);
this.desiredAccess = desiredAccess;
// if desiredAccess is Write or Read/Write
// find volumes on this disk
// lock the volumes using FSCTL_LOCK_VOLUME
// unlock the volumes on Close() or in destructor
this.fileHandle = this.openFile(diskID, desiredAccess);
}
private SafeFileHandle openFile(string id, FileAccess desiredAccess)
{
uint access;
switch (desiredAccess)
{
case FileAccess.Read:
access = DeviceIO.GENERIC_READ;
break;
case FileAccess.Write:
access = DeviceIO.GENERIC_WRITE;
break;
case FileAccess.ReadWrite:
access = DeviceIO.GENERIC_READ | DeviceIO.GENERIC_WRITE;
break;
default:
access = DeviceIO.GENERIC_READ;
break;
}
SafeFileHandle ptr = DeviceIO.CreateFile(
id,
access,
DeviceIO.FILE_SHARE_READ,
IntPtr.Zero,
DeviceIO.OPEN_EXISTING,
DeviceIO.FILE_FLAG_NO_BUFFERING | DeviceIO.FILE_FLAG_WRITE_THROUGH,
IntPtr.Zero);
if (ptr.IsInvalid)
{
Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
}
return ptr;
}
public override bool CanRead
{
get
{
return (this.desiredAccess == FileAccess.Read || this.desiredAccess == FileAccess.ReadWrite) ? true : false;
}
}
public override bool CanWrite
{
get
{
return (this.desiredAccess == FileAccess.Write || this.desiredAccess == FileAccess.ReadWrite) ? true : false;
}
}
public override bool CanSeek
{
get
{
return true;
}
}
public override long Length {
get { return (long)this.Length; }
}
public ulong LengthU
{
get { return this.diskInfo.Size; }
}
public override long Position {
get {
return (long)PositionU;
}
set {
PositionU = (ulong)value;
}
}
public ulong PositionU
{
get
{
ulong n = 0;
if (!DeviceIO.SetFilePointerEx(this.fileHandle, 0, out n, (uint)SeekOrigin.Current))
Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
return n;
}
set
{
if (value > (this.LengthU - 1))
throw new EndOfStreamException("Cannot set position beyond the end of the disk.");
ulong n = 0;
if (!DeviceIO.SetFilePointerEx(this.fileHandle, value, out n, (uint)SeekOrigin.Begin))
Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
}
}
public override void Flush()
{
// not required, since FILE_FLAG_WRITE_THROUGH and FILE_FLAG_NO_BUFFERING are used
//if (!Unmanaged.FlushFileBuffers(this.fileHandle))
// Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
}
public override void Close()
{
if (this.fileHandle != null) {
DeviceIO.CloseHandle(this.fileHandle);
this.fileHandle.SetHandleAsInvalid();
this.fileHandle = null;
}
base.Close();
}
public override void SetLength(long value)
{
throw new NotSupportedException("Setting the length is not supported with DiskStream objects.");
}
public override int Read(byte[] buffer, int offset, int count) {
return (int)Read(buffer, (uint)offset, (uint)count);
}
public unsafe uint Read(byte[] buffer, uint offset, uint count)
{
uint n = 0;
fixed (byte* p = buffer)
{
if (!DeviceIO.ReadFile(this.fileHandle, p + offset, count, &n, IntPtr.Zero))
Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
}
return n;
}
public override void Write(byte[] buffer, int offset, int count) {
Write(buffer, (uint)offset, (uint)count);
}
public unsafe void Write(byte[] buffer, uint offset, uint count)
{
uint n = 0;
fixed (byte* p = buffer)
{
if (!DeviceIO.WriteFile(this.fileHandle, p + offset, count, &n, IntPtr.Zero))
Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
}
}
public override long Seek(long offset, SeekOrigin origin) {
return (long)SeekU((ulong)offset, origin);
}
public ulong SeekU(ulong offset, SeekOrigin origin)
{
ulong n = 0;
if (!DeviceIO.SetFilePointerEx(this.fileHandle, offset, out n, (uint)origin))
Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
return n;
}
public uint ReadSector(DiskSector sector)
{
return this.Read(sector.Data, 0, sector.SectorSize);
}
public void WriteSector(DiskSector sector)
{
this.Write(sector.Data, 0, sector.SectorSize);
}
public void SeekSector(DiskSector sector)
{
this.Seek(sector.Offset, SeekOrigin.Begin);
}
}
The DeviceIO class is a p/invoke boilerplate for Win32 API.
The DiskInfo class is a wrapper to WMI Win32_DiskDrive and Win32_DiskPartition classes.
I used that library once to clone a disk (on Win7). Hope that helps to find the solution.
For completeness and because of the details, the DeviceIO class is throughout using unsigned integers:
/// <summary>
/// P/Invoke wrappers around Win32 functions and constants.
/// </summary>
internal partial class DeviceIO
{
#region Constants used in unmanaged functions
public const uint FILE_SHARE_READ = 0x00000001;
public const uint FILE_SHARE_WRITE = 0x00000002;
public const uint FILE_SHARE_DELETE = 0x00000004;
public const uint OPEN_EXISTING = 3;
public const uint GENERIC_READ = (0x80000000);
public const uint GENERIC_WRITE = (0x40000000);
public const uint FILE_FLAG_NO_BUFFERING = 0x20000000;
public const uint FILE_FLAG_WRITE_THROUGH = 0x80000000;
public const uint FILE_READ_ATTRIBUTES = (0x0080);
public const uint FILE_WRITE_ATTRIBUTES = 0x0100;
public const uint ERROR_INSUFFICIENT_BUFFER = 122;
#endregion
#region Unamanged function declarations
[DllImport("kernel32.dll", SetLastError = true)]
public static unsafe extern SafeFileHandle CreateFile(
string FileName,
uint DesiredAccess,
uint ShareMode,
IntPtr SecurityAttributes,
uint CreationDisposition,
uint FlagsAndAttributes,
IntPtr hTemplateFile);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool CloseHandle(SafeFileHandle hHandle);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool DeviceIoControl(
SafeFileHandle hDevice,
uint dwIoControlCode,
IntPtr lpInBuffer,
uint nInBufferSize,
[Out] IntPtr lpOutBuffer,
uint nOutBufferSize,
ref uint lpBytesReturned,
IntPtr lpOverlapped);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern unsafe bool WriteFile(
SafeFileHandle hFile,
byte* pBuffer,
uint NumberOfBytesToWrite,
uint* pNumberOfBytesWritten,
IntPtr Overlapped);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern unsafe bool ReadFile(
SafeFileHandle hFile,
byte* pBuffer,
uint NumberOfBytesToRead,
uint* pNumberOfBytesRead,
IntPtr Overlapped);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool SetFilePointerEx(
SafeFileHandle hFile,
ulong liDistanceToMove,
out ulong lpNewFilePointer,
uint dwMoveMethod);
[DllImport("kernel32.dll")]
public static extern bool FlushFileBuffers(
SafeFileHandle hFile);
#endregion
}

Related

How to delete multi-selected items from recyclebin using C#?

how to delete multi-items via C# or windows api, I have search for this for a long time and no solution. I used FolderItemVerb in Shell32.dll to delete file one by one, but it would pop a dialog at same time. I will be appreciated if you know how to solve this problem. Thank you.
It isn't for the faint of heart!
Code compatible with Windows >= Vista, no XP! The XP code is at the end
This is the first time I take a look at the Shell32 interfaces... Wow... Even the most simple things are complex :-) But then, COM is always complex... Now... There are 2.5 competing data structures/interfaces for manipulating files in Shell... IShellFolder (the old COM interface, not used in this sample), IShellItem (the new COM interface, used in this sample), IDLIST (here used as a PIDLIST_ABSOLUTE, used for "collecting" multiple files together. I count it as 0.5 😀). I hope there is no memory leak anywhere (always complex). Note that .NET will free COM objects automatically. I've even added some code that I've written to help me debug (like the code to get the list of verbs supported).
Now, here there are the COM interfaces plus some Shell32.dll PInvoke methods.
[StructLayout(LayoutKind.Sequential)]
public struct PIDLIST_ABSOLUTE
{
public IntPtr Ptr;
}
[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("43826d1e-e718-42ee-bc55-a1e261c37bfe")]
public interface IShellItem
{
void BindToHandler(
IntPtr pbc,
[In, MarshalAs(UnmanagedType.LPStruct)] Guid bhid,
[In, MarshalAs(UnmanagedType.LPStruct)] Guid riid,
[MarshalAs(UnmanagedType.IUnknown)] out object ppv);
void GetParent(out IShellItem ppsi);
void GetDisplayName(int sigdnName, out IntPtr ppszName);
void GetAttributes(uint sfgaoMask, out uint psfgaoAttribs);
[PreserveSig]
int Compare(IShellItem psi, uint hint, out int piOrder);
}
[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("70629033-e363-4a28-a567-0db78006e6d7")]
public interface IEnumShellItems
{
void Next(int celt, [Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)] IShellItem[] rgelt, out int pceltFetched);
void Skip(int celt);
void Reset();
void Clone(out IEnumShellItems ppenum);
}
[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("b63ea76d-1f85-456f-a19c-48159efa858b")]
public interface IShellItemArray
{
void BindToHandler(
IntPtr pbc,
[In, MarshalAs(UnmanagedType.LPStruct)] Guid bhid,
[In, MarshalAs(UnmanagedType.LPStruct)] Guid riid,
[MarshalAs(UnmanagedType.IUnknown)] out object ppvOut);
void GetPropertyStore(
uint flags,
[In, MarshalAs(UnmanagedType.LPStruct)] Guid riid,
[MarshalAs(UnmanagedType.IUnknown)] out object ppv);
void GetPropertyDescriptionList(
IntPtr keyType,
[In, MarshalAs(UnmanagedType.LPStruct)] Guid riid,
[MarshalAs(UnmanagedType.IUnknown)] out object ppv);
void GetAttributes(uint AttribFlags, uint sfgaoMask, out uint psfgaoAttribs);
void GetCount(out int pdwNumItems);
void GetItemAt(int dwIndex, out IShellItem ppsi);
void EnumItems(out IEnumShellItems ppenumShellItems);
}
[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("000214e4-0000-0000-c000-000000000046")]
public interface IContextMenu
{
[PreserveSig]
int QueryContextMenu(IntPtr hMenu, uint indexMenu, int idCmdFirst, int idCmdLast, uint uFlags);
void InvokeCommand([In] ref CMINVOKECOMMANDINFOEX pici);
[PreserveSig]
int GetCommandString(UIntPtr idCmd, uint uType, IntPtr pReserved, IntPtr pszName, int cchMax);
}
[StructLayout(LayoutKind.Sequential)]
public struct CMINVOKECOMMANDINFOEX
{
public int cbSize;
public uint fMask;
public IntPtr hwnd;
// Non-unicode verbs (are there unicode verbs?)
[MarshalAs(UnmanagedType.LPStr)]
public string lpVerb;
[MarshalAs(UnmanagedType.LPStr)]
public string lpParameters;
[MarshalAs(UnmanagedType.LPStr)]
public string lpDirectory;
public int nShow;
public uint dwHotKey;
public IntPtr hIcon;
[MarshalAs(UnmanagedType.LPStr)]
public string lpTitle;
// Use CMIC_MASK_UNICODE
[MarshalAs(UnmanagedType.LPWStr)]
public string lpVerbW;
[MarshalAs(UnmanagedType.LPWStr)]
public string lpParametersW;
[MarshalAs(UnmanagedType.LPWStr)]
public string lpDirectoryW;
[MarshalAs(UnmanagedType.LPWStr)]
public string lpTitleW;
public int ptInvokeX;
public int ptInvokeY;
}
// Windows >= Vista
public static class ShellItemUtilities
{
public static readonly Guid FOLDERID_RecycleBinFolder = new Guid("b7534046-3ecb-4c18-be4e-64cd4cb7d6ac");
public static readonly Guid BHID_EnumItems = new Guid("94f60519-2850-4924-aa5a-d15e84868039");
public static readonly Guid BHID_SFUIObject = new Guid("3981e225-f559-11d3-8e3a-00c04f6837d5");
// From Windows 7
[DllImport("Shell32.dll", ExactSpelling = true, SetLastError = false)]
public static extern int SHGetKnownFolderItem(
[In, MarshalAs(UnmanagedType.LPStruct)] Guid rfid,
uint dwFlags,
IntPtr hToken,
[In, MarshalAs(UnmanagedType.LPStruct)] Guid riid,
out IShellItem ppv);
// For Windows Vista
[DllImport("Shell32.dll", ExactSpelling = true, SetLastError = false)]
public static extern int SHCreateItemFromIDList(PIDLIST_ABSOLUTE pidl, [In, MarshalAs(UnmanagedType.LPStruct)] Guid riid, out IShellItem ppv);
// For Windows Vista
[DllImport("Shell32.dll", ExactSpelling = true, SetLastError = false)]
public static extern int SHGetKnownFolderIDList(
[In, MarshalAs(UnmanagedType.LPStruct)] Guid rfid,
uint dwFlags,
IntPtr hToken,
out PIDLIST_ABSOLUTE ppidl);
// From Windows Vista
[DllImport("Shell32.dll", ExactSpelling = true, SetLastError = false)]
public static extern int SHGetIDListFromObject([MarshalAs(UnmanagedType.Interface)] object punk, out PIDLIST_ABSOLUTE ppidl);
[DllImport("Shell32.dll", ExactSpelling = true, SetLastError = false)]
public static extern int SHCreateShellItemArrayFromIDLists(int cidl, [In] PIDLIST_ABSOLUTE[] rgpidl, out IShellItemArray ppsiItemArray);
public static IEnumerable<IShellItem> Enumerate(this IShellItem si)
{
object pesiTemp;
si.BindToHandler(IntPtr.Zero, BHID_EnumItems, typeof(IEnumShellItems).GUID, out pesiTemp);
var pesi = (IEnumShellItems)pesiTemp;
var items = new IShellItem[10];
while (true)
{
int fetched;
pesi.Next(1, items, out fetched);
if (fetched == 0)
{
break;
}
yield return items[0];
}
}
}
public static class ContextMenuUtilities
{
[DllImport("User32.dll", ExactSpelling = true, SetLastError = true)]
public static extern IntPtr CreateMenu();
[DllImport("User32.dll", ExactSpelling = true, SetLastError = true)]
public static extern bool DestroyMenu(IntPtr hMenu);
[DllImport("User32.dll", ExactSpelling = true, SetLastError = true)]
public static extern int GetMenuItemCount(IntPtr hMenu);
[DllImport("User32.dll", ExactSpelling = true, SetLastError = true)]
public static extern uint GetMenuItemID(IntPtr hMenu, int nPos);
[DllImport("User32.dll", CharSet = CharSet.Unicode, EntryPoint = "GetMenuStringW", ExactSpelling = true, SetLastError = true)]
public static extern int GetMenuString(IntPtr hMenu, uint uIDItem, [Out] StringBuilder lpString, int nMaxCount, uint uFlag);
public static string[] GetVerbs(IContextMenu cm, bool ansi = true)
{
IntPtr menu = IntPtr.Zero;
try
{
menu = CreateMenu();
// It isn't clear why short.MaxValue, but 0x7FFF is very used around the .NET!
int res = cm.QueryContextMenu(menu, 0, 0, short.MaxValue, 0);
if (res < 0)
{
Marshal.ThrowExceptionForHR(res);
}
//var sb = new StringBuilder(128);
int count = GetMenuItemCount(menu);
var verbs = new List<string>(count);
var handle = default(GCHandle);
try
{
var bytes = new byte[ansi ? 128 : 256];
handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
IntPtr ptr = handle.AddrOfPinnedObject();
for (int i = 0; i < count; i++)
{
uint id = GetMenuItemID(menu, i);
if (id == uint.MaxValue)
{
continue;
}
//GetMenuString(menu, (uint)i, sb, sb.Capacity, 0x00000400 /* MF_BYPOSITION */);
//string description = sb.ToString();
//sb.Clear();
res = cm.GetCommandString((UIntPtr)id, ansi ? (uint)0x00000002 /* GCS_VALIDATEA */ : 0x00000006 /* GCS_VALIDATEW */, IntPtr.Zero, ptr, bytes.Length);
if (res < 0)
{
continue;
}
if (res == 0)
{
res = cm.GetCommandString((UIntPtr)id, ansi ? (uint)0x00000000 /* GCS_VERBA */ : 0x00000004 /* GCS_VERBW */, IntPtr.Zero, ptr, bytes.Length);
if (res < 0)
{
Marshal.ThrowExceptionForHR(res);
}
verbs.Add(ansi ? Marshal.PtrToStringAnsi(ptr) : Marshal.PtrToStringUni(ptr));
}
}
}
finally
{
if (handle.IsAllocated)
{
handle.Free();
}
}
return verbs.ToArray();
}
finally
{
if (menu != IntPtr.Zero)
{
DestroyMenu(menu);
}
}
}
}
And then finally a small example program that uses it... It is a small console method that will list all the files that are present in the recycle bin, put 10 of them in an array and delete them in a single operation (verb). The SHGetKnownFolderItem is from Windows 7 on, so I'm using SHGetKnownFolderIDList + SHCreateItemFromIDList that are from Windows Vista.
The code here enumerates the IShellItem files contained in the recycle bin, saves their PIDL_ABSOLUTE in a List<>, from that List<> creates a IShellItemArray, on that IShellItemArray binds a IContextMenu, for that IContextMenu executes the delete verb. Clearly by adding/not adding all the PIDL_ABSOLUTE to the List<> you can control which files will be deleted.
private static void TestShellItem()
{
IShellItem recyleBin;
string str;
IntPtr ptr = IntPtr.Zero;
int res;
//// From Windows 7
//res = ShellItemUtilities.SHGetKnownFolderItem(ShellItemUtilities.FOLDERID_RecycleBinFolder, 0, IntPtr.Zero, typeof(IShellItem).GUID, out recyleBin);
//if (res < 0)
//{
// Marshal.ThrowExceptionForHR(res);
//}
// Windows >= Vista equivalent
var pidl = default(PIDLIST_ABSOLUTE);
try
{
res = ShellItemUtilities.SHGetKnownFolderIDList(ShellItemUtilities.FOLDERID_RecycleBinFolder, 0, IntPtr.Zero, out pidl);
if (res < 0)
{
Marshal.ThrowExceptionForHR(res);
}
res = ShellItemUtilities.SHCreateItemFromIDList(pidl, typeof(IShellItem).GUID, out recyleBin);
if (res < 0)
{
Marshal.ThrowExceptionForHR(res);
}
}
finally
{
Marshal.FreeCoTaskMem(pidl.Ptr);
}
//// Example of use of GetDisplayName
//try
//{
// recyleBin.GetDisplayName(2, out ptr);
// str = Marshal.PtrToStringUni(ptr);
//}
//finally
//{
// if (ptr != IntPtr.Zero)
// {
// Marshal.FreeCoTaskMem(ptr);
// ptr = IntPtr.Zero;
// }
//}
var pids = new List<PIDLIST_ABSOLUTE>();
try
{
foreach (IShellItem si in recyleBin.Enumerate())
{
try
{
si.GetDisplayName(0, out ptr);
str = Marshal.PtrToStringUni(ptr);
// Some condition to include/exclude...
if (pids.Count < 10)
{
Console.WriteLine(str);
// Remember to free the pidl!
res = ShellItemUtilities.SHGetIDListFromObject(si, out pidl);
if (res < 0)
{
Marshal.ThrowExceptionForHR(res);
}
pids.Add(pidl);
}
}
finally
{
if (ptr != IntPtr.Zero)
{
Marshal.FreeCoTaskMem(ptr);
ptr = IntPtr.Zero;
}
}
}
var pids2 = pids.ToArray();
IShellItemArray sia;
res = ShellItemUtilities.SHCreateShellItemArrayFromIDLists(pids2.Length, pids2, out sia);
if (res < 0)
{
Marshal.ThrowExceptionForHR(res);
}
object cmTemp;
sia.BindToHandler(IntPtr.Zero, ShellItemUtilities.BHID_SFUIObject, typeof(IContextMenu).GUID, out cmTemp);
var cm = (IContextMenu)cmTemp;
// To see verbs
//var verbsAnsi = ContextMenuUtilities.GetVerbs(cm, true);
//var verbsUnicode = ContextMenuUtilities.GetVerbs(cm, false);
var cmd = new CMINVOKECOMMANDINFOEX
{
cbSize = Marshal.SizeOf(typeof(CMINVOKECOMMANDINFOEX)),
fMask = 0x00000400 /* CMIC_MASK_FLAG_NO_UI */,
lpVerb = "delete",
};
cm.InvokeCommand(ref cmd);
}
finally
{
foreach (var pid in pids)
{
Marshal.FreeCoTaskMem(pid.Ptr);
}
}
//// Verb executed one by one
//foreach (var item in recyleBin.Enumerate())
//{
// object cmTemp;
// item.BindToHandler(IntPtr.Zero, ShellItemUtilities.BHID_SFUIObject, typeof(IContextMenu).GUID, out cmTemp);
// var cm = (IContextMenu)cmTemp;
////// To see verbs
//// var verbsAnsi = ContextMenuUtilities.GetVerbs(cm, true);
//// var verbsUnicode = ContextMenuUtilities.GetVerbs(cm, false);
// var cmd = new CMINVOKECOMMANDINFOEX
// {
// cbSize = Marshal.SizeOf(typeof(CMINVOKECOMMANDINFOEX)),
// fMask = 0x00000400 /* CMIC_MASK_FLAG_NO_UI */,
// lpVerb = "delete",
// };
// cm.InvokeCommand(ref cmd);
//}
}
Code for Windows XP, uses some code from the other version, you have to take what is needed
It uses the IShellFolder interface. Note that the IContextMenu handling is exactly the same.
[StructLayout(LayoutKind.Sequential)]
public struct PIDLIST_RELATIVE
{
public IntPtr Ptr;
}
[StructLayout(LayoutKind.Sequential)]
public struct LPITEMIDLIST
{
public IntPtr Ptr;
}
[StructLayout(LayoutKind.Sequential)]
public struct PITEMID_CHILD
{
public IntPtr Ptr;
}
public enum STRRET_TYPE
{
WSTR = 0,
OFFSET = 0x1,
CSTR = 0x2
};
[StructLayout(LayoutKind.Sequential, Pack = 8, Size = 268)]
public sealed class STRRET : IDisposable
{
public STRRET_TYPE uType;
public IntPtr pOleStr;
[DllImport("Shlwapi.dll", ExactSpelling = true, SetLastError = false)]
private static extern int StrRetToBSTR(STRRET pstr, PITEMID_CHILD pidl, [MarshalAs(UnmanagedType.BStr)] out string pbstr);
~STRRET()
{
Dispose(false);
}
public override string ToString()
{
return ToString(default(PITEMID_CHILD));
}
public string ToString(PITEMID_CHILD pidl)
{
if (uType == STRRET_TYPE.WSTR)
{
if (pOleStr == IntPtr.Zero)
{
return null;
}
string str = Marshal.PtrToStringUni(pOleStr);
return str;
}
else
{
string str;
int res = StrRetToBSTR(this, pidl, out str);
if (res < 0)
{
Marshal.ThrowExceptionForHR(res);
}
return str;
}
}
#region IDisposable Support
// This code added to correctly implement the disposable pattern.
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
#endregion
private void Dispose(bool disposing)
{
Marshal.FreeCoTaskMem(pOleStr);
pOleStr = IntPtr.Zero;
}
}
[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("000214e6-0000-0000-c000-000000000046")]
public interface IShellFolder
{
void ParseDisplayName(
IntPtr hwnd,
IntPtr pbc,
[MarshalAs(UnmanagedType.LPWStr)] string pszDisplayName,
out int pchEaten,
out PIDLIST_RELATIVE ppidl,
ref uint pdwAttributes);
void EnumObjects(IntPtr hwnd, uint grfFlags, out IEnumIDList ppenumIDList);
void BindToObject(
PIDLIST_RELATIVE pidl,
IntPtr pbc,
[In, MarshalAs(UnmanagedType.LPStruct)] Guid riid,
[MarshalAs(UnmanagedType.IUnknown)] out object ppv);
void BindToStorage(
PIDLIST_RELATIVE pidl,
IntPtr pbc,
[In, MarshalAs(UnmanagedType.LPStruct)] Guid riid,
[MarshalAs(UnmanagedType.IUnknown)] out object ppv);
[PreserveSig]
int CompareIDs(IntPtr lParam, PIDLIST_RELATIVE pidl1, PIDLIST_RELATIVE pidl2);
void CreateViewObject(
IntPtr hwndOwner,
[In, MarshalAs(UnmanagedType.LPStruct)] Guid riid,
[MarshalAs(UnmanagedType.IUnknown)] out object ppv);
void GetAttributesOf(
int cidl,
[In, MarshalAs(UnmanagedType.LPArray)] LPITEMIDLIST[] apidl,
ref uint rgfInOut);
void GetUIObjectOf(
IntPtr hwndOwner,
int cidl,
[In, MarshalAs(UnmanagedType.LPArray)] PITEMID_CHILD[] apidl,
[In, MarshalAs(UnmanagedType.LPStruct)] Guid riid,
IntPtr rgfReserved,
[MarshalAs(UnmanagedType.IUnknown)] out object ppv);
void GetDisplayNameOf(
PITEMID_CHILD pidl,
uint uFlags,
STRRET pName);
void SetNameOf(
IntPtr hwnd,
PITEMID_CHILD pidl,
[MarshalAs(UnmanagedType.LPWStr)] string pszName,
uint uFlags,
out PITEMID_CHILD ppidlOut);
}
[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("000214f2-0000-0000-c000-000000000046")]
public interface IEnumIDList
{
void Next(int celt, [Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)] PITEMID_CHILD[] rgelt, out int pceltFetched);
void Skip(int celt);
void Reset();
void Clone(out IEnumIDList ppenum);
}
// Windows >= XP
public static class ShellFolderUtilities
{
[DllImport("Shell32.dll", ExactSpelling = true, SetLastError = false)]
public static extern int SHGetSpecialFolderLocation(IntPtr hwnd, int csidl, out PIDLIST_ABSOLUTE ppidl);
[DllImport("Shell32.dll", ExactSpelling = true, SetLastError = false)]
public static extern int SHGetDesktopFolder(out IShellFolder ppshf);
public static readonly int CSIDL_DESKTOP = 0x0000;
public static readonly int CSIDL_BITBUCKET = 0x000a;
// https://blogs.msdn.microsoft.com/oldnewthing/20110830-00/?p=9773
public static void BindToCsidl(int csidl, Guid riid, out object ppv)
{
var pidl = default(PIDLIST_ABSOLUTE);
try
{
int res;
if (csidl != CSIDL_DESKTOP)
{
res = SHGetSpecialFolderLocation(IntPtr.Zero, csidl, out pidl);
if (res < 0)
{
Marshal.ThrowExceptionForHR(res);
}
}
IShellFolder psfDesktop;
res = SHGetDesktopFolder(out psfDesktop);
if (res < 0)
{
Marshal.ThrowExceptionForHR(res);
}
if (csidl == CSIDL_DESKTOP)
{
ppv = psfDesktop;
return;
}
psfDesktop.BindToObject(new PIDLIST_RELATIVE { Ptr = pidl.Ptr }, IntPtr.Zero, riid, out ppv);
}
finally
{
Marshal.FreeCoTaskMem(pidl.Ptr);
}
}
public static IEnumerable<PITEMID_CHILD> Enumerate(this IShellFolder sf)
{
IEnumIDList ppenumIDList;
sf.EnumObjects(IntPtr.Zero, 0x00020 /* SHCONTF_FOLDERS */ | 0x00040 /* SHCONTF_NONFOLDERS */, out ppenumIDList);
if (ppenumIDList == null)
{
yield break;
}
var items = new PITEMID_CHILD[1];
while (true)
{
int fetched;
ppenumIDList.Next(items.Length, items, out fetched);
if (fetched == 0)
{
break;
}
yield return items[0];
}
}
}
And then finally a small example program that uses it...
private static void TestShellFolder()
{
object recycleBinTemp;
ShellFolderUtilities.BindToCsidl(ShellFolderUtilities.CSIDL_BITBUCKET, typeof(IShellFolder).GUID, out recycleBinTemp);
var recycleBin = (IShellFolder)recycleBinTemp;
var pids = new List<PITEMID_CHILD>();
try
{
foreach (PITEMID_CHILD pidl in recycleBin.Enumerate())
{
// Remember to free the pidl!
string str;
using (var ret = new STRRET { uType = STRRET_TYPE.CSTR })
{
recycleBin.GetDisplayNameOf(pidl, 0, ret);
str = ret.ToString(pidl);
}
// Some condition to include/exclude...
if (pids.Count < 10)
{
Console.WriteLine(str);
pids.Add(pidl);
}
else
{
Marshal.FreeCoTaskMem(pidl.Ptr);
}
}
var pids2 = pids.ToArray();
object cmTemp;
recycleBin.GetUIObjectOf(IntPtr.Zero, pids2.Length, pids2, typeof(IContextMenu).GUID, IntPtr.Zero, out cmTemp);
var cm = (IContextMenu)cmTemp;
// To see verbs
//var verbsAnsi = ContextMenuUtilities.GetVerbs(cm, true);
//var verbsUnicode = ContextMenuUtilities.GetVerbs(cm, false);
var cmd = new CMINVOKECOMMANDINFOEX
{
cbSize = Marshal.SizeOf(typeof(CMINVOKECOMMANDINFOEX)),
fMask = 0x00000400 /* CMIC_MASK_FLAG_NO_UI */,
lpVerb = "delete",
};
cm.InvokeCommand(ref cmd);
}
finally
{
foreach (var pid in pids)
{
Marshal.FreeCoTaskMem(pid.Ptr);
}
}
}
First of all add System.Runtime.InteropService to your project. We will use SHEmptyRecycleBin method, which accepts 3 parameters.
After that import the Shell32.dll in your class by using DllImport.
[DllImport("Shell32.dll")]
static extern int SHEmptyRecycleBin(IntPtr hwnd, string pszRootPath, RecycleFlag dwFlags);
Then define an enum for the RecycleBin flags(dwFlags). The values are in Hexadecimal.
enum RecycleFlag : int
{
SHERB_NOCONFIRMATION = 0x00000001, // No confirmation
SHERB_NOPROGRESSUI = 0x00000001, // No progress tracking window
SHERB_NOSOUND = 0x00000004 // No sound played
}
Place the following code in empty recycle bin button which calls the system method in Shell32.dll as:
SHEmptyRecycleBin(IntPtr.Zero, null, RecycleFlag.SHERB_NOSOUND | RecycleFlag.SHERB_NOCONFIRMATION);
reference: here

Pipelist names on Windows

How to get a pipelist with C#? I know about the next trick:
Directory.GetFile(#"\\.\\pipe\")
but I want to get number of instances of each pipe. I've got a code:
using System;
using System.IO;
using System.ComponentModel;
using Microsoft.Win32.SafeHandles;
using System.Runtime.InteropServices;
using System.Diagnostics.CodeAnalysis;
namespace PipeList {
[StructLayout(LayoutKind.Explicit, Size = 8)]
internal struct LARGE_INTEGER {
[SuppressMessage("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")]
[FieldOffset(0)]
internal Int64 QuadPart;
[FieldOffset(0)]
internal Int32 LowPart;
[SuppressMessage("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")]
[FieldOffset(4)]
internal UInt32 HighPart;
} //LARGE_INTEGER
[StructLayout(LayoutKind.Sequential)]
internal struct IO_STATUS_BLOCK {
internal UInt32 Status;
internal UInt64 Information;
} //IO_STATUS_BLOCK
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
internal struct FILE_DIRECTORY_INFORMATION {
internal UInt32 NextEntryOffset;
internal UInt32 FileIndex;
internal LARGE_INTEGER CreationTime;
internal LARGE_INTEGER LastAccessTime;
internal LARGE_INTEGER LastWriteTime;
internal LARGE_INTEGER ChangeTime;
internal LARGE_INTEGER EndOfFile;
internal LARGE_INTEGER AllocationSize;
internal UInt32 FileAttributes;
internal UInt32 FileNameLength;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
internal Byte[] FileName;
} //FILE_DIRECTORY_INFORMATION
internal static class NativeMethods {
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
internal static extern SafeFileHandle CreateFile(
String lpFileName,
UInt32 dwDesiredAccess,
FileShare dwShareMode,
IntPtr lpSecurityAttributes,
FileMode dwCreationDisposition,
UInt32 dwFlagsAndAttributes,
IntPtr hTemplateFile
);
[DllImport("ntdll.dll")]
internal static extern Int32 NtQueryDirectoryFile(
SafeFileHandle FileHandle,
IntPtr Event,
IntPtr ApcRoutine,
IntPtr ApcContext,
out IO_STATUS_BLOCK IoStatusBlock,
[Out] IntPtr FileInformation,
UInt32 Length,
UInt32 FileInformationClass,
[MarshalAs(UnmanagedType.Bool)]
Boolean ReturnSingleEntry,
IntPtr FileName,
[MarshalAs(UnmanagedType.Bool)]
Boolean RestartScan
);
} //NativeMethods
internal sealed class Program {
const UInt32 FileDirectoryInformation = 1;
const UInt32 GENERIC_READ = 0x80000000;
const Int32 STATUS_SUCCESS = 0x00000000;
static T PtrToStruct<T>(IntPtr p) {
return (T)Marshal.PtrToStructure(p, typeof(T));
}
static void GetLastError() {
Console.WriteLine(new Win32Exception(Marshal.GetLastWin32Error()).Message);
}
static void Main() {
SafeFileHandle pipes;
IntPtr ptr, ofs;
IO_STATUS_BLOCK isb;
Int32 ntstatus;
Boolean query = true;
pipes = NativeMethods.CreateFile(
#"\\.\pipe\", GENERIC_READ, FileShare.ReadWrite | FileShare.Delete,
IntPtr.Zero, FileMode.Open, 0, IntPtr.Zero
);
if (pipes.IsInvalid) {
GetLastError();
return;
}
Console.WriteLine("{0,-40}{1,14}{2,20}", "Pipe Name", "Instances", "Max Instances");
Console.WriteLine("{0,-40}{1,14}{2,20}", "---------", "---------", "-------------");
ptr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(FILE_DIRECTORY_INFORMATION)));
try {
ofs = ptr;
while (true) {
ntstatus = NativeMethods.NtQueryDirectoryFile(
pipes, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, out isb, ofs, 1024,
FileDirectoryInformation, false, IntPtr.Zero, query
);
if (ntstatus != STATUS_SUCCESS) break;
while (true) {
FILE_DIRECTORY_INFORMATION fdi = PtrToStruct<FILE_DIRECTORY_INFORMATION>(ofs);
IntPtr name = (IntPtr)(
Marshal.OffsetOf(typeof(FILE_DIRECTORY_INFORMATION), "FileName").ToInt64() + ofs.ToInt64()
);
Console.WriteLine("{0,-40}{1,14}{2,20}",
Marshal.PtrToStringUni(name, (Int32)(fdi.FileNameLength / 2)),
fdi.EndOfFile.LowPart,
fdi.AllocationSize.LowPart
);
if (fdi.NextEntryOffset == 0) break;
ofs = (IntPtr)(ofs.ToInt64() + fdi.NextEntryOffset);
} //while
query = false;
} //while
}
catch (Exception e) { Console.WriteLine(e.Message); }
finally {
Marshal.FreeHGlobal(ptr);
}
pipes.Close();
}
} //Program
}
but it works well only on CLR 2. Could someone explain me an issue of a code above? Why it does not correctly work on CLR 4?

EnumJobs returning a different JOB_INFO_1 size than Marshal.SizeOF

I'm calling the Win32 function EnumJobs (http://msdn.microsoft.com/en-us/library/windows/desktop/dd162625(v=vs.85).aspx) from managed code (C#).
[DllImport("Winspool.drv", SetLastError = true, EntryPoint = "EnumJobsA")]
public static extern bool EnumJobs(
IntPtr hPrinter, // handle to printer object
UInt32 FirstJob, // index of first job
UInt32 NoJobs, // number of jobs to enumerate
UInt32 Level, // information level
IntPtr pJob, // job information buffer
UInt32 cbBuf, // size of job information buffer
out UInt32 pcbNeeded, // bytes received or required
out UInt32 pcReturned // number of jobs received
);
EnumJobs(_printerHandle, 0, 99, 1, IntPtr.Zero, 0, out nBytesNeeded, out pcReturned);
I'm specifying Level 1 to receive a JOB_INFO_1 but the problem I'm having is the above function is returning nBytesNeeded as 240 per struct while the Marshal.SizeOf(typeof(JOB_INFO_1)) is 64 bytes causing a memory exception when I run Marshal.PtrToStructure. Counting the bytes manually for the struct gives 64 so I'm at a bit of a loss as to why I'm receiving the 240 byte structures from function, any insight would be appreciated.
[StructLayout(LayoutKind.Sequential, Pack = 1, CharSet=CharSet.Unicode)]
public struct JOB_INFO_1
{
public UInt32 JobId;
public string pPrinterName;
public string pMachineName;
public string pUserName;
public string pDocument;
public string pDatatype;
public string pStatus;
public UInt32 Status;
public UInt32 Priority;
public UInt32 Position;
public UInt32 TotalPages;
public UInt32 PagesPrinted;
public SYSTEMTIME Submitted;
}
The size of 64 is indeed correct for JOB_INFO_1 but if you look closely at the documentation, it talks about an array of structs :
pJob [out]
A pointer to a buffer that receives an array of JOB_INFO_1, JOB_INFO_2, or JOB_INFO_3 structures.
Additionally it is written :
The buffer must be large enough to receive the array of structures and any strings or other data to which the structure members point.
So there are bytes here for extra data beside the structs themselves.
Solution:
Populate the structs, increment pointer for next struct and ignore the remaining bytes.
Complete example:
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Windows;
namespace WpfApplication3
{
public partial class MainWindow
{
public MainWindow()
{
InitializeComponent();
Loaded += MainWindow_Loaded;
}
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
// Get handle to a printer
var hPrinter = new IntPtr();
bool open = NativeMethods.OpenPrinterW("Microsoft XPS Document Writer", ref hPrinter, IntPtr.Zero);
Debug.Assert(open);
/* Query for 99 jobs */
const uint firstJob = 0u;
const uint noJobs = 99u;
const uint level = 1u;
// Get byte size required for the function
uint needed;
uint returned;
bool b1 = NativeMethods.EnumJobsW(
hPrinter, firstJob, noJobs, level, IntPtr.Zero, 0, out needed, out returned);
Debug.Assert(!b1);
uint lastError = NativeMethods.GetLastError();
Debug.Assert(lastError == NativeConstants.ERROR_INSUFFICIENT_BUFFER);
// Populate the structs
IntPtr pJob = Marshal.AllocHGlobal((int) needed);
uint bytesCopied;
uint structsCopied;
bool b2 = NativeMethods.EnumJobsW(
hPrinter, firstJob, noJobs, level, pJob, needed, out bytesCopied, out structsCopied);
Debug.Assert(b2);
var jobInfos = new JOB_INFO_1W[structsCopied];
int sizeOf = Marshal.SizeOf(typeof (JOB_INFO_1W));
IntPtr pStruct = pJob;
for (int i = 0; i < structsCopied; i++)
{
var jobInfo_1W = (JOB_INFO_1W) Marshal.PtrToStructure(pStruct, typeof (JOB_INFO_1W));
jobInfos[i] = jobInfo_1W;
pStruct += sizeOf;
}
Marshal.FreeHGlobal(pJob);
// do something with your structs
}
}
public class NativeConstants
{
public const int ERROR_INSUFFICIENT_BUFFER = 122;
}
public partial class NativeMethods
{
[DllImport("kernel32.dll", EntryPoint = "GetLastError")]
public static extern uint GetLastError();
}
public partial class NativeMethods
{
[DllImport("Winspool.drv", EntryPoint = "OpenPrinterW")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool OpenPrinterW([In] [MarshalAs(UnmanagedType.LPWStr)] string pPrinterName,
ref IntPtr phPrinter, [In] IntPtr pDefault);
[DllImport("Winspool.drv", EntryPoint = "EnumJobsW")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool EnumJobsW([In] IntPtr hPrinter, uint FirstJob, uint NoJobs, uint Level, IntPtr pJob,
uint cbBuf, [Out] out uint pcbNeeded, [Out] out uint pcReturned);
}
[StructLayout(LayoutKind.Sequential)]
public struct JOB_INFO_1W
{
public uint JobId;
[MarshalAs(UnmanagedType.LPWStr)] public string pPrinterName;
[MarshalAs(UnmanagedType.LPWStr)] public string pMachineName;
[MarshalAs(UnmanagedType.LPWStr)] public string pUserName;
[MarshalAs(UnmanagedType.LPWStr)] public string pDocument;
[MarshalAs(UnmanagedType.LPWStr)] public string pDatatype;
[MarshalAs(UnmanagedType.LPWStr)] public string pStatus;
public uint Status;
public uint Priority;
public uint Position;
public uint TotalPages;
public uint PagesPrinted;
public SYSTEMTIME Submitted;
}
[StructLayout(LayoutKind.Sequential)]
public struct SYSTEMTIME
{
public ushort wYear;
public ushort wMonth;
public ushort wDayOfWeek;
public ushort wDay;
public ushort wHour;
public ushort wMinute;
public ushort wSecond;
public ushort wMilliseconds;
}
}
Result:
Job 1:
Job 2 :
EDIT
It seems that you will have to get a little more robust checking than I did, because EnumJobs seems to return true when there are no jobs in queue. In the case of my example the assertion will fail but that won't mean that the code is wrong; just make sure that you have some jobs in the queue for the purpose of testing the function.

C# Dll injector, VB.Net Dll injector

I had made the dll injector easy before, but i had Windows 7, I made it in C# and C++, it worked great! but now when i try the same codes in Windows 8, it seems that it doesn't inject the DLL in the right way! :) as the DLL is not working...
(The code which i'm trying is the public one <)
VB.Net Code:
Private TargetProcessHandle As Integer
Private pfnStartAddr As Integer
Private pszLibFileRemote As String
Private TargetBufferSize As Integer
Public Const PROCESS_VM_READ = &H10
Public Const TH32CS_SNAPPROCESS = &H2
Public Const MEM_COMMIT = 4096
Public Const PAGE_READWRITE = 4
Public Const PROCESS_CREATE_THREAD = (&H2)
Public Const PROCESS_VM_OPERATION = (&H8)
Public Const PROCESS_VM_WRITE = (&H20)
Dim DLLFileName As String
Public Declare Function ReadProcessMemory Lib "kernel32" ( _
ByVal hProcess As Integer, _
ByVal lpBaseAddress As Integer, _
ByVal lpBuffer As String, _
ByVal nSize As Integer, _
ByRef lpNumberOfBytesWritten As Integer) As Integer
Public Declare Function LoadLibrary Lib "kernel32" Alias "LoadLibraryA" ( _
ByVal lpLibFileName As String) As Integer
Public Declare Function VirtualAllocEx Lib "kernel32" ( _
ByVal hProcess As Integer, _
ByVal lpAddress As Integer, _
ByVal dwSize As Integer, _
ByVal flAllocationType As Integer, _
ByVal flProtect As Integer) As Integer
Public Declare Function WriteProcessMemory Lib "kernel32" ( _
ByVal hProcess As Integer, _
ByVal lpBaseAddress As Integer, _
ByVal lpBuffer As String, _
ByVal nSize As Integer, _
ByRef lpNumberOfBytesWritten As Integer) As Integer
Public Declare Function GetProcAddress Lib "kernel32" ( _
ByVal hModule As Integer, ByVal lpProcName As String) As Integer
Private Declare Function GetModuleHandle Lib "Kernel32" Alias "GetModuleHandleA" ( _
ByVal lpModuleName As String) As Integer
Public Declare Function CreateRemoteThread Lib "kernel32" ( _
ByVal hProcess As Integer, _
ByVal lpThreadAttributes As Integer, _
ByVal dwStackSize As Integer, _
ByVal lpStartAddress As Integer, _
ByVal lpParameter As Integer, _
ByVal dwCreationFlags As Integer, _
ByRef lpThreadId As Integer) As Integer
Public Declare Function OpenProcess Lib "kernel32" ( _
ByVal dwDesiredAccess As Integer, _
ByVal bInheritHandle As Integer, _
ByVal dwProcessId As Integer) As Integer
Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" ( _
ByVal lpClassName As String, _
ByVal lpWindowName As String) As Integer
Private Declare Function CloseHandle Lib "kernel32" Alias "CloseHandle" ( _
ByVal hObject As Integer) As Integer
Dim ExeName As String = IO.Path.GetFileNameWithoutExtension(Application.ExecutablePath)
Private Sub Inject()
Try
Timer1.Stop()
Dim TargetProcess As Process() = Process.GetProcessesByName(TextBox1.Text)
TargetProcessHandle = OpenProcess(PROCESS_CREATE_THREAD Or PROCESS_VM_OPERATION Or PROCESS_VM_WRITE, False, TargetProcess(0).Id)
pszLibFileRemote = OpenFileDialog1.FileName
pfnStartAddr = GetProcAddress(GetModuleHandle("Kernel32"), "LoadLibraryA")
TargetBufferSize = 1 + Len(pszLibFileRemote)
Dim Rtn As Integer
Dim LoadLibParamAdr As Integer
LoadLibParamAdr = VirtualAllocEx(TargetProcessHandle, 0, TargetBufferSize, MEM_COMMIT, PAGE_READWRITE)
Rtn = WriteProcessMemory(TargetProcessHandle, LoadLibParamAdr, pszLibFileRemote, TargetBufferSize, 0)
CreateRemoteThread(TargetProcessHandle, 0, 0, pfnStartAddr, LoadLibParamAdr, 0, 0)
CloseHandle(TargetProcessHandle)
Catch ex As Exception
MessageBox.Show("EX:" + ex.ToString)
End Try
End Sub
It was giving an error "System.EntryPointNotFoundException....." but after i changed this:
CloseHandle Lib "kernel32" Alias "CloseHandleA"
TO this:
CloseHandle Lib "kernel32" Alias "CloseHandle"
it doesn't show the error again, but it also doesn't inject it in right way!
For C# :
[DllImport("kernel32")]
public static extern IntPtr CreateRemoteThread(
IntPtr hProcess,
IntPtr lpThreadAttributes,
uint dwStackSize,
UIntPtr lpStartAddress, // raw Pointer into remote process
IntPtr lpParameter,
uint dwCreationFlags,
out IntPtr lpThreadId
);
[DllImport("kernel32.dll")]
public static extern IntPtr OpenProcess(
UInt32 dwDesiredAccess,
Int32 bInheritHandle,
Int32 dwProcessId
);
[DllImport("kernel32.dll")]
public static extern Int32 CloseHandle(
IntPtr hObject
);
[DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
static extern bool VirtualFreeEx(
IntPtr hProcess,
IntPtr lpAddress,
UIntPtr dwSize,
uint dwFreeType
);
[DllImport("kernel32.dll", CharSet = CharSet.Ansi, ExactSpelling = true)]
public static extern UIntPtr GetProcAddress(
IntPtr hModule,
string procName
);
[DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
static extern IntPtr VirtualAllocEx(
IntPtr hProcess,
IntPtr lpAddress,
uint dwSize,
uint flAllocationType,
uint flProtect
);
[DllImport("kernel32.dll")]
static extern bool WriteProcessMemory(
IntPtr hProcess,
IntPtr lpBaseAddress,
string lpBuffer,
UIntPtr nSize,
out IntPtr lpNumberOfBytesWritten
);
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr GetModuleHandle(
string lpModuleName
);
[DllImport("kernel32", SetLastError = true, ExactSpelling = true)]
internal static extern Int32 WaitForSingleObject(
IntPtr handle,
Int32 milliseconds
);
public Int32 GetProcessId(String proc)
{
Process[] ProcList;
ProcList = Process.GetProcessesByName(proc);
return ProcList[0].Id;
}
public void InjectDLL(IntPtr hProcess, String strDLLName)
{
IntPtr bytesout;
// Length of string containing the DLL file name +1 byte padding
Int32 LenWrite = strDLLName.Length + 1;
// Allocate memory within the virtual address space of the target process
IntPtr AllocMem = (IntPtr)VirtualAllocEx(hProcess, (IntPtr)null, (uint)LenWrite, 0x1000, 0x40); //allocation pour WriteProcessMemory
// Write DLL file name to allocated memory in target process
WriteProcessMemory(hProcess, AllocMem, strDLLName, (UIntPtr)LenWrite, out bytesout);
// Function pointer "Injector"
UIntPtr Injector = (UIntPtr)GetProcAddress( GetModuleHandle("kernel32.dll"), "LoadLibraryA");
if (Injector == null)
{
MessageBox.Show(" Injector Error! \n ");
// return failed
return;
}
// Create thread in target process, and store handle in hThread
IntPtr hThread = (IntPtr)CreateRemoteThread(hProcess, (IntPtr)null, 0, Injector, AllocMem, 0, out bytesout);
// Make sure thread handle is valid
if ( hThread == null )
{
//incorrect thread handle ... return failed
MessageBox.Show(" hThread [ 1 ] Error! \n ");
return;
}
// Time-out is 10 seconds...
int Result = WaitForSingleObject(hThread, 10 * 1000);
// Check whether thread timed out...
if (Result == 0x00000080L || Result == 0x00000102L || Result == 0xFFFFFFFF)
{
/* Thread timed out... */
MessageBox.Show(" hThread [ 2 ] Error! \n ");
// Make sure thread handle is valid before closing... prevents crashes.
if (hThread != null)
{
//Close thread in target process
CloseHandle(hThread);
}
return;
}
// Sleep thread for 1 second
Thread.Sleep(1000);
// Clear up allocated space ( Allocmem )
VirtualFreeEx(hProcess, AllocMem, (UIntPtr)0, 0x8000);
// Make sure thread handle is valid before closing... prevents crashes.
if (hThread != null)
{
//Close thread in target process
CloseHandle(hThread);
}
// return succeeded
return;
}
private void button1_Click(object sender, EventArgs e)
{
String strDLLName = "C:\\test.dll";
String strProcessName = "notepad";
Int32 ProcID = GetProcessId(strProcessName);
if (ProcID >= 0)
{
IntPtr hProcess = (IntPtr)OpenProcess(0x1F0FFF, 1,ProcID);
if (hProcess == null)
{
MessageBox.Show("OpenProcess() Failed!");
return;
}
else
InjectDLL(hProcess, strDLLName);
}
}
It doesn't give me errors, but it also doesn't inject in right way!
Thanks very much! :)
What happens?
If the remote process crashes then it is likely that You should not use GetProcAddress since the remote API address may be different. It can be different for example when the injecting process is hooked using EAT hooking and the remote one isn't, so it does not have anything meaningful at the address You got from local GetProcAddress. It will be also different even when both processes are hooked using EAT hooking, since the hook implementations cause more random or entirely random locations.
I had similar sitation where the crashes occurred under Windows 8, but not in earlier versions.
One solution would be reading EAT table of the remote process and getting the address from there.
Related code follows
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.ComponentModel;
using System.Reflection;
using System.IO;
using System.Threading;
using Microsoft.Win32.SafeHandles;
using System.Runtime.CompilerServices;
using System.Security;
namespace Injection
{
public static class Remote_EAT_Reader
{
public static IntPtr? GetProcessModuleHandle(int processID, string moduleName)
{
IntPtr snapshot = IntPtr.Zero;
try
{
//http://pastebin.com/BzD1jdmH
snapshot = CreateToolhelp32Snapshot(SnapshotFlags.Module | SnapshotFlags.Module32, processID);
MODULEENTRY32 mod = new MODULEENTRY32() { dwSize = MODULEENTRY32.SizeOf };
if (!Module32First(snapshot, ref mod))
return null;
string searchString = moduleName.ToLowerInvariant();
do
{
if (mod.szModule.ToLowerInvariant() == searchString)
return mod.modBaseAddr;
}
while (Module32Next(snapshot, ref mod));
return IntPtr.Zero;
}
finally
{
if (snapshot != IntPtr.Zero)
CloseHandle(snapshot);
}
}
public static IntPtr? GetProcessProcAddress(IntPtr hProcess, int processID, string moduleName, string procName)
{
IntPtr? moduleHandle = GetProcessModuleHandle(processID, moduleName);
if (!moduleHandle.HasValue)
return null;
//code adapted from http://alter.org.ua/en/docs/nt_kernel/procaddr/index3.php
UIntPtr hmodCaller = new UIntPtr(unchecked((ulong)moduleHandle.Value.ToInt64()));
//http://stackoverflow.com/questions/769537/hook-loadlibrary-call-from-managed-code
//parse dos header
UIntPtr dos_header_ptr = hmodCaller;
if (dos_header_ptr == UIntPtr.Zero)
return null;
IMAGE_DOS_HEADER dos_header;
if (!ReadProcessMemory(hProcess, dos_header_ptr, out dos_header, IMAGE_DOS_HEADER.SizeOf, IntPtr.Zero))
return null;
if (dos_header.e_magic != IMAGE_DOS_SIGNATURE)
return null; //not a dos program
IMAGE_DATA_DIRECTORY[] DataDirectory;
if (IntPtr.Size == 4)
{
//parse nt header
UIntPtr nt_header_ptr = new UIntPtr((uint)dos_header.e_lfanew + hmodCaller.ToUInt32());
if (nt_header_ptr == UIntPtr.Zero)
return null;
IMAGE_NT_HEADERS32 nt_header;
if (!ReadProcessMemory(hProcess, nt_header_ptr, out nt_header, IMAGE_NT_HEADERS32.SizeOf, IntPtr.Zero))
return null;
if (nt_header.Signature != IMAGE_NT_SIGNATURE)
return null; //not a windows program
//http://newgre.net/ncodehook
//if (ntHeaders.FileHeader.Characteristics & IMAGE_FILE_DLL)
// throw std::runtime_error("Error while setting image base address: not the image base of an executable");
//optional header (pretty much not optional)
IMAGE_OPTIONAL_HEADER32 optional_header = nt_header.OptionalHeader32;
if (optional_header.Magic != IMAGE_NT_OPTIONAL_HDR32_MAGIC)
return null; //no optional header
DataDirectory = optional_header.DataDirectory;
}
else //if (IntPtr.Size == 4)
{
//parse nt header
UIntPtr nt_header_ptr = new UIntPtr((uint)dos_header.e_lfanew + hmodCaller.ToUInt64());
if (nt_header_ptr == UIntPtr.Zero)
return null;
IMAGE_NT_HEADERS64 nt_header;
if (!ReadProcessMemory(hProcess, nt_header_ptr, out nt_header, IMAGE_NT_HEADERS64.SizeOf, IntPtr.Zero))
return null;
if (nt_header.Signature != IMAGE_NT_SIGNATURE)
return null; //not a windows program
//http://newgre.net/ncodehook
//if (ntHeaders.FileHeader.Characteristics & IMAGE_FILE_DLL)
// throw std::runtime_error("Error while setting image base address: not the image base of an executable");
//optional header (pretty much not optional)
IMAGE_OPTIONAL_HEADER64 optional_header = nt_header.OptionalHeader64;
if (optional_header.Magic != IMAGE_NT_OPTIONAL_HDR64_MAGIC)
return null; //no optional header
DataDirectory = optional_header.DataDirectory;
} //if (IntPtr.Size == 4)
//http://stackoverflow.com/questions/3430718/thunk-table-in-import-address-table and http://forums.codeguru.com/showthread.php?512610-RESOLVED-api-hooking and especially http://svn.coderepos.org/share/lang/objective-cplusplus/i3/trunk/tmp/dwmedit/ApiHook.cc
DirectoryEntries entryIndex = DirectoryEntries.IMAGE_DIRECTORY_ENTRY_EXPORT;
uint size = DataDirectory[(int)entryIndex].Size;
if (size == 0)
return null; //no import table
uint virtualAddress = DataDirectory[(int)entryIndex].VirtualAddress;
//http://newgre.net/ncodehook
if (virtualAddress == 0)
return null; //no import directory
UIntPtr pExports_ptr = new UIntPtr((ulong)virtualAddress + hmodCaller.ToUInt64());
if (pExports_ptr == UIntPtr.Zero)
return null;
IMAGE_EXPORT_DIRECTORY pExports;
if (!ReadProcessMemory(hProcess, pExports_ptr,
out pExports, IMAGE_EXPORT_DIRECTORY.SizeOf, IntPtr.Zero))
{
return null;
}
UIntPtr functions_ptr = new UIntPtr(hmodCaller.ToUInt64() + (ulong)pExports.AddressOfFunctions);
UIntPtr ordinals_ptr = new UIntPtr(hmodCaller.ToUInt64() + (ulong)pExports.AddressOfNameOrdinals);
UIntPtr names_ptr = new UIntPtr(hmodCaller.ToUInt64() + (ulong)pExports.AddressOfNames);
uint max_name = pExports.NumberOfNames;
uint max_func = pExports.NumberOfFunctions;
uint max_ordinal = max_name;
uint[] functions = new uint[max_func];
if (!ReadProcessMemory(hProcess, functions_ptr, functions, (int)max_func * sizeof(uint), IntPtr.Zero))
{
return null;
}
ushort[] ordinals = new ushort[max_ordinal];
if (!ReadProcessMemory(hProcess, ordinals_ptr, ordinals, (int)max_ordinal * sizeof(ushort), IntPtr.Zero))
{
return null;
}
uint[] names = new uint[max_name];
if (!ReadProcessMemory(hProcess, names_ptr, names, (int)max_name * sizeof(uint), IntPtr.Zero))
{
return null;
}
for (uint i = 0; i < max_ordinal; i++)
{
uint ord = ordinals[i];
if (i >= max_name || ord >= max_func)
{
return null;
}
if (functions[ord] < virtualAddress || functions[ord] >= virtualAddress + size)
{
UIntPtr name_ptr = new UIntPtr(hmodCaller.ToUInt64() + (ulong)names[i]);
if (name_ptr != UIntPtr.Zero)
{
byte[] name_buf = new byte[procName.Length + 1]; //NB! +1 for terminating zero
if (!ReadProcessMemory(hProcess, name_ptr, name_buf, name_buf.Length, IntPtr.Zero))
{
continue;
}
if (name_buf[name_buf.Length - 1] == 0) //check for partial name that does not end with terminating zero
{
string name = Encoding.ASCII.GetString(name_buf, 0, name_buf.Length - 1); //NB! buf length - 1
if (name == procName)
{
var pFunctionAddress1 = new UIntPtr(hmodCaller.ToUInt64() + (ulong)functions[ord]);
return new IntPtr(unchecked((long)pFunctionAddress1.ToUInt64()));
}
}
} //if (name_ptr != UIntPtr.Zero)
} //if (functions[ord] < virtualAddress || functions[ord] >= virtualAddress + size)
} //for (uint i = 0; i < pExports.AddressOfNames; i++)
return null;
} //static IntPtr? GetProcessProcAddress(int procID, string moduleName, string procName)
#region PE Structs
private const uint IMAGE_DOS_SIGNATURE = 0x5A4D; // MZ
private const uint IMAGE_OS2_SIGNATURE = 0x454E; // NE
private const uint IMAGE_OS2_SIGNATURE_LE = 0x454C; // LE
private const uint IMAGE_VXD_SIGNATURE = 0x454C; // LE
private const uint IMAGE_NT_SIGNATURE = 0x00004550; // PE00
private const uint IMAGE_NT_OPTIONAL_HDR32_MAGIC = 0x10b;
private const uint IMAGE_NT_OPTIONAL_HDR64_MAGIC = 0x20b;
private enum DirectoryEntries
{
IMAGE_DIRECTORY_ENTRY_EXPORT = 0, // Export Directory
IMAGE_DIRECTORY_ENTRY_IMPORT = 1, // Import Directory
IMAGE_DIRECTORY_ENTRY_RESOURCE = 2, // Resource Directory
IMAGE_DIRECTORY_ENTRY_EXCEPTION = 3, // Exception Directory
IMAGE_DIRECTORY_ENTRY_SECURITY = 4, // Security Directory
IMAGE_DIRECTORY_ENTRY_BASERELOC = 5, // Base Relocation Table
IMAGE_DIRECTORY_ENTRY_DEBUG = 6, // Debug Directory
// IMAGE_DIRECTORY_ENTRY_COPYRIGHT 7, // (X86 usage)
IMAGE_DIRECTORY_ENTRY_ARCHITECTURE = 7, // Architecture Specific Data
IMAGE_DIRECTORY_ENTRY_GLOBALPTR = 8, // RVA of GP
IMAGE_DIRECTORY_ENTRY_TLS = 9, // TLS Directory
IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG = 10, // Load Configuration Directory
IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT = 11, // Bound Import Directory in headers
IMAGE_DIRECTORY_ENTRY_IAT = 12, // Import Address Table
IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT = 13, // Delay Load Import Descriptors
IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR = 14, // COM Runtime descriptor
} //private enum DirectoryEntries
//code taken from http://www.sergeyakopov.com/2010/11/reading-pe-format-using-data-marshaling-in-net
[StructLayout(LayoutKind.Sequential)]
public struct IMAGE_DOS_HEADER
{
public static int SizeOf = Marshal.SizeOf(typeof(IMAGE_DOS_HEADER));
public UInt16 e_magic;
public UInt16 e_cblp;
public UInt16 e_cp;
public UInt16 e_crlc;
public UInt16 e_cparhdr;
public UInt16 e_minalloc;
public UInt16 e_maxalloc;
public UInt16 e_ss;
public UInt16 e_sp;
public UInt16 e_csum;
public UInt16 e_ip;
public UInt16 e_cs;
public UInt16 e_lfarlc;
public UInt16 e_ovno;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
public UInt16[] e_res1;
public UInt16 e_oemid;
public UInt16 e_oeminfo;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
public UInt16[] e_res2;
public UInt32 e_lfanew;
}
[StructLayout(LayoutKind.Sequential)]
public struct IMAGE_NT_HEADERS32
{
public static int SizeOf = Marshal.SizeOf(typeof(IMAGE_NT_HEADERS32));
public UInt32 Signature;
public IMAGE_FILE_HEADER FileHeader;
public IMAGE_OPTIONAL_HEADER32 OptionalHeader32;
}
[StructLayout(LayoutKind.Sequential)]
public struct IMAGE_NT_HEADERS64
{
public static int SizeOf = Marshal.SizeOf(typeof(IMAGE_NT_HEADERS64));
public UInt32 Signature;
public IMAGE_FILE_HEADER FileHeader;
public IMAGE_OPTIONAL_HEADER64 OptionalHeader64;
}
[StructLayout(LayoutKind.Sequential)]
public struct IMAGE_FILE_HEADER
{
public UInt16 Machine;
public UInt16 NumberOfSections;
public UInt32 TimeDateStamp;
public UInt32 PointerToSymbolTable;
public UInt32 NumberOfSymbols;
public UInt16 SizeOfOptionalHeader;
public UInt16 Characteristics;
}
[StructLayout(LayoutKind.Sequential)]
public struct IMAGE_OPTIONAL_HEADER32
{
public UInt16 Magic;
public Byte MajorLinkerVersion;
public Byte MinorLinkerVersion;
public UInt32 SizeOfCode;
public UInt32 SizeOfInitializedData;
public UInt32 SizeOfUninitializedData;
public UInt32 AddressOfEntryPoint;
public UInt32 BaseOfCode;
public UInt32 BaseOfData;
public UInt32 ImageBase;
public UInt32 SectionAlignment;
public UInt32 FileAlignment;
public UInt16 MajorOperatingSystemVersion;
public UInt16 MinorOperatingSystemVersion;
public UInt16 MajorImageVersion;
public UInt16 MinorImageVersion;
public UInt16 MajorSubsystemVersion;
public UInt16 MinorSubsystemVersion;
public UInt32 Win32VersionValue;
public UInt32 SizeOfImage;
public UInt32 SizeOfHeaders;
public UInt32 CheckSum;
public UInt16 Subsystem;
public UInt16 DllCharacteristics;
public UInt32 SizeOfStackReserve;
public UInt32 SizeOfStackCommit;
public UInt32 SizeOfHeapReserve;
public UInt32 SizeOfHeapCommit;
public UInt32 LoaderFlags;
public UInt32 NumberOfRvaAndSizes;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
public IMAGE_DATA_DIRECTORY[] DataDirectory;
}
[StructLayout(LayoutKind.Sequential)]
public struct IMAGE_OPTIONAL_HEADER64
{
public UInt16 Magic;
public Byte MajorLinkerVersion;
public Byte MinorLinkerVersion;
public UInt32 SizeOfCode;
public UInt32 SizeOfInitializedData;
public UInt32 SizeOfUninitializedData;
public UInt32 AddressOfEntryPoint;
public UInt32 BaseOfCode;
public UInt64 ImageBase;
public UInt32 SectionAlignment;
public UInt32 FileAlignment;
public UInt16 MajorOperatingSystemVersion;
public UInt16 MinorOperatingSystemVersion;
public UInt16 MajorImageVersion;
public UInt16 MinorImageVersion;
public UInt16 MajorSubsystemVersion;
public UInt16 MinorSubsystemVersion;
public UInt32 Win32VersionValue;
public UInt32 SizeOfImage;
public UInt32 SizeOfHeaders;
public UInt32 CheckSum;
public UInt16 Subsystem;
public UInt16 DllCharacteristics;
public UInt64 SizeOfStackReserve;
public UInt64 SizeOfStackCommit;
public UInt64 SizeOfHeapReserve;
public UInt64 SizeOfHeapCommit;
public UInt32 LoaderFlags;
public UInt32 NumberOfRvaAndSizes;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
public IMAGE_DATA_DIRECTORY[] DataDirectory;
}
[StructLayout(LayoutKind.Sequential)]
public struct IMAGE_DATA_DIRECTORY
{
public UInt32 VirtualAddress;
public UInt32 Size;
}
[StructLayout(LayoutKind.Sequential)]
public struct ImgDelayDescr
{
public static uint SizeOf = (uint)Marshal.SizeOf(typeof(ImgDelayDescr));
public uint grAttrs; // attributes
public uint rvaDLLName; // RVA to dll name
public uint rvaHmod; // RVA of module handle
public uint rvaIAT; // RVA of the IAT
public uint rvaINT; // RVA of the INT
public uint rvaBoundIAT; // RVA of the optional bound IAT
public uint rvaUnloadIAT; // RVA of optional copy of original IAT
public uint dwTimeStamp; // 0 if not bound,
// O.W. date/time stamp of DLL bound to (Old BIND)
} //ImgDelayDescr, * PImgDelayDescr;
//http://www.gamedev.net/topic/409936-advanced-c-native-dll-image-import-reading/
[StructLayout(LayoutKind.Explicit)]
public struct IMAGE_IMPORT_DESCRIPTOR
{
public static uint SizeOf = (uint)Marshal.SizeOf(typeof(IMAGE_IMPORT_DESCRIPTOR));
#region union
/// <summary>
/// CSharp doesnt really support unions, but they can be emulated by a field offset 0
/// </summary>
[FieldOffset(0)]
public uint Characteristics; // 0 for terminating null import descriptor
[FieldOffset(0)]
public uint OriginalFirstThunk; // RVA to original unbound IAT (PIMAGE_THUNK_DATA)
#endregion
[FieldOffset(4)]
public uint TimeDateStamp;
[FieldOffset(8)]
public uint ForwarderChain;
[FieldOffset(12)]
public uint Name;
[FieldOffset(16)]
public uint FirstThunk;
}
//http://pinvoke.net/default.aspx/Structures/IMAGE_EXPORT_DIRECTORY.html
[StructLayout(LayoutKind.Sequential)]
public struct IMAGE_EXPORT_DIRECTORY
{
public static uint SizeOf = (uint)Marshal.SizeOf(typeof(IMAGE_EXPORT_DIRECTORY));
public UInt32 Characteristics;
public UInt32 TimeDateStamp;
public UInt16 MajorVersion;
public UInt16 MinorVersion;
public UInt32 Name;
public UInt32 Base;
public UInt32 NumberOfFunctions;
public UInt32 NumberOfNames;
public UInt32 AddressOfFunctions; // RVA from base of image
public UInt32 AddressOfNames; // RVA from base of image
public UInt32 AddressOfNameOrdinals; // RVA from base of image
}
[StructLayout(LayoutKind.Sequential)]
public struct IMAGE_THUNK_DATA
{
public static uint SizeOf = (uint)Marshal.SizeOf(typeof(IMAGE_THUNK_DATA));
public IntPtr ForwarderString; // PBYTE
public IntPtr Function; // PDWORD
public IntPtr Ordinal;
public IntPtr AddressOfData; // PIMAGE_IMPORT_BY_NAME
}
#endregion
[DllImport("kernel32.dll", SetLastError = true)]
static public extern bool CloseHandle(IntPtr hHandle);
[DllImport("kernel32.dll")]
static extern bool Module32First(IntPtr hSnapshot, ref MODULEENTRY32 lpme);
[DllImport("kernel32.dll")]
static extern bool Module32Next(IntPtr hSnapshot, ref MODULEENTRY32 lpme);
[DllImport("kernel32.dll", SetLastError = true)]
static public extern IntPtr CreateToolhelp32Snapshot(SnapshotFlags dwFlags, int th32ProcessID);
public const short INVALID_HANDLE_VALUE = -1;
[Flags]
public enum SnapshotFlags : uint
{
HeapList = 0x00000001,
Process = 0x00000002,
Thread = 0x00000004,
Module = 0x00000008,
Module32 = 0x00000010,
Inherit = 0x80000000,
All = 0x0000001F
}
public struct MODULEENTRY32
{
public static uint SizeOf = (uint)Marshal.SizeOf(typeof(MODULEENTRY32));
//http://pastebin.com/BzD1jdmH
private const int MAX_PATH = 255;
internal uint dwSize;
internal uint th32ModuleID;
internal uint th32ProcessID;
internal uint GlblcntUsage;
internal uint ProccntUsage;
internal IntPtr modBaseAddr;
internal uint modBaseSize;
internal IntPtr hModule;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = MAX_PATH + 1)]
internal string szModule;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = MAX_PATH + 5)]
internal string szExePath;
}
[DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
private static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr baseAddress, [Out] byte[] buffer, int size, out IntPtr numBytesRead);
[DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
private static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr baseAddress, [Out] byte[] buffer, int size, IntPtr numBytesRead);
[DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
private static extern bool ReadProcessMemory(IntPtr hProcess, UIntPtr baseAddress, [Out] byte[] buffer, int size, IntPtr numBytesRead);
[DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
private static extern bool ReadProcessMemory(IntPtr hProcess, UIntPtr baseAddress, [Out] uint[] buffer, int size, IntPtr numBytesRead);
[DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
private static extern bool ReadProcessMemory(IntPtr hProcess, UIntPtr baseAddress, [Out] ushort[] buffer, int size, IntPtr numBytesRead);
[DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
private static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr baseAddress, out IntPtr buffer, int size, IntPtr numBytesRead);
[DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
private static extern bool ReadProcessMemory(IntPtr hProcess, UIntPtr baseAddress, out IMAGE_DOS_HEADER buffer, int size, IntPtr numBytesRead);
[DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
private static extern bool ReadProcessMemory(IntPtr hProcess, UIntPtr baseAddress, out IMAGE_NT_HEADERS32 buffer, int size, IntPtr numBytesRead);
[DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
private static extern bool ReadProcessMemory(IntPtr hProcess, UIntPtr baseAddress, out IMAGE_NT_HEADERS64 buffer, int size, IntPtr numBytesRead);
[DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
private static extern bool ReadProcessMemory(IntPtr hProcess, UIntPtr baseAddress, out ImgDelayDescr buffer, uint size, IntPtr numBytesRead);
[DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
private static extern bool ReadProcessMemory(IntPtr hProcess, UIntPtr baseAddress, out IMAGE_THUNK_DATA buffer, uint size, IntPtr numBytesRead);
[DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
private static extern bool ReadProcessMemory(IntPtr hProcess, UIntPtr baseAddress, out IMAGE_IMPORT_DESCRIPTOR buffer, uint size, IntPtr numBytesRead);
[DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
private static extern bool ReadProcessMemory(IntPtr hProcess, UIntPtr baseAddress, out IMAGE_EXPORT_DIRECTORY buffer, uint size, IntPtr numBytesRead);
}
}

I need to add a custom form size to the local Print Server through code

I need to add a custom form size to the local Print Server through code. I would like to do it in a bat file or in C#, or possibly in something else I can run when running my InstallShield installer.
To better explain, to do this manually, open Devices and Printers and click on a printer. Then click Print Server Properties. The form below will open, and you can view/add/delete Forms. I would like to add a new one here through code (then eventually select this new paper size in the printers Advanced options).
This code project article explains how to do it, and presumably has some helper classes and methods. Some sample code is below:
var formInfo = new FormInfo1();
formInfo.Flags = 0;
formInfo.pName = paperName;
// all sizes in 1000ths of millimeters
formInfo.Size.width = (int)(widthMm * 1000.0);
formInfo.Size.height = (int)(heightMm * 1000.0);
formInfo.ImageableArea.left = 0;
formInfo.ImageableArea.right = formInfo.Size.width;
formInfo.ImageableArea.top = 0;
formInfo.ImageableArea.bottom = formInfo.Size.height;
// Add the paper size to the printer's list of available paper sizes:
bool bFormAdded = AddForm(hPrinter, 1, ref formInfo);
http://www.codeproject.com/Articles/12229/Adding-custom-paper-sizes-to-named-printers
I get code from here: http://www.codeproject.com/Articles/12229/Adding-custom-paper-sizes-to-named-printers. But it dosnt work.
You need to replace FormInfo1 from source code to this --v--v--v--
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
internal struct FormInfo1
{
public uint Flags;
public String pName;
public structSize Size;
public structRect ImageableArea;
};
And dont use this method "AddCustomPaperSize" - it dosn't work
Only method "AddCustomPaperSizeToDefaultPrinter" is working (for me)
Here is result:
Here is full work code:
using System;
using System.Text;
using System.Runtime.InteropServices;
using System.Security;
using System.ComponentModel;
using System.Drawing.Printing;
namespace MJMCustomPrintForm
{
/// <summary>
/// Summary description for MJMCustomPrintForm.
/// </summary>
public class MJMCustomPrintForm
{
// Make a static class
private MJMCustomPrintForm()
{
}
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)]
internal struct structPrinterDefaults
{
[MarshalAs(UnmanagedType.LPTStr)] public String pDatatype;
public IntPtr pDevMode;
[MarshalAs(UnmanagedType.I4)] public int DesiredAccess;
};
[DllImport("winspool.Drv", EntryPoint="OpenPrinter", SetLastError=true,
CharSet=CharSet.Unicode, ExactSpelling=false,CallingConvention=CallingConvention.StdCall),
SuppressUnmanagedCodeSecurityAttribute()]
internal static extern bool OpenPrinter([MarshalAs(UnmanagedType.LPTStr)]
string printerName,
out IntPtr phPrinter,
ref structPrinterDefaults pd);
[DllImport("winspool.Drv", EntryPoint="ClosePrinter", SetLastError=true,
CharSet=CharSet.Unicode, ExactSpelling=false,
CallingConvention=CallingConvention.StdCall),SuppressUnmanagedCodeSecurityAttribute()]
internal static extern bool ClosePrinter(IntPtr phPrinter);
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)]
internal struct structSize
{
public Int32 width;
public Int32 height;
}
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)]
internal struct structRect
{
public Int32 left;
public Int32 top;
public Int32 right;
public Int32 bottom;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
internal struct FormInfo1
{
public uint Flags;
public String pName;
public structSize Size;
public structRect ImageableArea;
};
//[StructLayout(LayoutKind.Explicit, CharSet = CharSet.Unicode)]
//internal struct FormInfo1
//{
// [FieldOffset(0), MarshalAs(UnmanagedType.I4)] public uint Flags;
// [FieldOffset(4), MarshalAs(UnmanagedType.LPWStr)] public String pName;
// [FieldOffset(8)] public structSize Size;
// [FieldOffset(16)] public structRect ImageableArea;
//};
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi/* changed from CharSet=CharSet.Auto */)]
internal struct structDevMode
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=32)] public String
dmDeviceName;
[MarshalAs(UnmanagedType.U2)] public short dmSpecVersion;
[MarshalAs(UnmanagedType.U2)] public short dmDriverVersion;
[MarshalAs(UnmanagedType.U2)] public short dmSize;
[MarshalAs(UnmanagedType.U2)] public short dmDriverExtra;
[MarshalAs(UnmanagedType.U4)] public int dmFields;
[MarshalAs(UnmanagedType.I2)] public short dmOrientation;
[MarshalAs(UnmanagedType.I2)] public short dmPaperSize;
[MarshalAs(UnmanagedType.I2)] public short dmPaperLength;
[MarshalAs(UnmanagedType.I2)] public short dmPaperWidth;
[MarshalAs(UnmanagedType.I2)] public short dmScale;
[MarshalAs(UnmanagedType.I2)] public short dmCopies;
[MarshalAs(UnmanagedType.I2)] public short dmDefaultSource;
[MarshalAs(UnmanagedType.I2)] public short dmPrintQuality;
[MarshalAs(UnmanagedType.I2)] public short dmColor;
[MarshalAs(UnmanagedType.I2)] public short dmDuplex;
[MarshalAs(UnmanagedType.I2)] public short dmYResolution;
[MarshalAs(UnmanagedType.I2)] public short dmTTOption;
[MarshalAs(UnmanagedType.I2)] public short dmCollate;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=32)] public String dmFormName;
[MarshalAs(UnmanagedType.U2)] public short dmLogPixels;
[MarshalAs(UnmanagedType.U4)] public int dmBitsPerPel;
[MarshalAs(UnmanagedType.U4)] public int dmPelsWidth;
[MarshalAs(UnmanagedType.U4)] public int dmPelsHeight;
[MarshalAs(UnmanagedType.U4)] public int dmNup;
[MarshalAs(UnmanagedType.U4)] public int dmDisplayFrequency;
[MarshalAs(UnmanagedType.U4)] public int dmICMMethod;
[MarshalAs(UnmanagedType.U4)] public int dmICMIntent;
[MarshalAs(UnmanagedType.U4)] public int dmMediaType;
[MarshalAs(UnmanagedType.U4)] public int dmDitherType;
[MarshalAs(UnmanagedType.U4)] public int dmReserved1;
[MarshalAs(UnmanagedType.U4)] public int dmReserved2;
}
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)]
internal struct PRINTER_INFO_9
{
public IntPtr pDevMode;
}
[DllImport("winspool.Drv", EntryPoint="AddFormW", SetLastError=true,
CharSet=CharSet.Unicode, ExactSpelling=true,
CallingConvention=CallingConvention.StdCall), SuppressUnmanagedCodeSecurityAttribute()]
internal static extern bool AddForm(
IntPtr phPrinter,
[MarshalAs(UnmanagedType.I4)] int level,
ref FormInfo1 form);
[DllImport("winspool.Drv", EntryPoint="DeleteForm", SetLastError=true,
CharSet=CharSet.Unicode, ExactSpelling=false,CallingConvention=CallingConvention.StdCall),
SuppressUnmanagedCodeSecurityAttribute()]
internal static extern bool DeleteForm(
IntPtr phPrinter,
[MarshalAs(UnmanagedType.LPTStr)] string pName);
[DllImport("kernel32.dll", EntryPoint="GetLastError", SetLastError=false,
ExactSpelling=true, CallingConvention=CallingConvention.StdCall),
SuppressUnmanagedCodeSecurityAttribute()]
internal static extern Int32 GetLastError();
[DllImport("GDI32.dll", EntryPoint="CreateDC", SetLastError=true,
CharSet=CharSet.Unicode, ExactSpelling=false,
CallingConvention=CallingConvention.StdCall),
SuppressUnmanagedCodeSecurityAttribute()]
internal static extern IntPtr CreateDC([MarshalAs(UnmanagedType.LPTStr)]
string pDrive,
[MarshalAs(UnmanagedType.LPTStr)] string pName,
[MarshalAs(UnmanagedType.LPTStr)] string pOutput,
ref structDevMode pDevMode);
[DllImport("GDI32.dll", EntryPoint="ResetDC", SetLastError=true,
CharSet=CharSet.Unicode, ExactSpelling=false,
CallingConvention=CallingConvention.StdCall),
SuppressUnmanagedCodeSecurityAttribute()]
internal static extern IntPtr ResetDC(
IntPtr hDC,
ref structDevMode
pDevMode);
[DllImport("GDI32.dll", EntryPoint="DeleteDC", SetLastError=true,
CharSet=CharSet.Unicode, ExactSpelling=false,
CallingConvention=CallingConvention.StdCall),
SuppressUnmanagedCodeSecurityAttribute()]
internal static extern bool DeleteDC(IntPtr hDC);
[DllImport("winspool.Drv", EntryPoint="SetPrinterA", SetLastError=true,
CharSet=CharSet.Auto, ExactSpelling=true,
CallingConvention=CallingConvention.StdCall), SuppressUnmanagedCodeSecurityAttribute()]
internal static extern bool SetPrinter(
IntPtr hPrinter,
[MarshalAs(UnmanagedType.I4)] int level,
IntPtr pPrinter,
[MarshalAs(UnmanagedType.I4)] int command);
/*
LONG DocumentProperties(
HWND hWnd, // handle to parent window
HANDLE hPrinter, // handle to printer object
LPTSTR pDeviceName, // device name
PDEVMODE pDevModeOutput, // modified device mode
PDEVMODE pDevModeInput, // original device mode
DWORD fMode // mode options
);
*/
[DllImport("winspool.Drv", EntryPoint="DocumentPropertiesA", SetLastError=true,
ExactSpelling=true, CallingConvention=CallingConvention.StdCall)]
public static extern int DocumentProperties(
IntPtr hwnd,
IntPtr hPrinter,
[MarshalAs(UnmanagedType.LPStr)] string pDeviceName /* changed from String to string */,
IntPtr pDevModeOutput,
IntPtr pDevModeInput,
int fMode
);
[DllImport("winspool.Drv", EntryPoint="GetPrinterA", SetLastError=true,
ExactSpelling=true, CallingConvention=CallingConvention.StdCall)]
public static extern bool GetPrinter(
IntPtr hPrinter,
int dwLevel /* changed type from Int32 */,
IntPtr pPrinter,
int dwBuf /* chagned from Int32*/,
out int dwNeeded /* changed from Int32*/
);
// SendMessageTimeout tools
[Flags] public enum SendMessageTimeoutFlags : uint
{
SMTO_NORMAL = 0x0000,
SMTO_BLOCK = 0x0001,
SMTO_ABORTIFHUNG = 0x0002,
SMTO_NOTIMEOUTIFNOTHUNG = 0x0008
}
const int WM_SETTINGCHANGE = 0x001A;
const int HWND_BROADCAST = 0xffff;
[DllImport("user32.dll", SetLastError=true, CharSet=CharSet.Auto)]
public static extern IntPtr SendMessageTimeout(
IntPtr windowHandle,
uint Msg,
IntPtr wParam,
IntPtr lParam,
SendMessageTimeoutFlags flags,
uint timeout,
out IntPtr result
);
public static void AddMjm80MmPaperSizeToDefaultPrinter()
{
AddCustomPaperSizeToDefaultPrinter("MJM 80mm * Receipt Length", 80.1f, 4003.9f);
}
public static void AddMjm104MmPaperSizeToDefaultPrinter()
{
AddCustomPaperSizeToDefaultPrinter("MJM 104mm * Receipt Length", 104.1f, 4003.9f);
}
/// <summary>
/// Adds the printer form to the default printer
/// </summary>
/// <param name="paperName">Name of the printer form</param>
/// <param name="widthMm">Width given in millimeters</param>
/// <param name="heightMm">Height given in millimeters</param>
public static void AddCustomPaperSizeToDefaultPrinter(string paperName, float widthMm, float heightMm)
{
PrintDocument pd = new PrintDocument();
string sPrinterName = pd.PrinterSettings.PrinterName;
AddCustomPaperSize(sPrinterName, paperName, widthMm, heightMm);
}
/// <summary>
/// Add the printer form to a printer
/// </summary>
/// <param name="printerName">The printer name</param>
/// <param name="paperName">Name of the printer form</param>
/// <param name="widthMm">Width given in millimeters</param>
/// <param name="heightMm">Height given in millimeters</param>
public static void AddCustomPaperSize(string printerName, string paperName, float
widthMm, float heightMm)
{
if (PlatformID.Win32NT == Environment.OSVersion.Platform)
{
// The code to add a custom paper size is different for Windows NT then it is
// for previous versions of windows
const int PRINTER_ACCESS_USE = 0x00000008;
const int PRINTER_ACCESS_ADMINISTER = 0x00000004;
const int FORM_PRINTER = 0x00000002;
structPrinterDefaults defaults = new structPrinterDefaults();
defaults.pDatatype = null;
defaults.pDevMode = IntPtr.Zero;
defaults.DesiredAccess = PRINTER_ACCESS_ADMINISTER | PRINTER_ACCESS_USE;
IntPtr hPrinter = IntPtr.Zero;
// Open the printer.
if (OpenPrinter(printerName, out hPrinter, ref defaults))
{
try
{
// delete the form incase it already exists
DeleteForm(hPrinter, paperName);
// create and initialize the FORM_INFO_1 structure
FormInfo1 formInfo = new FormInfo1();
formInfo.Flags = 0;
formInfo.pName = paperName;
// all sizes in 1000ths of millimeters
formInfo.Size.width = (int)(widthMm * 1000.0);
formInfo.Size.height = (int)(heightMm * 1000.0);
formInfo.ImageableArea.left = 0;
formInfo.ImageableArea.right = formInfo.Size.width;
formInfo.ImageableArea.top = 0;
formInfo.ImageableArea.bottom = formInfo.Size.height;
if (!AddForm(hPrinter, 1, ref formInfo))
{
StringBuilder strBuilder = new StringBuilder();
strBuilder.AppendFormat("Failed to add the custom paper size {0} to the printer {1}, System error number: {2}",
paperName, printerName, GetLastError());
throw new ApplicationException(strBuilder.ToString());
}
// INIT
const int DM_OUT_BUFFER = 2;
const int DM_IN_BUFFER = 8;
structDevMode devMode = new structDevMode();
IntPtr hPrinterInfo, hDummy;
PRINTER_INFO_9 printerInfo;
printerInfo.pDevMode = IntPtr.Zero;
int iPrinterInfoSize, iDummyInt;
// GET THE SIZE OF THE DEV_MODE BUFFER
int iDevModeSize = DocumentProperties(IntPtr.Zero, hPrinter, printerName, IntPtr.Zero, IntPtr.Zero, 0);
if(iDevModeSize < 0)
throw new ApplicationException("Cannot get the size of the DEVMODE structure.");
// ALLOCATE THE BUFFER
IntPtr hDevMode = Marshal.AllocCoTaskMem(iDevModeSize + 100);
// GET A POINTER TO THE DEV_MODE BUFFER
int iRet = DocumentProperties(IntPtr.Zero, hPrinter, printerName, hDevMode, IntPtr.Zero, DM_OUT_BUFFER);
if(iRet < 0)
throw new ApplicationException("Cannot get the DEVMODE structure.");
// FILL THE DEV_MODE STRUCTURE
devMode = (structDevMode)Marshal.PtrToStructure(hDevMode, devMode.GetType());
// SET THE FORM NAME FIELDS TO INDICATE THAT THIS FIELD WILL BE MODIFIED
devMode.dmFields = 0x10000; // DM_FORMNAME
// SET THE FORM NAME
devMode.dmFormName = paperName;
// PUT THE DEV_MODE STRUCTURE BACK INTO THE POINTER
Marshal.StructureToPtr(devMode, hDevMode, true);
// MERGE THE NEW CHAGES WITH THE OLD
iRet = DocumentProperties(IntPtr.Zero, hPrinter, printerName,
printerInfo.pDevMode, printerInfo.pDevMode, DM_IN_BUFFER | DM_OUT_BUFFER);
if(iRet < 0)
throw new ApplicationException("Unable to set the orientation setting for this printer.");
// GET THE PRINTER INFO SIZE
GetPrinter(hPrinter, 9, IntPtr.Zero, 0, out iPrinterInfoSize);
if(iPrinterInfoSize == 0)
throw new ApplicationException("GetPrinter failed. Couldn't get the # bytes needed for shared PRINTER_INFO_9 structure");
// ALLOCATE THE BUFFER
hPrinterInfo = Marshal.AllocCoTaskMem(iPrinterInfoSize + 100);
// GET A POINTER TO THE PRINTER INFO BUFFER
bool bSuccess = GetPrinter(hPrinter, 9, hPrinterInfo, iPrinterInfoSize, out iDummyInt);
if(!bSuccess)
throw new ApplicationException("GetPrinter failed. Couldn't get the shared PRINTER_INFO_9 structure");
// FILL THE PRINTER INFO STRUCTURE
printerInfo = (PRINTER_INFO_9)Marshal.PtrToStructure(hPrinterInfo, printerInfo.GetType());
printerInfo.pDevMode = hDevMode;
// GET A POINTER TO THE PRINTER INFO STRUCTURE
Marshal.StructureToPtr(printerInfo, hPrinterInfo, true);
// SET THE PRINTER SETTINGS
bSuccess = SetPrinter(hPrinter, 9, hPrinterInfo, 0);
if(!bSuccess)
throw new Win32Exception(Marshal.GetLastWin32Error(), "SetPrinter() failed. Couldn't set the printer settings");
// Tell all open programs that this change occurred.
SendMessageTimeout(
new IntPtr(HWND_BROADCAST),
WM_SETTINGCHANGE,
IntPtr.Zero,
IntPtr.Zero,
MJMCustomPrintForm.SendMessageTimeoutFlags.SMTO_NORMAL,
1000,
out hDummy);
}
finally
{
ClosePrinter(hPrinter);
}
}
else
{
StringBuilder strBuilder = new StringBuilder();
strBuilder.AppendFormat("Failed to open the {0} printer, System error number: {1}",
printerName, GetLastError());
throw new ApplicationException(strBuilder.ToString());
}
}
else
{
structDevMode pDevMode = new structDevMode();
IntPtr hDC = CreateDC(null, printerName, null, ref pDevMode);
if (hDC != IntPtr.Zero)
{
const long DM_PAPERSIZE = 0x00000002L;
const long DM_PAPERLENGTH = 0x00000004L;
const long DM_PAPERWIDTH = 0x00000008L;
pDevMode.dmFields = (int)(DM_PAPERSIZE | DM_PAPERWIDTH | DM_PAPERLENGTH);
pDevMode.dmPaperSize = 256;
pDevMode.dmPaperWidth = (short)(widthMm * 1000.0);
pDevMode.dmPaperLength = (short)(heightMm * 1000.0);
ResetDC(hDC, ref pDevMode);
DeleteDC(hDC);
}
}
}
}
}

Categories