GDI printing not triggering printer - c#

I am writing a printing application in c#. In my application i done following steps
Created Device context (hdc)
Created and started Document StartDoc
Created and Started Page StartPage
Assigned images to context
called BitBlt and assigned conext to right hdc
EndPage(hdc)
EndDoc(hdc)
After all these steps, printing is not trigered. I want to ask that printing should be trigered itself
after all these steps or i need to trigger it by some other method?
Following is source code where CardPrinterLib is class in which i imported gdi32.dll and mapped its function
IntPtr hdc = IntPtr.Zero;
hdc = CardPrinterLib.CreateDC(null, ps.PrinterName, null, IntPtr.Zero);
CardPrinterLib.DOCINFO docinfo = new CardPrinterLib.DOCINFO();
docinfo.cbSize = Marshal.SizeOf(docinfo);
docinfo.pDocName = "PrinterTest";
docinfo.pOutputFile = null;
docinfo.pDataType = null;
docinfo.fwType = 0;
if (CardPrinterLib.StartDoc(hdc, ref docinfo) > 0)
{
if (CardPrinterLib.StartPage(hdc) > 0)
{
Bitmap bmpFrontCard = (Bitmap)imgFrontCard;
Graphics grFrontCard = this.CreateGraphics();
IntPtr dcFrontCard = grFrontCard.GetHdc();
IntPtr chdcFrontCard = CardPrinterLib.CreateCompatibleDC(dcFrontCard);
CardPrinterLib.SelectObject(chdcFrontCard, bmpFrontCard.GetHbitmap());
Graphics ghdcFrontCard = Graphics.FromHdc(chdcFrontCard);
grFrontCard.ReleaseHdc(dcFrontCard);
IntPtr hhdcFrontCard = ghdcFrontCard.GetHdc();
bool resultFrontCard = CardPrinterLib.BitBlt(hdc, 0, 0, 994, 624, hhdcFrontCard, 0, 0, 13369376);
CardPrinterLib.EndPage(hdc);
}
CardPrinterLib.EndDoc(hdc);
CardPrinterLib.DeleteDC(hdc);

Related

IntPtr - Memory & GDI leak [C#]

The Problem:
When taking Screenshots of a Screen (in a loop) I get a RAM and GDI leak.
private Bitmap GetSS(int ScreenWidth, int ScreenHeight, int ScreenWidthCut, int ScreenHeightCut)
{
int ScreenLocWidth = Screen.PrimaryScreen.Bounds.Width - ScreenWidth;
int ScreenLocHeight = Screen.PrimaryScreen.Bounds.Height - ScreenHeight;
IntPtr dc1 = CreateDC("DISPLAY", null, null, (IntPtr)null);
//Create the DC of the display
Graphics g1 = Graphics.FromHdc(dc1);
//Create a new Graphics object from the handle of a specified device
Bitmap MyImage = new Bitmap(ScreenWidthCut, ScreenHeightCut, g1);
//Create a Bitmap object of the same size according to the screen size
Graphics g2 = Graphics.FromImage(MyImage);
//Get the handle of the screen
IntPtr dc3 = g1.GetHdc();
//Get the handle of the bitmap
IntPtr dc2 = g2.GetHdc();
BitBlt(dc2, 0, 0, ScreenWidth, ScreenHeight, dc3, ScreenLocWidth, ScreenLocHeight,
(int)CopyPixelOperation.SourceCopy | (int)CopyPixelOperation.CaptureBlt);
g1.ReleaseHdc(dc3);
//Release the screen handle
g2.ReleaseHdc(dc2);
//Release the bitmap handle
DeleteObject(dc1);
DeleteObject(dc2);
DeleteObject(dc3);
return MyImage;
}
Debugging gave me these lines which are potentially causing the leak.
//Get the handle of the screen
IntPtr dc3 = g1.GetHdc();
//Get the handle of the bitmap
IntPtr dc2 = g2.GetHdc();
With the following I am trying to release and delete the objects created, with no effect.
g1.ReleaseHdc(dc3);
//Release the screen handle
g2.ReleaseHdc(dc2);
//Release the bitmap handle
DeleteObject(dc1);
DeleteObject(dc2);
DeleteObject(dc3);
I found a solution using the GarbageCollector. That works! No more memory nor GDI leak. I simply call
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
after I call "GetSS".
But I would like to understand why releasing and deleting the objects manually doesn't work, I want to avoid using the GarbageCollector at all if possible.
EDIT: This is how I call GetSS
while (startLoc.x == 0)
{
using (Bitmap imgScene = GetSS(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height, Screen.PrimaryScreen.Bounds.Width, (int)(Screen.PrimaryScreen.Bounds.Height * 0.20)))
{
//the stuff I do with the image is commented out for testing purposes, this is not causing th leak
}
Thread.Sleep(10);
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
}
And this is for deleting the Object:
[System.Runtime.InteropServices.DllImport("gdi32.dll")]
public static extern bool DeleteObject(IntPtr hObject);
Stay healthy everyone.
If a forced GC solves the problem, it is probably due to some finalizer kicking in and freeing memory. That hints that it might be some disposable object not being disposed. The Graphics class are IDisposable, so they should be inside a using statement to ensure disposal. The bitmap seem to be correctly disposed outside the function.
This suggest that the corresponding function for CreateDC is DeleteDC.
I might also recommend releasing all resources inside finally-statements, to ensure they are disposed even if some exception occur.
You are missing using blocks, and also DeleteObject should be DeleteDC, which should also be in a finally.
Also, dc3 is not necessary as you have that already in dc1.
private Bitmap GetSS(int ScreenWidth, int ScreenHeight, int ScreenWidthCut, int ScreenHeightCut)
{
int ScreenLocWidth = Screen.PrimaryScreen.Bounds.Width - ScreenWidth;
int ScreenLocHeight = Screen.PrimaryScreen.Bounds.Height - ScreenHeight;
Bitmap MyImage;
IntPtr dc1 = IntPtr.Zero;
IntPtr dc2 = IntPtr.Zero;
try
{
dc1 = CreateDC("DISPLAY", null, null, (IntPtr)null);
//Create the DC of the display
//Create a Bitmap object of the same size according to the screen size
using (Graphics g1 = Graphics.FromHdc(dc1))
{
MyImage = new Bitmap(ScreenWidthCut, ScreenHeightCut, g1);
using (Graphics g2 = Graphics.FromImage(MyImage))
{
//Get the handle of the bitmap
dc2 = g2.GetHdc();
BitBlt(dc2, 0, 0, ScreenWidth, ScreenHeight, dc1, ScreenLocWidth, ScreenLocHeight,
(int)CopyPixelOperation.SourceCopy | (int)CopyPixelOperation.CaptureBlt);
}
}
}
catch
{
MyImage?.Dispose();
throw;
}
finally
{
//Release the bitmap handle
if (dc1 != IntPtr.Zero)
DeleteObject(dc1);
if (dc2 != IntPtr.Zero)
g2.ReleaseHdc(dc2);
}
return MyImage;
}
Don't forget that the image you return also must be disposed at some point.

How to properly save an HICON from an IMAGELIST as an image file

My goal :
Saving all Window Icon Handle(HICON) from inside an HIMAGELIST as multiple image files (.png or .tiff).
My issue :
After my saving procedure some images have poor quality but some don't.
I only noticed this problem on the images of folders with subfolders / subfiles.
My attempt :
Code background:
I'm using Vanara to help me with PInvoke calls and a lot more.
The HIMAGELISTcome from a ListView using the ListViewMessage:LVM_GETIMAGELIST.
This method is part of a Shell Extension (I know, I shouldn't do that).
private void Saving()
{
var hWnd = GetListViewHWnd(); // This is the Desktop SysListView32 HWND
IntPtr lParam = IntPtr.Zero;
IntPtr pHil = SendMessage(hWnd, ListViewMessage.LVM_GETIMAGELIST, 0, ref lParam);
var sHil = new SafeHIMAGELIST(pHil); // This is the IMAGELIST of the ListView
var imageCount = sHil.Interface.GetImageCount(); // sHil.Interface == IImageList Interface
for (int i = 0; i < imageCount; i++)
{
using (var fs = File.OpenWrite(#"C:\Users\Julien\Desktop\Icons\" + i + ".tiff"))
{
using (SafeHICON sHIcon = sHil.Interface.GetIcon(i, IMAGELISTDRAWFLAGS.ILD_NORMAL))
{
var bmpS = Imaging.CreateBitmapSourceFromHIcon(
sHIcon.DangerousGetHandle(),
Int32Rect.Empty,
BitmapSizeOptions.FromEmptyOptions());
BitmapEncoder enc = new TiffBitmapEncoder();
enc.Frames.Add(BitmapFrame.Create(bmpS));
enc.Save(fs);
}
}
}
sHil.Dispose();
}
Also :
var bmp = Bitmap.FromHicon(sHIcon.DangerousGetHandle());
bmp.Save(fs);
FAQ :
Why I am using the listview imagelist and not SHGetFileInfo ?
Because SHGetFileInfo will give me an HICON like that :
for a folder that in reality look like this :
What about passing SHGFI_SYSICONINDEX in your SHGetFileInfo ?
Same thing, the icons of non-empty folders is not stored in the System Image List.
Since I can wrote my extension in C++ I am open to any solution written in C++ too.
Edit :
I tried to draw those glitched images using IImageList.Draw() and it seem to work. So clearly the problem come from how I create an image from an HICON.
var hdc = GetDC(notepadHWnd);
var dp = new IMAGELISTDRAWPARAMS(
hdc,
new RECT(73, 73, 73, 73), 12,
COLORREF.None,
IMAGELISTDRAWFLAGS.ILD_NORMAL);
sHil.Interface.Draw(dp);
As I am stubborn I insisted with the IMAGELIST of the desktop listview.
I managed to get the icons / thumbnails from it. By drawing them in a in-memory device context.
No more glitched images.
private void Saving()
{
var hWnd = GetListViewHWnd(); // Desktop SysListView32 HWND
IntPtr lParam = IntPtr.Zero;
IntPtr pHil = SendMessage(hWnd, ListViewMessage.LVM_GETIMAGELIST, 0, ref lParam);
var sHil = new SafeHIMAGELIST(pHil); // IMAGELIST of the ListView
sHil.Interface.GetIconSize(out var cx, out var cy);
var imageCount = sHil.Interface.GetImageCount(); // IImageList Interface
var desktopHdc = new SafeHDC(GetDC(GetListViewHWnd()).DangerousGetHandle());
var inMemoryHdc = CreateCompatibleDC(desktopHdc);
for (int i = 0; i < imageCount; i++)
{
var inMemoryBmp = CreateCompatibleBitmap(desktopHdc, cx, cy);
SelectObject(inMemoryHdc, inMemoryBmp);
var ilDp = new IMAGELISTDRAWPARAMS(
inMemoryHdc,
new RECT(0, 0, 0, 0),
i,
COLORREF.None,
IMAGELISTDRAWFLAGS.ILD_NORMAL);
sHil.Interface.Draw(ilDp);
var bmpS = Imaging.CreateBitmapSourceFromHBitmap(
inMemoryBmp.DangerousGetHandle(),
IntPtr.Zero,
Int32Rect.Empty,
BitmapSizeOptions.FromEmptyOptions());
using (var fs = File.OpenWrite(#"C:\Users\Julien\Desktop\Icons\" + i + ".png"))
{
BitmapEncoder enc = new PngBitmapEncoder();
enc.Frames.Add(BitmapFrame.Create(bmpS));
enc.Save(fs);
}
inMemoryBmp.Dispose();
}
sHil.Dispose();
desktopHdc.Dispose();
inMemoryHdc.Dispose();
}
But as Jonathan Potter said it is not a good idea :
A combination of IShellItemImageFactory and SHCreateItemFromIDList for example seem better.

Window capture with embeded (setparent) directx game C#

Goal:
- Capture a winform window containing a directx game window (like screenshot, but specific handle).
Scenario:
- Inside a winform I insert a panel1 normally.
- I used the winapi SETPARENT to insert a directx main game window inside the penel1
Problem:
When capturing the winform window (this.handle), the image of the embedded game in the panel1 does not appear. Only the panel appears.
I can't use the ScreenShot method, because it will get another window over.
Example:
public Image CaptureWindow(IntPtr handle, int imgX = 0, int imgY = 0, int largura = 0, int altura = 0)
{
// get te hDC of the target window
IntPtr hdcSrc = User32.GetWindowDC(handle);
// get the size
User32.RECT windowRect = new User32.RECT();
User32.GetWindowRect(handle, ref windowRect);
if(largura == 0 || altura == 0)
{
largura = windowRect.right - windowRect.left;
altura = windowRect.bottom - windowRect.top;
}
// create a device context we can copy to
IntPtr hdcDest = GDI32.CreateCompatibleDC(hdcSrc);
// create a bitmap we can copy it to,
// using GetDeviceCaps to get the width/height
IntPtr hBitmap = GDI32.CreateCompatibleBitmap(hdcSrc, largura, altura);
// select the bitmap object
IntPtr hOld = GDI32.SelectObject(hdcDest, hBitmap);
// bitblt over
//GDI32.BitBlt(hdcDest, 0, 0, largura, altura, hdcSrc, 0, 0, GDI32.SRCCOPY);
GDI32.BitBlt(hdcDest, 0, 0, largura, altura, hdcSrc, imgX, imgY, GDI32.SRCCOPY);
// restore selection
GDI32.SelectObject(hdcDest, hOld);
// clean up
GDI32.DeleteDC(hdcDest);
User32.ReleaseDC(handle, hdcSrc);
// get a .NET image object for it
Image img = Image.FromHbitmap(hBitmap);
// free up the Bitmap object
GDI32.DeleteObject(hBitmap);
return img;
}
After use SetParent with Directx window, need to refresh the winform.
Just: this.Refresh();

Windows7 Winapi Image returns black clientrect

I am having a problem with capturing the graphics of another proccess, because for some users it is just a pure black screen which I capture. Unfortunately I have no idea why this is happening for some users only. I am using the sub window directly instead of the window handle and I ensured by posting the address of the windowhandle and checking with spy++ that this window handle is actually the right one.
const string className = "BlueStacksApp";
const string windowName = "_ctl.Window";
processMainHWND = process.MainWindowHandle;
clientRectangleHandle = User32.FindWindowEx(processMainHWND, 0, className, windowName);
internal Bitmap MakeSnapshot(IntPtr AppWndHandle, RECT rect)
{
int width = rect.right - rect.left;
int height = rect.bottom - rect.top;
IntPtr hdcTo = IntPtr.Zero;
IntPtr hdcFrom = IntPtr.Zero;
IntPtr hBitmap = IntPtr.Zero;
try
{
Bitmap clsRet = null;
// get device context of the window...
hdcFrom = User32.GetWindowDC(AppWndHandle);
// create dc that we can draw to...
hdcTo = GDI32.CreateCompatibleDC(hdcFrom);
hBitmap = GDI32.CreateCompatibleBitmap(hdcFrom, width, height);
// validate
if (hBitmap != IntPtr.Zero)
{
// adjust and copy
IntPtr hLocalBitmap = GDI32.SelectObject(hdcTo, hBitmap);
bool result = GDI32.BitBlt(hdcTo, 0, 0, width, height, hdcFrom, 0, 0, GDI32.SRCCOPY);
GDI32.SelectObject(hdcTo, hLocalBitmap);
// create bitmap for window image...
clsRet = Image.FromHbitmap(hBitmap);
}
return clsRet;
}
finally
{
// release the unmanaged resources
if (hdcFrom != IntPtr.Zero)
User32.ReleaseDC(AppWndHandle, hdcFrom);
if (hdcTo != IntPtr.Zero)
GDI32.DeleteDC(hdcTo);
if (hBitmap != IntPtr.Zero)
GDI32.DeleteObject(hBitmap);
}
}

Print into XPS-file and then print it to printer

I've tried to print content of RichTextBox and there are too many bugs if I print to printer.
But when I'm printing to XPS-file (through XPS-printer in windows) and then printing this file to printer all is ok.
So can I do all these things programmatically?
Here is my printing method:
public int PrintRotate(bool rotate, PrintPageEventArgs e, int charFrom, int charTo)
{
//Calculate the area to render and print
RECT rectToPrint;
rectToPrint.Top = (int)(e.MarginBounds.Top * anInch);
rectToPrint.Bottom = (int)(e.MarginBounds.Bottom * anInch);
rectToPrint.Left = (int)(e.MarginBounds.Left * anInch);
rectToPrint.Right = (int)(e.MarginBounds.Right * anInch);
//Calculate the size of the page
RECT rectPage;
rectPage.Top = (int)(e.PageBounds.Top * anInch);
rectPage.Bottom = (int)(e.PageBounds.Bottom * anInch);
rectPage.Left = (int)(e.PageBounds.Left * anInch);
rectPage.Right = (int)(e.PageBounds.Right * anInch);
IntPtr hdc = e.Graphics.GetHdc();
FORMATRANGE fmtRange;
fmtRange.chrg.cpMax = charTo; //Indicate character from to character to
fmtRange.chrg.cpMin = charFrom;
fmtRange.hdc = hdc; //Use the same DC for measuring and rendering
fmtRange.hdcTarget = hdc; //Point at printer hDC
fmtRange.rc = rectToPrint; //Indicate the area on page to print
fmtRange.rcPage = rectPage; //Indicate size of page
SetGraphicsMode(fmtRange.hdc, GM_ADVANCED);
XFORM par = new XFORM();
par = new XFORM();
par.eM11 = 1;
par.eM12 = 0;
par.eM21 = 0;
par.eM22 = 1;
par.eDx = -e.PageSettings.Margins.Left / 100 * e.PageSettings.PrinterResolution.X;
par.eDy = -e.PageSettings.Margins.Top / 100 * e.PageSettings.PrinterResolution.Y;
ModifyWorldTransform(fmtRange.hdc, ref par, MWT_LEFTMULTIPLY);
IntPtr res = IntPtr.Zero;
IntPtr wparam = IntPtr.Zero;
wparam = new IntPtr(1);
//Get the pointer to the FORMATRANGE structure in memory
IntPtr lparam = IntPtr.Zero;
lparam = Marshal.AllocCoTaskMem(Marshal.SizeOf(fmtRange));
Marshal.StructureToPtr(fmtRange, lparam, false);
//Send the rendered data for printing
res = SendMessage(Handle, EM_FORMATRANGE, wparam, lparam);
//Free the block of memory allocated
Marshal.FreeCoTaskMem(lparam);
//Release the device context handle obtained by a previous call
e.Graphics.ReleaseHdc(hdc);
//Return last + 1 character printer
return res.ToInt32();
}
I've had a problem like this, and ended up making an .XPS file and then sending that to the printer.
From your question, it sounds like you already have the process of "printing" to the xps file down, which is good since I don't know anything about the process of printing the rich text box to the xps file. In my scenaria I needed to print a dokument without using ms office, so I ended up making a XPS file, edit it in code, and then send it to the printer.
This is the code I use to send the xps-file directly to the printer:
LocalPrintServer localPrintServer = new LocalPrintServer();
var queue = localPrintServer.GetPrintQueue("NameOfPrinter");
PrintSystemJobInfo xpsPrintJob = queue.AddJob("name of print job", "my/xps/path.xps",false);
Also remember that for this code to work you need to add references to System.Printing AND "ReachFramework". Took me longer than I care to remember to find out why I couldn't access the printjob.
Most printers should support this in my experience. The common ones and it even works on the odd "barcode-printer" at our storage-department.

Categories

Resources