Does PrinterSettings.GetHdevmode() have a bug? - c#

I would like to be able to change the printer properties without bringing up the printer properties window...
Using the DocumentProperties (imported from winspool.drv) function has so far failed, because while it is easy to suppress the dialog from showing up, it seems that the value returned by PrinterSettings.GetHdevmode() is not reflecting the PrinterSettings that is calling it, but instead the value from the previous printer properties returning OK. For example, this gives me the previous (wrong) values from the last call to the properties, instead of the values it should have from the PrinterSettings object:
IntPtr hdevmode = PrinterSettings.GetHdevmode(PrinterSettings.DefaultPageSettings);
PrinterSettings.SetHdevmode(hdevmode);
PrinterSettings.DefaultPageSettings.SetHdevmode(hdevmode);
So does GetHdevmode have a bug or is this what its supposed to do? Is there a C# work around for this or does anyone even have any information about it? I have been hard pressed even to find info on the topic.
Thanks in advance for any insight.
EDIT:
I didn't want to make this too personal of a problem, but hopefully having all the info in this case can provide an answer that is a useful solution for others too.
Here is a C++ DLL I have written in order to have a workaround for this issue. Its not currently working - it changes other memory such as copies, and doesn't succeed in changing the "underlying" papersize. I thought all I needed to do was specify the out buffer flag in order to make the changes?
extern "C" __declspec(dllexport) DEVMODE* __stdcall GetRealHDevMode(int width, int height, char *printerName, DEVMODE* inDevMode)
{
//declare handles and variables
HANDLE printerHandle;
LPHANDLE printerHandlePointer(&printerHandle);
//get printer handle pointer
OpenPrinter((LPWSTR)printerName, printerHandlePointer, NULL);
//Get size needed for public and private devmode data and declare devmode structure
size_t devmodeSize = DocumentProperties(NULL, printerHandle, (LPWSTR)printerName, NULL, NULL, 0);
DEVMODE* devmode = reinterpret_cast<DEVMODE*>(new char[devmodeSize + sizeof(DEVMODE) + sizeof(inDevMode->dmDriverExtra)]);
//lock memory
GlobalLock(devmode);
//fill the out buffer
DocumentProperties(NULL, printerHandle, (LPWSTR)printerName, devmode, NULL, DM_OUT_BUFFER);
//change the values as required
devmode->dmPaperWidth = width;
devmode->dmPaperLength = height;
devmode->dmPaperSize = DMPAPER_USER;
devmode->dmFields &= ~DM_PAPERSIZE;
devmode->dmFields &= ~DM_PAPERLENGTH;
devmode->dmFields &= ~DM_PAPERWIDTH;
devmode->dmFields |= (DM_PAPERSIZE | DM_PAPERLENGTH | DM_PAPERWIDTH);
//input flag on now to put the changes back in
DocumentProperties(NULL, printerHandle, (LPWSTR)printerName, devmode, devmode, DM_IN_BUFFER | DM_OUT_BUFFER);
//unlock memory
GlobalUnlock(devmode);
//return the devmode that was used to alter the settings
return devmode;
}
I figured the C++ code was enough to change the settings, so all I do in C# is this:
public PrinterSettings ChangePrinterProperties(PrinterSettings inPrinterSettings)
{
IntPtr TemphDevMode = inPrinterSettings.GetHdevmode(inPrinterSettings.DefaultPageSettings);
IntPtr hDevMode = GetRealHDevMode((int)(inPrinterSettings.DefaultPageSettings.PaperSize.Width * 2.54F),
(int)(inPrinterSettings.DefaultPageSettings.PaperSize.Height * 2.54F),
inPrinterSettings.PrinterName, TemphDevMode);
GlobalFree(hDevMode);
return inPrinterSettings;
}
UPDATE: Changed up the order a bit with dmPaperSize and dmFields. Improved results; not quite there yet.
UPDATE 2: Okay, I found a microsoft page that says the documentation is wrong. MSDN says to set dmPaperSize to 0 when you want to specify width and height whereas the Microsoft Support correction says to set it to DMPAPER_USER. http://support.microsoft.com/kb/108924

