How to write an old-style Windows metafile to a file - c#

I can generate an old (not enhanced) metafile. How can I write it to disk so that it is a proper .wmf file?

Petzold doesn't mention it, but there is a convention for writing metafiles to disk: prefix the metafile data with the WmfPlaceableFileHeader structure. Apparently this was invented by Aldus, back int the day and is called a "placeable metafile".
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct WmfPlaceableFileHeader
{
public uint key; // 0x9aC6CDD7
public ushort hmf;
public ushort bboxLeft;
public ushort bboxTop;
public ushort bboxRight;
public ushort bboxBottom;
public ushort inch;
public uint reserved;
public ushort checksum;
}
Win32.WmfPlaceableFileHeader header = new Win32.WmfPlaceableFileHeader();
const ushorttwips_per_inch = 1440;
header.key = 0x9aC6CDD7; // magic number
header.hmf = 0;
header.bboxLeft = 0;
header.bboxRight = width_in_inches * twips_per_inch;
header.bboxTop = 0;
header.bboxBottom = height_in_inches * twips_per_inch;
header.inch = twips_per_inch;
header.reserved = 0;
// Calculate checksum for first 10 WORDs.
ushort checksum = 0;
byte[] header_bytes = StructureToByteArray(header);
for (int i = 0; i < 10; i++)
checksum ^= BitConverter.ToUInt16(header_bytes, i * 2);
header.checksum = checksum;
// Construct the file in-memory.
header_bytes = StructureToByteArray(header);
file_contents.Write(header_bytes, 0, header_bytes.Length);
file_contents.Write(metafile_bytes, 0, metafile_bytes.Length);

Related

How to convert a structure that contains an array to a byte array?

