GDI Write Bitmap to File in C# - c#

I'm trying to Capture an Image from a window given the Handle/DC. I want to capture the Image with all the transparency and pixels so I decided I'd use GDI/GDI+.
The below code is what I have, but when it "writes" the bitmap, it writes it wrong :S In other words, the file is unreadable. It is blank. Yet it is 20kb in size :S
Any ideas what is wrong?
Imports.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
namespace Imaging
{
static class Imports
{
public enum ObjectType : uint
{
OBJ_PEN = 1,
OBJ_BRUSH = 2,
OBJ_DC = 3,
OBJ_METADC = 4,
OBJ_PAL = 5,
OBJ_FONT = 6,
OBJ_BITMAP = 7,
OBJ_REGION = 8,
OBJ_METAFILE = 9,
OBJ_MEMDC = 10,
OBJ_EXTPEN = 11,
OBJ_ENHMETADC = 12,
OBJ_ENHMETAFILE = 13
}
public enum TernaryRasterOperations : uint
{
SRCCOPY = 0x00CC0020,
SRCPAINT = 0x00EE0086,
SRCAND = 0x008800C6,
SRCINVERT = 0x00660046,
SRCERASE = 0x00440328,
NOTSRCCOPY = 0x00330008,
NOTSRCERASE = 0x001100A6,
MERGECOPY = 0x00C000CA,
MERGEPAINT = 0x00BB0226,
PATCOPY = 0x00F00021,
PATPAINT = 0x00FB0A09,
PATINVERT = 0x005A0049,
DSTINVERT = 0x00550009,
BLACKNESS = 0x00000042,
WHITENESS = 0x00FF0062,
CAPTUREBLT = 0x40000000
}
[StructLayout(LayoutKind.Sequential)]
public struct Rect
{
public int Left, Top, Right, Bottom;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct BITMAPFILEHEADER
{
public ushort bfType;
public uint bfSize;
public ushort bfReserved1;
public ushort bfReserved2;
public uint bfOffBits;
}
[StructLayout(LayoutKind.Sequential)]
public struct BITMAPINFOHEADER
{
public uint biSize;
public int biWidth;
public int biHeight;
public ushort biPlanes;
public ushort biBitCount;
public uint biCompression;
public uint biSizeImage;
public int biXPelsPerMeter;
public int biYPelsPerMeter;
public uint biClrUsed;
public uint biClrImportant;
public void Init()
{
biSize = (uint)Marshal.SizeOf(this);
}
}
[StructLayout(LayoutKind.Sequential)]
public struct BITMAPINFO
{
public uint biSize;
public int biWidth;
public int biHeight;
public ushort biPlanes;
public ushort biBitCount;
public uint biCompression;
public uint biSizeImage;
public int biXPelsPerMeter;
public int biYPelsPerMeter;
public uint biClrUsed;
public uint biClrImportant;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)]
public uint[] cols;
}
[StructLayout(LayoutKind.Sequential)]
public struct BITMAP
{
public int bmType;
public int bmWidth;
public int bmHeight;
public int bmWidthBytes;
public ushort bmPlanes;
public ushort bmBitsPixel;
public IntPtr bmBits;
};
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
[DllImport("user32.dll")]
public static extern IntPtr GetDC(IntPtr WindowHandle);
[DllImport("user32.dll")]
public static extern void ReleaseDC(IntPtr WindowHandle, IntPtr DC);
[DllImport("user32.dll")]
public static extern IntPtr GetWindowRect(IntPtr WindowHandle, ref Rect rect);
[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool PrintWindow(IntPtr hwnd, IntPtr DC, uint nFlags);
[DllImport("user32.dll")]
public static extern int SetForegroundWindow(IntPtr hWnd);
[DllImport("user32.dll")]
public static extern IntPtr ShowWindow(IntPtr hWnd, int nCmdShow);
[DllImport("user32.dll")]
public static extern int GetWindowRgn(IntPtr hWnd, IntPtr hRgn);
[DllImport("gdi32.dll")]
public static extern IntPtr GetCurrentObject(IntPtr DC, ObjectType uObjectType);
[DllImport("gdi32.dll")]
public static extern int GetObject(IntPtr hObject, int nCount, ref BITMAP lpObject);
[DllImport("gdi32.dll", SetLastError = true)]
public static extern IntPtr CreateCompatibleDC(IntPtr DC);
[DllImport("gdi32.dll", ExactSpelling = true, SetLastError = true)]
public static extern bool DeleteDC(IntPtr DC);
[DllImport("gdi32.dll")]
public static extern IntPtr CreateCompatibleBitmap(IntPtr DC, int nWidth, int nHeight);
[DllImport("gdi32.dll", ExactSpelling = true, PreserveSig = true, SetLastError = true)]
public static extern IntPtr SelectObject(IntPtr DC, IntPtr hgdiobj);
[DllImport("gdi32.dll")]
public static extern bool DeleteObject(IntPtr hObject);
[DllImport("gdi32.dll")]
public static extern IntPtr CreateRectRgn(int nLeftRect, int nTopRect, int nRightRect, int nBottomRect);
[DllImport("gdi32.dll")]
static extern bool FillRgn(IntPtr DC, IntPtr hrgn, IntPtr hbr);
[DllImport("gdi32.dll")]
public static extern IntPtr CreateSolidBrush(uint crColor);
[DllImport("gdi32.dll")]
public static extern int GetDIBits(IntPtr DC, IntPtr hbmp, uint uStartScan, uint cScanLines, [Out] byte[] lpvBits, ref BITMAPINFO lpbmi, uint uUsage);
[DllImport("gdi32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool BitBlt(IntPtr DC, int nXDest, int nYDest, int nWidth, int nHeight, IntPtr SrcDC, int nXSrc, int nYSrc, TernaryRasterOperations dwRop);
}
}
My PBitmap class:
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;
namespace Imaging
{
class PBitmap
{
private Bitmap Img = null;
private Imports.BITMAPINFO Info;
private Imports.BITMAPFILEHEADER bFileHeader;
public PBitmap(IntPtr Window, int X, int Y, uint BitsPerPixel = 24)
{
IntPtr DC = Imports.GetDC(Window);
Imports.BITMAP Bmp = new Imports.BITMAP();
IntPtr hBmp = Imports.GetCurrentObject(DC, Imports.ObjectType.OBJ_BITMAP);
if (Imports.GetObject(hBmp, Marshal.SizeOf(Bmp), ref Bmp) == 0)
throw new Exception("ERROR_INVALID_WINDOW_HANDLE.");
Imports.Rect BMBox = new Imports.Rect();
Imports.GetWindowRect(Window, ref BMBox); //GetClientRect
int width = BMBox.Right - BMBox.Left;
int height = BMBox.Bottom - BMBox.Top;
IntPtr MemDC = Imports.GetDC(IntPtr.Zero);
IntPtr SDC = Imports.CreateCompatibleDC(MemDC);
IntPtr hSBmp = Imports.CreateCompatibleBitmap(MemDC, width, height);
Imports.DeleteObject(Imports.SelectObject(SDC, hSBmp));
Imports.BitBlt(SDC, 0, 0, width, height, DC, X, Y, Imports.TernaryRasterOperations.SRCCOPY);
long size = ((width * BitsPerPixel + 31) / 32) * 4 * height;
Info = new Imports.BITMAPINFO();
bFileHeader = new Imports.BITMAPFILEHEADER();
Info.biSize = (uint)Marshal.SizeOf(Info);
Info.biWidth = width;
Info.biHeight = height;
Info.biPlanes = 1;
Info.biBitCount = (ushort)BitsPerPixel;
Info.biCompression = 0;
Info.biSizeImage = (uint)size;
bFileHeader.bfType = 0x4D42;
bFileHeader.bfOffBits = (uint)(Marshal.SizeOf(bFileHeader) + Marshal.SizeOf(Info));
bFileHeader.bfSize = (uint)(bFileHeader.bfOffBits + size);
byte[] ImageData = new byte[size];
Imports.GetDIBits(SDC, hSBmp, 0, (uint)height, ImageData, ref Info, 0);
var bw = new BinaryWriter(File.Open("C:/Users/*******/Desktop/Foo.bmp", FileMode.OpenOrCreate));
bw.Write((short)0x4D42);
bw.Write((uint)(bFileHeader.bfOffBits + size));
bw.Write((uint)0);
bw.Write((uint)54);
bw.Write((uint)Info.biSize);
bw.Write(width);
bw.Write(height);
bw.Write((short)Info.biPlanes);
bw.Write(BitsPerPixel);
bw.Write((uint)0);
bw.Write((uint)size);
bw.Write((uint)0);
bw.Write((uint)0);
bw.Write((uint)0);
bw.Write((uint)0);
bw.Write(ImageData);
bw.Flush();
bw.Close();
bw.Dispose();
Imports.DeleteDC(SDC);
Imports.DeleteObject(hSBmp);
Imports.ReleaseDC(IntPtr.Zero, MemDC);
Imports.ReleaseDC(Window, DC);
}
}
}

If you're looking for a quick band-aid fix to get this done (it just needs to work, not used often, etc) consider just loading the image back into .NET and re-saving it with PNG compression enabled. The .NET framework will handle this for you and the file size will be a lot more optimal. Of course, if this is for a real-time application this isn't ideal.

Related

Wpf - .Net Core 3.1 - Get all opened process window preview image and display

Currently I'm developing a project that needs to show a preview image of all opened process and display it on a grid.
Exactly like Microsoft Teams shows when you try to select a window to be shared.
So far I've achive the following design
Which is mounted using the following code:
private void CreateOpenedWindowsMenuItems(IEnumerable<CapturableWindow> openedWindows)
{
var dataGridRowNumber = 0;
var dataGridRowColumnNumber = 0;
CreateNewRowDefinition();
foreach (var openedWindow in openedWindows)
{
if (dataGridRowColumnNumber == MaxItemsPerRow)
dataGridRowColumnNumber = AddNewRowToGrid(ref dataGridRowNumber);
var openedWindowCard = CreateCard(CardSize);
var selectItemCheckbox = new CheckBox { IsEnabled = false, Margin = new Thickness(5,0,0,0)};
var openedWindowCardStackPanel = CreateStackPanel(openedWindow, selectItemCheckbox);
openedWindowCard.Content = openedWindowCardStackPanel;
openedWindowCard.MouseLeftButtonDown += (sender, args) =>
{
if (selectItemCheckbox.IsChecked == true)
_capturedWindows.Remove(openedWindow);
else
_capturedWindows.Add(openedWindow);
selectItemCheckbox.IsChecked = !selectItemCheckbox.IsChecked;
};
Grid.SetRow(openedWindowCard, dataGridRowNumber);
Grid.SetColumn(openedWindowCard, dataGridRowColumnNumber);
AvailableCapturableWindows.Children.Add(openedWindowCard);
dataGridRowColumnNumber++;
}
}
private StackPanel CreateStackPanel(CapturableWindow window, CheckBox selectItemCheckbox)
{
var stackPanel = new StackPanel();
var textStackPanel = new StackPanel { Orientation = Orientation.Horizontal };
textStackPanel.Children.Add(new TextBlock
{
Text = $"{window.Name.Substring(0, 25)}...",
TextWrapping = TextWrapping.NoWrap,
TextAlignment = TextAlignment.Center,
FontSize = 10,
FontWeight = FontWeights.Bold,
Foreground = new SolidColorBrush(Colors.White),
});
textStackPanel.Children.Add(selectItemCheckbox);
stackPanel.Children.Add(textStackPanel);
var previewImage = CreatePreviewImage(window.Handle);
if (previewImage != null) stackPanel.Children.Add(previewImage);
return stackPanel;
}
private UIElement CreatePreviewImage(IntPtr hWnd)
{
try
{
var handle = hWnd;
if (!NativeMethods.IsWindow(handle))
return null;
var hdcSrc = NativeMethods.GetWindowDC(handle);
NativeMethods.GetWindowRect(handle, out var windowRect);
var width = windowRect.Right - windowRect.Left;
var height = windowRect.Bottom - windowRect.Top;
var hdcDest = NativeMethods.CreateCompatibleDC(hdcSrc);
var hBitmap = NativeMethods.CreateCompatibleBitmap(hdcSrc, width, height);
var hOld = NativeMethods.SelectObject(hdcDest, hBitmap);
var bRet = NativeMethods.BitBlt(hdcDest, 0, 0, width, height, hdcSrc, 0, 0, NativeMethods.SRCCOPY);
NativeMethods.SelectObject(hdcDest, hOld);
NativeMethods.DeleteDC(hdcDest);
NativeMethods.ReleaseDC(handle, hdcSrc);
var imageSource = Imaging.CreateBitmapSourceFromHBitmap(
hBitmap,
IntPtr.Zero,
Int32Rect.Empty,
BitmapSizeOptions.FromEmptyOptions());
return new Image { Source = imageSource, VerticalAlignment = VerticalAlignment.Center, HorizontalAlignment = HorizontalAlignment.Center };
}
catch (Exception)
{
return new PackIcon { Kind = PackIconKind.Image };
}
}
private static Card CreateCard(int cardSize)
{
var card = new Card
{
Width = cardSize,
Height = cardSize,
VerticalAlignment = VerticalAlignment.Center,
HorizontalContentAlignment = HorizontalAlignment.Center,
Margin = new Thickness(10),
Cursor = Cursors.Hand
};
card.MouseEnter += (sender, args) =>
{
card.Width = CardSizeHovered;
card.Height = CardSizeHovered;
};
card.MouseLeave += (sender, args) =>
{
card.Width = cardSize;
card.Height = cardSize;
};
return card;
}
private int AddNewRowToGrid(ref int dataGridRowNumber)
{
dataGridRowNumber++;
CreateNewRowDefinition();
return InitialColumnItemNumber;
}
private void CreateNewRowDefinition()
{
AvailableCapturableWindows.RowDefinitions.Add(new RowDefinition { Height = new GridLength(256) });
}
I also have a NativeMethods Class:
public static class NativeMethods
{
public const int SRCCOPY = 0x00CC0020;
[DllImport("user32.dll")]
public static extern bool PrintWindow(IntPtr hwnd, IntPtr hdcBlt, int nFlags);
[DllImport("gdi32.dll")]
public static extern bool DeleteObject(IntPtr hObject);
[DllImport("gdi32.dll")]
public static extern IntPtr CreateCompatibleDC(IntPtr hdc);
[DllImport("gdi32.dll")]
public static extern IntPtr CreateCompatibleBitmap(IntPtr hdc, int nWidth, int nHeight);
[DllImport("gdi32.dll")]
public static extern IntPtr SelectObject(IntPtr hdc, IntPtr hg);
[DllImport("user32.dll")]
public static extern IntPtr GetWindowDC(IntPtr hwnd);
[DllImport("user32.dll")]
public static extern int ReleaseDC(IntPtr hwnd, IntPtr hdc);
[DllImport("gdi32.dll")]
public static extern bool DeleteDC(IntPtr hdc);
[DllImport("gdi32.dll")]
public static extern int BitBlt(IntPtr hdcDest, int xDest, int yDest, int wDest, int hDest, IntPtr hdcSource, int xSrc, int ySrc, int rop);
[DllImport("user32.dll")]
public static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool PrintWindow(IntPtr hwnd, IntPtr hDC, uint nFlags);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);
[DllImport("d3d11.dll", EntryPoint = "CreateDirect3D11DeviceFromDXGIDevice", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
public static extern uint CreateDirect3D11DeviceFromDXGIDevice(IntPtr dxgiDevice, out IntPtr graphicsDevice);
public delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool EnumWindows(EnumWindowsProc lpEnumFunc, IntPtr lParam);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern int GetWindowTextLength(IntPtr hWnd);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);
[DllImport("user32.dll")]
public static extern bool IsWindow(IntPtr hWnd);
[DllImport("user32.dll")]
public static extern bool IsWindowVisible(IntPtr hWnd);
[DllImport("user32.dll", SetLastError = true)]
public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
[DllImport("user32.dll")]
private static extern IntPtr GetForegroundWindow();
public static IntPtr GetActiveWindow() => GetForegroundWindow();
[DllImport("CoreMessaging.dll", EntryPoint = "CreateDispatcherQueueController", SetLastError = true, CharSet = CharSet.Unicode,ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
public static extern uint CreateDispatcherQueueController(DispatcherQueueOptions options, out IntPtr dispatcherQueueController);
}
How can i get the preview window image, and use It in my card element, after the window text name,based on IntPtr?

Trying to capture an specific part of a hidden window

Been losing some days on this, I'm trying to capture an specific part of a hidden window, like per example, capture only coordinates 300, 300 to 350, 350. I'm trying to do some image recognition and using a full screenshot takes too long to find matches. Below is the code I'm using to capture a region:
public class WindowCapture
{
private class User32
{
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public int left;
public int top;
public int right;
public int bottom;
}
[DllImport("user32.dll")]
public static extern IntPtr GetWindowDC(IntPtr hWnd);
[DllImport("user32.dll")]
public static extern IntPtr ReleaseDC(IntPtr hWnd, IntPtr hDC);
[DllImport("user32.dll")]
public static extern IntPtr GetWindowRect(IntPtr hWnd, ref RECT rect);
[DllImport("user32.dll")]
public static extern bool PrintWindow(IntPtr hWnd, IntPtr hDC, uint nFlags);
}
private class GDI32
{
public const int SRCCOPY = 0x00CC0020;
[DllImport("gdi32.dll")]
public static extern bool BitBlt(IntPtr hObject, int nXDest, int nYDest,
int nWidth, int nHeight, IntPtr hObjectSource,
int nXSrc, int nYSrc, int dwRop);
[DllImport("gdi32.dll")]
public static extern IntPtr CreateCompatibleBitmap(IntPtr hDC, int nWidth,
int nHeight);
[DllImport("gdi32.dll")]
public static extern IntPtr CreateCompatibleDC(IntPtr hDC);
[DllImport("gdi32.dll")]
public static extern bool DeleteDC(IntPtr hDC);
[DllImport("gdi32.dll")]
public static extern bool DeleteObject(IntPtr hObject);
[DllImport("gdi32.dll")]
public static extern IntPtr SelectObject(IntPtr hDC, IntPtr hObject);
}
public Image CaptureWindowRegion(IntPtr hWnd, int x, int y, int w, int h)
{
IntPtr hdcSrc = User32.GetWindowDC(hWnd);
User32.RECT windowRect = new User32.RECT();
User32.GetWindowRect(hWnd, ref windowRect);
int originalwidth = windowRect.right - windowRect.left;
int originalheight = windowRect.bottom - windowRect.top;
CropRect(ref windowRect, x, y, w, h);
int width = windowRect.right - windowRect.left;
int height = windowRect.bottom - windowRect.top;
IntPtr hdcDest = GDI32.CreateCompatibleDC(hdcSrc);
IntPtr hBitmap = GDI32.CreateCompatibleBitmap(hdcSrc, originalwidth, originalheight);
IntPtr hOld = GDI32.SelectObject(hdcDest, hBitmap);
User32.PrintWindow(hWnd, hdcDest, 0);
IntPtr hdcDest2 = GDI32.CreateCompatibleDC(hdcDest);
IntPtr hBitmap2 = GDI32.CreateCompatibleBitmap(hdcDest, width, height);
IntPtr hOld2 = GDI32.SelectObject(hdcDest2, hBitmap2);
GDI32.BitBlt(hdcDest2, 0, 0, width, height, hdcDest, x, y, GDI32.SRCCOPY);
GDI32.SelectObject(hdcDest, hOld);
GDI32.SelectObject(hdcDest2, hOld2);
GDI32.DeleteDC(hdcDest);
GDI32.DeleteDC(hdcDest2);
User32.ReleaseDC(hWnd, hdcSrc);
Image img = Image.FromHbitmap(hBitmap2);
GDI32.DeleteObject(hBitmap);
GDI32.DeleteObject(hBitmap2);
return img;
}
private static void CropRect(ref User32.RECT windowRect, int x, int y, int w, int h)
{
windowRect.left = (x > 0) ? Math.Min(windowRect.right, windowRect.left + x) : windowRect.left;
windowRect.right = (w > 0) ? Math.Min(windowRect.right, windowRect.left + w) : windowRect.right;
windowRect.top = (y > 0) ? Math.Min(windowRect.bottom, windowRect.top + y) : windowRect.top;
windowRect.bottom = (h > 0) ? Math.Min(windowRect.bottom, windowRect.top + h) : windowRect.bottom;
}
}
After researching a bunch, all I could manage was a smaller all black bitmap.
Any idea on how this could be done?

Remove space left after console scrollbars in C#

I'm making a console game in C# in Visual Studio and I want to make the game to be in the full console window.
I resized the buffer and window to be the same size, but it looks like the space after the scrollbars remained, which will probably be really annoying to look at. I wonder if there's any way to remove that blank space in C#, or at least add grayed-out scrollbars. I saw a similar thread, but it was in C++.
Here's a reproducible example:
using Microsoft.Win32.SafeHandles;
using System;
using System.IO;
using System.Runtime.InteropServices;
namespace ConsoleTest1
{
class Program
{
[DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern SafeFileHandle CreateFile(
string fileName,
[MarshalAs(UnmanagedType.U4)] uint fileAccess,
[MarshalAs(UnmanagedType.U4)] uint fileShare,
IntPtr securityAttributes,
[MarshalAs(UnmanagedType.U4)] FileMode creationDisposition,
[MarshalAs(UnmanagedType.U4)] int flags,
IntPtr template);
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool WriteConsoleOutput(
SafeFileHandle hConsoleOutput,
CharInfo[] lpBuffer,
Coord dwBufferSize,
Coord dwBufferCoord,
ref SmallRect lpWriteRegion);
[StructLayout(LayoutKind.Sequential)]
public struct Coord
{
public short X, Y;
public Coord(short x, short y)
{
X = x;
Y = y;
}
}
[StructLayout(LayoutKind.Explicit)]
public struct CharUnion
{
[FieldOffset(0)] public char UnicodeChar;
[FieldOffset(0)] public byte AsciiChar;
}
[StructLayout(LayoutKind.Explicit)]
public struct CharInfo
{
[FieldOffset(0)] public CharUnion Char;
[FieldOffset(2)] public short Attributes;
}
[StructLayout(LayoutKind.Sequential)]
public struct SmallRect
{
public short Left, Top, Right, Bottom;
public SmallRect(short width, short height)
{
Left = Top = 0;
Right = width;
Bottom = height;
}
}
[STAThread]
static void Main(string[] args)
{
short width = 50, height = 20;
SafeFileHandle handle = CreateFile("CONOUT$", 0x40000000, 2, IntPtr.Zero, FileMode.Open, 0, IntPtr.Zero);
CharInfo[] buffer = new CharInfo[width * height];
SmallRect writeRegion = new SmallRect(width, height);
Console.SetWindowSize(width, height);
Console.SetBufferSize(width, height);
for (int i = 0; i < buffer.Length; ++i)
{
buffer[i].Attributes = 0xb0;
buffer[i].Char.UnicodeChar = ' ';
}
WriteConsoleOutput(handle, buffer, new Coord(width, height), new Coord(0, 0), ref writeRegion);
Console.ReadKey();
}
}
}
I don't really know how to do this or even if it's possible to remove the black space in this language.
Finally, after a lot of head-scratching, I think I've solved this issue. Firstly, I had to add some additional WinAPI methods:
[DllImport("kernel32.dll", SetLastError = true)]
private static extern IntPtr GetStdHandle(int nStdHandle);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool GetConsoleScreenBufferInfoEx(
IntPtr hConsoleOutput,
ref ConsoleScreenBufferInfoEx ConsoleScreenBufferInfo);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool SetConsoleScreenBufferInfoEx(
IntPtr hConsoleOutput,
ref ConsoleScreenBufferInfoEx ConsoleScreenBufferInfoEx);
and structs:
[StructLayout(LayoutKind.Sequential)]
private struct ConsoleScreenBufferInfoEx
{
public uint cbSize;
public Coord dwSize;
public Coord dwCursorPosition;
public short wAttributes;
public SmallRect srWindow;
public Coord dwMaximumWindowSize;
public ushort wPopupAttributes;
public bool bFullscreenSupported;
public Colorref black, darkBlue, darkGreen, darkCyan, darkRed, darkMagenta, darkYellow, gray, darkGray, blue, green, cyan, red, magenta, yellow, white;
}
[StructLayout(LayoutKind.Sequential)]
private struct Colorref
{
public uint ColorDWORD;
}
After that, it was time to modify the part where the buffer and window were resized:
Console.SetWindowSize(width - 2, height);
Console.SetBufferSize(width, height);
IntPtr stdHandle = GetStdHandle(-11);
ConsoleScreenBufferInfoEx bufferInfo = new ConsoleScreenBufferInfoEx();
bufferInfo.cbSize = (uint)Marshal.SizeOf(bufferInfo);
GetConsoleScreenBufferInfoEx(stdHandle, ref bufferInfo);
++bufferInfo.srWindow.Right;
++bufferInfo.srWindow.Bottom;
SetConsoleScreenBufferInfoEx(stdHandle, ref bufferInfo);
And that's it! No more ugly black space (at least on my computer, haven't tested it on other Windows versions).

Process32FirstW always returns false

Process32FirstW is suppose to return true when it finds process and return the first process in a PROCESSENTRY32W structure but it doesnt, so i cant enumerate it in Process32Next sadly. What is the reason it doesnt return true as a bool function? [([([( I have changed the struct deceleration )])])])]
[StructLayout(LayoutKind.Sequential)]
public struct PROCESSENTRY32W
{
public uint dwSize;
public uint cntUsage;
public uint th32ProcessID;
public IntPtr th32DefaultHeapID;
public uint th32ModuleID;
public uint cntThreads;
public uint th32ParentProcessID;
public int pcPriClassBase;
public uint dwFlags;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
public string szExeFile;
};
[DllImport("kernel32.dll", EntryPoint = "CreateToolhelp32Snapshot")]
public static extern IntPtr CreateToolhelp32SnapshotRtlMoveMemory(UInt32 dwFlagsdes, UInt32 th32ProcessID);
[DllImport("kernel32", EntryPoint = "Process32FirstW", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool Process32FirstW(IntPtr hSnapshot, IntPtr lppe);
[DllImport("kernel32", EntryPoint = "Process32Next")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool Process32Next(IntPtr hSnapshot, IntPtr lppe);
[DllImport("kernel32", EntryPoint = "CloseHandle")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool CloseHandle(IntPtr handle);
public static Dictionary<int, Process> get_process_list()
{
//Dictionary<string, Process> returnable_processes_ = new Dictionary<string, Process>();
Dictionary<int, Process> returnable_processes_ = new Dictionary<int, Process>();
IntPtr HANLDE_Processes = CreateToolhelp32SnapshotRtlMoveMemory(2,0);
PROCESSENTRY32W p32Iw = new PROCESSENTRY32W();
p32Iw.dwSize = System.Runtime.InteropServices.Marshal.SizeOf(typeof(PROCESSENTRY32W));
IntPtr p32IntPtr = Marshal.AllocHGlobal(Marshal.SizeOf(p32Iw));
Marshal.StructureToPtr(p32Iw, p32IntPtr, false);
bool blFirstProcess = Process32FirstW(HANLDE_Processes, p32IntPtr); // returns false no matter what?
int x = Marshal.GetLastWin32Error();
if(blFirstProcess)
{
do
{
Marshal.PtrToStructure(p32IntPtr, p32Iw);
int PID = (int) p32Iw.th32ProcessID;
returnable_processes_.Add(PID, new Process());
}
while(Process32Next(HANLDE_Processes, p32IntPtr));
}
Marshal.FreeHGlobal(p32IntPtr);
return returnable_processes_;
}
It's really a charset problem. You use the ***W version of api. The actual type of PROCESSENTRY32W.szExeFile is wchar_t, which is 2 bytes, so the structure size you passed is 260 bytes shorter(you can simply try p32Iw.dwSize + 260, Process32FirstW will return ture, if we are regardless of the subsequent execution).
Fix the struct declaration and run the "C/C++ Programming" C# code.
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct PROCESSENTRY32
{
public uint dwSize;
public uint cntUsage;
public uint th32ProcessID;
public IntPtr th32DefaultHeapID;
public uint th32ModuleID;
public uint cntThreads;
public uint th32ParentProcessID;
public int pcPriClassBase;
public uint dwFlags;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=260)] public string szExeFile;
};
But according to the document, Marshal.StructureToPtr and Marshal.PtrToStructure are out of date and we should avoid using it. And this way of passing values through pointers is usually replaced by references in C#.
First, declare charset and reference parameters for Process32First and Process32Next
[DllImport("kernel32", EntryPoint = "Process32First", SetLastError = true, CharSet = CharSet.Auto)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool Process32First(IntPtr hSnapshot, ref PROCESSENTRY32W lppe);
[DllImport("kernel32", EntryPoint = "Process32Next", CharSet = CharSet.Auto)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool Process32Next(IntPtr hSnapshot, ref PROCESSENTRY32W lppe);
Then, Pass the ref type of PROCESSENTRY32W:
bool blFirstProcess = Process32First(HANLDE_Processes, ref p32Iw); // returns false no matter what?
if (blFirstProcess)
{
do
{
//Marshal.PtrToStructure(p32IntPtr, p32Iw);
int PID = (int)p32Iw.th32ProcessID;
returnable_processes_.Add(PID, new Process());
Console.WriteLine(p32Iw.szExeFile);
}
while (Process32Next(HANLDE_Processes, ref p32Iw));
}
Summarize, a minimal, compilable example:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
using System.Diagnostics;
namespace ConsoleApp
{
class Program
{
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct PROCESSENTRY32W
{
public uint dwSize;
public uint cntUsage;
public uint th32ProcessID;
public IntPtr th32DefaultHeapID;
public uint th32ModuleID;
public uint cntThreads;
public uint th32ParentProcessID;
public int pcPriClassBase;
public uint dwFlags;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] public string szExeFile;
};
[DllImport("kernel32.dll", EntryPoint = "CreateToolhelp32Snapshot")]
public static extern IntPtr CreateToolhelp32SnapshotRtlMoveMemory(UInt32 dwFlagsdes, UInt32 th32ProcessID);
[DllImport("kernel32", EntryPoint = "Process32First", SetLastError = true, CharSet = CharSet.Auto)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool Process32First(IntPtr hSnapshot, ref PROCESSENTRY32W lppe);
[DllImport("kernel32", EntryPoint = "Process32Next", CharSet = CharSet.Auto)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool Process32Next(IntPtr hSnapshot, ref PROCESSENTRY32W lppe);
[DllImport("kernel32", EntryPoint = "CloseHandle")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool CloseHandle(IntPtr handle);
public static Dictionary<int, Process> get_process_list()
{
//Dictionary<string, Process> returnable_processes_ = new Dictionary<string, Process>();
Dictionary<int, Process> returnable_processes_ = new Dictionary<int, Process>();
IntPtr HANLDE_Processes = CreateToolhelp32SnapshotRtlMoveMemory(2, 0);
PROCESSENTRY32W p32Iw = new PROCESSENTRY32W();
int size = System.Runtime.InteropServices.Marshal.SizeOf(typeof(PROCESSENTRY32W));
p32Iw.dwSize = Convert.ToUInt32(size);
//IntPtr p32IntPtr = Marshal.AllocHGlobal(Marshal.SizeOf(p32Iw));
//Marshal.StructureToPtr(p32Iw, p32IntPtr, false);
bool blFirstProcess = Process32First(HANLDE_Processes, ref p32Iw); // returns false no matter what?
int x = Marshal.GetLastWin32Error();
if (blFirstProcess)
{
do
{
int PID = (int)p32Iw.th32ProcessID;
returnable_processes_.Add(PID, new Process());
Console.WriteLine(p32Iw.szExeFile);
}
while (Process32Next(HANLDE_Processes, ref p32Iw));
}
return returnable_processes_;
}
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
get_process_list();
Console.ReadKey();
}
}
}

How to set foreground window by partial title name>

I want to bring another application into the foreground, however, I only have a partial name of the window. Back in the day, I'd hook into the EnumWindows API and look for what I needed.
Is there a way to do this better in C#? An example would be great.
This should do the job:
[DllImport("user32.dll", EntryPoint="SystemParametersInfo")]
public static extern bool SystemParametersInfo(uint uiAction, uint uiParam, uint pvParam, uint fWinIni);
[DllImport("user32.dll", EntryPoint="SetForegroundWindow")]
public static extern bool SetForegroundWindow(IntPtr hWnd);
[DllImport("User32.dll", EntryPoint="ShowWindowAsync")]
private static extern bool ShowWindowAsync(IntPtr hWnd, int cmdShow);
private const int WS_SHOWNORMAL = 1;
private const int SW_SHOWMAXIMIZED = 3;
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool GetWindowPlacement(IntPtr hWnd, ref WINDOWPLACEMENT lpwndpl);
private struct WINDOWPLACEMENT
{
public int length;
public int flags;
public int showCmd;
public System.Drawing.Point ptMinPosition;
public System.Drawing.Point ptMaxPosition;
public System.Drawing.Rectangle rcNormalPosition;
}
Process[] processes = Process.GetProcesses();
foreach(Process p in processes){
if(p.MainWindowTitle.Contains("nice_title")){
WINDOWPLACEMENT wp = new WINDOWPLACEMENT();
placement.length = Marshal.SizeOf(placement);
GetWindowPlacement(p.MainWindowHandle, ref placement);
int proposedPlacement = wp.showCmd;
if (wp.showCmd == SW_SHOWMINIMIZED)
proposedPlacement = SW_SHOWMAXIMIZED;
SystemParametersInfo( (uint) 0x2001, 0, 0, 0x0002 | 0x0001);
ShowWindowAsync(p.MainWindowHandle, proposedPlacement);
SetForegroundWindow(p.MainWindowHandle);
SystemParametersInfo( (uint) 0x2001, 200000, 200000, 0x0002 | 0x0001);
}
}

Categories

Resources