|
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.
|
|
|
|
|
OK add the following just before the first DllImport statement:
static void Main(string[] args)
{
byte[] regkey = ReadRegKey(HKEY_LOCAL_MACHINE, lpSubKey, "");
}
In order to change the key value(s) you will need to modify the relevant bytes and use a call to RegSetValueEx() [^] to store the updated value, in a similar way to what you have done here.
I am not sure how you came to be working on such a complex application given your level of knowledge, but I would urge you to spend some good study time learning C# and general Windows application programming. A good start for C# and .NET would be .Net Book Zero[^] by Charles Petzold. There are also some good quick tutorials here[^] on MSDN, which is another resource that you should get familiar with.
Unrequited desire is character building. OriginalGriff
I'm sitting here giving you a standing ovation - Len Goodman
|
|
|
|
|
Richard, thank you.
I knew that Main had to go somewhere, I just wasn't sure where.
I know (having spent hours on pinvoke.net) that I need to use RegSetValueEx, but thanks.
I came to this following a desire to have an EventLog monitor which exports Security Events to a SQL db.
The Application & System logs work perfectly but the only reason that the Security log wasn't working was because Local Policies\Audit Policy wasn't configured for Success\Failure audits. Thus I needed a way to programmatically enable auditing. So here I am.
I have a 3-week holiday coming up soon. I'll spend it reading.
The problem I (and a whole bunch of other forum-posters by the looks of things) have is that the tuts are fine for the day-to-day stuff but when you need to go from Beginner to Novice or Novice to Intermediate the only real recourse is experience or the experience of others (read: forums).
There are, for example, dozens of sites with very detailed and informative instructions on using PInvoke for REG_SZ, DWORD & other types. I have yet to see one on getting "Default" i.e. "unknown" types and REG_NONE values (where the example actually works and some attention seeker isn't just posting garbage)
Anyhoo, rant complete, thanks muchly to you and Luc.
|
|
|
|
|