How do I convert a structure that contains an array to a byte array in C#?
There was a question here about a struct without array.
But if the struct contains an array like this:
public struct DiObject
{
public byte Command;
public byte ErrorClass;
public byte Reserved;
public byte Flags;
}
public struct MyPacket
{
public uint ProtocolIdentifier;
public uint NumDi;
public DiObject[] Di;
}
It results with an access violation exception when converting the struct in a byte:
private static byte[] GetBytes(MyPacket packet, int packetSize)
{
var data = new byte[packetSize];
var ptr = Marshal.AllocHGlobal(packetSize);
// ==== Access violation exception occurs here ====
Marshal.StructureToPtr(packet, ptr, true);
Marshal.Copy(ptr, data, 0, packetSize);
Marshal.FreeHGlobal(ptr);
return data;
}
My goal is to send a message in bytes in a message queue with MSMQ.
Here the complete code that compiles and reproduce the problem.
using System;
//using System.IO;
//using System.Messaging;
using System.Runtime.InteropServices;
namespace StructToBytes
{
// 4 bytes
[Serializable]
public struct DiObject
{
public byte Command;
public byte ErrorClass;
public byte Reserved;
public byte Flags;
}
// 8 + (numDi*4) bytes
[Serializable]
public struct MyPacket
{
public uint ProtocolIdentifier;
public uint NumDi;
public DiObject[] Di;
}
internal class Program
{
private static byte[] GetBytes(MyPacket packet, int packetSize)
{
var data = new byte[packetSize];
var ptr = Marshal.AllocHGlobal(packetSize);
// ==== Access violation exception occurs here ====
Marshal.StructureToPtr(packet, ptr, true);
Marshal.Copy(ptr, data, 0, packetSize);
Marshal.FreeHGlobal(ptr);
return data;
}
private static MyPacket FromBytes(byte[] data)
{
var packet = new MyPacket();
var dataSize = Marshal.SizeOf(packet);
var ptr = Marshal.AllocHGlobal(dataSize);
Marshal.Copy(data, 0, ptr, dataSize);
packet = (MyPacket) Marshal.PtrToStructure(ptr, packet.GetType());
Marshal.FreeHGlobal(ptr);
return packet;
}
private static void Main(string[] args)
{
const string queuePath = #".\private$\test_msmq";
// Create the packet
var packet = new MyPacket();
// 8 bytes
packet.ProtocolIdentifier = 1;
packet.NumDi = 2;
// 8 bytes
packet.Di = new DiObject[packet.NumDi];
packet.Di[0].Command = 2;
packet.Di[0].ErrorClass = 3;
packet.Di[0].Flags = 4;
packet.Di[0].Reserved = 5;
packet.Di[1].Command = 6;
packet.Di[1].ErrorClass = 7;
packet.Di[1].Flags = 8;
packet.Di[1].Reserved = 9;
// Convert the struct in bytes
const int packetSize = 16;
var packetBytes = GetBytes(packet, packetSize);
// Create the message
/*
var msg = new Message();
msg.BodyStream = new MemoryStream(packetBytes);
// Open or create the message queue
if (!MessageQueue.Exists(queuePath))
MessageQueue.Create(queuePath);
// Open the queue
var q = new MessageQueue(queuePath); // {Formatter = new BinaryMessageFormatter()};
// Send the message to the queue
q.Send(msg);
*/
}
}
}
The problem lies with wrong assumption about how structure is represented in C#
// 8 + (numDi*4) bytes
[Serializable]
public struct MyPacket
{
public uint ProtocolIdentifier;
public uint NumDi;
public DiObject[] Di;
}
The assumption that size of public DiObject[] Di member is numDi * 4 is not true. In place of this field there is a pointer to the array of structures. Array is a class in .NET and is not included in place in structure declaration.
To solve this problem one can use fixed arrays. I understand that the idea behind the design is to get variable length array and it is presented in next code listing.
This code does not raise AccessViolationException during executin:
using System;
using System.IO;
using System.Messaging;
using System.Runtime.InteropServices;
namespace StructToBytes
{
// 4 bytes
[Serializable]
[StructLayout(LayoutKind.Explicit)]
public unsafe struct DiObject
{
[FieldOffset(0)]
public byte Command;
[FieldOffset(1)]
public byte ErrorClass;
[FieldOffset(2)]
public byte Reserved;
[FieldOffset(3)]
public byte Flags;
}
// 8 + (numDi*4) bytes
[Serializable]
public unsafe struct MyPacket
{
public uint ProtocolIdentifier;
public uint NumDi;
public fixed byte Di[2 * 4];
}
internal unsafe class Program
{
private static byte[] GetBytes(MyPacket packet, int packetSize)
{
var data = new byte[packetSize];
var ptr = Marshal.AllocHGlobal(packetSize);
// ==== Access violation exception occurs here ====
Marshal.StructureToPtr(packet, ptr, true);
Marshal.Copy(ptr, data, 0, packetSize);
Marshal.FreeHGlobal(ptr);
return data;
}
private static MyPacket FromBytes(byte[] data)
{
var packet = new MyPacket();
var dataSize = Marshal.SizeOf(packet);
var ptr = Marshal.AllocHGlobal(dataSize);
Marshal.Copy(data, 0, ptr, dataSize);
packet = (MyPacket)Marshal.PtrToStructure(ptr, packet.GetType());
Marshal.FreeHGlobal(ptr);
return packet;
}
private static void Main(string[] args)
{
const string queuePath = #".\private$\test_msmq";
// Create the packet
var packet = new MyPacket();
// 8 bytes
packet.ProtocolIdentifier = 1;
packet.NumDi = 2;
// 8 bytes
// packet.Di = new DiObject[packet.NumDi];
packet.Di[0] = 2;
packet.Di[1] = 3;
packet.Di[2] = 4;
packet.Di[3] = 5;
packet.Di[4] = 6;
packet.Di[5] = 7;
packet.Di[6] = 8;
packet.Di[7] = 9;
// Convert the struct in bytes
int packetSize = Marshal.SizeOf<MyPacket>();
var packetBytes = GetBytes(packet, packetSize);
// Create the message
var msg = new Message();
msg.BodyStream = new MemoryStream(packetBytes);
// Open or create the message queue
if (!MessageQueue.Exists(queuePath))
MessageQueue.Create(queuePath);
// Open the queue
var q = new MessageQueue(queuePath); // {Formatter = new BinaryMessageFormatter()};
// Send the message to the queue
q.Send(msg);
}
}
}
Code below provides efficient conversion to byte array and from byte array for MyPacket struct with variable internal array size. Implementation avoids casts and bounds checks by using unsafe pointer arithmetic.
using System;
using System.IO;
using System.Messaging;
using System.Runtime.InteropServices;
namespace StructToBytes
{
// 4 bytes
[Serializable]
[StructLayout(LayoutKind.Explicit)]
public unsafe struct DiObject
{
[FieldOffset(0)]
public byte Command;
[FieldOffset(1)]
public byte ErrorClass;
[FieldOffset(2)]
public byte Reserved;
[FieldOffset(3)]
public byte Flags;
}
[Serializable]
public unsafe struct MyPacket
{
public uint ProtocolIdentifier;
public uint NumDi;
public DiObject[] Di;
public byte[] ToBytes()
{
byte[] buffer = new byte[NumDi];
fixed(DiObject* pDi = Di)
fixed(byte* pBuff = buffer)
{
var pBuffDi = (DiObject*)pBuff;
var pDiPtr = pDi;
for (int i = 0; i < NumDi; i++)
*pBuffDi++ = *pDiPtr++;
}
return buffer;
}
public static MyPacket Create(byte[] buffer)
{
// argument checking code here
var packet = new MyPacket();
packet.ProtocolIdentifier = buffer[0];
packet.NumDi = buffer[1];
packet.Di = new DiObject[packet.NumDi];
fixed (byte* pBuf = buffer)
fixed (DiObject* pDi = packet.Di)
{
byte* pBufPtr = pBuf;
pBufPtr += 2;
var pBufDi = (DiObject*)pBufPtr;
var pDiPtr = pDi;
for (int i = 0; i < packet.NumDi; i++)
*pDiPtr++ = *pBufDi++;
}
return packet;
}
}
internal unsafe class Program
{
private static void Main(string[] args)
{
const string queuePath = #".\private$\test_msmq";
// Create the packet
var packet = new MyPacket();
// 8 bytes
packet.ProtocolIdentifier = 1;
packet.NumDi = 5;
// 8 bytes
packet.Di = new DiObject[packet.NumDi];
packet.Di[0].Command = 2;
packet.Di[0].ErrorClass = 3;
packet.Di[0].Flags = 4;
packet.Di[0].Reserved = 5;
packet.Di[1].Command = 6;
packet.Di[1].ErrorClass = 7;
packet.Di[1].Flags = 8;
packet.Di[1].Reserved = 9;
packet.Di[2].Command = 6;
packet.Di[2].ErrorClass = 7;
packet.Di[2].Flags = 8;
packet.Di[2].Reserved = 9;
packet.Di[3].Command = 6;
packet.Di[3].ErrorClass = 7;
packet.Di[3].Flags = 8;
packet.Di[3].Reserved = 9;
// Create the message
var msg = new Message();
msg.BodyStream = new MemoryStream(packet.ToBytes());
// Open or create the message queue
if (!MessageQueue.Exists(queuePath))
MessageQueue.Create(queuePath);
// Open the queue
var q = new MessageQueue(queuePath);
// Send the message to the queue
q.Send(msg);
}
}
}

