ZEBRA PRINTERS uses the native commands called ZPL programming language, printing PDF to the printers usually do not work, the best solution for printing using C# is?
I created this question and answer as I could not find an effective solution on the Internet, and this one will help a lot of people with this issue
The best solution below:
Requirement: Install the free library Ghostscript 32 bits in the computer
https://www.ghostscript.com/download.html
Main methods: pdfbase64 to ZPL or Stream pdf to ZPL
public static List<string> ZplFromPdf(string pdfBase64, int dpi = 300)
{
return ZplFromPdf(new MemoryStream(Convert.FromBase64String(pdfBase64)), new Size(0,0), dpi);
}
public static List<string> ZplFromPdf(Stream pdf, Size size, int dpi = 300)
{
var zpls = new List<string>();
if (size == new Size(0, 0))
{
size = new Size(812, 1218);
}
using (var rasterizer = new GhostscriptRasterizer())
{
rasterizer.Open(pdf);
var images = new List<Image>();
for (int pageNumber = 1; pageNumber <= rasterizer.PageCount; pageNumber++)
{
var bmp = new Bitmap(rasterizer.GetPage(dpi, dpi, pageNumber), size.Width, size.Height);
var zpl = new StringBuilder();
zpl.Append(ZPLHelper.GetGrfStoreCommand("R:LBLRA2.GRF", bmp));
zpl.Append("^XA^FO0,0^XGR:LBLRA2.GRF,1,1^FS^XZ");
zpl.Append("^XA^IDR:LBLRA2.GRF^FS^XZ");
zpls.Add(zpl.ToString());
}
return zpls;
}
}
Core methods
public class ZPLHelper
{
static Regex regexFilename = new Regex("^[REBA]:[A-Z0-9]{1,8}\\.GRF$");
public static bool PrintLabelBase64Image(string printerName, string base64Image, string jobName = "label")
{
try
{
var bmpLabel = Base64ToBitmap(base64Image);
var baseStream = new MemoryStream();
var tw = new StreamWriter(baseStream, Encoding.UTF8);
tw.WriteLine(GetGrfStoreCommand("R:LBLRA2.GRF", bmpLabel));
tw.WriteLine(GetGrfPrintCommand("R:LBLRA2.GRF"));
tw.WriteLine(GetGrfDeleteCommand("R:LBLRA2.GRF"));
tw.Flush();
baseStream.Position = 0;
var gdipj = new GdiPrintJob(printerName, GdiPrintJobDataType.Raw, jobName, null);
gdipj.WritePage(baseStream);
gdipj.CompleteJob();
return true;
}
catch (Exception)
{
return false;
}
}
public static bool PrintLabelZpl(string printerName, string zplCommand, string jobName = "label")
{
var baseStream = new MemoryStream();
var tw = new StreamWriter(baseStream, Encoding.UTF8);
tw.WriteLine(zplCommand);
tw.Flush();
baseStream.Position = 0;
var gdiJob = new GdiPrintJob(printerName, GdiPrintJobDataType.Raw, jobName, null);
gdiJob.WritePage(baseStream);
gdiJob.CompleteJob();
return true;
}
private static Bitmap Base64ToBitmap(string base64Image)
{
Image image;
using (var ms = new MemoryStream(Convert.FromBase64String(base64Image)))
{
image = Image.FromStream(ms);
}
return new Bitmap(image);
}
public static string GetGrfStoreCommand(string filename, Bitmap bmpSource)
{
if (bmpSource == null)
{
throw new ArgumentNullException("bmpSource");
}
validateFilename(filename);
var dim = new Rectangle(Point.Empty, bmpSource.Size);
var stride = ((dim.Width + 7) / 8);
var bytes = stride * dim.Height;
using (var bmpCompressed = bmpSource.Clone(dim, PixelFormat.Format1bppIndexed))
{
var result = new StringBuilder();
result.AppendFormat("^XA~DG{2},{0},{1},", stride * dim.Height, stride, filename);
byte[][] imageData = GetImageData(dim, stride, bmpCompressed);
byte[] previousRow = null;
foreach (var row in imageData)
{
appendLine(row, previousRow, result);
previousRow = row;
}
result.Append(#"^FS^XZ");
return result.ToString();
}
}
public static string GetGrfDeleteCommand(string filename)
{
validateFilename(filename);
return string.Format("^XA^ID{0}^FS^XZ", filename);
}
public static string GetGrfPrintCommand(string filename)
{
validateFilename(filename);
return string.Format("^XA^FO0,0^XG{0},1,1^FS^XZ", filename);
}
private static void validateFilename(string filename)
{
if (!regexFilename.IsMatch(filename))
{
throw new ArgumentException("Filename must be in the format "
+ "R:XXXXXXXX.GRF. Drives are R, E, B, A. Filename can "
+ "be alphanumeric between 1 and 8 characters.", "filename");
}
}
unsafe private static byte[][] GetImageData(Rectangle dim, int stride, Bitmap bmpCompressed)
{
byte[][] imageData;
var data = bmpCompressed.LockBits(dim, ImageLockMode.ReadOnly, PixelFormat.Format1bppIndexed);
try
{
byte* pixelData = (byte*)data.Scan0.ToPointer();
byte rightMask = (byte)(0xff << (data.Stride * 8 - dim.Width));
imageData = new byte[dim.Height][];
for (int row = 0; row < dim.Height; row++)
{
byte* rowStart = pixelData + row * data.Stride;
imageData[row] = new byte[stride];
for (int col = 0; col < stride; col++)
{
byte f = (byte)(0xff ^ rowStart[col]);
f = (col == stride - 1) ? (byte)(f & rightMask) : f;
imageData[row][col] = f;
}
}
}
finally
{
bmpCompressed.UnlockBits(data);
}
return imageData;
}
private static void appendLine(byte[] row, byte[] previousRow, StringBuilder baseStream)
{
if (row.All(r => r == 0))
{
baseStream.Append(",");
return;
}
if (row.All(r => r == 0xff))
{
baseStream.Append("!");
return;
}
if (previousRow != null && MatchByteArray(row, previousRow))
{
baseStream.Append(":");
return;
}
byte[] nibbles = new byte[row.Length * 2];
for (int i = 0; i < row.Length; i++)
{
nibbles[i * 2] = (byte)(row[i] >> 4);
nibbles[i * 2 + 1] = (byte)(row[i] & 0x0f);
}
for (int i = 0; i < nibbles.Length; i++)
{
byte cPixel = nibbles[i];
int repeatCount = 0;
for (int j = i; j < nibbles.Length && repeatCount <= 400; j++)
{
if (cPixel == nibbles[j])
{
repeatCount++;
}
else
{
break;
}
}
if (repeatCount > 2)
{
if (repeatCount == nibbles.Length - i
&& (cPixel == 0 || cPixel == 0xf))
{
if (cPixel == 0)
{
if (i % 2 == 1)
{
baseStream.Append("0");
}
baseStream.Append(",");
return;
}
else if (cPixel == 0xf)
{
if (i % 2 == 1)
{
baseStream.Append("F");
}
baseStream.Append("!");
return;
}
}
else
{
baseStream.Append(getRepeatCode(repeatCount));
i += repeatCount - 1;
}
}
baseStream.Append(cPixel.ToString("X"));
}
}
private static string getRepeatCode(int repeatCount)
{
if (repeatCount > 419)
throw new ArgumentOutOfRangeException();
int high = repeatCount / 20;
int low = repeatCount % 20;
const string lowString = " GHIJKLMNOPQRSTUVWXY";
const string highString = " ghijklmnopqrstuvwxyz";
string repeatStr = "";
if (high > 0)
{
repeatStr += highString[high];
}
if (low > 0)
{
repeatStr += lowString[low];
}
return repeatStr;
}
private static bool MatchByteArray(byte[] row, byte[] previousRow)
{
for (int i = 0; i < row.Length; i++)
{
if (row[i] != previousRow[i])
{
return false;
}
}
return true;
}
}
internal static class NativeMethods
{
#region winspool.drv
#region P/Invokes
[DllImport("winspool.Drv", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern bool OpenPrinter(string szPrinter, out IntPtr hPrinter, IntPtr pd);
[DllImport("winspool.Drv", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern bool ClosePrinter(IntPtr hPrinter);
[DllImport("winspool.Drv", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern UInt32 StartDocPrinter(IntPtr hPrinter, Int32 level, IntPtr di);
[DllImport("winspool.Drv", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern bool EndDocPrinter(IntPtr hPrinter);
[DllImport("winspool.Drv", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern bool StartPagePrinter(IntPtr hPrinter);
[DllImport("winspool.Drv", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern bool EndPagePrinter(IntPtr hPrinter);
[DllImport("winspool.Drv", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern bool WritePrinter(
// 0
IntPtr hPrinter,
[MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)] byte[] pBytes,
// 2
UInt32 dwCount,
out UInt32 dwWritten);
#endregion
#region Structs
[StructLayout(LayoutKind.Sequential)]
internal struct DOC_INFO_1
{
[MarshalAs(UnmanagedType.LPWStr)]
public string DocName;
[MarshalAs(UnmanagedType.LPWStr)]
public string OutputFile;
[MarshalAs(UnmanagedType.LPWStr)]
public string Datatype;
}
#endregion
#endregion
}
/// <summary>
/// Represents a print job in a spooler queue
/// </summary>
public class GdiPrintJob
{
IntPtr PrinterHandle;
//IntPtr DocHandle;
/// <summary>
/// The ID assigned by the print spooler to identify the job
/// </summary>
public UInt32 PrintJobID { get; private set; }
/// <summary>
/// Create a print job with a enumerated datatype
/// </summary>
/// <param name="PrinterName"></param>
/// <param name="dataType"></param>
/// <param name="jobName"></param>
/// <param name="outputFileName"></param>
public GdiPrintJob(string PrinterName, GdiPrintJobDataType dataType, string jobName, string outputFileName)
: this(PrinterName, translateType(dataType), jobName, outputFileName)
{
}
/// <summary>
/// Create a print job with a string datatype
/// </summary>
/// <param name="PrinterName"></param>
/// <param name="dataType"></param>
/// <param name="jobName"></param>
/// <param name="outputFileName"></param>
public GdiPrintJob(string PrinterName, string dataType, string jobName, string outputFileName)
{
if (string.IsNullOrWhiteSpace(PrinterName))
throw new ArgumentNullException("PrinterName");
if (string.IsNullOrWhiteSpace(dataType))
throw new ArgumentNullException("PrinterName");
IntPtr hPrinter;
if (!NativeMethods.OpenPrinter(PrinterName, out hPrinter, IntPtr.Zero))
throw new Win32Exception();
this.PrinterHandle = hPrinter;
NativeMethods.DOC_INFO_1 docInfo = new NativeMethods.DOC_INFO_1()
{
DocName = jobName,
Datatype = dataType,
OutputFile = outputFileName
};
IntPtr pDocInfo = Marshal.AllocHGlobal(Marshal.SizeOf(docInfo));
RuntimeHelpers.PrepareConstrainedRegions();
try
{
Marshal.StructureToPtr(docInfo, pDocInfo, false);
UInt32 docid = NativeMethods.StartDocPrinter(hPrinter, 1, pDocInfo);
if (docid == 0)
throw new Win32Exception();
this.PrintJobID = docid;
}
finally
{
Marshal.FreeHGlobal(pDocInfo);
}
}
/// <summary>
/// Write the data of a single page or a precomposed PCL document
/// </summary>
/// <param name="data"></param>
public void WritePage(Stream data)
{
if (data == null)
throw new ArgumentNullException("data");
if (!data.CanRead && !data.CanWrite)
throw new ObjectDisposedException("data");
if (!data.CanRead)
throw new NotSupportedException("stream is not readable");
if (!NativeMethods.StartPagePrinter(this.PrinterHandle))
throw new Win32Exception();
byte[] buffer = new byte[0x14000]; /* 80k is Stream.CopyTo default */
uint read = 1;
while ((read = (uint)data.Read(buffer, 0, buffer.Length)) != 0)
{
UInt32 written;
if (!NativeMethods.WritePrinter(this.PrinterHandle, buffer, read, out written))
throw new Win32Exception();
if (written != read)
throw new InvalidOperationException("Error while writing to stream");
}
if (!NativeMethods.EndPagePrinter(this.PrinterHandle))
throw new Win32Exception();
}
/// <summary>
/// Complete the current job
/// </summary>
public void CompleteJob()
{
if (!NativeMethods.EndDocPrinter(this.PrinterHandle))
throw new Win32Exception();
}
#region datatypes
private readonly static string[] dataTypes = new string[]
{
// 0
null,
"RAW",
// 2
"RAW [FF appended]",
"RAW [FF auto]",
// 4
"NT EMF 1.003",
"NT EMF 1.006",
// 6
"NT EMF 1.007",
"NT EMF 1.008",
// 8
"TEXT",
"XPS_PASS",
// 10
"XPS2GDI"
};
private static string translateType(GdiPrintJobDataType type)
{
return dataTypes[(int)type];
}
#endregion
}
public enum GdiPrintJobDataType
{
Unknown = 0,
Raw = 1,
RawAppendFF = 2,
RawAuto = 3,
NtEmf1003 = 4,
NtEmf1006 = 5,
NtEmf1007 = 6,
NtEmf1008 = 7,
Text = 8,
XpsPass = 9,
Xps2Gdi = 10
}
You might be interested in my NuGet package for converting PDF files into ZPL code.
The official Zebra SDK might contain a conversion but I haven't checked (not sure about the licensing here).
Adapting your previous code for the first page would be
public static string ZplFromPdf(string pdfBase64, int dpi = 300)
{
return PDFtoZPL.Conversion.ConvertPdfPage(pdfBase64, dpi: dpi);
}
Disclaimer: I ran into the same problem, stitched together ZPL code found on the internet and bundled it into a tiny .NET API.
Related
I am trying to take some old code written by my predecessor in C and migrate it to C#. I have tried using the P/invoke way but running into issues with sprint_s. Any recommendation on how to fix this or maybe write it using C#'s SerialPort class?
[StructLayout(LayoutKind.Sequential)]
internal struct Dcb
{
internal uint DCBLength;
internal uint BaudRate;
private BitVector32 Flags;
private ushort wReserved; // not currently used
internal ushort XonLim; // transmit XON threshold
internal ushort XoffLim; // transmit XOFF threshold
internal byte ByteSize;
internal Parity Parity;
internal StopBits StopBits;
internal sbyte XonChar; // Tx and Rx XON character
internal sbyte XoffChar; // Tx and Rx XOFF character
internal sbyte ErrorChar; // error replacement character
internal sbyte EofChar; // end of input character
internal sbyte EvtChar; // received event character
private ushort wReserved1; // reserved; do not use
private static readonly int fBinary;
private static readonly int fParity;
private static readonly int fOutxCtsFlow;
private static readonly int fOutxDsrFlow;
private static readonly BitVector32.Section fDtrControl;
private static readonly int fDsrSensitivity;
private static readonly int fTXContinueOnXoff;
private static readonly int fOutX;
private static readonly int fInX;
private static readonly int fErrorChar;
private static readonly int fNull;
private static readonly BitVector32.Section fRtsControl;
private static readonly int fAbortOnError;
static Dcb()
{
// Create Boolean Mask
int previousMask;
fBinary = BitVector32.CreateMask();
fParity = BitVector32.CreateMask(fBinary);
fOutxCtsFlow = BitVector32.CreateMask(fParity);
fOutxDsrFlow = BitVector32.CreateMask(fOutxCtsFlow);
previousMask = BitVector32.CreateMask(fOutxDsrFlow);
previousMask = BitVector32.CreateMask(previousMask);
fDsrSensitivity = BitVector32.CreateMask(previousMask);
fTXContinueOnXoff = BitVector32.CreateMask(fDsrSensitivity);
fOutX = BitVector32.CreateMask(fTXContinueOnXoff);
fInX = BitVector32.CreateMask(fOutX);
fErrorChar = BitVector32.CreateMask(fInX);
fNull = BitVector32.CreateMask(fErrorChar);
previousMask = BitVector32.CreateMask(fNull);
previousMask = BitVector32.CreateMask(previousMask);
fAbortOnError = BitVector32.CreateMask(previousMask);
// Create section Mask
BitVector32.Section previousSection;
previousSection = BitVector32.CreateSection(1);
previousSection = BitVector32.CreateSection(1, previousSection);
previousSection = BitVector32.CreateSection(1, previousSection);
previousSection = BitVector32.CreateSection(1, previousSection);
fDtrControl = BitVector32.CreateSection(2, previousSection);
previousSection = BitVector32.CreateSection(1, fDtrControl);
previousSection = BitVector32.CreateSection(1, previousSection);
previousSection = BitVector32.CreateSection(1, previousSection);
previousSection = BitVector32.CreateSection(1, previousSection);
previousSection = BitVector32.CreateSection(1, previousSection);
previousSection = BitVector32.CreateSection(1, previousSection);
fRtsControl = BitVector32.CreateSection(3, previousSection);
previousSection = BitVector32.CreateSection(1, fRtsControl);
}
public bool Binary
{
get { return Flags[fBinary]; }
set { Flags[fBinary] = value; }
}
public bool CheckParity
{
get { return Flags[fParity]; }
set { Flags[fParity] = value; }
}
public bool OutxCtsFlow
{
get { return Flags[fOutxCtsFlow]; }
set { Flags[fOutxCtsFlow] = value; }
}
public bool OutxDsrFlow
{
get { return Flags[fOutxDsrFlow]; }
set { Flags[fOutxDsrFlow] = value; }
}
public DtrControl DtrControl
{
get { return (DtrControl)Flags[fDtrControl]; }
set { Flags[fDtrControl] = (int)value; }
}
public bool DsrSensitivity
{
get { return Flags[fDsrSensitivity]; }
set { Flags[fDsrSensitivity] = value; }
}
public bool TxContinueOnXoff
{
get { return Flags[fTXContinueOnXoff]; }
set { Flags[fTXContinueOnXoff] = value; }
}
public bool OutX
{
get { return Flags[fOutX]; }
set { Flags[fOutX] = value; }
}
public bool InX
{
get { return Flags[fInX]; }
set { Flags[fInX] = value; }
}
public bool ReplaceErrorChar
{
get { return Flags[fErrorChar]; }
set { Flags[fErrorChar] = value; }
}
public bool Null
{
get { return Flags[fNull]; }
set { Flags[fNull] = value; }
}
public RtsControl RtsControl
{
get { return (RtsControl)Flags[fRtsControl]; }
set { Flags[fRtsControl] = (int)value; }
}
public bool AbortOnError
{
get { return Flags[fAbortOnError]; }
set { Flags[fAbortOnError] = value; }
}
}
public enum DtrControl : int
{
/// <summary>
/// Disables the DTR line when the device is opened and leaves it disabled.
/// </summary>
Disable = 0,
/// <summary>
/// Enables the DTR line when the device is opened and leaves it on.
/// </summary>
Enable = 1,
/// <summary>
/// Enables DTR handshaking. If handshaking is enabled, it is an error for the application to adjust the line by
/// using the EscapeCommFunction function.
/// </summary>
Handshake = 2
}
public enum RtsControl : int
{
/// <summary>
/// Disables the RTS line when the device is opened and leaves it disabled.
/// </summary>
Disable = 0,
/// <summary>
/// Enables the RTS line when the device is opened and leaves it on.
/// </summary>
Enable = 1,
/// <summary>
/// Enables RTS handshaking. The driver raises the RTS line when the "type-ahead" (input) buffer
/// is less than one-half full and lowers the RTS line when the buffer is more than
/// three-quarters full. If handshaking is enabled, it is an error for the application to
/// adjust the line by using the EscapeCommFunction function.
/// </summary>
Handshake = 2,
/// <summary>
/// Specifies that the RTS line will be high if bytes are available for transmission. After
/// all buffered bytes have been sent, the RTS line will be low.
/// </summary>
Toggle = 3
}
public enum Parity : byte
{
None = 0,
Odd = 1,
Even = 2,
Mark = 3,
Space = 4,
}
public enum StopBits : byte
{
One = 0,
OnePointFive = 1,
Two = 2
}
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool ReadFile(IntPtr handle,
byte[] buffer, uint toRead, ref uint read, IntPtr lpOverLapped);
[DllImport("msvcrt.dll", EntryPoint = "memset", CallingConvention = CallingConvention.Cdecl, SetLastError = false)]
public static extern IntPtr MemSet(IntPtr dest, int c, int byteCount);
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool SetCommTimeouts(IntPtr hFile, [In] ref COMMTIMEOUTS
lpCommTimeouts);
struct COMMTIMEOUTS
{
public UInt32 ReadIntervalTimeout;
public UInt32 ReadTotalTimeoutMultiplier;
public UInt32 ReadTotalTimeoutConstant;
public UInt32 WriteTotalTimeoutMultiplier;
public UInt32 WriteTotalTimeoutConstant;
}
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr CreateFile(
[MarshalAs(UnmanagedType.LPTStr)] string filename,
[MarshalAs(UnmanagedType.U4)] FileAccess access,
[MarshalAs(UnmanagedType.U4)] FileShare share,
IntPtr securityAttributes, // optional SECURITY_ATTRIBUTES struct or IntPtr.Zero
[MarshalAs(UnmanagedType.U4)] FileMode creationDisposition,
[MarshalAs(UnmanagedType.U4)] FileAttributes flagsAndAttributes,
IntPtr templateFile);
struct FILE
{
IntPtr _ptr;
int _cnt;
IntPtr _base;
int _flag;
int _file;
int _charbuf;
int _bufsiz;
IntPtr _tmpfname;
};
[DllImport("kernel32.dll")]
static extern bool WriteFile(IntPtr hFile, byte[] lpBuffer,
uint nNumberOfBytesToWrite, out uint lpNumberOfBytesWritten,
IntPtr lpOverLapped);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool FlushFileBuffers(IntPtr handle);
public bool InitSerialComms()
{
FILE file = new FILE();
COMMTIMEOUTS timeouts;
Dcb dcb = new Dcb();
long len;
char[] name = new char[10];
char[] settings = new char[40];
string str;
// Form the initialization file name
sprintf_s(str, 800, "%s\\SerialComms.ini", path);
// Open the initialization file
fopen_s(&file, str, "r");
// Check for errors
if (file)
{
Console.WriteLine("Error: cannot open file %s\n");
return false;
}
// Scan the serial port name
fgets(name, 10, file);
len = strlen(name);
name[len - 1] = 0;
// Scan the serial port settings
fgets(settings, 40, file);
len = settings.Length;
settings[len - 1] = 0;
// Scan the timeout settings
fgets(str, 40, file); len = strlen(str); string[len - 1] = 0;
sscanf_s(str, "%d,%d,%d,%d,%d",
&timeouts.ReadIntervalTimeout,
&timeouts.ReadTotalTimeoutConstant,
&timeouts.ReadTotalTimeoutMultiplier,
&timeouts.WriteTotalTimeoutConstant,
&timeouts.WriteTotalTimeoutMultiplier);
// Close the initialization file
fclose(file);
// Open the serial port
port = CreateFile(name, GENERIC_READ | GENERIC_WRITE,
0, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
// Check for errors
if (port == INVALID_HANDLE_VALUE)
{
// Report the error and return
fprintf(stderr, "Error: cannot open serial port %s\n", name);
fflush(stderr);
return false;
}
// Build the serial port device control block
MemSet(dcb., 0, sizeof(DCB));
dcb.DCBlength = sizeof(DCB);
if (!BuildCommDCB(settings, &dcb))
{
// Report the error and return
fprintf(stderr, "Error: cannot create device control block for %s\n", name);
CloseHandle(port);
fflush(stderr);
return false;
}
// Configure the serial port
if (!SetCommState(port, &dcb))
{
// Report the error and return
fprintf(stderr, "Error: cannot configure serial port %s\n", name);
CloseHandle(port);
fflush(stderr);
return false;
}
// Set the timeouts for the serial port
if (!SetCommTimeouts(port, &timeouts))
{
// Report the error and return
fprintf(stderr, "Error: cannot set timeouts for %s\n", name);
CloseHandle(port);
fflush(stderr);
return false;
}
// Success
return true;
}
bool ReceiveReply(IntPtr port, ref byte[] reply, ref byte num)
{
uint num_read = 0;
uint num_to_read = 255;
ushort crc = 0XFFFF;
byte i, j;
// Clear the reply buffer
//reply = new byte[255];
num = 0;
// Read the data
if (!ReadFile(port, reply, num_to_read, ref num_read, IntPtr.Zero)) return false;
// Check number of bytes that were read
if (num_read < 2) return false;
// Check number of bytes that were read
if (num_read > 255) return false;
// Form the CRC
for (i = 0; i < num_read - 2; i++)
{
crc ^= reply[i];
for (j = 0; j < 8; j++)
{
ushort flag = (ushort) (crc & 0X0001);
crc >>= 1;
//TODO: risky flag check
if (flag == 0) crc ^= 0XA001;
}
}
// Check the CRC
if (reply[i++] != (crc & 0X00FF)) return false;
if (reply[i++] != (crc & 0XFF00) >> 8) return false;
num = (byte)(num_read - 2);
// Success
return true;
}
public static bool SendRequest(IntPtr port, ref byte[] request, ref byte num)
{
ushort crc = 0XFFFF;
byte i, j;
// Check number of bytes
if (num > 253) return false;
// Set number of bytes to write
uint num_to_write = num;
// Form the CRC
for (i = 0; i < num_to_write; i++)
{
crc ^= request[i];
for (j = 0; j < 8; j++)
{
ushort flag = (ushort) (crc & 0X0001);
crc >>= 1; if (flag == 0) crc = (ushort) (crc ^ 0XA001);
}
}
// Set the CRC bytes in the request
request[num_to_write++] = (byte) (crc & 0X00FF);
request[num_to_write++] = (byte) ((crc & 0XFF00) >> 8);
// Send the request
if (!WriteFile(port, request, num_to_write, out uint _, IntPtr.Zero)) return false;
string text = request.ToString().Substring(0, (int) num_to_write).Replace("\r\n", " ");
// Flush the serial line
if (!FlushFileBuffers(port)) return false;
// Success
return true;
}
You don't need sprintf-family functions in higher level languages like C# since they normally allow string concatenation and assignment with the simple = and += operators.
Just write idiomatic C# code for it:
str = path + "\\SerialComms.ini";
Commenter #itsme86 points out that for the task of building a path, you should instead use Path.Combine:
Path.Combine(path, "SerialComms.ini");
I'm trying to get a byte[] from blob stored in SQLite, but the byte[] length is always 1
foreach (DataRow row in table.Rows)
{
Byte[] bytes = (Byte[])row["password_value"];
}
This the Wrapper Class I used, I suppose the problem in the wrapper I tested the code using normal SQLite wrapper it works fine, but I could not figure out what's the problem with wrapper
public class SQLiteBase
{
// imports system functions for work with pointers
[DllImport("kernel32")]
private extern static IntPtr HeapAlloc(IntPtr heap, UInt32 flags, UInt32 bytes);
[DllImport("kernel32")]
private extern static IntPtr GetProcessHeap();
[DllImport("kernel32")]
private extern static int lstrlen(IntPtr str);
// imports SQLite functions
[DllImport("sqlite3")]
private static extern int sqlite3_open(IntPtr fileName, out IntPtr database);
[DllImport("sqlite3")]
private static extern int sqlite3_close(IntPtr database);
[DllImport("sqlite3")]
private static extern int sqlite3_exec(IntPtr database, IntPtr query, IntPtr callback, IntPtr arguments,
out IntPtr error);
[DllImport("sqlite3")]
private static extern IntPtr sqlite3_errmsg(IntPtr database);
[DllImport("sqlite3")]
private static extern int sqlite3_prepare_v2(IntPtr database, IntPtr query, int length, out IntPtr statement,
out IntPtr tail);
[DllImport("sqlite3")]
private static extern int sqlite3_step(IntPtr statement);
[DllImport("sqlite3")]
private static extern int sqlite3_column_count(IntPtr statement);
[DllImport("sqlite3")]
private static extern IntPtr sqlite3_column_name(IntPtr statement, int columnNumber);
[DllImport("sqlite3")]
private static extern int sqlite3_column_type(IntPtr statement, int columnNumber);
[DllImport("sqlite3")]
private static extern int sqlite3_column_int(IntPtr statement, int columnNumber);
[DllImport("sqlite3")]
private static extern double sqlite3_column_double(IntPtr statement, int columnNumber);
[DllImport("sqlite3")]
private static extern IntPtr sqlite3_column_text(IntPtr statement, int columnNumber);
[DllImport("sqlite3")]
private static extern IntPtr sqlite3_column_blob(IntPtr statement, int columnNumber);
[DllImport("sqlite3")]
private static extern IntPtr sqlite3_column_table_name(IntPtr statement, int columnNumber);
[DllImport("sqlite3")]
private static extern int sqlite3_finalize(IntPtr handle);
// SQLite constants
private const int SQL_OK = 0;
private const int SQL_ROW = 100;
private const int SQL_DONE = 101;
/// <summary>
/// SQLite data types.
/// </summary>
public enum SQLiteDataTypes
{
/// <summary>
/// Integer numbers.
/// </summary>
INT = 1,
/// <summary>
/// Decimal numbers.
/// </summary>
FLOAT,
/// <summary>
/// All kinds of texts.
/// </summary>
TEXT,
/// <summary>
/// Blob objects - binary large objects.
/// </summary>
BLOB,
/// <summary>
/// Nothing.
/// </summary>
NULL
};
// pointer to database
private IntPtr database;
/// <summary>
/// Creates new instance of SQLiteBase class with no database attached.
/// </summary>
public SQLiteBase()
{
database = IntPtr.Zero;
}
/// <summary>
/// Creates new instance of SQLiteBase class and opens database with given name.
/// </summary>
/// <param name="baseName">Name (and path) to SQLite database file</param>
public SQLiteBase(String baseName)
{
OpenDatabase(baseName);
}
/// <summary>
/// Opens database.
/// </summary>
/// <param name="baseName">Name of database file</param>
public void OpenDatabase(String baseName)
{
// opens database
if (sqlite3_open(StringToPointer(baseName), out database) != SQL_OK)
{
// if there is some error, database pointer is set to 0 and exception is throws
database = IntPtr.Zero;
throw new Exception("Error with opening database " + baseName + "!");
}
}
/// <summary>
/// Closes opened database.
/// </summary>
public void CloseDatabase()
{
// closes the database if there is one opened
if (database != IntPtr.Zero)
{
sqlite3_close(database);
}
}
/// <summary>
/// Returns the list of tables in opened database.
/// </summary>
/// <returns></returns>
public ArrayList GetTables()
{
// executes query that select names of all tables and views in master table of every database
String query = "SELECT name FROM sqlite_master " +
"WHERE type IN ('table','view') AND name NOT LIKE 'sqlite_%'" +
"UNION ALL " +
"SELECT name FROM sqlite_temp_master " +
"WHERE type IN ('table','view') " +
"ORDER BY 1";
DataTable table = ExecuteQuery(query);
// when table is generater, it writes all table names in list that is returned
ArrayList list = new ArrayList();
foreach (DataRow row in table.Rows)
{
list.Add(row.ItemArray[0].ToString());
}
return list;
}
/// <summary>
/// Executes query that does not return anything (e.g. UPDATE, INSERT, DELETE).
/// </summary>
/// <param name="query"></param>
public void ExecuteNonQuery(String query)
{
// calles SQLite function that executes non-query
IntPtr error;
sqlite3_exec(database, StringToPointer(query), IntPtr.Zero, IntPtr.Zero, out error);
// if there is error, excetion is thrown
if (error != IntPtr.Zero)
throw new Exception("Error with executing non-query: \"" + query + "\"!\n" +
PointerToString(sqlite3_errmsg(error)));
}
/// <summary>
/// Executes query that does return something (e.g. SELECT).
/// </summary>
/// <param name="query"></param>
/// <returns></returns>
public DataTable ExecuteQuery(String query)
{
// processed query
IntPtr statement;
// excess data, it has no use
IntPtr excessData;
// process query and make statement
sqlite3_prepare_v2(database, StringToPointer(query), GetPointerLenght(StringToPointer(query)),
out statement, out excessData);
// table for result of function
DataTable table = new DataTable();
// reads first row - it is different from next rows because it also creates table columns
// result - returns SLQ_ROW while there is next row
int result = ReadFirstRow(statement, ref table);
// reads rows
while (result == SQL_ROW)
{
result = ReadNextRow(statement, ref table);
}
// finalize executing this query
sqlite3_finalize(statement);
// returns table
return table;
}
// private function for reading firs row and creating DataTable
private int ReadFirstRow(IntPtr statement, ref DataTable table)
{
// create new instance of DataTable with name "resultTable"
table = new DataTable("resultTable");
// evaluates statement
int resultType = sqlite3_step(statement);
// if result of statement is SQL_ROW, create new table and write row in it
if (resultType == SQL_ROW)
{
// returns number of columns returned by statement
int columnCount = sqlite3_column_count(statement);
// declartaion of variables for reading first row
String columnName = "";
int columnType = 0;
object[] columnValues = new object[columnCount];
// reads columns one by one
for (int i = 0; i < columnCount; i++)
{
// returns the name of current column
columnName = PointerToString(sqlite3_column_name(statement, i));
// returns the type of current column
columnType = sqlite3_column_type(statement, i);
// checks type of columns - neccessary because different functions are required for different types
switch (columnType)
{
// in case of integer column
case (int) SQLiteDataTypes.INT:
{
// adds new integer column to table
table.Columns.Add(columnName, Type.GetType("System.Int32"));
// writes column value in object array
columnValues[i] = sqlite3_column_int(statement, i);
break;
}
// same as for integer, this one is for float
case (int) SQLiteDataTypes.FLOAT:
{
table.Columns.Add(columnName, Type.GetType("System.Single"));
columnValues[i] = sqlite3_column_double(statement, i);
break;
}
// ... for text
case (int) SQLiteDataTypes.TEXT:
{
table.Columns.Add(columnName, Type.GetType("System.String"));
columnValues[i] = PointerToString(sqlite3_column_text(statement, i));
break;
}
// ... for blob - blob are written in table as strings!!
case (int) SQLiteDataTypes.BLOB:
{
table.Columns.Add(columnName, Type.GetType("System.Byte[]"));
columnValues[i] = PointerToByte(sqlite3_column_blob(statement, i));
break;
}
// in case of something other, value is read as string
default:
{
table.Columns.Add(columnName, Type.GetType("System.String"));
columnValues[i] = "";
break;
}
}
}
// writes column values to table
table.Rows.Add(columnValues);
}
// evalute statemnet for next results
return sqlite3_step(statement);
}
// private function for reading rows other than first
// it' same like first row, only without creating table and columns
private int ReadNextRow(IntPtr statement, ref DataTable table)
{
int columnCount = sqlite3_column_count(statement);
int columnType = 0;
object[] columnValues = new object[columnCount];
for (int i = 0; i < columnCount; i++)
{
columnType = sqlite3_column_type(statement, i);
switch (columnType)
{
case (int) SQLiteDataTypes.INT:
{
columnValues[i] = sqlite3_column_int(statement, i);
break;
}
case (int) SQLiteDataTypes.FLOAT:
{
columnValues[i] = sqlite3_column_double(statement, i);
break;
}
case (int) SQLiteDataTypes.TEXT:
{
columnValues[i] = PointerToString(sqlite3_column_text(statement, i));
break;
}
case (int) SQLiteDataTypes.BLOB:
{
columnValues[i] = PointerToByte(sqlite3_column_blob(statement, i));
break;
}
default:
{
columnValues[i] = "";
break;
}
}
}
table.Rows.Add(columnValues);
return sqlite3_step(statement);
}
// converts string to pointer
private IntPtr StringToPointer(String str)
{
// if string is null, pointer is 0
if (str == null)
{
return IntPtr.Zero;
}
else
{
// else, convert it to pointer
Encoding encoding = Encoding.UTF8;
Byte[] bytes = encoding.GetBytes(str);
int length = bytes.Length + 1;
IntPtr pointer = HeapAlloc(GetProcessHeap(), 0, (UInt32) length);
Marshal.Copy(bytes, 0, pointer, bytes.Length);
Marshal.WriteByte(pointer, bytes.Length, 0);
return pointer;
}
}
// convert pointer to string
private String PointerToString(IntPtr ptr)
{
if (ptr == IntPtr.Zero)
return null;
Encoding encoding = Encoding.UTF8;
int length = GetPointerLenght(ptr);
Byte[] bytes = new Byte[length];
Marshal.Copy(ptr, bytes, 0, length);
return encoding.GetString(bytes, 0, length);
}
private Byte[] PointerToByte(IntPtr ptr)
{
if (ptr == IntPtr.Zero)
return null;
int length = GetPointerLenght(ptr);
Byte[] bytes = new Byte[length];
Marshal.Copy(ptr, bytes, 0, length);
return bytes;
}
// returns length of pointer
private int GetPointerLenght(IntPtr ptr)
{
if (ptr == IntPtr.Zero)
return 0;
return lstrlen(ptr);
}
}
, I think the problem is in this section, or not
private Byte[] PointerToByte(IntPtr ptr)
{
if (ptr == IntPtr.Zero)
return null;
int length = GetPointerLenght(ptr);
Byte[] bytes = new Byte[length];
Marshal.Copy(ptr, bytes, 0, length);
return bytes;
}
UPDATE
private int GetPointerLenght(IntPtr ptr)
{
if (ptr == IntPtr.Zero)
return 0;
return lstrlen(ptr); // here the problem this gets the length of string and im passing an array of bytes
}
so the question now how to get the length of the byte array from pointer ?
Thanks to #FCin pointing out
sqlite3_column_bytes() or sqlite3_column_bytes16()
here the solution get the size or byte[] array from unmanaged code
[DllImport("sqlite3")]
private static extern int sqlite3_column_bytes(IntPtr statement, int columnNumber);
private int getSizeOfBytes(IntPtr statement,int i)
{
return sqlite3_column_bytes(statement, i);
}
ANd you pass it like that
columnValues[i] = PointerToByte(sqlite3_column_blob(statement, i), getSizeOfBytes(statement,i));
I'm building a .Net application that should import a VS2010/2008 solution and find the start up project by reading the .suo file. I looked at How do I programmatically find out the Action of each StartUp Project in a solution? but the solution mentioned works only if I select Multiple Startup Projects in the Solution Properties window. My solution does not have multiple startup project. Is there a way to find the startup project when the solution has only 1 startup proj?
Here's the code
public static class StartUpProjectHelper
{
public static FileInfo GetStartUpProject(FileInfo solutionFile)
{
FileInfo startUpProject = null;
string suoFile = solutionFile.FullName.Substring(0, solutionFile.FullName.Length - 4) + ".suo";
string guid = null;
bool found = false;
foreach (var kvp in ReadStartupOptions(suoFile))
{
if (((kvp.Value & 1) != 0 || (kvp.Value & 2) != 0) && !found)
{
guid = kvp.Key.ToString();
found = true;
}
}
if (!string.IsNullOrEmpty(guid))
{
string projectname = GetProjectNameFromGuid(solutionFile, guid).Trim().TrimStart('\"').TrimEnd('\"');
startUpProject = new FileInfo(Path.Combine(solutionFile.DirectoryName, projectname));
}
return startUpProject;
}
public static string GetProjectNameFromGuid(FileInfo solutionFile, string guid)
{
string projectName = null;
using (var reader = new StreamReader(solutionFile.FullName))
{
string line;
bool found = false;
while ((line = reader.ReadLine()) != null && !found)
{
// sample format
//Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Saltus.digiTICKET.laptop", "digiTICKET.laptop\Saltus.digiTICKET.laptop.csproj",
//"{236D51A1-DEB7-41C3-A4C1-1D16D0A85382}" EndProject
if ((line.IndexOf(guid.ToUpper()) > -1) && line.Contains(",") && line.Contains("="))
{
projectName = line.Split(',')[1].Split(',')[0];
found = true;
}
}
}
return projectName;
}
//// from https://stackoverflow.com/questions/8817693/how-do-i-programmatically-find-out-the-action-of-each-startup-project-in-a-solut
public static IDictionary<Guid, int> ReadStartupOptions(string filePath)
{
if (filePath == null)
{
throw new InvalidOperationException("No file selected");
}
// look for this token in the file
const string token = "dwStartupOpt\0=";
byte[] tokenBytes = Encoding.Unicode.GetBytes(token);
var dic = new Dictionary<Guid, int>();
byte[] bytes;
using (var stream = new MemoryStream())
{
ExtractStream(filePath, "SolutionConfiguration", stream);
bytes = stream.ToArray();
}
int i = 0;
do
{
bool found = true;
for (int j = 0; j < tokenBytes.Length; j++)
{
if (bytes[i + j] != tokenBytes[j])
{
found = false;
break;
}
}
if (found)
{
// back read the corresponding project guid
// guid is formatted as {guid}
// len to read is Guid length* 2 and there are two offset bytes between guid and startup options token
var guidBytes = new byte[38*2];
Array.Copy(bytes, i - guidBytes.Length - 2, guidBytes, 0, guidBytes.Length);
var guid = new Guid(Encoding.Unicode.GetString(guidBytes));
// skip VT_I4
int options = BitConverter.ToInt32(bytes, i + tokenBytes.Length + 2);
dic[guid] = options;
}
i++;
} while (i < bytes.Length);
return dic;
}
public static void ExtractStream(string filePath, string streamName, Stream output)
{
if (filePath == null)
throw new ArgumentNullException("filePath");
if (streamName == null)
throw new ArgumentNullException("streamName");
if (output == null)
throw new ArgumentNullException("output");
IStorage storage;
int hr = StgOpenStorage(filePath, null, STGM.READ | STGM.SHARE_DENY_WRITE, IntPtr.Zero, 0, out storage);
if (hr != 0)
throw new Win32Exception(hr);
try
{
IStream stream;
hr = storage.OpenStream(streamName, IntPtr.Zero, STGM.READ | STGM.SHARE_EXCLUSIVE, 0, out stream);
if (hr != 0)
throw new Win32Exception(hr);
int read = 0;
IntPtr readPtr = Marshal.AllocHGlobal(Marshal.SizeOf(read));
try
{
var bytes = new byte[0x1000];
do
{
stream.Read(bytes, bytes.Length, readPtr);
read = Marshal.ReadInt32(readPtr);
if (read == 0)
break;
output.Write(bytes, 0, read);
} while (true);
}
finally
{
Marshal.FreeHGlobal(readPtr);
Marshal.ReleaseComObject(stream);
}
}
finally
{
Marshal.ReleaseComObject(storage);
}
}
[DllImport("ole32.dll")]
private static extern int StgOpenStorage([MarshalAs(UnmanagedType.LPWStr)] string pwcsName,
IStorage pstgPriority, STGM grfMode, IntPtr snbExclude, uint reserved,
out IStorage ppstgOpen);
#region Nested type: IStorage
[ComImport, Guid("0000000b-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
private interface IStorage
{
void Unimplemented0();
[PreserveSig]
int OpenStream([MarshalAs(UnmanagedType.LPWStr)] string pwcsName, IntPtr reserved1, STGM grfMode,
uint reserved2, out IStream ppstm);
// other methods not declared for simplicity
}
#endregion
#region Nested type: STGM
[Flags]
private enum STGM
{
READ = 0x00000000,
SHARE_DENY_WRITE = 0x00000020,
SHARE_EXCLUSIVE = 0x00000010,
// other values not declared for simplicity
}
#endregion
}
//// from https://stackoverflow.com/questions/8817693/how-do-i-programmatically-find-out-the-action-of-each-startup-project-in-a-solut``
Here's the solution. It is mostly based on #SimonMourier post, but instead of searching for "dwStartupOpt\0=", I'm looking for "StartupProject\0=&\0". This token occurs only once in the .suo file and is followed by the startup project's GUID.
public static FileInfo GetStartUpProject(FileInfo solutionFile)
{
FileInfo startUpProject = null;
string projectName = Path.GetFileNameWithoutExtension(solutionFile.FullName);
FileInfo suoFileInfo = new FileInfo(Path.Combine(solutionFile.Directory.FullName, string.Format(projectName + "{0}", ".suo")));
string guid = ReadStartupOptions(suoFileInfo.FullName).ToString();
if (!string.IsNullOrEmpty(guid))
{
string projectname = GetProjectNameFromGuid(solutionFile, guid).Trim().TrimStart('\"').TrimEnd('\"');
startUpProject = new FileInfo(Path.Combine(solutionFile.DirectoryName, projectname));
}
return startUpProject;
}
public static string GetProjectNameFromGuid(FileInfo solutionFile, string guid)
{
string projectName = null;
using (var reader = new StreamReader(solutionFile.FullName))
{
string line;
bool found = false;
while ((line = reader.ReadLine()) != null && !found)
{
if ((line.IndexOf(guid.ToUpper()) > -1) && line.Contains(",") && line.Contains("="))
{
projectName = line.Split(',')[1].Split(',')[0];
found = true;
}
}
}
return projectName;
}
// from https://stackoverflow.com/questions/8817693/how-do-i-programmatically-find-out-the-action-of-each-startup-project-in-a-solut
public static Guid ReadStartupOptions(string filePath)
{
Guid guid = new Guid();
if (filePath == null)
{
throw new InvalidOperationException("No file selected");
}
const string token = "StartupProject\0=&\0";
byte[] tokenBytes = Encoding.Unicode.GetBytes(token);
byte[] bytes;
using (var stream = new MemoryStream())
{
ExtractStream(filePath, "SolutionConfiguration", stream);
bytes = stream.ToArray();
}
var guidBytes = new byte[36 * 2];
for (int i2 = 0; i2 < bytes.Length; i2++)
{
if (bytes.Skip(i2).Take(tokenBytes.Length).SequenceEqual(tokenBytes))
{
Array.Copy(bytes, i2 + tokenBytes.Length + 2, guidBytes, 0, guidBytes.Length);
guid = new Guid(Encoding.Unicode.GetString(guidBytes));
break;
}
}
return guid;
}
public static void ExtractStream(string filePath, string streamName, Stream output)
{
if (filePath == null)
throw new ArgumentNullException("filePath");
if (streamName == null)
throw new ArgumentNullException("streamName");
if (output == null)
throw new ArgumentNullException("output");
IStorage storage;
int hr = StgOpenStorage(filePath, null, STGM.READ | STGM.SHARE_DENY_WRITE, IntPtr.Zero, 0, out storage);
if (hr != 0)
throw new Win32Exception(hr);
try
{
IStream stream;
hr = storage.OpenStream(streamName, IntPtr.Zero, STGM.READ | STGM.SHARE_EXCLUSIVE, 0, out stream);
if (hr != 0)
throw new Win32Exception(hr);
int read = 0;
IntPtr readPtr = Marshal.AllocHGlobal(Marshal.SizeOf(read));
try
{
var bytes = new byte[0x1000];
do
{
stream.Read(bytes, bytes.Length, readPtr);
read = Marshal.ReadInt32(readPtr);
if (read == 0)
break;
output.Write(bytes, 0, read);
} while (true);
}
finally
{
Marshal.FreeHGlobal(readPtr);
Marshal.ReleaseComObject(stream);
}
}
finally
{
Marshal.ReleaseComObject(storage);
}
}
[DllImport("ole32.dll")]
private static extern int StgOpenStorage([MarshalAs(UnmanagedType.LPWStr)] string pwcsName,
IStorage pstgPriority, STGM grfMode, IntPtr snbExclude, uint reserved,
out IStorage ppstgOpen);
#region Nested type: IStorage
[ComImport, Guid("0000000b-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
private interface IStorage
{
void Unimplemented0();
[PreserveSig]
int OpenStream([MarshalAs(UnmanagedType.LPWStr)] string pwcsName, IntPtr reserved1, STGM grfMode,
uint reserved2, out IStream ppstm);
// other methods not declared for simplicity
}
#endregion
#region Nested type: STGM
[Flags]
private enum STGM
{
READ = 0x00000000,
SHARE_DENY_WRITE = 0x00000020,
SHARE_EXCLUSIVE = 0x00000010,
// other values not declared for simplicity
}
#endregion
I'm trying to hook the winsock connect function and route the TCP connection through socks5 proxy /w auth.
This works if the socket is a blocking socket, but while using firefox ( nonblocking sockets ) I get a lot of 10035, 10022 winsock Errors.
How can i determine if it's a nonblocking / blocking socket?
I would really appreciate any hints or ideas to achieve the functionality to hook the wsock connect function and route tcp traffic through a socks5 server.
I can put the demo application on github if anybody wants to test it. ( Works with any version of firefox )
Edit1: https://github.com/duketwo/WinsockConnectHookSocks5/
( You have to edit the proxy information in WSockConnectHook/HookManager.cs and the path of firefox in Injector/MainForm.cs )
Edit2: It's easyhook which is causing the trouble, anything after the original function call doesn't work properly.
Edit3: Seems like i got it working with many flaws, in fact it is required differentiate between nonblocking sockets and blocking sockets. Any ideas how to achieve this?
Edit4: Windows doesn't offer any method to retrieve the blocking-attribute of a socket, so I might have to hook the ioctlsocket function to keep track of the blocking status of the sockets.
Thanks
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
using EasyHook;
using System.IO;
using System.Windows.Forms;
namespace WSockConnectHook
{
public class WinSockConnectController : IDisposable, IHook
{
[UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Ansi, SetLastError = true)]
private delegate int WinsockConnectDelegate(IntPtr s, IntPtr addr, int addrsize);
[DllImport("WS2_32.dll", SetLastError = true)]
public static extern int connect(IntPtr s, IntPtr addr, int addrsize);
[StructLayout(LayoutKind.Sequential, Size = 16)]
public struct sockaddr_in
{
public const int Size = 16;
public short sin_family;
public ushort sin_port;
public struct in_addr
{
public uint S_addr;
public struct _S_un_b
{
public byte s_b1, s_b2, s_b3, s_b4;
}
public _S_un_b S_un_b;
public struct _S_un_w
{
public ushort s_w1, s_w2;
}
public _S_un_w S_un_w;
}
public in_addr sin_addr;
}
[DllImport("ws2_32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern int WSAGetLastError();
[DllImport("ws2_32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern void WSASetLastError(int set);
[DllImport("Ws2_32.dll", CharSet = CharSet.Ansi)]
public static extern uint inet_addr(string cp);
[DllImport("Ws2_32.dll")]
public static extern ushort htons(ushort hostshort);
[DllImport("ws2_32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern IntPtr socket(short af, short socket_type, int protocol);
[DllImport("Ws2_32.dll")]
public static extern int send(IntPtr s, IntPtr buf, int len, int flags);
[DllImport("Ws2_32.dll")]
public static extern int recv(IntPtr s, IntPtr buf, int len, int flags);
[DllImport("ws2_32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern int closesocket(IntPtr s);
[DllImport("Ws2_32.dll")]
public static extern ushort ntohs(ushort netshort);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern void SetLastError(int errorCode);
private string _name;
private LocalHook _hook;
public bool Error { get; set; }
public string Name { get; set; }
private string proxyIp, proxyPort, proxyUser, proxyPass;
public WinSockConnectController(IntPtr address, string proxyIp, string proxyPort, string proxyUser, string proxyPass)
{
this.Name = typeof(WinSockConnectController).Name;
this.proxyIp = proxyIp;
this.proxyPort = proxyPort;
this.proxyUser = proxyUser;
this.proxyPass = proxyPass;
try
{
_name = string.Format("WinsockHook_{0:X}", address.ToInt32());
_hook = LocalHook.Create(address, new WinsockConnectDelegate(WinsockConnectDetour), this);
_hook.ThreadACL.SetExclusiveACL(new Int32[] { 1 });
}
catch (Exception)
{
this.Error = true;
}
}
private object wSockLock = new object();
private int WinsockConnectDetour(IntPtr s, IntPtr addr, int addrsize)
{
lock (wSockLock)
{
// retrieve remote ip
sockaddr_in structure = (sockaddr_in)Marshal.PtrToStructure(addr, typeof(sockaddr_in));
string remoteIp = new System.Net.IPAddress(structure.sin_addr.S_addr).ToString();
ushort remotePort = ntohs(structure.sin_port);
HookManager.Log("Ip: " + remoteIp + " Port: " + remotePort.ToString() + " Addrsize: " + addrsize);
if (!proxyIp.Equals(""))
//if (!proxyIp.Equals(""))
{
// connect to socks5 server
SetAddr(s, addr, proxyIp, proxyPort);
var result = Connect(s, addr, addrsize);
if (result == -1)
return -1;
// send socks 5 request
IntPtr socksProtocolRequest = SetUpSocks5Request();
result = send(s, socksProtocolRequest, 4, 0);
if (result == -1)
return -1;
// retrieve server repsonse
var response = Recieve(s, 2);
if (response == IntPtr.Zero)
return -1;
byte[] recvBytes = new byte[2] { Marshal.ReadByte(response), Marshal.ReadByte(response, 1) };
if (recvBytes[1] == 255)
{
HookManager.Log("No authentication method was accepted by the proxy server");
return -1;
}
if (recvBytes[0] != 5)
{
HookManager.Log("No SOCKS5 proxy");
return -1;
}
// if auth request response, send authenicate request
if (recvBytes[1] == 2)
{
int length = 0;
var authenticateRequest = SetUpAuthenticateRequest(proxyUser, proxyPass, out length);
result = Send(s, authenticateRequest, length);
response = Recieve(s, 2);
if (response == IntPtr.Zero)
return -1;
recvBytes = new byte[2] { Marshal.ReadByte(response), Marshal.ReadByte(response, 1) };
if (recvBytes[1] != 0)
{
HookManager.Log("Proxy: incorrect username/password");
return -1;
}
}
// request bind with server
var bindRequest = SetUpBindWithRemoteHost(remoteIp, remotePort);
result = Send(s, bindRequest, 10);
if (result == -1)
return -1;
// response
response = Recieve(s, 10);
if (response == IntPtr.Zero)
return -1;
if (!VerifyBindResponse(response))
return -1;
// success
WSASetLastError(0);
SetLastError(0);
// clean memory
foreach (var ptr in allocatedMemory)
Marshal.FreeHGlobal(ptr);
allocatedMemory.Clear();
return 0;
}
else
{
var result = connect(s, addr, addrsize);
return result;
}
}
}
private int Connect(IntPtr socket, IntPtr addr, int addrsize)
{
var result = connect(socket, addr, addrsize);
while (result == -1)
{
var errorcode = WSAGetLastError();
HookManager.Log("Error: " + errorcode);
if (errorcode == 10056)
break;
if (errorcode == 10037)
break;
if (errorcode != 10035 && errorcode != 10037)
return -1;
//flag = 1;
result = connect(socket, addr, addrsize);
}
return result;
}
private int Send(IntPtr socket, IntPtr buf, int len)
{
var result = send(socket, buf, len, 0);
while (result == -1)
{
var errorcode = WSAGetLastError();
HookManager.Log("Error: " + errorcode);
if (errorcode == 10056)
break;
if (errorcode == 10037)
break;
if (errorcode != 10035 && errorcode != 10037)
return -1;
result = send(socket, buf, 4, 0);
}
return result;
}
private List<IntPtr> allocatedMemory = new List<IntPtr>();
private IntPtr Recieve(IntPtr socket, int len)
{
var buffer = Marshal.AllocHGlobal(len);
allocatedMemory.Add(buffer);
var result = recv(socket, buffer, len, 0);
if (result == -1)
{
HookManager.Log("Error2: " + WSAGetLastError());
return IntPtr.Zero;
}
return buffer;
}
private IntPtr RecieveAuth(IntPtr socket, int len)
{
var buffer = Marshal.AllocHGlobal(len);
allocatedMemory.Add(buffer);
var result = recv(socket, buffer, len, 0);
if (result == -1)
{
HookManager.Log("Error3: " + WSAGetLastError());
return IntPtr.Zero; ;
}
if (result == 0)
return buffer;
if (result != 2)
{
HookManager.Log("Proxy: Bad response from server");
return IntPtr.Zero;
}
return buffer;
}
private IntPtr RecieveBind(IntPtr socket, int len)
{
var buffer = Marshal.AllocHGlobal(len);
allocatedMemory.Add(buffer);
var result = recv(socket, buffer, len, 0);
if (result == -1)
{
HookManager.Log("Error3: " + WSAGetLastError());
return IntPtr.Zero; ;
}
if (result == 0)
return buffer;
if (result != 10)
{
HookManager.Log("Proxy: Bad response from server");
return IntPtr.Zero;
}
return buffer;
}
private void SetAddr(IntPtr socket, IntPtr addr, string ip, string port)
{
sockaddr_in structure = (sockaddr_in)Marshal.PtrToStructure(addr, typeof(sockaddr_in));
string originalip = new System.Net.IPAddress(structure.sin_addr.S_addr).ToString();
ushort originalport = ntohs(structure.sin_port);
structure.sin_addr.S_addr = inet_addr(ip);
structure.sin_port = htons(Convert.ToUInt16(port));
Marshal.StructureToPtr(structure, addr, true);
structure = (sockaddr_in)Marshal.PtrToStructure(addr, typeof(sockaddr_in));
}
private IntPtr SetUpSocks5Request()
{
var initialRequest = Marshal.AllocHGlobal(4);
Marshal.WriteByte(initialRequest, Convert.ToByte(5));
Marshal.WriteByte(initialRequest + 1, Convert.ToByte(2));
Marshal.WriteByte(initialRequest + 2, Convert.ToByte(0));
Marshal.WriteByte(initialRequest + 3, Convert.ToByte(2));
return initialRequest;
}
private IntPtr SetUpAuthenticateRequest(string username, string password, out int index)
{
index = 0;
var size = 3 + Encoding.Default.GetBytes(username).Length + Encoding.Default.GetBytes(password).Length;
var authenticateBuffer = Marshal.AllocHGlobal(size);
Marshal.WriteByte(authenticateBuffer + index++, Convert.ToByte(1));
Marshal.WriteByte(authenticateBuffer + index++, Convert.ToByte(username.Length));
byte[] rawBytes;
if (username.Length > 0)
{
rawBytes = Encoding.Default.GetBytes(username);
for (int i = 0; i < rawBytes.Length; i++)
{
Marshal.WriteByte(authenticateBuffer + index++, rawBytes[i]);
}
}
Marshal.WriteByte(authenticateBuffer + index++, Convert.ToByte(password.Length));
if (password.Length > 0)
{
rawBytes = Encoding.Default.GetBytes(password);
for (int i = 0; i < rawBytes.Length; i++)
{
Marshal.WriteByte(authenticateBuffer + index++, rawBytes[i]);
}
}
return authenticateBuffer;
}
private IntPtr SetUpBindWithRemoteHost(string eveIP, ushort evePort)
{
var bindWithEveBuffer = Marshal.AllocHGlobal(10);
var iplist = eveIP.Split('.').ToList();
byte[] portbyte = BitConverter.GetBytes(evePort).Reverse().ToArray();
byte[] newbyte = new byte[2];
int indexy = 0;
foreach (var byty in portbyte)
{
newbyte[indexy] = byty;
indexy++;
}
// bind with remote server
Marshal.WriteByte(bindWithEveBuffer, Convert.ToByte(5));
Marshal.WriteByte(bindWithEveBuffer + 1, Convert.ToByte(1));
Marshal.WriteByte(bindWithEveBuffer + 2, Convert.ToByte(0));
Marshal.WriteByte(bindWithEveBuffer + 3, Convert.ToByte(1));
Marshal.WriteByte(bindWithEveBuffer + 4, Convert.ToByte(iplist[0]));
Marshal.WriteByte(bindWithEveBuffer + 5, Convert.ToByte(iplist[1]));
Marshal.WriteByte(bindWithEveBuffer + 6, Convert.ToByte(iplist[2]));
Marshal.WriteByte(bindWithEveBuffer + 7, Convert.ToByte(iplist[3]));
Marshal.WriteByte(bindWithEveBuffer + 8, newbyte[0]);
Marshal.WriteByte(bindWithEveBuffer + 9, newbyte[1]);
return bindWithEveBuffer;
}
private bool VerifyBindResponse(IntPtr buffer)
{
var recvBytes = new byte[10] { Marshal.ReadByte(buffer), Marshal.ReadByte(buffer, 1), Marshal.ReadByte(buffer, 2), Marshal.ReadByte(buffer, 3), Marshal.ReadByte(buffer, 4), Marshal.ReadByte(buffer, 5), Marshal.ReadByte(buffer, 6), Marshal.ReadByte(buffer, 7), Marshal.ReadByte(buffer, 8), Marshal.ReadByte(buffer, 9) };
if (recvBytes[1] != 0)
{
if (recvBytes[1] == 1)
HookManager.Log("General failure");
if (recvBytes[1] == 2)
HookManager.Log("connection not allowed by ruleset");
if (recvBytes[1] == 3)
HookManager.Log("network unreachable");
if (recvBytes[1] == 4)
HookManager.Log("host unreachable");
if (recvBytes[1] == 5)
HookManager.Log("connection refused by destination host");
if (recvBytes[1] == 6)
HookManager.Log("TTL expired");
if (recvBytes[1] == 7)
HookManager.Log("command not supported / protocol error");
if (recvBytes[1] == 8)
HookManager.Log("address type not supported");
HookManager.Log("Proxy: Connection error binding eve server");
return false;
}
return true;
}
public void Dispose()
{
if (_hook == null)
return;
_hook.Dispose();
_hook = null;
}
}
}
Below is the code sample which I got from online resource but it's suppose to work with fullframework, but when I try to build it using C# smart device, it throws exception saying it's out of memory. Does anybody know how can I fix it to use on compact? the out of memory exception when I make the second call to VerQueryValue which is the last one.
thanks,
[DllImport("coredll.dll")]
public static extern bool VerQueryValue(byte[] buffer, string subblock, out IntPtr blockbuffer, out uint len);
[DllImport("coredll.dll")]
public static extern bool VerQueryValue(byte[] pBlock, string pSubBlock, out string pValue, out uint len);
//
private static void GetAssemblyVersion()
{
string filename = #"\Windows\MyLibrary.dll";
if (File.Exists(filename))
{
try {
int handle = 0;
Int32 size = 0;
size = GetFileVersionInfoSize(filename, out handle);
if (size > 0)
{
bool retValue;
byte[] buffer = new byte[size];
retValue = GetFileVersionInfo(filename, handle, size, buffer);
if (retValue == true)
{
bool success = false;
IntPtr blockbuffer = IntPtr.Zero;
uint len = 0;
//success = VerQueryValue(buffer, "\\", out blockbuffer, out len);
success = VerQueryValue(buffer, #"\VarFileInfo\Translation", out blockbuffer, out len);
if(success)
{
int p = (int)blockbuffer;
//Reads a 16-bit signed integer from unmanaged memory
int j = Marshal.ReadInt16((IntPtr)p);
p += 2;
//Reads a 16-bit signed integer from unmanaged memory
int k = Marshal.ReadInt16((IntPtr)p);
string sb = string.Format("{0:X4}{1:X4}", j, k);
string spv = #"\StringFileInfo\" + sb + #"\ProductVersion";
string versionInfo;
VerQueryValue(buffer, spv, out versionInfo, out len);
}
}
}
}
catch (Exception err)
{
string error = err.Message;
}
}
}
After adding these two statements:
Int32 dwVerMinor = j & 0xffff;
Int32 dwVerBuild = k & 0xffff;
it's able to retrieve the DLL version.
Here's an implementation:
using DWORD = System.UInt32;
public static class NativeFile
{
public struct NativeFileInfo
{
public Version Version;
public NameValueCollection StringTable;
}
public unsafe static NativeFileInfo GetFileInfo(string path)
{
if (!File.Exists(path))
{
throw new FileNotFoundException();
}
IntPtr handle;
var size = GetFileVersionInfoSize(path, out handle);
var buffer = Marshal.AllocHGlobal(size);
try
{
if (!GetFileVersionInfo(path, handle, size, buffer))
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
IntPtr pVersion;
int versionLength;
VerQueryValue(buffer, “\”, out pVersion, out versionLength);
var versionInfo = (VS_FIXEDFILEINFO)Marshal.PtrToStructure(pVersion, typeof(VS_FIXEDFILEINFO));
var version = new Version((int)versionInfo.dwFileVersionMS >> 16,
(int)versionInfo.dwFileVersionMS & 0xFFFF,
(int)versionInfo.dwFileVersionLS >> 16,
(int)versionInfo.dwFileVersionLS & 0xFFFF);
// move to the string table and parse
var pStringTable = ((byte*)pVersion.ToPointer()) + versionLength;
var strings = ParseStringTable(pStringTable, size – versionLength);
return new NativeFileInfo
{
Version = version,
StringTable = strings
};
}
finally
{
Marshal.FreeHGlobal(buffer);
}
}
private unsafe static NameValueCollection ParseStringTable(byte* pStringTable, int length)
{
NameValueCollection nvc = new NameValueCollection();
byte* p = pStringTable;
short stringFileInfoLength = (short)*p;
byte* end = pStringTable + length;
p += (2 + 2 + 2); // length + valuelength + type
// verify key
var key = Marshal.PtrToStringUni(new IntPtr(p), 14);
if (key != "StringFileInfo") throw new ArgumentException();
// move past the key to the first string table
p += 30;
short stringTableLength = (short)*p;
p += (2 + 2 + 2); // length + valuelength + type
// get locale info
key = Marshal.PtrToStringUni(new IntPtr(p), 8);
// move to the first string
p += 18;
while (p < end)
{
short stringLength = (short)*p;
p += 2;
short valueChars = (short)*p;
p += 2;
short type = (short)*p;
p += 2;
if (stringLength == 0) break;
if ((valueChars == 0) || (type != 1))
{
p += stringLength;
continue;
}
var keyLength = stringLength – (valueChars * 2) – 6;
key = Marshal.PtrToStringUni(new IntPtr(p), keyLength / 2).TrimEnd(”);
p += keyLength;
var value = Marshal.PtrToStringUni(new IntPtr(p), valueChars).TrimEnd(”);
p += valueChars * 2;
if ((int)p % 4 != 0) p += 2;
nvc.Add(key, value);
}
return nvc;
}
private const string COREDLL = "coredll.dll";
[DllImport(COREDLL, SetLastError = true)]
private static extern int GetFileVersionInfoSize(string lptstrFilename, out IntPtr lpdwHandle);
[DllImport(COREDLL, SetLastError = true)]
private static extern bool GetFileVersionInfo(string lptstrFilename, IntPtr dwHandle, int dwLen, IntPtr lpData);
[DllImport(COREDLL, SetLastError = true)]
private static extern bool VerQueryValue(IntPtr pBlock, string lpSubBlock, out IntPtr lplpBuffer, out int puLen);
[StructLayout(LayoutKind.Sequential)]
private struct VS_FIXEDFILEINFO
{
public DWORD dwSignature;
public DWORD dwStrucVersion;
public DWORD dwFileVersionMS;
public DWORD dwFileVersionLS;
public DWORD dwProductVersionMS;
public DWORD dwProductVersionLS;
public DWORD dwFileFlagsMask;
public DWORD dwFileFlags;
public FileOS dwFileOS;
public FileType dwFileType;
public DWORD dwFileSubtype;
public DWORD dwFileDateMS;
public DWORD dwFileDateLS;
};
public enum FileOS : uint
{
Unknown = 0x00000000,
DOS = 0x00010000,
OS2_16 = 0x00020000,
OS2_32 = 0x00030000,
NT = 0x00040000,
WindowsCE = 0x00050000,
}
public enum FileType : uint
{
Unknown = 0x00,
Application = 0x01,
DLL = 0x02,
Driver = 0x03,
Font = 0x04,
VXD = 0x05,
StaticLib = 0x07
}
}
And an example of usage:
class Program
{
static void Main(string[] args)
{
string target = “\FlashFX Disk\ARMv4i\conmanclient2.exe”;
var version = NativeFile.GetFileInfo(target);
Console.WriteLine(string.Format(“File: { 0}”, Path.GetFileName(target)));
Console.WriteLine(string.Format(“Version: { 0}”, version.Version.ToString(4)));
foreach (var key in version.StringTable.AllKeys)
{
Console.WriteLine(string.Format(“{ 0}: { 1}”, key, version.StringTable[key]));
}
Console.ReadLine();
}