There are 2 problems with the way you are specifying the paper size in the DEVMODE:
(1) If you specify DM_PAPERWIDTH or DM_PAPERLENGTH or both, you MUST NOT also set the DM_PAPERSIZE bit. It depends on the printer driver, but many drivers will ignore DM_PAPERLENGTH/WIDTH in the above code.
(2) Many drivers don't support DM_PAPERLENGTH/WIDTH at all. With such drivers, you simply cannot set the paper size like you are trying to do above. You can only select one of the predefined dmPaperSizes.
You can use DeviceCapabilities(DC_FIELDS) to determine if your driver supports DM_PAPERLENGTH/WIDTH.
You can use DeviceCapabilities(DC_PAPERS) to enumerate the allowable dmPaperSizes.

Related

How to detect keyboard input code page

I need to detect the code page the keyboard input is using while user is entering data into application fields.
I tried to use System.Text.Encoding.Default.CodePage; but it gives the code page of what is configured in regional settings.
Then i thought Console.InputEncoding.CodePage; could work, but still, code page is the same as with the above example.
The problem is, user may have a Cyrilic (Windows-1251) code page because of the regional settings, but he may wish to use a different input language. The data user enters is then saved to a file, and that file can be opened in a system which has a different regional settings. Along with text, i am saving code page number, so my app can load the file and display the text correctly. I cannot use Unicode for cross compatibility with a different app which does not support unicode.
Disclaimer: I'm not an expert on the globalization side of the .NET Framework, so it might wrap equivalent functionality somewhere or another. If so, and you can locate it, great—use that instead. I thought maybe Globalization.CultureInfo.CurrentCulture would return the information we're looking for, but alas it does not; instead, it appears to return the system default culture even if the keyboard layout is changed. I stopped investigating. The described approach is guaranteed to work, albeit with a little extra code.
To determine the code page associated with a particular keyboard layout, you can call the Win32 GetLocaleInfo function, specifying the language ID associated with the current keyboard layout. You request this using the LOCALE_IDEFAULTANSICODEPAGE constant.
To call these functions from a .NET application, you will need to use P/Invoke. You'll also need to define function equivalents for some essential macros.
const int LOCALE_IDEFAULTANSICODEPAGE = 0x1004;
const int LOCALE_RETURN_NUMBER = 0x20000000;
const int SORT_DEFAULT = 0x0;
static int LOWORD(IntPtr val)
{
return (unchecked((int)(long)val)) & 0xFFFF;
}
static int MAKELCID(int languageID, int sortID)
{
return ((0xFFFF & languageID) | (((0x000F) & sortID) << 16));
}
static int MAKELANGID(int primaryLang, int subLang)
{
return ((((ushort)(subLang)) << 10) | (ushort)(primaryLang));
}
[DllImport("kernel32.dll", SetLastError = true)]
static extern int GetLocaleInfo(int locale,
int lcType,
out uint lpLCData,
int cchData);
[DllImport("user32.dll")]
static extern IntPtr GetKeyboardLayout(uint idThread);
Use it like this:
// Get the keyboard layout for the current thread.
IntPtr keybdLayout = GetKeyboardLayout(0);
// Extract the language ID from it, contained in its low-order word.
int langID = LOWORD(keybdLayout);
// Call the GetLocaleInfo function to retrieve the default ANSI code page
// associated with that language ID.
uint codePage = 0;
GetLocaleInfo(MAKELCID(langID, SORT_DEFAULT),
LOCALE_IDEFAULTANSICODEPAGE | LOCALE_RETURN_NUMBER,
out codePage,
Marshal.SizeOf(codePage));
When tested with a US English keyboard layout, codePage is 1252. After switching to a Greek keyboard layout, codePage is 1253. Likewise, Turkish returns 1254, and the various cyrillic languages return 1251. Exactly as documented.
It is worth noting that, in the linked documentation, these API functions are indicated as having been superseded. With modern versions of Windows, Microsoft has moved to named locales, first because they were out of room for numeric IDs, and second to enable support for custom locales. But you will need to use the old functions for what you're doing. Modern Windows applications don't use ANSI code pages, either.
However, you do need to be aware of this fact, because it may come back to bite you. There are keyboard layouts that do not have an associated ANSI code page. For these, only Unicode can be used. The above code will return CP_ACP (which is equivalent to the numeric value 0). Handling that is up to you. You will either need to display an error, or save the file as Unicode (albeit breaking the other application, but complying with user expectations).
Finally, I must point out that if you cache the codePage value, it may become stale since the user can change the keyboard layout at any time. It is probably easiest just not to cache the value, determining it each time you perform a save. But if you want to cache it, you will need to handle the WM_INPUTLANGCHANGE message and update your cached value in response.