Marshaling Struct Array with Strings Between c# and c++. Strings are empty

I am having the most difficult time marshaling this struct between C# and C++.
What makes it very hard to troubleshoot is that SOMETIMES the strings are populated with data (wtf), but most of the time they are not.
I've tried sending over an Array of structs as well as a IntPtr, but the results are similar, the strings in the struct are almost always empty and I can't figure out what I'm doing wrong in the marshaling. The code is posted below. Any help would be appreciated.
Edit***
Turns out the problem was on the C++ side and all the marshaling stuff was correct. Thanks for the tip Hans. ***
C++:
#pragma pack (push, 1)
typedef struct
{
char FirmwareVers[FS_MAX_FIRMWARE_VER];
char SerialNum[FS_MAX_SERIAL_NUM];
char HardwareVers[FS_MAX_HW_VER];
ULONG StatusFlags;
int LMIndex;
} FS_LMON_STATUS, *PFS_LMON_STATUS;
DllExport int _stdcall FS_GetLMs(PFS_LMON_STATUS pLaunchMonInfo, int MaxLaunchMons, int *pNumLaunchMons)
{
int Cnt;
FS_LMON_STATUS LMStatus;
if(!g_IsInitalized)
return FS_NOT_INITALIZED;
*pNumLaunchMons = 0;
if(MaxLaunchMons == 0)
return FS_ERROR;
for(Cnt = 0; Cnt < MAX_LM_CONNECTIONS; Cnt++)
{
if(g_CreatedClasses.pLMList->GetLMStatus(Cnt, &LMStatus) != FS_SUCCESS)
continue;
if(LMStatus.LMIndex != INVALID_LM_INDEX)
{
memcpy(pLaunchMonInfo, &LMStatus, sizeof(LMStatus));
pLaunchMonInfo++;
(*pNumLaunchMons)++;
MaxLaunchMons--;
if(MaxLaunchMons == 0)
{
return FS_SUCCESS;
}
}
}
return FS_SUCCESS;
}
C#:
[DllImport("FSADLL", SetLastError = false)]
private static extern int FS_GetLMs([Out] IntPtr pLaunchMonInfo, int MaxLaunchMons, ref int pNumLaunchMons);
[StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Ansi)] //, Size = 38)]
public struct FS_LMON_STATUS
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = FS_MAX_FIRMWARE_VER)] //10 bytes
public string FirmwareVers;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = FS_MAX_SERIAL_NUM)] // 15 bytes
public string SerialNum;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = FS_MAX_HW_VER)] // 5 bytes
public string HardwareVers;
public uint StatusFlags; //4 bytes
public int LMIndex; // identifies which index //4 bytes
}
const int max_launch_monitors = 8;
FS_LMON_STATUS[] dev_info = new FS_LMON_STATUS[max_launch_monitors];
int num_launch_monitors = 0;
IntPtr pAddr = Marshal.AllocHGlobal(max_launch_monitors * Marshal.SizeOf(typeof(FS_LMON_STATUS)));
Marshal.StructureToPtr(dev_info, pAddr, false);
int result = FS_GetLMs(pAddr, max_launch_monitors, ref num_launch_monitors);
UnityEngine.Debug.Log("Result of FS_GetLMs: " + result);
FS_LMON_STATUS[] device_info = new FS_LMON_STATUS[max_launch_monitors];
//Marshal.Copy(pAddr, device_info, (int)0, num_launch_monitors * (int)Marshal.SizeOf(typeof(FS_LMON_STATUS)));
//Marshal.ReadIntPtr(pAddr, 0);
//device_info = (FS_LMON_STATUS[]) Marshal.PtrToStructure(Marshal.AllocHGlobal(max_launch_monitors * Marshal.SizeOf(typeof(FS_LMON_STATUS[]))), typeof(FS_LMON_STATUS[]));
if (num_launch_monitors > 0)
UnityEngine.Debug.Log("GC2 Device Found.");
else // If there is no devices found, remove the previous device from the holder variable
GC2Device = null;
for (int i = 0; i < num_launch_monitors; i++)
{
device_info[i] = (FS_LMON_STATUS)Marshal.PtrToStructure(pAddr, typeof(FS_LMON_STATUS));
pAddr = new IntPtr(Marshal.SizeOf(typeof(FS_LMON_STATUS)) + pAddr.ToInt64());
}
//*** There will only ever be 1 device in the list until the old SDK is fixed ***
for (int lm_index = 0; lm_index < num_launch_monitors; lm_index++)
{
if (device_info[lm_index].StatusFlags != LM_STATUS_DISCONNECTED)
{
UnityEngine.Debug.Log("device_info.SerialNum: " + device_info[lm_index].SerialNum);
//assign each LM to a LM data structure
LaunchMonitor logical_device = new LaunchMonitor(inst);
logical_device.mLaunchMonitorType = LaunchMonitorType.LAUNCH_MONITOR_TYPE_GC2;
logical_device.mConnectionType = ConnectionType.USB_CONNECTION;
IntPtr pnt = Marshal.AllocHGlobal(Marshal.SizeOf(device_info[lm_index]));
Marshal.StructureToPtr(device_info[lm_index], pnt, false);
//Marshal.Copy(device_info[lm_index], dv_info, 0, (uint)Marshal.SizeOf(typeof(FS_LMON_STATUS)));
logical_device.mConnectionToken = pnt;
//GC2Devices.Add(logical_device);
logical_device.Serial = logical_device.GetSerialNumber();
GC2Device = logical_device;
}
}
Turns out the problem was on the C++ side and all the marshaling stuff was correct. Thanks for the tip Hans.

