Skip to content

Instantly share code, notes, and snippets.

@ynkdir
Last active December 15, 2015 18:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ynkdir/5301490 to your computer and use it in GitHub Desktop.
Save ynkdir/5301490 to your computer and use it in GitHub Desktop.
drawcustom.dll: draw background image
/* compile: cl /MD /LD drawcusotm.c user32.lib gdi32.lib
* usage: :echo libcall('C:\fullpath\to\drawcustom.dll', 'init', '/path/to/background-image.bmp')
* bmp is loaded with LoadImage() API. And it doesn't support BITMAPV4HEADER and above.
*/
#include <windows.h>
#include <string.h>
typedef int (WINAPI *fFillRect)(HDC hDC, CONST RECT *lprc, HBRUSH hbr);
typedef BOOL (WINAPI *fExtTextOutA)(HDC, int, int, UINT, CONST RECT *, LPCSTR, UINT, CONST INT *);
typedef BOOL (WINAPI *fExtTextOutW)(HDC, int, int, UINT, CONST RECT *, LPCWSTR, UINT, CONST INT *);
typedef int (WINAPI *fScrollWindowEx)(HWND, int, int, CONST RECT *, CONST RECT *, HRGN, LPRECT, UINT);
int WINAPI MyFillRect(HDC hDC, CONST RECT *lprc, HBRUSH hbr);
BOOL WINAPI MyExtTextOutA(HDC hdc, int X, int Y, UINT fuOptions, CONST RECT *lprc, LPCSTR lpString, UINT cbCount, CONST INT *lpDx);
BOOL WINAPI MyExtTextOutW(HDC hdc, int X, int Y, UINT fuOptions, CONST RECT *lprc, LPCWSTR lpString, UINT cbCount, CONST INT *lpDx);
int WINAPI MyScrollWindowEx(HWND hWnd, int dx, int dy, CONST RECT *prcScroll, CONST RECT *prcClip, HRGN hrgnUpdate, LPRECT prcUpdate, UINT flags);
BOOL LoadBitmapFromBMPFile( LPCTSTR szFileName, HBITMAP *phBitmap, HPALETTE *phPalette);
HDC create_image_dc(const char *szFileName);
BOOL install_hook(HINSTANCE hInst, const char *funcname, FARPROC* orig, FARPROC newfn);
void show_lasterr();
__declspec(dllexport) const char *init(const char *args);
HINSTANCE hinstSelf = NULL;
HDC hdcimage = NULL;
fFillRect OrigFillRect = NULL;
fExtTextOutA OrigExtTextOutA = NULL;
fExtTextOutW OrigExtTextOutW = NULL;
fScrollWindowEx OrigScrollWindowEx = NULL;
int WINAPI
MyFillRect(HDC hDC, CONST RECT *lprc, HBRUSH hbr)
{
int ret;
/* Fill with red. */
//HBRUSH b = CreateSolidBrush(lprc->top % 0xFF); /* BBGGRR */
//ret = OrigFillRect(hDC, lprc, b);
//DeleteObject(b);
//return ret;
BitBlt(hDC, lprc->left, lprc->top, lprc->right - lprc->left, lprc->bottom - lprc->top, hdcimage, lprc->left, lprc->top, SRCCOPY);
return TRUE;
}
/* ExtTextOutA is always used for ASCII text even if encoding=utf-8. */
BOOL WINAPI
MyExtTextOutA(HDC hdc, int X, int Y, UINT fuOptions, CONST RECT *lprc, LPCSTR lpString, UINT cbCount, CONST INT *lpDx)
{
#if 0
BOOL ret;
char *buf;
int i;
buf = strdup(lpString);
/* Swap X and Y */
for (i = 0; i < cbCount; ++i)
{
if (buf[i] == 'X')
buf[i] = 'Y';
else if (buf[i] == 'Y')
buf[i] = 'X';
}
ret = OrigExtTextOutA(hdc, X, Y, fuOptions, lprc, buf, cbCount, lpDx);
free(buf);
return ret;
#endif
return OrigExtTextOutA(hdc, X, Y, fuOptions, lprc, lpString, cbCount, lpDx);
}
BOOL WINAPI
MyExtTextOutW(HDC hdc, int X, int Y, UINT fuOptions, CONST RECT *lprc, LPCWSTR lpString, UINT cbCount, CONST INT *lpDx)
{
return OrigExtTextOutW(hdc, X, Y, fuOptions, lprc, lpString, cbCount, lpDx);
}
int WINAPI
MyScrollWindowEx(HWND hWnd, int dx, int dy, CONST RECT *prcScroll, CONST RECT *prcClip, HRGN hrgnUpdate, LPRECT prcUpdate, UINT flags)
{
// return OrigScrollWindowEx(hWnd, dx, dy, prcScroll, prcClip, hrgnUpdate, prcUpdate, flags);
RedrawWindow(hWnd, NULL, NULL, RDW_INVALIDATE);
return TRUE;
}
/* http://support.microsoft.com/kb/158898/ja */
BOOL
LoadBitmapFromBMPFile( LPCTSTR szFileName, HBITMAP *phBitmap, HPALETTE *phPalette)
{
BITMAP bm;
*phBitmap = NULL;
*phPalette = NULL;
// Use LoadImage() to get the image loaded into a DIBSection
*phBitmap = (HBITMAP)LoadImage( NULL, szFileName, IMAGE_BITMAP, 0, 0,
LR_CREATEDIBSECTION | LR_LOADFROMFILE);
if( *phBitmap == NULL )
return FALSE;
// Get the color depth of the DIBSection
GetObject(*phBitmap, sizeof(BITMAP), &bm );
// If the DIBSection is 256 color or less, it has a color table
if( ( bm.bmBitsPixel * bm.bmPlanes ) <= 8 )
{
HDC hMemDC;
HBITMAP hOldBitmap;
RGBQUAD rgb[256];
LPLOGPALETTE pLogPal;
WORD i;
// Create a memory DC and select the DIBSection into it
hMemDC = CreateCompatibleDC( NULL );
hOldBitmap = (HBITMAP)SelectObject( hMemDC, *phBitmap );
// Get the DIBSection's color table
GetDIBColorTable( hMemDC, 0, 256, rgb );
// Create a palette from the color tabl
pLogPal = (LOGPALETTE *)malloc( sizeof(LOGPALETTE) + (256*sizeof(PALETTEENTRY)) );
pLogPal->palVersion = 0x300;
pLogPal->palNumEntries = 256;
for(i=0;i<256;i++)
{
pLogPal->palPalEntry[i].peRed = rgb[i].rgbRed;
pLogPal->palPalEntry[i].peGreen = rgb[i].rgbGreen;
pLogPal->palPalEntry[i].peBlue = rgb[i].rgbBlue;
pLogPal->palPalEntry[i].peFlags = 0;
}
*phPalette = CreatePalette( pLogPal );
// Clean up
free( pLogPal );
SelectObject( hMemDC, hOldBitmap );
DeleteDC( hMemDC );
}
else // It has no color table, so use a halftone palette
{
HDC hRefDC;
hRefDC = GetDC( NULL );
*phPalette = CreateHalftonePalette( hRefDC );
ReleaseDC( NULL, hRefDC );
}
return TRUE;
}
HDC
create_image_dc(const char *szFileName)
{
PAINTSTRUCT ps;
HBITMAP hBitmap, hOldBitmap;
HPALETTE hPalette, hOldPalette;
HDC hDC, hMemDC;
BITMAP bm;
if (!LoadBitmapFromBMPFile( szFileName, &hBitmap, &hPalette))
return NULL;
hDC = GetDC(NULL);
GetObject( hBitmap, sizeof(BITMAP), &bm );
hMemDC = CreateCompatibleDC( hDC );
hOldBitmap = (HBITMAP)SelectObject( hMemDC, hBitmap );
/* hOldPalette = SelectPalette( hDC, hPalette, FALSE ); */
/* RealizePalette( hDC ); */
ReleaseDC(NULL, hDC);
return hMemDC;
/*
BitBlt( hDC, 0, 0, bm.bmWidth, bm.bmHeight,
hMemDC, 0, 0, SRCCOPY );
SelectObject( hMemDC, hOldBitmap );
DeleteObject( hBitmap );
SelectPalette( hDC, hOldPalette, FALSE );
DeleteObject( hPalette );
*/
}
BOOL
install_hook(HINSTANCE hInst, const char *funcname, FARPROC* orig, FARPROC newfn)
{
PBYTE pImage = (PBYTE)hInst;
PIMAGE_DOS_HEADER pDOS = (PIMAGE_DOS_HEADER)hInst;
PIMAGE_NT_HEADERS pPE;
PIMAGE_IMPORT_DESCRIPTOR pImpDesc;
PIMAGE_THUNK_DATA pIAT; /* Import Address Table */
PIMAGE_THUNK_DATA pINT; /* Import Name Table */
PIMAGE_IMPORT_BY_NAME pImpName;
if (pDOS->e_magic != IMAGE_DOS_SIGNATURE)
return FALSE;
pPE = (PIMAGE_NT_HEADERS)(pImage + pDOS->e_lfanew);
if (pPE->Signature != IMAGE_NT_SIGNATURE)
return FALSE;
pImpDesc = (PIMAGE_IMPORT_DESCRIPTOR)(pImage
+ pPE->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]
.VirtualAddress);
for (; pImpDesc->FirstThunk; ++pImpDesc)
{
if (!pImpDesc->OriginalFirstThunk)
continue;
pIAT = (PIMAGE_THUNK_DATA)(pImage + pImpDesc->FirstThunk);
pINT = (PIMAGE_THUNK_DATA)(pImage + pImpDesc->OriginalFirstThunk);
for (; pIAT->u1.Function; ++pIAT, ++pINT)
{
if (IMAGE_SNAP_BY_ORDINAL(pINT->u1.Ordinal))
continue;
pImpName = (PIMAGE_IMPORT_BY_NAME)(pImage
+ (UINT_PTR)(pINT->u1.AddressOfData));
if (strcmp(pImpName->Name, funcname) == 0)
{
FARPROC *ppfn = (FARPROC*)&pIAT->u1.Function;
DWORD dwDummy;
*orig = (FARPROC)(*ppfn);
if (!VirtualProtect(ppfn, sizeof(ppfn), PAGE_EXECUTE_READWRITE, &dwDummy))
return FALSE;
if (!WriteProcessMemory(GetCurrentProcess(), ppfn, &newfn, sizeof(newfn), NULL))
return FALSE;
return TRUE;
}
}
}
return TRUE;
}
void
show_lasterr()
{
LPVOID lpMessageBuffer;
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lpMessageBuffer,
0,
NULL );
MessageBox(NULL, lpMessageBuffer, "Error", MB_OK);
LocalFree( lpMessageBuffer );
}
__declspec(dllexport)
const char *
init(const char *bgimage)
{
char buf[1024];
GetModuleFileName(hinstSelf, buf, sizeof(buf));
LoadLibrary(buf);
hdcimage = create_image_dc(bgimage);
if (hdcimage == NULL)
{
show_lasterr();
return "NG";
}
if (!install_hook((HINSTANCE)GetModuleHandle(NULL), "FillRect", (FARPROC*)&OrigFillRect, (FARPROC)MyFillRect))
{
show_lasterr();
return "NG";
}
if (!install_hook((HINSTANCE)GetModuleHandle(NULL), "ExtTextOutA", (FARPROC*)&OrigExtTextOutA, (FARPROC)MyExtTextOutA))
{
show_lasterr();
return "NG";
}
if (!install_hook((HINSTANCE)GetModuleHandle(NULL), "ExtTextOutW", (FARPROC*)&OrigExtTextOutW, (FARPROC)MyExtTextOutW))
{
show_lasterr();
return "NG";
}
if (!install_hook((HINSTANCE)GetModuleHandle(NULL), "ScrollWindowEx", (FARPROC*)&OrigScrollWindowEx, (FARPROC)MyScrollWindowEx))
{
show_lasterr();
return "NG";
}
return "OK";
}
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
switch(fdwReason)
{
case DLL_PROCESS_ATTACH:
hinstSelf = hinstDLL;
break;
case DLL_PROCESS_DETACH:
break;
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
break;
}
return TRUE;
}
/* gdiplus version
* compile: cl /MD /LD drawcusotm.cpp user32.lib gdi32.lib gdiplus.lib
* usage: :echo libcall('C:\fullpath\to\drawcustom.dll', 'init', '/path/to/background-image.bmp')
* png and jpg also can be used.
*/
#include <windows.h>
#include <gdiplus.h>
#include <string.h>
extern "C" {
typedef int (WINAPI *fFillRect)(HDC hDC, CONST RECT *lprc, HBRUSH hbr);
typedef BOOL (WINAPI *fExtTextOutA)(HDC, int, int, UINT, CONST RECT *, LPCSTR, UINT, CONST INT *);
typedef BOOL (WINAPI *fExtTextOutW)(HDC, int, int, UINT, CONST RECT *, LPCWSTR, UINT, CONST INT *);
typedef int (WINAPI *fScrollWindowEx)(HWND, int, int, CONST RECT *, CONST RECT *, HRGN, LPRECT, UINT);
int WINAPI MyFillRect(HDC hDC, CONST RECT *lprc, HBRUSH hbr);
BOOL WINAPI MyExtTextOutA(HDC hdc, int X, int Y, UINT fuOptions, CONST RECT *lprc, LPCSTR lpString, UINT cbCount, CONST INT *lpDx);
BOOL WINAPI MyExtTextOutW(HDC hdc, int X, int Y, UINT fuOptions, CONST RECT *lprc, LPCWSTR lpString, UINT cbCount, CONST INT *lpDx);
int WINAPI MyScrollWindowEx(HWND hWnd, int dx, int dy, CONST RECT *prcScroll, CONST RECT *prcClip, HRGN hrgnUpdate, LPRECT prcUpdate, UINT flags);
wchar_t *mbtowide(const char *mb);
BOOL load_image(const char *filename, Gdiplus::Image **pimg);
BOOL install_hook(HINSTANCE hInst, const char *funcname, FARPROC* orig, FARPROC newfn);
void show_lasterr();
__declspec(dllexport) const char *init(const char *args);
HINSTANCE hinstSelf = NULL;
HDC hdcimage = NULL;
Gdiplus::Image *bgimage;
fFillRect OrigFillRect = NULL;
fExtTextOutA OrigExtTextOutA = NULL;
fExtTextOutW OrigExtTextOutW = NULL;
fScrollWindowEx OrigScrollWindowEx = NULL;
int WINAPI
MyFillRect(HDC hDC, CONST RECT *lprc, HBRUSH hbr)
{
#if 0
/* drwaing scaled image is slow */
Gdiplus::Graphics g(hDC);
double scale = g.GetDpiX() / bgimage->GetHorizontalResolution();
Gdiplus::REAL x = lprc->left;
Gdiplus::REAL y = lprc->top;
Gdiplus::REAL srcx = lprc->left / scale;
Gdiplus::REAL srcy = lprc->top / scale;
Gdiplus::REAL srcwidth = (lprc->right - lprc->left) / scale;
Gdiplus::REAL srcheight = (lprc->bottom - lprc->top) / scale;
Gdiplus::GpUnit srcUnit = Gdiplus::UnitPixel;
g.DrawImage(bgimage, x, y, srcx, srcy, srcwidth, srcheight, srcUnit);
#else
BitBlt(hDC, lprc->left, lprc->top, lprc->right - lprc->left, lprc->bottom - lprc->top, hdcimage, lprc->left, lprc->top, SRCCOPY);
#endif
return TRUE;
}
/* ExtTextOutA is always used for ASCII text even if encoding=utf-8. */
BOOL WINAPI
MyExtTextOutA(HDC hdc, int X, int Y, UINT fuOptions, CONST RECT *lprc, LPCSTR lpString, UINT cbCount, CONST INT *lpDx)
{
#if 0
BOOL ret;
char *buf;
int i;
buf = strdup(lpString);
/* Swap X and Y */
for (i = 0; i < cbCount; ++i)
{
if (buf[i] == 'X')
buf[i] = 'Y';
else if (buf[i] == 'Y')
buf[i] = 'X';
}
ret = OrigExtTextOutA(hdc, X, Y, fuOptions, lprc, buf, cbCount, lpDx);
free(buf);
return ret;
#endif
return OrigExtTextOutA(hdc, X, Y, fuOptions, lprc, lpString, cbCount, lpDx);
}
BOOL WINAPI
MyExtTextOutW(HDC hdc, int X, int Y, UINT fuOptions, CONST RECT *lprc, LPCWSTR lpString, UINT cbCount, CONST INT *lpDx)
{
return OrigExtTextOutW(hdc, X, Y, fuOptions, lprc, lpString, cbCount, lpDx);
}
int WINAPI
MyScrollWindowEx(HWND hWnd, int dx, int dy, CONST RECT *prcScroll, CONST RECT *prcClip, HRGN hrgnUpdate, LPRECT prcUpdate, UINT flags)
{
//return OrigScrollWindowEx(hWnd, dx, dy, prcScroll, prcClip, hrgnUpdate, prcUpdate, flags);
RedrawWindow(hWnd, NULL, NULL, RDW_INVALIDATE);
return TRUE;
}
wchar_t *
mbtowide(const char *mb)
{
DWORD len;
wchar_t *wc;
len = MultiByteToWideChar(CP_ACP, 0, mb, -1, NULL, 0);
if (len == 0)
return NULL;
wc = (wchar_t *)malloc(sizeof(wchar_t) * len);
if (wc == NULL)
return NULL;
if (MultiByteToWideChar(CP_ACP, 0, mb, -1, wc, len) != len)
return NULL;
return wc;
}
BOOL
load_image(const char *filename, Gdiplus::Image **pimg)
{
wchar_t *wfilename;
wfilename = mbtowide(filename);
if (wfilename == NULL)
return FALSE;
*pimg = Gdiplus::Image::FromFile(wfilename, FALSE);
free(wfilename);
HDC screen = GetDC(NULL);
HDC hDC = CreateCompatibleDC(screen);
HBITMAP hBM = CreateCompatibleBitmap(screen, (*pimg)->GetWidth(), (*pimg)->GetHeight());
SelectObject(hDC, hBM);
ReleaseDC(NULL, screen);
Gdiplus::Graphics g(hDC);
double scale = g.GetDpiX() / bgimage->GetHorizontalResolution();
g.ScaleTransform(1.0 / scale, 1.0 / scale);
g.DrawImage(*pimg, 0, 0);
hdcimage = hDC;
*pimg = Gdiplus::Bitmap::FromHBITMAP(hBM, NULL);
return TRUE;
}
BOOL
install_hook(HINSTANCE hInst, const char *funcname, FARPROC* orig, FARPROC newfn)
{
PBYTE pImage = (PBYTE)hInst;
PIMAGE_DOS_HEADER pDOS = (PIMAGE_DOS_HEADER)hInst;
PIMAGE_NT_HEADERS pPE;
PIMAGE_IMPORT_DESCRIPTOR pImpDesc;
PIMAGE_THUNK_DATA pIAT; /* Import Address Table */
PIMAGE_THUNK_DATA pINT; /* Import Name Table */
PIMAGE_IMPORT_BY_NAME pImpName;
if (pDOS->e_magic != IMAGE_DOS_SIGNATURE)
return FALSE;
pPE = (PIMAGE_NT_HEADERS)(pImage + pDOS->e_lfanew);
if (pPE->Signature != IMAGE_NT_SIGNATURE)
return FALSE;
pImpDesc = (PIMAGE_IMPORT_DESCRIPTOR)(pImage
+ pPE->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]
.VirtualAddress);
for (; pImpDesc->FirstThunk; ++pImpDesc)
{
if (!pImpDesc->OriginalFirstThunk)
continue;
pIAT = (PIMAGE_THUNK_DATA)(pImage + pImpDesc->FirstThunk);
pINT = (PIMAGE_THUNK_DATA)(pImage + pImpDesc->OriginalFirstThunk);
for (; pIAT->u1.Function; ++pIAT, ++pINT)
{
if (IMAGE_SNAP_BY_ORDINAL(pINT->u1.Ordinal))
continue;
pImpName = (PIMAGE_IMPORT_BY_NAME)(pImage
+ (UINT_PTR)(pINT->u1.AddressOfData));
if (strcmp((const char *)pImpName->Name, funcname) == 0)
{
FARPROC *ppfn = (FARPROC*)&pIAT->u1.Function;
DWORD dwDummy;
*orig = (FARPROC)(*ppfn);
if (!VirtualProtect(ppfn, sizeof(ppfn), PAGE_EXECUTE_READWRITE, &dwDummy))
return FALSE;
if (!WriteProcessMemory(GetCurrentProcess(), ppfn, &newfn, sizeof(newfn), NULL))
return FALSE;
return TRUE;
}
}
}
return TRUE;
}
void
show_lasterr()
{
LPVOID lpMessageBuffer;
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lpMessageBuffer,
0,
NULL );
MessageBox(NULL, (LPTSTR)lpMessageBuffer, "Error", MB_OK);
LocalFree( lpMessageBuffer );
}
__declspec(dllexport)
const char *
init(const char *bgimagefile)
{
char buf[1024];
GetModuleFileName(hinstSelf, buf, sizeof(buf));
LoadLibrary(buf);
if (!load_image(bgimagefile, &bgimage))
{
show_lasterr();
return "NG";
}
if (!install_hook((HINSTANCE)GetModuleHandle(NULL), "FillRect", (FARPROC*)&OrigFillRect, (FARPROC)MyFillRect))
{
show_lasterr();
return "NG";
}
if (!install_hook((HINSTANCE)GetModuleHandle(NULL), "ExtTextOutA", (FARPROC*)&OrigExtTextOutA, (FARPROC)MyExtTextOutA))
{
show_lasterr();
return "NG";
}
if (!install_hook((HINSTANCE)GetModuleHandle(NULL), "ExtTextOutW", (FARPROC*)&OrigExtTextOutW, (FARPROC)MyExtTextOutW))
{
show_lasterr();
return "NG";
}
if (!install_hook((HINSTANCE)GetModuleHandle(NULL), "ScrollWindowEx", (FARPROC*)&OrigScrollWindowEx, (FARPROC)MyScrollWindowEx))
{
show_lasterr();
return "NG";
}
return "OK";
}
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
Gdiplus::GdiplusStartupInput gdiplusStartupInput;
static ULONG_PTR gdiplusToken;
switch(fdwReason)
{
case DLL_PROCESS_ATTACH:
hinstSelf = hinstDLL;
Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
break;
case DLL_PROCESS_DETACH:
Gdiplus::GdiplusShutdown(gdiplusToken);
break;
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
break;
}
return TRUE;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment