I am using VS2010. .NET Framework 3.5.
Pre-conditions:
Adobe is not installed on the system.
3rd party dlls not to be used
Windows dlls should be used.
So far, I am able to print a pdf file, but not able to change printer setting at run time.
The basic thought for printing pdf file is that, I read bytes of pdf file and directly send them to printer. Here it prints the pdf file without any problem.
But the file is printing side by side on a page, but I want it to print only one side of the page not the both sides. For that I am trying to change printer setting at run time.
I have tested my code on two different printers. On one printer it is printing side by side of the same page and on another printer on one page only. That means, printer settings is not getting changed.
Code is as shown below. Used 4 classes:
//Class 1: This class basically tries to change the printer setting at run time
public class PrinterSettingForPdf
{
#region "Private Variables"
private IntPtr hPrinter = new System.IntPtr();
private PRINTER_DEFAULTS PrinterValues = new PRINTER_DEFAULTS();
private PRINTER_INFO_2 pinfo = new PRINTER_INFO_2();
//private PRINTER_INFO_9 pinfo9 = new PRINTER_INFO_9();
private DEVMODE dm;
private IntPtr ptrDM;
private IntPtr ptrPrinterInfo;
private int sizeOfDevMode = 0;
private int lastError;
private int nBytesNeeded;
private long nRet;
private int intError;
private System.Int32 nJunk;
private IntPtr yDevModeData;
#endregion
#region "Constants"
private const int DM_DUPLEX = 0x1000;
private const int DM_IN_BUFFER = 8;
private const int DM_OUT_BUFFER = 2;
private const int PRINTER_ACCESS_ADMINISTER = 0x4;
private const int PRINTER_ACCESS_USE = 0x8;
private const int STANDARD_RIGHTS_REQUIRED = 0xF0000;
private const int PRINTER_ALL_ACCESS =
(STANDARD_RIGHTS_REQUIRED | PRINTER_ACCESS_ADMINISTER
| PRINTER_ACCESS_USE);
#endregion
#region "Win API Def"
[DllImport("kernel32.dll", EntryPoint = "GetLastError", SetLastError = false,
ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
private static extern Int32 GetLastError();
[DllImport("winspool.Drv", EntryPoint = "ClosePrinter", SetLastError = true,
ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
private static extern bool ClosePrinter(IntPtr hPrinter);
[DllImport("winspool.Drv", EntryPoint = "DocumentPropertiesA", SetLastError = true,
ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
private static extern int DocumentProperties(IntPtr hwnd, IntPtr hPrinter,
[MarshalAs(UnmanagedType.LPStr)] string pDeviceNameg,
IntPtr pDevModeOutput, ref IntPtr pDevModeInput, int fMode);
[DllImport("winspool.Drv", EntryPoint = "GetPrinterA", SetLastError = true,
CharSet = CharSet.Ansi, ExactSpelling = true,
CallingConvention = CallingConvention.StdCall)]
private static extern bool GetPrinter(IntPtr hPrinter, Int32 dwLevel,
IntPtr pPrinter, Int32 dwBuf, out Int32 dwNeeded);
[DllImport("winspool.Drv", EntryPoint = "OpenPrinterA",
SetLastError = true, CharSet = CharSet.Ansi,
ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
private static extern bool
OpenPrinter([MarshalAs(UnmanagedType.LPStr)] string szPrinter,
out IntPtr hPrinter, ref PRINTER_DEFAULTS pd);
[DllImport("winspool.drv", CharSet = CharSet.Ansi, SetLastError = true)]
private static extern bool SetPrinter(IntPtr hPrinter, int Level, IntPtr
pPrinter, int Command);
#endregion
#region "Data structure"
[StructLayout(LayoutKind.Sequential)]
public struct PRINTER_DEFAULTS
{
public int pDatatype;
public int pDevMode;
public int DesiredAccess;
}
[StructLayout(LayoutKind.Sequential)]
private struct PRINTER_INFO_2
{
[MarshalAs(UnmanagedType.LPStr)]
public string pServerName;
[MarshalAs(UnmanagedType.LPStr)]
public string pPrinterName;
[MarshalAs(UnmanagedType.LPStr)]
public string pShareName;
[MarshalAs(UnmanagedType.LPStr)]
public string pPortName;
[MarshalAs(UnmanagedType.LPStr)]
public string pDriverName;
[MarshalAs(UnmanagedType.LPStr)]
public string pComment;
[MarshalAs(UnmanagedType.LPStr)]
public string pLocation;
public IntPtr pDevMode;
[MarshalAs(UnmanagedType.LPStr)]
public string pSepFile;
[MarshalAs(UnmanagedType.LPStr)]
public string pPrintProcessor;
[MarshalAs(UnmanagedType.LPStr)]
public string pDatatype;
[MarshalAs(UnmanagedType.LPStr)]
public string pParameters;
public IntPtr pSecurityDescriptor;
public Int32 Attributes;
public Int32 Priority;
public Int32 DefaultPriority;
public Int32 StartTime;
public Int32 UntilTime;
public Int32 Status;
public Int32 cJobs;
public Int32 AveragePPM;
}
/// <summary>
/// The PRINTER_INFO_9 structure specifies the per-user default printer settings.
/// </summary>
/// <seealso href="http://msdn.microsoft.com/en-us/library/windows/desktop/dd162852(v=vs.85).aspx"/>
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
internal class PRINTER_INFO_9
{
/// <summary>
/// A pointer to a DEVMODE structure that defines the per-user
/// default printer data such as the paper orientation and the resolution.
/// The DEVMODE is stored in the user's registry.
/// </summary>
public IntPtr pDevMode;
}
private const short CCDEVICENAME = 32;
private const short CCFORMNAME = 32;
[StructLayout(LayoutKind.Sequential)]
public struct DEVMODE
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCDEVICENAME)]
public string dmDeviceName;
public short dmSpecVersion;
public short dmDriverVersion;
public short dmSize;
public short dmDriverExtra;
public int dmFields;
public short dmOrientation;
public short dmPaperSize;
public short dmPaperLength;
public short dmPaperWidth;
public short dmScale;
public short dmCopies;
public short dmDefaultSource;
public short dmPrintQuality;
public short dmColor;
public short dmDuplex;
public short dmYResolution;
public short dmTTOption;
public short dmCollate;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCFORMNAME)]
public string dmFormName;
public short dmUnusedPadding;
public short dmBitsPerPel;
public int dmPelsWidth;
public int dmPelsHeight;
public int dmDisplayFlags;
public int dmDisplayFrequency;
}
#endregion
#region "Function to change printer settings"
public bool ChangePrintersetting(string sPrinterName, PrinterData pd, int numbeOfCopies, bool preserverOldSettings)
{
if (((int)pd.Duplex < 1) || ((int)pd.Duplex > 3))
{
throw new ArgumentOutOfRangeException("nDuplexSetting", "nDup lexSetting is incorrect.");
}
else
{
const int PRINTER_ACCESS_ADMINISTER = 0x4;
const int PRINTER_ACCESS_USE = 0x8;
const int PRINTER_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED | PRINTER_ACCESS_ADMINISTER | PRINTER_ACCESS_USE);
//const int READ_CONTROL = 0x20000;
//const int PRINTER_NORMAL_ACCESS = 131080; // (READ_CONTROL | PRINTER_ACCESS_USE);
PrinterValues.pDatatype = 0;
PrinterValues.pDevMode = 0;
PrinterValues.DesiredAccess = PRINTER_ALL_ACCESS;
nRet = Convert.ToInt32(OpenPrinter(sPrinterName, out hPrinter, ref PrinterValues));
if (nRet == 0)
{
lastError = Marshal.GetLastWin32Error();
throw new Win32Exception(Marshal.GetLastWin32Error());
}
GetPrinter(hPrinter, 2, IntPtr.Zero, 0, out nBytesNeeded);
if (nBytesNeeded <= 0)
{
return false;
}
// Allocate enough space for PRINTER_INFO_2...
//ptrPrinterInfo = Marshal.AllocCoTaskMem(nBytesNeeded);
ptrPrinterInfo = Marshal.AllocHGlobal(nBytesNeeded);
// The second GetPrinter fills in all the current settings, so all you
// need to do is modify what you're interested in...
nRet = Convert.ToInt32(GetPrinter(hPrinter, 2, ptrPrinterInfo, nBytesNeeded, out nJunk));
if (nRet == 0)
{
lastError = Marshal.GetLastWin32Error();
throw new Win32Exception(Marshal.GetLastWin32Error());
}
pinfo = (PRINTER_INFO_2)Marshal.PtrToStructure(ptrPrinterInfo, typeof(PRINTER_INFO_2));
IntPtr Temp = new IntPtr();
int i1 = DocumentProperties(IntPtr.Zero, hPrinter, sPrinterName, IntPtr.Zero, ref Temp, 0);
//IntPtr yDevModeData = Marshal.AllocCoTaskMem(i1);
IntPtr yDevModeData = Marshal.AllocHGlobal(i1);
i1 = DocumentProperties(IntPtr.Zero, hPrinter, sPrinterName, yDevModeData, ref Temp, 2);
dm = (DEVMODE)Marshal.PtrToStructure(yDevModeData, typeof(DEVMODE));
dm.dmDefaultSource = (short)pd.Source;
dm.dmOrientation = (short)pd.Orientation;
dm.dmPaperSize = (short)pd.Size;
dm.dmCopies = (short)1;
dm.dmDuplex = (short)pd.Duplex;
Marshal.StructureToPtr(dm, yDevModeData, true);
//nRet = DocumentProperties(IntPtr.Zero, hPrinter, sPrinterName, yDevModeData
// , ref yDevModeData, (DM_IN_BUFFER | DM_OUT_BUFFER));
if ((nRet == 0) || (hPrinter == IntPtr.Zero))
{
lastError = Marshal.GetLastWin32Error();
//string myErrMsg = GetErrorMessage(lastError);
throw new Win32Exception(Marshal.GetLastWin32Error());
}
if (pinfo.pDevMode == IntPtr.Zero)
{
// If GetPrinter didn't fill in the DEVMODE, try to get it by calling
// DocumentProperties...
IntPtr ptrZero = IntPtr.Zero;
//get the size of the devmode structure
sizeOfDevMode = DocumentProperties(IntPtr.Zero, hPrinter, sPrinterName, ptrZero, ref ptrZero, 0);
if (nRet <= 0)
{
return false;
}
ptrDM = Marshal.AllocCoTaskMem(sizeOfDevMode);
int i;
i = DocumentProperties(IntPtr.Zero, hPrinter, sPrinterName, ptrDM,
ref ptrZero, DM_OUT_BUFFER);
if ((i < 0) || (ptrDM == IntPtr.Zero))
{
//Cannot get the DEVMODE structure.
return false;
}
pinfo.pDevMode = ptrDM;
}
if (!Convert.ToBoolean(dm.dmFields & DM_DUPLEX))
{
//You cannot modify the duplex flag for this printer
//because it does not support duplex or the driver does not support setting
//it from the Windows API.
//return false;
}
pinfo.pDevMode = yDevModeData;
pinfo.pSecurityDescriptor = IntPtr.Zero;
/*update driver dependent part of the DEVMODE
i1 = DocumentProperties(IntPtr.Zero, hPrinter, sPrinterName, yDevModeData
, ref pinfo.pDevMode, (DM_IN_BUFFER | DM_OUT_BUFFER));*/
if (i1 < 0)
{
//Unable to set duplex setting to this printer.
return false;
}
Marshal.StructureToPtr(pinfo, ptrPrinterInfo, true);
lastError = Marshal.GetLastWin32Error();
nRet = Convert.ToInt16(SetPrinter(hPrinter, 2, ptrPrinterInfo, 0));
if (nRet == 0)
{
//Unable to set shared printer settings.
lastError = Marshal.GetLastWin32Error();
//string myErrMsg = GetErrorMessage(lastError);
throw new Win32Exception(Marshal.GetLastWin32Error());
}
return Convert.ToBoolean(nRet);
}
}
#endregion
}
//Class 2: Supporting Printer setting class
public class PrinterData
{
public int Duplex { get; set; }
public int Source { get; set; }
public int Orientation { get; set; }
public int Size { get; set; }
}
//Class 3: This class uses Windows dlls methods to print pdf contents
public class PrintPdf
{
// Structure and API declarions:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public class DOCINFOA
{
[MarshalAs(UnmanagedType.LPStr)]
public string pDocName;
[MarshalAs(UnmanagedType.LPStr)]
public string pOutputFile;
[MarshalAs(UnmanagedType.LPStr)]
public string pDataType;
}
[DllImport("winspool.Drv", EntryPoint = "OpenPrinterA", SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
public static extern bool OpenPrinter([MarshalAs(UnmanagedType.LPStr)] string szPrinter, out IntPtr hPrinter, IntPtr pd);
[DllImport("winspool.Drv", EntryPoint = "ClosePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
public static extern bool ClosePrinter(IntPtr hPrinter);
[DllImport("winspool.Drv", EntryPoint = "StartDocPrinterA", SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
public static extern bool StartDocPrinter(IntPtr hPrinter, Int32 level, [In, MarshalAs(UnmanagedType.LPStruct)] DOCINFOA di);
[DllImport("winspool.Drv", EntryPoint = "EndDocPrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
public static extern bool EndDocPrinter(IntPtr hPrinter);
[DllImport("winspool.Drv", EntryPoint = "StartPagePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
public static extern bool StartPagePrinter(IntPtr hPrinter);
[DllImport("winspool.Drv", EntryPoint = "EndPagePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
public static extern bool EndPagePrinter(IntPtr hPrinter);
[DllImport("winspool.Drv", EntryPoint = "WritePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
public static extern bool WritePrinter(IntPtr hPrinter, IntPtr pBytes, Int32 dwCount, out Int32 dwWritten);
// SendBytesToPrinter()
// When the function is given a printer name and an unmanaged array
// of bytes, the function sends those bytes to the print queue.
// Returns true on success, false on failure.
public static bool SendBytesToPrinter(string szPrinterName, IntPtr pBytes, Int32 dwCount)
{
Int32 dwError = 0, dwWritten = 0;
IntPtr hPrinter = new IntPtr(0);
DOCINFOA di = new DOCINFOA();
bool bSuccess = false; // Assume failure unless you specifically succeed.
di.pDocName = "My C#.NET RAW Document";
di.pDataType = "RAW";
// Open the printer.
//if (OpenPrinter(szPrinterName.Normalize(), out hPrinter, IntPtr.Zero))
PrinterSettingForPdf objPS = new PrinterSettingForPdf();
PrinterData objPD = new PrinterData();
objPD.Duplex = 1;
objPD.Orientation = 1;
//objPD.Size = 1;
//objPD.Source = 1;
if (OpenPrinter(szPrinterName.Normalize(), out hPrinter, IntPtr.Zero))
{
// Start a document.
if (StartDocPrinter(hPrinter, 1, di))
{
// Start a page.
if (StartPagePrinter(hPrinter))
{
objPS.ChangePrintersetting(szPrinterName, objPD, 1, false);
// Write your bytes.
bSuccess = WritePrinter(hPrinter, pBytes, dwCount, out dwWritten);
EndPagePrinter(hPrinter);
}
EndDocPrinter(hPrinter);
}
if (hPrinter != IntPtr.Zero)
ClosePrinter(hPrinter);
// ClosePrinter(hPrinter);
}
// If you did not succeed, GetLastError may give more information
// about why not.
if (bSuccess == false)
{
dwError = Marshal.GetLastWin32Error();
}
return bSuccess;
}
public static bool SendFileToPrinter(string szPrinterName, string szFileName)
{
// Open the file.
FileStream fs = new FileStream(szFileName, FileMode.Open);
// Create a BinaryReader on the file.
BinaryReader br = new BinaryReader(fs);
// Dim an array of bytes big enough to hold the file's contents.
Byte[] bytes = new Byte[fs.Length];
bool bSuccess = false;
// Your unmanaged pointer.
IntPtr pUnmanagedBytes = new IntPtr(0);
int nLength;
nLength = Convert.ToInt32(fs.Length);
// Read the contents of the file into the array.
bytes = br.ReadBytes(nLength);
// Allocate some unmanaged memory for those bytes.
pUnmanagedBytes = Marshal.AllocCoTaskMem(nLength);
// Copy the managed byte array into the unmanaged array.
Marshal.Copy(bytes, 0, pUnmanagedBytes, nLength);
// Send the unmanaged bytes to the printer.
bSuccess = SendBytesToPrinter(szPrinterName, pUnmanagedBytes, nLength);
// Free the unmanaged memory that you allocated earlier.
Marshal.FreeCoTaskMem(pUnmanagedBytes);
return bSuccess;
}
public static bool SendStringToPrinter(string szPrinterName, string szString)
{
IntPtr pBytes;
Int32 dwCount;
// How many characters are in the string?
dwCount = szString.Length;
// Assume that the printer is expecting ANSI text, and then convert
// the string to ANSI text.
pBytes = Marshal.StringToCoTaskMemAnsi(szString);
// Send the converted ANSI string to the printer.
SendBytesToPrinter(szPrinterName, pBytes, dwCount);
Marshal.FreeCoTaskMem(pBytes);
return true;
}
}
//Class 4: Class that handles printing pdf
public class PrintingPDFData
{
public void SilentPrintPdf(string PdfFileName, string PrinterName)
{
try
{
PrintPdf.SendFileToPrinter(PrinterName, PdfFileName);
}
catch (Exception ex)
{
}
}
}
Please let me know, if any further clarification is needed.
Thanks in advance.
I believe the device mode settings are unavailable when printing a high-level document using the Win32 winspool API (if I am interpreting the documentation correctly). According to MSDN:
The DEVMODE settings defined in the PRINTER_DEFAULTS structure of the pDefault parameter are not used when the value of the pDatatype member of the DOC_INFO_1 structure that was passed in the pDocInfo parameter of the StartDocPrinter call is "RAW". When a high-level document (such as an Adobe PDF or Microsoft Word file) or other printer data (such PCL, PS, or HPGL) is sent directly to a printer with pDatatype set to "RAW", the document must fully describe the DEVMODE-style print job settings in the language understood by the hardware.
https://msdn.microsoft.com/en-us/library/windows/desktop/dd162751%28v=vs.85%29.aspx
You are using "RAW" to send your PDF bytes to the printer, which is the scenario described here.
i have a standalone public kiosk pc that startups automatically everyday. It is connected to a HD TV and sometimes it is not detected. i have to personally go down to the PC, go to Screen Resolution and press Detect which it works.
my question is how do i know if the monitor i want it to display is connected properly in code?
thanks
I did manage to find some code on the MSDN site where a user is using code to determine which type of monitor is connected. I hope this may be a good starting point for you.
using System;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interop;
namespace CRT
{
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class Window1 : Window
{
public class NativeMethods
{
[DllImport("user32.dll", EntryPoint = "MonitorFromWindow", SetLastError = true)]
public static extern IntPtr MonitorFromWindow(
[In] IntPtr hwnd, uint dwFlags);
[DllImport("dxva2.dll", EntryPoint = "GetNumberOfPhysicalMonitorsFromHMONITOR", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetNumberOfPhysicalMonitorsFromHMONITOR(
IntPtr hMonitor, ref uint pdwNumberOfPhysicalMonitors);
[DllImport("dxva2.dll", EntryPoint = "GetPhysicalMonitorsFromHMONITOR", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetPhysicalMonitorsFromHMONITOR(
IntPtr hMonitor,
uint dwPhysicalMonitorArraySize,
[Out] NativeStructures.PHYSICAL_MONITOR[] pPhysicalMonitorArray);
[DllImport("dxva2.dll", EntryPoint = "DestroyPhysicalMonitors", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool DestroyPhysicalMonitors(
uint dwPhysicalMonitorArraySize, [Out] NativeStructures.PHYSICAL_MONITOR[] pPhysicalMonitorArray);
[DllImport("dxva2.dll", EntryPoint = "GetMonitorTechnologyType", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetMonitorTechnologyType(
IntPtr hMonitor, ref NativeStructures.MC_DISPLAY_TECHNOLOGY_TYPE pdtyDisplayTechnologyType);
[DllImport("dxva2.dll", EntryPoint = "GetMonitorCapabilities", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetMonitorCapabilities(
IntPtr hMonitor, ref uint pdwMonitorCapabilities, ref uint pdwSupportedColorTemperatures);
}
public class NativeConstants
{
public const int MONITOR_DEFAULTTOPRIMARY = 1;
public const int MONITOR_DEFAULTTONEAREST = 2;
public const int MONITOR_DEFAULTTONULL = 0;
}
public class NativeStructures
{
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct PHYSICAL_MONITOR
{
public IntPtr hPhysicalMonitor;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)] public string szPhysicalMonitorDescription;
}
public enum MC_DISPLAY_TECHNOLOGY_TYPE
{
MC_SHADOW_MASK_CATHODE_RAY_TUBE,
MC_APERTURE_GRILL_CATHODE_RAY_TUBE,
MC_THIN_FILM_TRANSISTOR,
MC_LIQUID_CRYSTAL_ON_SILICON,
MC_PLASMA,
MC_ORGANIC_LIGHT_EMITTING_DIODE,
MC_ELECTROLUMINESCENT,
MC_MICROELECTROMECHANICAL,
MC_FIELD_EMISSION_DEVICE,
}
}
public Window1() { InitializeComponent(); }
private void Button_Click(object sender, RoutedEventArgs e)
{
WindowInteropHelper helper = new WindowInteropHelper(this);
IntPtr hMonitor = NativeMethods.MonitorFromWindow(helper.Handle, NativeConstants.MONITOR_DEFAULTTOPRIMARY);
int lastWin32Error = Marshal.GetLastWin32Error();
uint pdwNumberOfPhysicalMonitors = 0u;
bool numberOfPhysicalMonitorsFromHmonitor = NativeMethods.GetNumberOfPhysicalMonitorsFromHMONITOR(
hMonitor, ref pdwNumberOfPhysicalMonitors);
lastWin32Error = Marshal.GetLastWin32Error();
NativeStructures.PHYSICAL_MONITOR[] pPhysicalMonitorArray =
new NativeStructures.PHYSICAL_MONITOR[pdwNumberOfPhysicalMonitors];
bool physicalMonitorsFromHmonitor = NativeMethods.GetPhysicalMonitorsFromHMONITOR(
hMonitor, pdwNumberOfPhysicalMonitors, pPhysicalMonitorArray);
lastWin32Error = Marshal.GetLastWin32Error();
uint pdwMonitorCapabilities = 0u;
uint pdwSupportedColorTemperatures = 0u;
var monitorCapabilities = NativeMethods.GetMonitorCapabilities(
pPhysicalMonitorArray[0].hPhysicalMonitor, ref pdwMonitorCapabilities, ref pdwSupportedColorTemperatures);
lastWin32Error = Marshal.GetLastWin32Error();
NativeStructures.MC_DISPLAY_TECHNOLOGY_TYPE type =
NativeStructures.MC_DISPLAY_TECHNOLOGY_TYPE.MC_SHADOW_MASK_CATHODE_RAY_TUBE;
var monitorTechnologyType = NativeMethods.GetMonitorTechnologyType(
pPhysicalMonitorArray[0].hPhysicalMonitor, ref type);
lastWin32Error = Marshal.GetLastWin32Error();
var destroyPhysicalMonitors = NativeMethods.DestroyPhysicalMonitors(
pdwNumberOfPhysicalMonitors, pPhysicalMonitorArray);
lastWin32Error = Marshal.GetLastWin32Error();
this.lbl.Content = type;
}
}
}
I would like to add a string resource to an executable file programmatically. Just for example purposes, let's say I am trying to add a string named "String SO" which holds the value of "stringVal"
If this helps anyone - if I were to do this through VS.net I could just right click on my Project => Resources => Add New String Resource etc..
I am using the following Win32 API's:
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr BeginUpdateResource(string pFileName,
[MarshalAs(UnmanagedType.Bool)]bool bDeleteExistingResources);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool UpdateResource(IntPtr hUpdate, uint lpType, uint lpName, ushort wLanguage, byte[] lpData, uint cbData);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool EndUpdateResource(IntPtr hUpdate, bool fDiscard);
So, I have found a couple of pages online but none of them seem to help me in what I am trying to do. If any of you are able to find anything I would be very grateful.
Otherwise, I would greatly appreciate any snippets that may help.
Thank you,
Evan
There is a very helpful library for many resource-tasks at github.
Many classes and function do wrap those window-api-calls around UpdateResource(...), etc.
Hope that helps.
I'm injecting an application byte[] as a resource to execute it on runtime. Here's my piece of code, hope it helps:
class AddResource
{
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool UpdateResource(IntPtr hUpdate, string lpType, string lpName, ushort wLanguage, IntPtr lpData, uint cbData);
[DllImport("kernel32.dll", SetLastError = true)]
static extern IntPtr BeginUpdateResource(string pFileName,
[MarshalAs(UnmanagedType.Bool)]bool bDeleteExistingResources);
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool EndUpdateResource(IntPtr hUpdate, bool fDiscard);
private static IntPtr ToPtr(object data)
{
GCHandle h = GCHandle.Alloc(data, GCHandleType.Pinned);
IntPtr ptr;
try
{
ptr = h.AddrOfPinnedObject();
}
finally
{
h.Free();
}
return ptr;
}
public static bool InjectResource(string filename, byte[] bytes, string resourceName)
{
try
{
IntPtr handle = BeginUpdateResource(filename, false);
byte[] file1 = bytes;
IntPtr fileptr = ToPtr(file1);
bool res = UpdateResource(handle, resourceName,
//"RT_RCDATA",
"0", 0, fileptr, Convert.ToUInt32(file1.Length));
EndUpdateResource(handle, false);
}
catch
{
return false;
}
return true;
}
public static void CopyStream(Stream input, Stream output,long sz)
{
// Insert null checking here for production
byte[] buffer = new byte[sz];
int bytesRead;
while ((bytesRead = input.Read(buffer, 0, buffer.Length)) > 0)
{
output.Write(buffer, 0, bytesRead);
}
}
}
Here is how I use it:
using (Stream input = Assembly.GetExecutingAssembly().GetManifestResourceStream("AppLicensing.Resources.SAppStarter.exe"))
using (Stream output = File.Create(outputFilePath))
{
long sz = input.Length;
AddResource.CopyStream(input, output, sz);
}
//inject crypted bytes
AddResource.InjectResource(outputFilePath, Encryptor.cryptedbytes, "RT_RCDATA");
And here is how I extract the resource (notice the "RT_RCDATA" -> that s the name of the resource):
class ReadResource
{
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr GetModuleHandle(string lpModuleName);
[DllImport("kernel32.dll", SetLastError = true)]
static extern IntPtr FindResource(IntPtr hModule, string lpName, string lpType);
[DllImport("Kernel32.dll", EntryPoint = "SizeofResource", SetLastError = true)]
private static extern uint SizeofResource(IntPtr hModule, IntPtr hResource);
[DllImport("Kernel32.dll", EntryPoint = "LoadResource", SetLastError = true)]
private static extern IntPtr LoadResource(IntPtr hModule, IntPtr hResource);
public static byte[] GetFromResource(String resourceName)
{
try
{
IntPtr hModule = GetModuleHandle(System.Diagnostics.Process.GetCurrentProcess().MainModule.ModuleName);
IntPtr loc = FindResource(hModule, "0", resourceName);
uint size = SizeofResource(hModule, loc);
IntPtr x = LoadResource(hModule, loc);
byte[] bPtr = new byte[size];
Marshal.Copy(x, bPtr, 0, (int)(size));
return bPtr;
}
catch (Exception e)
{
System.Windows.Forms.MessageBox.Show(e.ToString());
System.Environment.Exit(0);
return null;
}
}
}
byte[] encryptedData = ReadResource.GetFromResource("RT_RCDATA");
The code gets a bit messy... hope this helps.
Although the author is dealing with his own issue right now, the SO question UpdateResource function fails has code snippet for using these calls.
The code from Samson work with String lpType, that mean you can't actually add RT_RCDATA resource either reading from it, it's only create and read lpType named "RT_RCDATA" only. If you want it to read real RT data you'll need to modify lpType from string to uint and this is RT API table:
private const uint RT_CURSOR = 0x00000001;
private const uint RT_BITMAP = 0x00000002;
private const uint RT_ICON = 0x00000003;
private const uint RT_MENU = 0x00000004;
private const uint RT_DIALOG = 0x00000005;
private const uint RT_STRING = 0x00000006;
private const uint RT_FONTDIR = 0x00000007;
private const uint RT_FONT = 0x00000008;
private const uint RT_ACCELERATOR = 0x00000009;
private const uint RT_RCDATA = 0x0000000a;
private const uint RT_MESSAGETABLE = 0x0000000b;
C# static constructor and GetVersion() any suggestions?
Hi,
I have defined struct like this in separate file OSVERSIONINFO.cs like this:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct OSVERSIONINFO
{
public static int SizeOf
{
get
{
return Marshal.SizeOf (typeof(OSVERSIONINFO));
}
}
public uint dwOSVersionInfoSize;
public uint dwMajorVersion;
public uint dwMinorVersion;
public uint dwBuildNumber;
public uint dwPlatformId;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
public string szCSDVersion;
}
Also I have this file OS.cs in which I have defined the following class:
public static class OS
{
static OS ()
{
OSVERSIONINFO info = new OSVERSIONINFO();
info.dwOSVersionInfoSize = (uint)OSVERSIONINFO.SizeOf;
if (!OS.GetVersion(ref info))
{
Console.WriteLine("Error!!!");
}
}
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetVersion (ref OSVERSIONINFO lpVersionInfo);
}
Way in static constructor of OS class population of info (instance of OSVERSIONINFO struct) fails?
If I call OS.GetVersion in other palce (not OS class) every thing is OK?
You should use the Environment.OSVersion.Platform property instead.
To answer the question, you need to call GetVersionEx.