Creating a managed structure for data

Last night I was working on cleaning up some code I found that was a port of an old game I used to play. One of the tricks I used to clean up data was to get rid of the home-brew DataOffsetAttribute that was made for a struct and just make it a plain jane struct and then I can (later) convert it to something a little more useful. Kind of like a person would do with a DataLayer. This worked really good for fixed sized data from a file. Using this method I was about to convert between the two "data types".
public static byte[] ToByteArray<T>(this T dataStructure) where T : struct
{
int size = Marshal.SizeOf(dataStructure);
byte[] arr = new byte[size];
IntPtr ptr = Marshal.AllocHGlobal(size);
Marshal.StructureToPtr(dataStructure, ptr, true);
Marshal.Copy(ptr, arr, 0, size);
Marshal.FreeHGlobal(ptr);
return arr;
}
public static T MarshalAs<T>(this byte[] rawDataStructure) where T : struct
{
var type = typeof(T);
int size = Marshal.SizeOf(type);
IntPtr ptr = Marshal.AllocHGlobal(size);
Marshal.Copy(rawDataStructure, 0, ptr, size);
T structure = (T)Marshal.PtrToStructure(ptr, type);
Marshal.FreeHGlobal(ptr);
return structure;
}
So then I started to wonder if this would work on another project I worked on a long time ago where the data was variable. Here is what I was hoping my data structure would look like
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct RfidReaderResponse
{
public byte PreambleA;
public byte PreambleB;
public byte Length;
public byte LengthLRC;
public ushort ReaderId;
public byte CommandId;
public byte ErrorCode;
public byte[] Data;
public byte LRC;
}
I would probably combine the preamble bytes into a ushort and check if it is a specific value... but that is a different discussion. I have 3 known good responses that I used for testing back in the day. I put those into linqpad as well as my hopeful datastructure. So here is what I am currently using for testing
void Main()
{
var readerResponses = new string[]
{
//data is null because of error EC
"AA-BB-05-05-02-39-0C-EC-21",
//data is 44-00
"AA-BB-07-07-02-39-0C-00-44-00-8B",
//data is 44-00-20-04-13-5E-1A-A4-33-80
"AA-BB-0F-0F-02-39-10-00-44-00-20-04-13-5E-1A-A4-33-80-FB",
};
readerResponses
.Select(x=> x.ToByteArray().MarshalAs<RfidReaderResponse>())
.Dump();
}
now if I comment out the last two fields of the struct I get back what I expect for the first part of the response, but I am just lost on how to get back the data portion. I would prefer to have it as I have it with some magical attribute that the Marshaler understands but so far I can't figure it out. I have tried
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct RfidReaderResponse
{
public byte PreambleA;
public byte PreambleB;
public byte Length;
public byte LengthLRC;
[MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)]
public IntPtr Data;
}
and
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct RfidReaderResponse
{
public byte PreambleA;
public byte PreambleB;
public byte Length;
public byte LengthLRC;
public ushort ReaderId;
public byte CommandId;
public byte ErrorCode;
[MarshalAs(UnmanagedType.SafeArray, SafeArraySubType=VarEnum.VT_I1)]
public byte[] Data;
public byte LRC;
}
after a few hours of research. Both of which didn't work stating that my method couldn't make a size for my structure. So I dunno. What am I doing wrong?
EDIT
forgot the method for converting hex strings to byte arrays
public static byte[] ToByteArray(this string hexString)
{
hexString = System.Text.RegularExpressions.Regex.Replace(hexString, "[^0-9A-F.]", "").ToUpper();
if (hexString.Length % 2 == 1)
throw new Exception("The binary key cannot have an odd number of digits");
byte[] arr = new byte[hexString.Length >> 1];
for (int i = 0; i < hexString.Length >> 1; ++i)
{
arr[i] = (byte)((GetHexVal(hexString[i << 1]) << 4) + (GetHexVal(hexString[(i << 1) + 1])));
}
return arr;
}
private static int GetHexVal(char hex)
{
int val = (int)hex;
return val - (val < 58 ? 48 : 55);
}