Getting EZTwain BARCODE_Recognize return value of -4

I'm trying to use the EZTwain scanning library to retrieve barcodes from a scanned image, and I'm getting a return value of -4 when I try to call EZTwain.BARCODE_Recognize(IntPtr, int, int), which there is no description for in the EZTwain user guide.
In the EZTwain user guide, it lists some return values that are used as error codes as you can see here.
BARCODE_Recognize
int BARCODE_Recognize(HDIB hdib, int nMaxCount, int nType)
Find and recognize barcodes in the given image.
Don't look for more than nMaxCount barcodes (-1 means 'any number').
Expect barcodes of the specified type (-1 means 'any supported type')
You can add or 'or' together barcode types, to tell the recognizer to look for more
than one symbology. Return values:
>0 n barcodes found
0 no barcodes found
-1 barcode services not available.
-3 invalid or null image
There is no -4 return value listed, and I don't know where else to look because the user guide is the only thing that I know that is available for documentation on this library.
This is the code I'm using when I get the code of -4 returned.
I do call EZTwain_SetVendorKey but left that out for obvious reasons.
I'm wondering if it has something to do with the IntPtr I'm passing? The documentation says Call BARCODE_Recognize, passing it the handle of the image to search, the
maximum number of barcode patches to find, and a mask of the barcode
types (symbologies) to look for. If this function finds any barcodes, it returns
a positive integer = the count of symbols (barcodes) found.
I pass the IntPtr i create using the image, and -1, -1 to find all barcodes in the image, using all of the barcode types.
public static string GetBarcode(Bitmap image, out BarcodeType barcodeType, int percentThatCanBeNonWhitish = 2, int pixelTolerance = 10)
{
// initialize barcodeType to appease the compiler
barcodeType = BarcodeType.NotBarcode;
BitmapData bd = image.LockBits(new Rectangle(0, 0, image.Width, image.Height),
ImageLockMode.ReadWrite,
image.PixelFormat);
List<string> barcodes = new List<string>();
EZTwain.BARCODE_SelectEngine(EZTwain.EZBAR_ENGINE_DOSADI);
EZTwain.BARCODE_SetDirectionFlags(EZTwain.EZBAR_HORIZONTAL | EZTwain.EZBAR_VERTICAL);
IntPtr imgPtr = image.GetHbitmap();
if (EZTwain.DIB_IsBlank(imgPtr, .002) == true)
{
// Do nothing, the page is blank
}
else if (EZTwain.BARCODE_IsEngineAvailable(EZTwain.EZBAR_ENGINE_DOSADI))
{
int count;
count = EZTwain.BARCODE_Recognize(imgPtr, -1, -1);
for (int i = 0; i < count; i++)
{
barcodes.Add(EZTwain.BARCODE_Text(i));
}
}
if (barcodes.Count != 0)
{
string barcode = barcodes[0];
// sets the type to coversheet if it is blank, else it uses ProcessBarcodeType()
barcodeType = image.IsBlank(percentThatCanBeNonWhitish, pixelTolerance) ? BarcodeType.CoversheetBarcode : ProcessBarcodeType(barcode);
return barcode;
}
else
{
return null;
}
}
Does anyone know what this error code means? And if so, where did you discover what it meant?
Zack, you probably already figured this out. For future readers:
There's a newer edition of the EZTwain toolkit, in which that -4 return code is documented.
But, it just says "memory error (low memory?)" - still not very helpful in this case!
Atalasoft makes the updated toolkit available on eztwain.com, and any valid EZTwain Pro 3 license will continue to work with those later 3.x versions of the toolkit.
That image parameter EZTwain expects (pretty much everywhere in its API) is not just an 'image handle' but specifically and precisely an HDIB, which is a Global Handle to a block of memory containing a packed DIB = Device Independent Bitmap. These are all ancient things from the Win32 API, and only that specific combination is understandable to EZTwain. Don't be confused by seeing these represented as IntPtr's - IntPtr's can hold all kinds of different underlying native objects.
I would guess that GetHBitmap does not return an HDIB, but an object that the old Windows API called an HBITMAP, which sounds confusingly similar and is completely non-interchangeable with an HDIB.
EZTwain includes a number of functions for converting to and from other kinds of in-memory image formats, such as HBITMAP. I even think in recent editions of the toolkit, the eztwain.cs file has some functions that go to and from System.Drawing.Bitmap <==> HDIB (represented as System.IntPtr).
That error just means that there was some sort of unspecified memory error.

