|
Yeah I believe so. I thought of it earlier and as a try, I changed the resolution of the image to 300. After printing, I realised the print output had become very small so I stopped using that approach. But if the cause has got something to do with the resolution, then It means that I have to make some calculations that will give the size of the print output the same as the size of the Panel control when I change the resolution. I will try that and see. Thanks.
|
|
|
|
|
Yep, you need to upscale the bitmap size by printerDPI/screenDPI . Both are available in their respective Graphics objects; the screen DPI is also available in other classes (SystemInformation, Screen, not sure). And they come as pairs (dpiX,dpiY), I have never seen those numbers not being equal though.
|
|
|
|
|
Luc, changing the resolution keeps changing the size of the print output but it's still not looking good, just the same. Don't know if I got it wrong. This is what I did:
bmp.SetResolution(ppea.Graphics.DpiX, ppea.Graphics.DpiX);
bmp is the Bitmap object, and ppea is the PrintPageEventArgs object which has the printer settings. You've already said you have not used them before, but do you know any link to tutorials or resources that can help? I don't know what to do again.
|
|
|
|
|
I hope you did increase the bitmap size by the DPI ration, and then applied SetResolution before executing Griff's panel1.DrawToBitmap(...) . I expect it to work properly, but then printing is a complex topic.
Warning: when you get it to work, it will show what the screen shows, even when the printer page is larger. There are ways to print all the content of panning Controls (ListBox, RichTextBox, DataGridView, ...) , that involves a fair amount of P/Invoke and Windows magic.
FWIW: when I want full control of the printed output, I simply avoid all WinForms Controls, and construct my own page control using Graphics.DrawXyz into a PrintDocument. My Sokoban article holds an example.
|
|
|
|
|
As Luc says - it is printing at screen resolution. Personally, I find it pretty useless - it is normally easier to get decent quality by setting up a PrintDocument[^] object and printing the data properly yourself. It may take a little longer to start with, but it is quicker, prettier and more flexible in the long run!
Ideological Purity is no substitute for being able to stick your thumb down a pipe to stop the water
|
|
|
|
|
Thanks Griff! Is it possible to avoid creating the image from the Panel control and setting up the PrintDocument to get the same output?
|
|
|
|
|
Yes - just loop through the panel Controls in the PrintDocument.PrintPage Event[^] and position them relative to the panel size / page size. If you just draw the Text (using the Graphics.DrawString method) then you will be able to see where they are.
I find it is handy to have a PDF printer driver installed for print debugging - it is a lot quieter, cheaper and quicker than a real printer, particularly in the early stages!
Ideological Purity is no substitute for being able to stick your thumb down a pipe to stop the water
|
|
|
|
|
Greeting Gurus,
I have been going around in circles trying to get a value from HKLM\Security\Policy\PolEdtEv and would like to ask for your help please. The key has a "Default" name and a REG_NONE type.
The Goal:
Retrieve the value (I'm pretty sure it's hex) which has a default of
00 fa 07 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 09 00 00 00
If the above value is retrieved, change it to
01 fa 07 00 03 00 00 00 03 00 00 00 03 00 00 00 03 00 00 00 03 00 00 00 03 00 00 00 03 00 00 00 03 00 00 00 03 00 00 00 09 00 00 00
This effectively enables Success & Failure audits under Local Policies\Audit Policy
The Problem:
According to MSoft…"GetValue does not support reading values of type REG_NONE or REG_LINK. In both cases, the default value (Nothing) is returned instead of the actual value."
this means you can't use:
RegistryKey RegKey = Registry.LocalMachine;
RegKeyIN = RegKey.OpenSubKey("SECURITY\\Policy\\PolEdtEv");
Object Value = RegKey.GetValue("SOMEVALUE");
…so you need to use P/Invoke, and that’s where I’m stuck
The Solution:
I understand you need to use
[DllImport("advapi32.dll", EntryPoint = "RegOpenKeyEx")]
public static extern int RegOpenKeyEx(UIntPtr hKey, string lpSubKey, int ulOptions, int samDesired, out string phkResult); to open the key and then
[DllImport("advapi32.dll",EntryPoint = "RegQueryValueEx")]
public static extern int RegQueryValueEx(UIntPtr hKey,string lpValueName,int lpReserved,out uint lpType,StringBuilder lpData,ref int lpcbData); to get the value.
Despite my best efforts and dozens of hours going through many forum examples I just can't get my code to work. I know the following code is a mess but having been over and over and over so many suggested methods and examples I am now at a complete loss. My code (please don't laugh)...
using Microsoft.Win32;
using System;
using System.Runtime.InteropServices;
using System.Text;
namespace ConsoleApplication6
{
class Program
{
public static readonly UIntPtr HKEY_LOCAL_MACHINE = new UIntPtr(0x80000002u);
public const string lpSubKey = "SECURITY\\Policy\\PolEdtEv";
public const int KEY_READ = 0x20019;
[DllImport("advapi32.dll", EntryPoint = "RegOpenKeyEx")]
public static extern int RegOpenKeyEx(UIntPtr hKey, string lpSubKey, int ulOptions, int samDesired, out string phkResult);
[DllImport("advapi32.dll",EntryPoint = "RegQueryValueEx")]
public static extern int RegQueryValueEx(UIntPtr hKey,string lpValueName,int lpReserved,out uint lpType,StringBuilder lpData,ref int lpcbData);
private static int ReadRegKey(UIntPtr HKEY_LOCAL_MACHINE, string lpSubKey, string valueName)
{
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, lpSubKey, 0, KEY_READ, out valueName ) == 0)
{
Console.WriteLine(valueName);
int size = 1024;
uint type;
string keyValue = null;
StringBuilder keyBuffer = new StringBuilder();
if (RegQueryValueEx(HKEY_LOCAL_MACHINE, valueName, 0, out type, keyBuffer, ref size) == 0)
keyValue = keyBuffer.ToString();
Console.WriteLine(keyValue);
return (keyValue);
}
}
}
}
The above returns "not all code paths return a value" which is the fewest errors I have got so far.
Could you help please?
|
|
|
|
|
CCodeNewbie wrote: not all code paths return a value
You need to add a return statement after the if block, for the situation where the call to RegOpenKeyEx() does not succeed.
Unrequited desire is character building. OriginalGriff
I'm sitting here giving you a standing ovation - Len Goodman
|
|
|
|
|
In RegQueryValueEx[^] lpData should be A pointer to a buffer that receives the value's data. They do mean a sufficiently large buffer, not a zero-length one, so you need to give your StringBuilder some Capacity .
|
|
|
|
|
@Luc - is this what you mean
StringBuilder keyBuffer = new StringBuilder(100);
@Richard - ok did this (following an example on pinvoke.net)
{
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, lpSubKey, 0, KEY_READ, out valueName ) == 0)
{
Console.WriteLine(valueName);
int size = 1024;
uint type;
string keyValue = null;
StringBuilder keyBuffer = new StringBuilder(100);
if (RegQueryValueEx(HKEY_LOCAL_MACHINE, valueName, 0, out type, keyBuffer, ref size) == 0)
keyValue = keyBuffer.ToString();
Console.WriteLine(keyValue);
return (keyValue);
}
return (null);
}
now I get "cannot convert string to int" on "return(keyValue);" &
"cannot convert null to int because it is a value type" on "return(null)";
|
|
|
|
|
Your function definition is:
private static int ReadRegKey(UIntPtr HKEY_LOCAL_MACHINE, string lpSubKey, string valueName)
You cannot return a string when you have defined the return value as int .
You are also declaring a StringBuilder of 100 characters and then telling RegQueryValueEx() that it is 1024 long. However, this will still (probably) not work as I don't think you can write directly into a StringBuilder object. You should allocate a BYTE array sufficiently large to get the key value; since your value is not a string in the first place it seems rather pointless to use string types. I suggest reviewing the MSDN documentation for the RegGetValue()[^] function, to see how to find the length of the value data before extracting it.
Unrequited desire is character building. OriginalGriff
I'm sitting here giving you a standing ovation - Len Goodman
|
|
|
|
|
Richard MacCutchan wrote: I don't think you can write directly into a StringBuilder object
Yes you can. When the native world needs to read a string, pass a string; when it needs to write a string, pass a capacitized StringBuilder (together with its Capacity when a length parameter is also required), and upon return apply ToString() to it.
More info could be found here[^]/
|
|
|
|
|
Thanks for the information.
Unrequited desire is character building. OriginalGriff
I'm sitting here giving you a standing ovation - Len Goodman
|
|
|
|
|
Sorry Richard I don't understand what you mean. The documentation refers to using 0x00000001 for REG_NONE but where do I use that?
I have changed the code to "int keyValue = 0;" is this right? I have also changed "StringBuilder keyBuffer = new StringBuilder(1024);"
Please forgive me if I am being dense but I am in way over my head here and trying hard to understand.
|
|
|
|
|
CCodeNewbie wrote: I am in way over my head here
Try the following:
public static readonly UIntPtr HKEY_LOCAL_MACHINE = new UIntPtr(0x80000002u);
public const string lpSubKey = "SECURITY\\Policy\\PolEdtEv";
public const int KEY_READ = 0x20019;
[DllImport("advapi32.dll", EntryPoint = "RegOpenKeyEx")]
public static extern int RegOpenKeyEx(UIntPtr hKey, string lpSubKey, int ulOptions, int samDesired, out UIntPtr phkResult);
[DllImport("advapi32.dll", EntryPoint = "RegQueryValueEx")]
public static extern int RegQueryValueEx(UIntPtr hKey, string pSubKey, int reserved, out uint lpType, byte[] lpData, ref int lpcbData);
private static string ReadRegKey(UIntPtr HKEY_LOCAL_MACHINE, string lpSubKey, string valueName)
{
UIntPtr hKey;
string keyValue = null;
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, lpSubKey, 0, KEY_READ, out hKey) == 0)
{
Console.WriteLine(valueName);
int size = 1024;
uint type = 0;
byte[] keyBuffer = new byte[1024];
if (RegQueryValueEx(hKey, valueName, 0, out type, keyBuffer, ref size) == 0)
keyValue = keyBuffer.ToString();
Console.WriteLine(keyValue);
}
return keyValue;
}
I do not have the exact key on my system but in general this code should work. Also you may need to run with admin privilege as HKEY_LOCAL_MACHINE is a protected hive. You should also be aware that the key value you are extracting is not a printable string so you will probably see garbage on the screen from the final WriteLine() call.
Unrequited desire is character building. OriginalGriff
I'm sitting here giving you a standing ovation - Len Goodman
|
|
|
|
|
Looks fine at first glance, except for the final few lines of course; one really should see the byte array as binary data (output best in hex), not as a string which it isn't.
|
|
|
|
|
Yes, I could not recall how to convert the byte array, but I don't think it matters since the data OP is reading is pure binary. I also revisited this and did get it to work with StringBuilder - my bad. Thanks for the hints, I am beginning to understand P/Invoke a bit more.
Unrequited desire is character building. OriginalGriff
I'm sitting here giving you a standing ovation - Len Goodman
|
|
|
|
|
you really should not use (nor suggest the use of) text-related data types or methods for non-textual data. If it isn't text, use the appropriate value types, byte[] being a prime candidate.
Abusing strings may or may not work, and the outcome may well depend on the data itself: text oriented data and code is an open invitation for the system to step in and do all kinds of fancy things with byte sequences that look like a NewLine, a NULL, an accent+letter combination; even ASCII/Unicode conversions and Unicode normalization are possible. None of these can happen when you stay away from textual types for binary data.
string s="";
foreach(byte b in bytes) s+=" "+b.ToString("X2");
Console.WriteLine(s);
|
|
|
|
|
This was my suggestion to OP; did you mean to post this to me?
Unrequited desire is character building. OriginalGriff
I'm sitting here giving you a standing ovation - Len Goodman
|
|
|
|
|
Yes,
You did offer a reply that correctly used byte[] however
Richard MacCutchan wrote: and did get it to work with StringBuilder
from your earlier post made me tell you off StringBuilder.
|
|
|
|
|
Unrequited desire is character building. OriginalGriff
I'm sitting here giving you a standing ovation - Len Goodman
|
|
|
|
|
Hi Richard,
I tried your code but it errors "does not contain a static Main method". I changed it to "private static int ReadRegKey(UIntPtr HKEY_LOCAL_MACHINE, string lpSubKey, string valueName)" which gave me the error "Cannot covert 'string' to 'int' on "return keyValue".
I explicity declared "string keyValue = keyBuffer.ToString();" and got the "Embedded statement cannot be a declaration or labeled statement" so I removed 'string' from "string keyValue = keyBuffer.ToString();", bracketed the snippet & changed "private static int" back to "private static string" and am now back where I started "ReadKey...: not all code paths return a value" Aaargh!!!
On another note, and trying a different approach, I found that 'Default' key Names can be referred to as "" (open quotes,close quotes with no spaces). So using Luc's IntPtr suggestion I am trying
public static readonly UIntPtr HKEY_LOCAL_MACHINE = new UIntPtr(0x80000002u);
public const string lpSubKey = "SECURITY\\Policy\\PolEdtEv";
public const int KEY_READ = 0x20019;
public const uint REG_NONE = 0x00000001;
public const string valueName = "";
[DllImport("advapi32.dll", EntryPoint = "RegOpenKeyEx")]
public static extern int RegOpenKeyEx(UIntPtr hKey, string lpSubKey, int ulOptions, int samDesired, out UIntPtr phkResult);
[DllImport("advapi32.dll", EntryPoint = "RegQueryValueEx")]
public static extern int RegQueryValueEx(UIntPtr hKey, string pSubKey, int reserved, out uint lpType, IntPtr lpData, ref int lpcbData);
private static int ReadRegKey(UIntPtr HKEY_LOCAL_MACHINE, string lpSubKey, string valueName)
{
UIntPtr hKey;
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, lpSubKey, 0, KEY_READ, out hKey) == 0)
{
int size = 1024;
uint type = 0;
IntPtr [] keyBuffer = new IntPtr [1024];
if (RegQueryValueEx(hKey, valueName, 0, out type, keyBuffer, ref size) == 0)
{
int keyValue = keyBuffer;
Console.WriteLine(keyBuffer);
return keyValue;
}
}
}
but I am getting "cannot convert from 'System.IntPtr[]' to 'System.IntPtr'" on the line "if (RegQueryValueEx(hKey, valueName, 0, out type, keyBuffer, ref size) == 0)" and "Cannot implicitly convert type 'System.IntPtr[]' to 'int'" on the line "int keyValue = keyBuffer;"
I have tried replacing the 'IntPtrs' with 'ints' and with 'bytes []' but it makes no difference.
|
|
|
|
|
You seem to be getting yourself totally tied in knots by confusing the different types that represent values and objects; I don't understand why you changed my definition of byte[] to IntPtr [] , and then tried to convert the array into a simple int , which is, of course, impossible.
The code I gave you will work with the following slight changes. The ReadRegKey() method should be changed to return a byte array and the keyValue variable should be changed to a byte[] thus:
private static byte[] ReadRegKey(UIntPtr HKEY_LOCAL_MACHINE, string lpSubKey, string valueName)
{
UIntPtr hKey;
byte[] keyValue = null;
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, lpSubKey, 0, KEY_READ, out hKey) == 0)
{
Console.WriteLine(valueName);
int size = 1024;
uint type = 0;
byte[] keyBuffer = new byte[1024];
if (RegQueryValueEx(hKey, valueName, 0, out type, keyBuffer, ref size) == 0)
keyValue = keyBuffer;
}
return keyValue;
}
Remember that you may only return the type that you define for your method signature; anything else will be rejected by the compiler. Also, as I stated before, and Luc reminded me, the data you are reading is binsry values, not a string and so cannot be displayed by a simple Console.WriteLine() statement.
Unrequited desire is character building. OriginalGriff
I'm sitting here giving you a standing ovation - Len Goodman
|
|
|
|
|
Firstly, thank you for patience with a newbie who feels very much like a noob at the moment
Secondly, I don't really need to writeline the result, all I need to do is check if the value is not "01 fa 07 00 03 00 00 00 03 00 00 00 03 00 00 00 03 00 00 00 03 00 00 00 03 00 00 00 03 00 00 00 03 00 00 00 03 00 00 00 09 00 00 00" and set it to that if it isn't
Thirdly, and most frustratingly, building the code you kindly offered results in "does not contain a static 'Main' method suitable for an entry point", which is why I started down the path mentioned earlier.
|
|
|
|
|