Is it possible to read serial number from a harddisk in c#? [duplicate]

I want to get the hardwired serial number from the hard disk but NOT using WMI. I tried using WMI code, and it doesn't work on my machine for sure. So is there any alternative in .NET for finding the Serial Number of a physical hard disk?
This should help get started:
How to get Physical HDD serial number without WMI
Regarding your problem with WMI not returning data; Are you sure how to the right privileges to get data from WMI? You can use WMI Tools to check/fix this.
Use the createfile as shown below. It may require administrative permissions.Add 4 text boxes and a button in your code.
private const int CREATE_NEW = 1;
private const int OPEN_EXISTING = 3;
private const uint GENERIC_READ = 0x80000000;
private const uint GENERIC_WRITE = 0x40000000;
// 10000000000000000000000000000000 --- GENERIC_READ
// 01000000000000000000000000000000 --- GENERIC_WRITE
// 00100000000000000000000000000000 --- GENERIC_EXECUTE
// 00010000000000000000000000000000 --- GENERIC_ALL
//Securable objects use an access mask format in which the
//four high-order bits specify generic access rights
private const int FILE_SHARE_READ = 0x1;
private const int FILE_SHARE_WRITE = 0x2;
// 00000000000000000000000000000001 --- FILE_SHARE_READ
// 00000000000000000000000000000010 --- FILE_SHARE_WRITE
// 00000000000000000000000000000100 --- FILE_SHARE_DELETE
private const int VER_PLATFORM_WIN32_NT = 2;
private const int DFP_RECEIVE_DRIVE_DATA = 0x7C088;
// 0 000000000000111 11 0 00000100010 00
// Common Device Type Required Access Custom Function Code Transfer Type
private const int INVALID_HANDLE_VALUE = -1;
public enum DriveTypes { Fixed, Removable, Unknown };
public string[] DriveStrings = { "Fixed", "Removable", "Unknown" };
[StructLayout(LayoutKind.Sequential, Size = 8)]
private class IDEREGS
{
public byte Features;
public byte SectorCount;
public byte SectorNumber;
public byte CylinderLow;
public byte CylinderHigh;
public byte DriveHead;
public byte Command;
public byte Reserved;
}
[StructLayout(LayoutKind.Sequential, Size = 32)]
private class SENDCMDINPARAMS
{
public int BufferSize;
public IDEREGS DriveRegs;
public byte DriveNumber;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
public byte[] Reserved;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
public int[] Reserved2;
public SENDCMDINPARAMS()
{
DriveRegs = new IDEREGS();
Reserved = new byte[3];
Reserved2 = new int[4];
}
}
[StructLayout(LayoutKind.Sequential, Size = 12)]
private class DRIVERSTATUS
{
public byte DriveError;
public byte IDEStatus;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
public byte[] Reserved;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
public int[] Reserved2;
public DRIVERSTATUS()
{
Reserved = new byte[2];
Reserved2 = new int[2];
}
}
[StructLayout(LayoutKind.Sequential)]
private class IDSECTOR
{
public short GenConfig;
public short NumberCylinders;
public short Reserved;
public short NumberHeads;
public short BytesPerTrack;
public short BytesPerSector;
public short SectorsPerTrack;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
public short[] VendorUnique;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
public char[] SerialNumber;
public short BufferClass;
public short BufferSize;
public short ECCSize;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
public char[] FirmwareRevision;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 40)]
public char[] ModelNumber;
public short MoreVendorUnique;
public short DoubleWordIO;
public short Capabilities;
public short Reserved1;
public short PIOTiming;
public short DMATiming;
public short BS;
public short NumberCurrentCyls;
public short NumberCurrentHeads;
public short NumberCurrentSectorsPerTrack;
public int CurrentSectorCapacity;
public short MultipleSectorCapacity;
public short MultipleSectorStuff;
public int TotalAddressableSectors;
public short SingleWordDMA;
public short MultiWordDMA;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 382)]
public byte[] Reserved2;
public IDSECTOR()
{
VendorUnique = new short[3];
Reserved2 = new byte[382];
FirmwareRevision = new char[8];
SerialNumber = new char[20];
ModelNumber = new char[40];
}
}
[StructLayout(LayoutKind.Sequential)]
private class SENDCMDOUTPARAMS
{
public int BufferSize;
public DRIVERSTATUS Status;
public IDSECTOR IDS;
public SENDCMDOUTPARAMS()
{
Status = new DRIVERSTATUS();
IDS = new IDSECTOR();
}
}
[System.Runtime.InteropServices.DllImport("kernel32.dll")]
private static extern int CloseHandle(int hObject);
[System.Runtime.InteropServices.DllImport("kernel32.dll")]
private static extern int CreateFile(
string lpFileName,
uint dwDesiredAccess,
int dwShareMode,
int lpSecurityAttributes,
int dwCreationDisposition,
int dwFlagsAndAttributes,
int hTemplateFile
);
[System.Runtime.InteropServices.DllImport("kernel32.dll")]
private static extern int DeviceIoControl(
int hDevice,
int dwIoControlCode,
[In(), Out()] SENDCMDINPARAMS lpInBuffer,
int lpInBufferSize,
[In(), Out()] SENDCMDOUTPARAMS lpOutBuffer,
int lpOutBufferSize,
ref int lpBytesReturned,
int lpOverlapped
);
private string SwapChars(char[] chars)
{
for (int i = 0; i <= chars.Length - 2; i += 2)
{
char t;
t = chars[i];
chars[i] = chars[i + 1];
chars[i + 1] = t;
}
string s = new string(chars);
return s;
}
private void button1_Click(object sender, System.EventArgs e)
{
string serialNumber = " ", model = " ", firmware = " ";
bool result;
DriveTypes driveType = DriveTypes.Unknown;
int handle, returnSize = 0;
int driveNumber = 0;
SENDCMDINPARAMS sci = new SENDCMDINPARAMS();
SENDCMDOUTPARAMS sco = new SENDCMDOUTPARAMS();
if (Environment.OSVersion.Platform == PlatformID.Win32NT)
// \\.\PhysicalDrive0 Opens the first physical drive.
// \\.\PhysicalDrive2 Opens the third physical drive.
// see more info on http://msdn.microsoft.com/en-us/library/aa365247%28VS.85%29.aspx
handle = CreateFile("\\\\.\\PhysicalDrive" + "0", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0);
else // for win'9x
handle = CreateFile("\\\\.\\Smartvsd", 0, 0, 0, CREATE_NEW, 0, 0);
if (handle != INVALID_HANDLE_VALUE)
{
sci.DriveNumber = (byte)driveNumber;
sci.BufferSize = Marshal.SizeOf(sco);
sci.DriveRegs.DriveHead = (byte)(0xA0 | driveNumber << 4);
sci.DriveRegs.Command = 0xEC;
sci.DriveRegs.SectorCount = 1;
sci.DriveRegs.SectorNumber = 1;
if (DeviceIoControl(handle, DFP_RECEIVE_DRIVE_DATA, sci, Marshal.SizeOf(sci), sco, Marshal.SizeOf(sco), ref returnSize, 0) != 0)
{
serialNumber = SwapChars(sco.IDS.SerialNumber);
model = SwapChars(sco.IDS.ModelNumber);
firmware = SwapChars(sco.IDS.FirmwareRevision);
}
textBox1.Text = serialNumber;
textBox2.Text = model;
textBox3.Text = firmware;
if ((sco.IDS.GenConfig & 0x80) == 0x40)
driveType = DriveTypes.Removable;
else if ((sco.IDS.GenConfig & 0x40) == 0x40)
driveType = DriveTypes.Fixed;
else
driveType = DriveTypes.Unknown;
CloseHandle(handle);
textBox4.Text = DriveStrings[(int)driveType];
}
}
A perfectly working solution can be found here:
http://www.codeproject.com/Articles/16941/Get-Physical-HDD-Serial-Number-without-WMI
[edit]
Sorry, missed that a reference to this has already been submitted.
You can use the:
GetVolumeInformation
Win32 API function to get this information, if you must avoid WMI. The linked page gives full the declaration signature (in both VB & C#) for the API function along with sample code.
Your best options is windows api,
i made a simple search and got this
General processor info and also read this post
I am using hdd firmware track in my projects. I programmed behind the mdi form to look for hdd firmware number, then its looping on all the provided numbers of hdds of companies computers and if it matches anyone amongst these provided hdds firmware numbers, then run the applicatoin and load mdi form other wise provides a message "The application is not registed on this machine, please call Mr. Afridi to register this application on 00923339176357. My email address is munawar_s_afridi#yahoo.com. I will send complete source code of fetching how to programm professionaly hdd firmware number and how to stop others by using your app illegaly.
One lovely thing that you should specify all the company computers hdd firmware at once and let the application first pick the hdd firmware number, store it in a variable and then pick the value of this variable(a string value) and loop it one by one with each hdd number by using OR logical operator. If it finds a matching number amongst the companies hdds numbers with variable value, then app should load main form(mdi) other wise notify the user for registration.
The sample code will be provided using vb6. You can easily programm later on in vb.net just by understanding how to call unmanaged code in to a managed .net application. In this case a .dll.

c# interop with ghostscript

I'm trying to access some Ghostscript functions like so:
[DllImport(#"C:\Program Files\GPLGS\gsdll32.dll", EntryPoint = "gsapi_revision")]
public static extern int Foo(gsapi_revision_t x, int len);
public struct gsapi_revision_t
{
[MarshalAs(UnmanagedType.LPTStr)]
string product;
[MarshalAs(UnmanagedType.LPTStr)]
string copyright;
long revision;
long revisiondate;
}
public static void Main()
{
gsapi_revision_t foo = new gsapi_revision_t();
Foo(foo, Marshal.SizeOf(foo));
This corresponds with these definitions from the iapi.h header from ghostscript:
typedef struct gsapi_revision_s {
const char *product;
const char *copyright;
long revision;
long revisiondate;
} gsapi_revision_t;
GSDLLEXPORT int GSDLLAPI
gsapi_revision(gsapi_revision_t *pr, int len);
But my code is reading nothing into the string fields. If I add 'ref' to the function, it reads gibberish. However, the following code reads in the data just fine:
public struct gsapi_revision_t
{
IntPtr product;
IntPtr copyright;
long revision;
long revisiondate;
}
public static void Main()
{
gsapi_revision_t foo = new gsapi_revision_t();
IntPtr x = Marshal.AllocHGlobal(20);
for (int i = 0; i < 20; i++)
Marshal.WriteInt32(x, i, 0);
int result = Foo(x, 20);
IntPtr productNamePtr = Marshal.ReadIntPtr(x);
IntPtr copyrightPtr = Marshal.ReadIntPtr(x, 4);
long revision = Marshal.ReadInt64(x, 8);
long revisionDate = Marshal.ReadInt64(x, 12);
byte[] dest = new byte[1000];
Marshal.Copy(productNamePtr, dest, 0, 1000);
string name = Read(productNamePtr);
string copyright = Read(copyrightPtr);
}
public static string Read(IntPtr p)
{
List<byte> bits = new List<byte>();
int i = 0;
while (true)
{
byte b = Marshal.ReadByte(new IntPtr(p.ToInt64() + i));
if (b == 0)
break;
bits.Add(b);
i++;
}
return Encoding.ASCII.GetString(bits.ToArray());
}
So what am I doing wrong with marshaling?
UnmanagedType.LPTStr is platform dependent (ANSI on Win98, Unicode on NT/XP). Your C++ structure uses the char * type, so you probably want UnmanagedType.LPStr instead.
Also, a long in C# is 64 bits while a long in C++ is 32 bits. You probably want to use int in your C# code.

Categories

Resources