How to get the display depth of the screen?

How do I get the colors of the screen? I'd like to know if the screen is set to True Colors (32 bits) or High Colors (16 bits).
I've been Googling and thought it would be easy to find, but I can't find anything (maybe I have to work on my Google skills?)
You could use the BitsPerPixel property:
int bitsPerPixel = Screen.PrimaryScreen.BitsPerPixel;
GetDisplayMode function in DirectX9 API can help you,I guess.You will simply write two statements after including necessary headers;
D3DDISPLAYMODE currentMode;
pDevice->GetDisplayMode(0,&currentMode);
After,you can check depth value like;
currentMode->Format
I'm sure there are other WinAPI functions,but thats all I know.
EDIT: Did not see you mentioned Winforms. This is useless then

Get large icon from file extension

There are several places that talk about how to get an icon from a file extension such as this one and this other one. After several hours of playing around with this kind of projects I have managed to build something like:
private void addButton_Click(object sender, System.EventArgs e)
{
System.Drawing.Icon temp = IconReader.GetFileIcon(".cs", IconReader.IconSize.Large, false);
pictureBox1.Image = temp.ToBitmap();
}
the execution of that button gets me:
but I am trying to actually get the large icon. Note how the icons on windows are much bigger:
How could I get that icon instead of the smaller one. I have spend so much time changing the other programs. Moreover I will like to make it work with wpf and most of the examples are with windows forms. I would appreciate if I can get an example of how to extract a files icon instead of modifying and entire project. If that is not possible that would still be very helpful and I will appreciate. It's just that I am not that good of a programmer and it took me a lot of time to modify the other examples.
here's a solution http://www.codeproject.com/KB/WPF/filetoiconconverter.aspx
The code you're referencing only uses 2 sizes:
public const uint SHGFI_LARGEICON = 0x000000000; // get large icon
public const uint SHGFI_SMALLICON = 0x000000001; // get small icon
to get the extra_large size, you need to add your own define (and add another enum for calling functions):
public const uint SHGFI_EXTRALARGEICON = 0x000000002; // get extra large icon
or if you just want a quick fix, replace 0x000000000 with 0x000000002 in the original large definition.
Here are all icon sizes:
http://msdn.microsoft.com/en-us/library/windows/desktop/bb762185%28v=vs.85%29.aspx
You cannot make ShGetFileInfo return ExtraLarge and Jumbo icons.
Instead,
1) Call ShGetFileInfo with the SysIconIndex ( 0x000004000) flag  
2) Get the System Imagelist with the proper size using ShGetImageList
3) Get the HICON using ImageList_GetIcon and the .iIcon value from SHFILEINFO.

How do I use GetThemeFont to read font data?

I am trying to use GetThemeFont to read the font data from a visual style, but I can't seem to get it to return anything. Here is how I'm using it:
IntPtr h = OpenThemeData(this.Handle, "Button");
LOGFONT font = new LOGFONT();
int r = GetThemeFont(h, IntPtr.Zero, 1, 1, 210, out font);
CloseThemeData(h);
The return value is always "-2147023728" and font is always null. The place where I put 210 (for TMT_FONT), I'm not really sure what to use there. Any help on this would be much appreciated.
-2147023728 is the decimal equivalent of 0x80070490L, which is the value assigned to E_PROP_ID_UNSUPPORTED. From the "Remarks" section of the documentation of of GetThemeFont:
If the property is not supported for
the specified part and state,
E_PROP_ID_UNSUPPORTED may be returned.
The property is not supported for the part and state combination that you are passing in.
See this gist https://gist.github.com/1219126
You'll never get back anything more than null, or the not supported result. Additionally, using TMT_FONT (210) will drop a nice memory error and clear the stack (for who knows what reason) if you attempt to use it within a managed app.
The internals of either VisualStyleRenderer.GetFont and/or GetThemeFont are inherently flawed. I'm trying to work with some people at MS to address this and provide either a workaround, more documentation or some acknowledgement that this is broken.

Categories

Resources