Author: Martin Stave
The snippets below show how you can capture the screen from your CF application using P/Invoked PocketPC functions. The captured image can be saved as a bmp file or loaded into a picturebox.
The code also shows how StretchBlt can be used to diplay bitmaps. (StretchBlt is used as BitBlt is not supported)
Credit goes to Alex Feinman for the code that converts the raw data to a bmp file.
The code will produce a bmp file for 24 bit high quality .
const int imgHeight = 320;
const int imgWidth = 240;
const int SRCCOPY = 0xCC0020;
// only used for demo of StretchBlt function
IntPtr h1 = FindWindow(null, "Form1" );
IntPtr dc1 = GetDC( IntPtr.Zero );
IntPtr InMemoryDC = CreateCompatibleDC( dc1 );
IntPtr hBitmap;
IntPtr ppvBits;
BITMAPINFO bmi = new BITMAPINFO();
bmi.biSize = Marshal.SizeOf(bmi);
bmi.biBitCount = 24;
bmi.biPlanes = 1;
bmi.biWidth = imgWidth;
bmi.biHeight = imgHeight;
bmi.biXPelsPerMeter = 0xb12;
bmi.biYPelsPerMeter = 0xb12;
bmi.biSizeImage = bmi.biWidth * bmi.biHeight * bmi.biBitCount / 8;
bmi.biClrUsed = 0;
bmi.biClrImportant = 0;
bmi.biCompression = 0; // RLE8 compression does not seem to work for PocketPC
// Insert palette array here for 8bit palettised image
hBitmap = CreateDIBSection( new IntPtr(0), bmi, 0, out ppvBits, new IntPtr(0), 0 );
IntPtr OldBitmap = SelectObject( InMemoryDC, hBitmap );
IntPtr dc2 = GetWindowDC( h1 );
StretchBlt( InMemoryDC, 0, 0, imgWidth, imgHeight, dc2, 0, -26, imgWidth, imgHeight, SRCCOPY );
byte[] rawData = new byte[bmi.biSizeImage];
Marshal.Copy( ppvBits, rawData, 0, bmi.biSizeImage );
// debug repaint only to verify bitmap content
StretchBlt( dc2, 0, 100, imgWidth, imgHeight, InMemoryDC, 0, 0, imgWidth, imgHeight, SRCCOPY );
SelectObject( InMemoryDC, OldBitmap );
DeleteDC( InMemoryDC );
ReleaseDC( IntPtr.Zero, dc1 );
ReleaseDC( h1, dc2 );
byte[] bitmap = CreateBitmap(imgWidth, imgHeight, rawData, bmi );
//Demonstrates how to save image to a file
FileStream fs = new FileStream(@"\out.bmp", FileMode.Create, FileAccess.Write);
fs.Write(bitmap, 0, bitmap.Length);
fs.Close();
//Demonstrates loading the bitmap to a picturebox
MemoryStream ms = new MemoryStream(bitmap);
pictureBox1.Image = new Bitmap( ms );
//Support functions
const int sizeBFH = 14; //sizeof(BITMAPFILEHEADER)
public byte[] CreateBitmap(int width, int height, byte[] bitmapData, BITMAPINFO bi)
{
// Calculate bitmap size - 3 bytes per pixel
// Bitmap scanlines must be aligned at 16 bit boundary.
// We cut some corners by requiring that width is an even number
if ( (width & 1) == 1 )
throw new ArgumentException("Width must be an even number");
int nSize = sizeBFH + Marshal.SizeOf(typeof(BITMAPINFO)) + (width << 3) * height;
byte[] data = new byte[nSize];
byte[] bfh = new byte[sizeBFH];
BitConverter.GetBytes(( short )0x4d42).CopyTo( data, 0 );
BitConverter.GetBytes( nSize ).CopyTo( data, 2 );
int bfhOffBits = (int)(sizeBFH + Marshal.SizeOf(typeof(BITMAPINFO)));
BitConverter.GetBytes(bfhOffBits).CopyTo(data, 10);
byte[] hdr = GetBytes(bi);
Buffer.BlockCopy( hdr, 0, data, sizeBFH, hdr.Length);
Buffer.BlockCopy( bitmapData, 0, data, (int)bfhOffBits, Math.Min(bitmapData.Length, bi.biSizeImage ));
return data;
}
// This works only for default-aligned structure.
// It does not work for BITMAPFILEHEADER because bfSize is not aligned to DWORD boundary
// Unfortunately CF does not allow specifying structure member alignment
private byte[] GetBytes(object o)
{
int size = Marshal.SizeOf(o.GetType());
IntPtr p = LocalAlloc(GPTR, size);
Marshal.StructureToPtr(o, p, false);
byte[] ret = new byte[size];
Marshal.Copy(p, ret, 0, size);
LocalFree(p);
return ret;
}
struct BITMAPFILEHEADER
{
public Int16 bfType;
public Int32 bfSize;
public Int16 bfReserved1;
public Int16 bfReserved2;
public Int32 bfOffBits;
}
public class BITMAPINFO
{
public Int32 biSize;
public Int32 biWidth;
public Int32 biHeight;
public Int16 biPlanes;
public Int16 biBitCount;
public Int32 biCompression;
public Int32 biSizeImage;
public Int32 biXPelsPerMeter;
public Int32 biYPelsPerMeter;
public Int32 biClrUsed;
public Int32 biClrImportant;
};
[System.Runtime.InteropServices.DllImport ("coredll.dll")]
static extern uint GetLastError();
[System.Runtime.InteropServices.DllImport ("coredll.dll")]
static extern IntPtr CreateDIBSection(IntPtr hdc, BITMAPINFO pbmi, uint iUsage, out IntPtr
ppvBits, IntPtr hSection, uint dwOffset);
[System.Runtime.InteropServices.DllImport ("coredll.dll")]
public static extern uint GetSystemPaletteEntries( IntPtr hdc, uint iStartIndexx,
uint nEntries, IntPtr lppe );
[System.Runtime.InteropServices.DllImport ("coredll.dll")]
public static extern int GetObject( IntPtr hgdiobj, int cBuffer, IntPtr lp );
[System.Runtime.InteropServices.DllImport ("coredll.dll")]
public static extern IntPtr DeleteDC( IntPtr hdc );
[System.Runtime.InteropServices.DllImport ("coredll.dll")]
public static extern IntPtr CreateCompatibleDC( IntPtr hdc );
[System.Runtime.InteropServices.DllImport ("coredll.dll")]
public static extern IntPtr SelectObject( IntPtr hdc, IntPtr hgdiobj );
[System.Runtime.InteropServices.DllImport ("coredll.dll")]
public static extern IntPtr CreateCompatibleBitmap( IntPtr hdc, int nWidth, int nHeight );
[System.Runtime.InteropServices.DllImport ("coredll.dll")]
public static extern IntPtr FindWindow( string s1, string s2);
[System.Runtime.InteropServices.DllImport ("coredll.dll")]
public static extern int GetWindowText( IntPtr handle, char [] c, int len );
[System.Runtime.InteropServices.DllImport ("coredll.dll")]
public static extern IntPtr GetWindow( IntPtr handle, int cmd );
[System.Runtime.InteropServices.DllImportAttribute("coredll.dll")]
private static extern IntPtr GetDC( IntPtr hWnd);
[System.Runtime.InteropServices.DllImportAttribute("coredll.dll")]
private static extern IntPtr GetWindowDC( IntPtr hWnd);
[System.Runtime.InteropServices.DllImportAttribute("coredll.dll")]
private static extern int GetDeviceCaps( IntPtr hWnd, int nIndex);
[System.Runtime.InteropServices.DllImportAttribute("coredll.dll")]
private static extern int DrawText( IntPtr hDC, string lpString, int nCount, ref int [] lpRect, uint uFormat );
[System.Runtime.InteropServices.DllImportAttribute("coredll.dll")]
private static extern IntPtr ReleaseDC( IntPtr hWnd, IntPtr hDC );
[System.Runtime.InteropServices.DllImportAttribute("coredll.dll")]
private static extern bool SetWindowPos( IntPtr hWnd, IntPtr hWndInsertAfter, int x, int Y, int cx, int cy, uint uFlags );
[System.Runtime.InteropServices.DllImportAttribute("coredll.dll")]
private static extern bool StretchBlt(
IntPtr hdcDest, // handle to destination DC
int nXDest, // x-coord of destination upper-left corner
int nYDest, // y-coord of destination upper-left corner
int nWidth, // width of destination rectangle
int nHeight, // height of destination rectangle, negative to switch upside down
IntPtr hdcSrc, // handle to source DC
int nXSrc, // x-coordinate of source upper-left corner
int nYSrc, // y-coordinate of source upper-left corner
int nWidthSrc,
int nHeightSrc,
uint dwRop // raster operation code
);
const int GPTR = 0x40;
[DllImport("coredll.dll")]
private static extern IntPtr LocalAlloc(uint flags, int cb);
[DllImport("coredll.dll")]
private static extern IntPtr LocalFree(IntPtr hMem);
// Although the code only works for 24 bit images, it will also produce raw data for an 8bit paletteized image if a palette is supplied right below where bmi is instantiated. (see comment)
Change bmi.biBitCount to 8.
Example of palette:
// Standard colors
byte [] pal = {
0x00, 0x00, 0x00, 0 , // System palette - first 10 colors
0x80, 0x00, 0x00, 0 ,
0x00, 0x80, 0x00, 0 ,
0x80, 0x80, 0x00, 0 ,
0x00, 0x00, 0x80, 0 ,
0x80, 0x00, 0x80, 0 ,
0x00, 0x80, 0x80, 0 ,
0xC0, 0xC0, 0xC0, 0 ,
0xC0, 0xDC, 0xC0, 0 ,
0xA6, 0xCA, 0xF0, 0 ,
0x2C, 0x00, 0x00, 0 ,
0x56, 0x00, 0x00, 0 ,
0x87, 0x00, 0x00, 0 ,
0xC0, 0x00, 0x00, 0 ,
0xFF, 0x00, 0x00, 0 ,
0x00, 0x2C, 0x00, 0 ,
0x2C, 0x2C, 0x00, 0 ,
0x56, 0x2C, 0x00, 0 ,
0x87, 0x2C, 0x00, 0 ,
0xC0, 0x2C, 0x00, 0 ,
0xFF, 0x2C, 0x00, 0 ,
0x00, 0x56, 0x00, 0 ,
0x2C, 0x56, 0x00, 0 ,
0x56, 0x56, 0x00, 0 ,
0x87, 0x56, 0x00, 0 ,
0xC0, 0x56, 0x00, 0 ,
0xFF, 0x56, 0x00, 0 ,
0x00, 0x87, 0x00, 0 ,
0x2C, 0x87, 0x00, 0 ,
0x56, 0x87, 0x00, 0 ,
0x87, 0x87, 0x00, 0 ,
0xC0, 0x87, 0x00, 0 ,
0xFF, 0x87, 0x00, 0 ,
0x00, 0xC0, 0x00, 0 ,
0x2C, 0xC0, 0x00, 0 ,
0x56, 0xC0, 0x00, 0 ,
0x87, 0xC0, 0x00, 0 ,
0xC0, 0xC0, 0x00, 0 ,
0xFF, 0xC0, 0x00, 0 ,
0x00, 0xFF, 0x00, 0 ,
0x2C, 0xFF, 0x00, 0 ,
0x56, 0xFF, 0x00, 0 ,
0x87, 0xFF, 0x00, 0 ,
0xC0, 0xFF, 0x00, 0 ,
0xFF, 0xFF, 0x00, 0 ,
0x00, 0x00, 0x2C, 0 ,
0x2C, 0x00, 0x2C, 0 ,
0x56, 0x00, 0x2C, 0 ,
0x87, 0x00, 0x2C, 0 ,
0xC0, 0x00, 0x2C, 0 ,
0xFF, 0x00, 0x2C, 0 ,
0x00, 0x2C, 0x2C, 0 ,
0x2C, 0x2C, 0x2C, 0 ,
0x56, 0x2C, 0x2C, 0 ,
0x87, 0x2C, 0x2C, 0 ,
0xC0, 0x2C, 0x2C, 0 ,
0xFF, 0x2C, 0x2C, 0 ,
0x00, 0x56, 0x2C, 0 ,
0x2C, 0x56, 0x2C, 0 ,
0x56, 0x56, 0x2C, 0 ,
0x87, 0x56, 0x2C, 0 ,
0xC0, 0x56, 0x2C, 0 ,
0xFF, 0x56, 0x2C, 0 ,
0x00, 0x87, 0x2C, 0 ,
0x2C, 0x87, 0x2C, 0 ,
0x56, 0x87, 0x2C, 0 ,
0x87, 0x87, 0x2C, 0 ,
0xC0, 0x87, 0x2C, 0 ,
0xFF, 0x87, 0x2C, 0 ,
0x00, 0xC0, 0x2C, 0 ,
0x2C, 0xC0, 0x2C, 0 ,
0x56, 0xC0, 0x2C, 0 ,
0x87, 0xC0, 0x2C, 0 ,
0xC0, 0xC0, 0x2C, 0 ,
0xFF, 0xC0, 0x2C, 0 ,
0x00, 0xFF, 0x2C, 0 ,
0x2C, 0xFF, 0x2C, 0 ,
0x56, 0xFF, 0x2C, 0 ,
0x87, 0xFF, 0x2C, 0 ,
0xC0, 0xFF, 0x2C, 0 ,
0xFF, 0xFF, 0x2C, 0 ,
0x00, 0x00, 0x56, 0 ,
0x2C, 0x00, 0x56, 0 ,
0x56, 0x00, 0x56, 0 ,
0x87, 0x00, 0x56, 0 ,
0xC0, 0x00, 0x56, 0 ,
0xFF, 0x00, 0x56, 0 ,
0x00, 0x2C, 0x56, 0 ,
0x2C, 0x2C, 0x56, 0 ,
0x56, 0x2C, 0x56, 0 ,
0x87, 0x2C, 0x56, 0 ,
0xC0, 0x2C, 0x56, 0 ,
0xFF, 0x2C, 0x56, 0 ,
0x00, 0x56, 0x56, 0 ,
0x2C, 0x56, 0x56, 0 ,
0x56, 0x56, 0x56, 0 ,
0x87, 0x56, 0x56, 0 ,
0xC0, 0x56, 0x56, 0 ,
0xFF, 0x56, 0x56, 0 ,
0x00, 0x87, 0x56, 0 ,
0x2C, 0x87, 0x56, 0 ,
0x56, 0x87, 0x56, 0 ,
0x87, 0x87, 0x56, 0 ,
0xC0, 0x87, 0x56, 0 ,
0xFF, 0x87, 0x56, 0 ,
0x00, 0xC0, 0x56, 0 ,
0x2C, 0xC0, 0x56, 0 ,
0x56, 0xC0, 0x56, 0 ,
0x87, 0xC0, 0x56, 0 ,
0xC0, 0xC0, 0x56, 0 ,
0xFF, 0xC0, 0x56, 0 ,
0x00, 0xFF, 0x56, 0 ,
0x2C, 0xFF, 0x56, 0 ,
0x56, 0xFF, 0x56, 0 ,
0x87, 0xFF, 0x56, 0 ,
0xC0, 0xFF, 0x56, 0 ,
0xFF, 0xFF, 0x56, 0 ,
0x00, 0x00, 0x87, 0 ,
0x2C, 0x00, 0x87, 0 ,
0x56, 0x00, 0x87, 0 ,
0x87, 0x00, 0x87, 0 ,
0xC0, 0x00, 0x87, 0 ,
0xFF, 0x00, 0x87, 0 ,
0x00, 0x2C, 0x87, 0 ,
0x2C, 0x2C, 0x87, 0 ,
0x56, 0x2C, 0x87, 0 ,
0x87, 0x2C, 0x87, 0 ,
0xC0, 0x2C, 0x87, 0 ,
0xFF, 0x2C, 0x87, 0 ,
0x00, 0x56, 0x87, 0 ,
0x2C, 0x56, 0x87, 0 ,
0x56, 0x56, 0x87, 0 ,
0x87, 0x56, 0x87, 0 ,
0xC0, 0x56, 0x87, 0 ,
0xFF, 0x56, 0x87, 0 ,
0x00, 0x87, 0x87, 0 ,
0x2C, 0x87, 0x87, 0 ,
0x56, 0x87, 0x87, 0 ,
0x87, 0x87, 0x87, 0 ,
0xC0, 0x87, 0x87, 0 ,
0xFF, 0x87, 0x87, 0 ,
0x00, 0xC0, 0x87, 0 ,
0x2C, 0xC0, 0x87, 0 ,
0x56, 0xC0, 0x87, 0 ,
0x87, 0xC0, 0x87, 0 ,
0xC0, 0xC0, 0x87, 0 ,
0xFF, 0xC0, 0x87, 0 ,
0x00, 0xFF, 0x87, 0 ,
0x2C, 0xFF, 0x87, 0 ,
0x56, 0xFF, 0x87, 0 ,
0x87, 0xFF, 0x87, 0 ,
0xC0, 0xFF, 0x87, 0 ,
0xFF, 0xFF, 0x87, 0 ,
0x00, 0x00, 0xC0, 0 ,
0x2C, 0x00, 0xC0, 0 ,
0x56, 0x00, 0xC0, 0 ,
0x87, 0x00, 0xC0, 0 ,
0xC0, 0x00, 0xC0, 0 ,
0xFF, 0x00, 0xC0, 0 ,
0x00, 0x2C, 0xC0, 0 ,
0x2C, 0x2C, 0xC0, 0 ,
0x56, 0x2C, 0xC0, 0 ,
0x87, 0x2C, 0xC0, 0 ,
0xC0, 0x2C, 0xC0, 0 ,
0xFF, 0x2C, 0xC0, 0 ,
0x00, 0x56, 0xC0, 0 ,
0x2C, 0x56, 0xC0, 0 ,
0x56, 0x56, 0xC0, 0 ,
0x87, 0x56, 0xC0, 0 ,
0xC0, 0x56, 0xC0, 0 ,
0xFF, 0x56, 0xC0, 0 ,
0x00, 0x87, 0xC0, 0 ,
0x2C, 0x87, 0xC0, 0 ,
0x56, 0x87, 0xC0, 0 ,
0x87, 0x87, 0xC0, 0 ,
0xC0, 0x87, 0xC0, 0 ,
0xFF, 0x87, 0xC0, 0 ,
0x00, 0xC0, 0xC0, 0 ,
0x2C, 0xC0, 0xC0, 0 ,
0x56, 0xC0, 0xC0, 0 ,
0x87, 0xC0, 0xC0, 0 ,
0xFF, 0xC0, 0xC0, 0 ,
0x00, 0xFF, 0xC0, 0 ,
0x2C, 0xFF, 0xC0, 0 ,
0x56, 0xFF, 0xC0, 0 ,
0x87, 0xFF, 0xC0, 0 ,
0xC0, 0xFF, 0xC0, 0 ,
0xFF, 0xFF, 0xC0, 0 ,
0x00, 0x00, 0xFF, 0 ,
0x2C, 0x00, 0xFF, 0 ,
0x56, 0x00, 0xFF, 0 ,
0x87, 0x00, 0xFF, 0 ,
0xC0, 0x00, 0xFF, 0 ,
0xFF, 0x00, 0xFF, 0 ,
0x00, 0x2C, 0xFF, 0 ,
0x2C, 0x2C, 0xFF, 0 ,
0x56, 0x2C, 0xFF, 0 ,
0x87, 0x2C, 0xFF, 0 ,
0xC0, 0x2C, 0xFF, 0 ,
0xFF, 0x2C, 0xFF, 0 ,
0x00, 0x56, 0xFF, 0 ,
0x2C, 0x56, 0xFF, 0 ,
0x56, 0x56, 0xFF, 0 ,
0x87, 0x56, 0xFF, 0 ,
0xC0, 0x56, 0xFF, 0 ,
0xFF, 0x56, 0xFF, 0 ,
0x00, 0x87, 0xFF, 0 ,
0x2C, 0x87, 0xFF, 0 ,
0x56, 0x87, 0xFF, 0 ,
0x87, 0x87, 0xFF, 0 ,
0xC0, 0x87, 0xFF, 0 ,
0xFF, 0x87, 0xFF, 0 ,
0x00, 0xC0, 0xFF, 0 ,
0x2C, 0xC0, 0xFF, 0 ,
0x56, 0xC0, 0xFF, 0 ,
0x87, 0xC0, 0xFF, 0 ,
0xC0, 0xC0, 0xFF, 0 ,
0xFF, 0xC0, 0xFF, 0 ,
0x2C, 0xFF, 0xFF, 0 ,
0x56, 0xFF, 0xFF, 0 ,
0x87, 0xFF, 0xFF, 0 ,
0xC0, 0xFF, 0xFF, 0 ,
0xFF, 0xFF, 0xFF, 0 ,
0x11, 0x11, 0x11, 0 ,
0x18, 0x18, 0x18, 0 ,
0x1E, 0x1E, 0x1E, 0 ,
0x25, 0x25, 0x25, 0 ,
0x2C, 0x2C, 0x2C, 0 ,
0x34, 0x34, 0x34, 0 ,
0x3C, 0x3C, 0x3C, 0 ,
0x44, 0x44, 0x44, 0 ,
0x4D, 0x4D, 0x4D, 0 ,
0x56, 0x56, 0x56, 0 ,
0x5F, 0x5F, 0x5F, 0 ,
0x69, 0x69, 0x69, 0 ,
0x72, 0x72, 0x72, 0 ,
0x7D, 0x7D, 0x7D, 0 ,
0x92, 0x92, 0x92, 0 ,
0x9D, 0x9D, 0x9D, 0 ,
0xA8, 0xA8, 0xA8, 0 ,
0xB4, 0xB4, 0xB4, 0 ,
0xCC, 0xCC, 0xCC, 0 ,
0xD8, 0xD8, 0xD8, 0 ,
0xE5, 0xE5, 0xE5, 0 ,
0xF2, 0xF2, 0xF2, 0 ,
0xFF, 0xFF, 0xFF, 0 ,
0xFF, 0xFB, 0xF0, 0 , // System palette - last 10 colors
0xA0, 0xA0, 0xA4, 0 ,
0x80, 0x80, 0x80, 0 ,
0xFF, 0x00, 0x00, 0 ,
0x00, 0xFF, 0x00, 0 ,
0xFF, 0xFF, 0x00, 0 ,
0x00, 0x00, 0xFF, 0 ,
0xFF, 0x00, 0xFF, 0 ,
0x00, 0xFF, 0xFF, 0 ,
0xFF, 0xFF, 0xFF, 0 ,
};
Warnings:
The uncompressed size of the full screen is large for 24 bits resolution, 320 X 240 X 4 = 307200 bytes of raw data.
If you are stepping through this raw data looking for you image, remember that the image is by default upside down.
Although inserting the palette this way worked for my demo, I believe the correct way to use it is to copy the structures and palette to unmanaged code using Marshal.Copy.
\Martin Stave