CmdBkg - use bitmap as background to console window
Moderator: DosItHelp
CmdBkg - use bitmap as background to console window
This idea was given to me by aGerman, who said he had been thinking about it for years but never actually tried it. So, I decided to give it a go. aGerman also helped out in testing the first version of this.
What is it?
cmdbkg.exe lets you use a BMP, JPG, GIF, or PNG image as console window background.
The image will automatically stretch to whatever window size is currently used.
Where?
Here : http://www.mediafire.com/download/628nj ... cmdbkg.zip
(Open in new tab if it doesn't load properly)
The archive also contains the source code.
Version?
The current version of CmdBkg is 2.1.
Usage?
Since version 2.0, CmdBkg uses the following format:
cmdbkg ["file.[bmp|jpg|gif|png]" [/t p] [/c [bg]] [/b]]
/t Transparency with p = percentage 0..100 (default 33).
/c Center image and preserve aspect ratio. Optionally specify background color with hex value format RRGGBB (default is the color of the console buffer's upper left corner on start).
/b Borders included.
Specify no arguments to remove previous background.
Specify /? as first argument to see this help message.
Screenshots (using syntax from pre 2.0 version):
What is it?
cmdbkg.exe lets you use a BMP, JPG, GIF, or PNG image as console window background.
The image will automatically stretch to whatever window size is currently used.
Where?
Here : http://www.mediafire.com/download/628nj ... cmdbkg.zip
(Open in new tab if it doesn't load properly)
The archive also contains the source code.
Version?
The current version of CmdBkg is 2.1.
Usage?
Since version 2.0, CmdBkg uses the following format:
cmdbkg ["file.[bmp|jpg|gif|png]" [/t p] [/c [bg]] [/b]]
/t Transparency with p = percentage 0..100 (default 33).
/c Center image and preserve aspect ratio. Optionally specify background color with hex value format RRGGBB (default is the color of the console buffer's upper left corner on start).
/b Borders included.
Specify no arguments to remove previous background.
Specify /? as first argument to see this help message.
Screenshots (using syntax from pre 2.0 version):
Last edited by misol101 on 30 Dec 2020 17:47, edited 7 times in total.
Re: CmdBkg - use bitmap as background to console window
Additional info:
No longer true since version 1.1, do NOT start cmdbkg with "start" !
1. If run from a batch script, cmdbkg.exe will block the script. To prevent this, it has to be run with: start "" cmdbkg.exe
2. It's possible to have different background images for different console windows. New console windows do not inherit the background of its parent.
3. It's possible to tint the image quite nicely by using the "color" command, e.g. "color d0" for a shade of purple with black text.
4. Transparency can be adjusted without reloading the image by using my other tool "cmdwiz.exe" with operation "setwindowtransparency".
No longer true since version 1.1, do NOT start cmdbkg with "start" !
1. If run from a batch script, cmdbkg.exe will block the script. To prevent this, it has to be run with: start "" cmdbkg.exe
2. It's possible to have different background images for different console windows. New console windows do not inherit the background of its parent.
3. It's possible to tint the image quite nicely by using the "color" command, e.g. "color d0" for a shade of purple with black text.
4. Transparency can be adjusted without reloading the image by using my other tool "cmdwiz.exe" with operation "setwindowtransparency".
Last edited by misol101 on 09 Nov 2020 12:05, edited 4 times in total.
Re: CmdBkg - use bitmap as background to console window
Thanks for the realization of my idea Nice work!
Steffen
Steffen
Re: CmdBkg - use bitmap as background to console window
+1
+1
+1
+1
etc....
Infinity!
+1
+1
+1
etc....
Infinity!
Re: CmdBkg - use bitmap as background to console window
What about adding something to the user autorun key.
e.g.
http://imgur.com/a/hcnYB
e.g.
Code: Select all
Reg Add "HKCU\Software\Microsoft\Command Processor" /V AutoRun /D "Start \"\" \"%UserProfile%\CmdBkg.exe\" \"%UserProfile%\123.bmp\" 25"
http://imgur.com/a/hcnYB
Re: CmdBkg - use bitmap as background to console window
Thanks for the comments, glad you all like it!
Just want to point out that there is an issue with this program which I didn't think of before. Try this:
timeout 5 & cmdbkg 123.bmp
then select any other window than the console, and wait. That's right, now that window gets the image instead
While this is a cool "feature" and all, it's obviously a bug. Not sure how to fix it at the moment, anybody with some Windows coding skill is invited to help out
Just want to point out that there is an issue with this program which I didn't think of before. Try this:
timeout 5 & cmdbkg 123.bmp
then select any other window than the console, and wait. That's right, now that window gets the image instead
While this is a cool "feature" and all, it's obviously a bug. Not sure how to fix it at the moment, anybody with some Windows coding skill is invited to help out
Re: CmdBkg - use bitmap as background to console window
Neat application!
Actually i cannot test it, but i think this line causes the bug (using the actual foreground window):
Better use the console window associated with the application instance:
penpen
Actually i cannot test it, but i think this line causes the bug (using the actual foreground window):
Code: Select all
hConsoleWnd = GetForegroundWindow();
Better use the console window associated with the application instance:
Code: Select all
hConsoleWnd = GetConsoleWindow();
penpen
Re: CmdBkg - use bitmap as background to console window
penpen wrote:Neat application!
Actually i cannot test it, but i think this line causes the bug (using the actual foreground window):Code: Select all
hConsoleWnd = GetForegroundWindow();
Better use the console window associated with the application instance:Code: Select all
hConsoleWnd = GetConsoleWindow();
You are right, penpen, that is the cause. Unfortunately I cannot run GetConsoleWindow() because it returns NULL in this case. The reason it returns NULL is that this is a GUI application, not a console app. If it was a console app it would block the console until the program finishes (I am experimenting with creating a new process instead, with limited success).
Otherwise I need a new, reliable way to get a handle to the console window.
Re: CmdBkg - use bitmap as background to console window
Im not sure if you're aware, but this works:this doesn't:your instruction was that default 33 is applied, this appears not to be the case when followed with the includeborders option.
Code: Select all
start "" cmdbkg 123.bmp 33 includeborders
Code: Select all
start "" cmdbkg 123.bmp includeborders
Re: CmdBkg - use bitmap as background to console window
Compo wrote:Im not sure if you're aware, but this works:this doesn't:Code: Select all
start "" cmdbkg 123.bmp 33 includeborders
your instruction was that default 33 is applied, this appears not to be the case when followed with the includeborders option.Code: Select all
start "" cmdbkg 123.bmp includeborders
Yeah, that's "by design" (or lack of)... I prefer it to using switches. So basically in order to specify includeBorders you have to set the transparency. It's currently even more liberal actually, it doesn't matter what your third param is as long as it's set, so you could do:
Code: Select all
start "" cmdbkg 123.bmp 33 1
I guess if I want to keep this design the help should actually be: cmdbkg "file.bmp" [transparency 0-100 (default 33) [includeBorders]]
Re: CmdBkg - use bitmap as background to console window
Just attach the console of the parent process (untested, but should be possible within cmd, even when using start):misol101 wrote:Unfortunately I cannot run GetConsoleWindow() because it returns NULL in this case.
Code: Select all
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
std::string filename = "filename.txt";
std::ofstream fileout(filename.c_str());
BOOL attached = FALSE;
fileout << "attached : " << ((attached) ? "TRUE" : "FALSE") << std::endl;
fileout << "GetConsoleWindow(): " << GetConsoleWindow() << std::endl;
fileout << std::endl;
if (AttachConsole(ATTACH_PARENT_PROCESS)) {
attached = TRUE;
fileout << "attached : " << ((attached) ? "TRUE" : "FALSE") << std::endl;
fileout << "GetConsoleWindow(): " << GetConsoleWindow() << std::endl;
FreeConsole();
} else {
fileout << "Needs to be executed under \"cmd.exe\"" << std::endl;
}
fileout.close();
return 0;
}
penpen
Edit: Corrected "cstr" to "c_str".
Last edited by penpen on 13 Sep 2016 18:04, edited 1 time in total.
Re: CmdBkg - use bitmap as background to console window
Or find the window of the parent process.
Steffen
//EDIT slightly changed ...
Code: Select all
/* CmdBkg (c) 2016 Mikael Sollenborn */
#ifndef UNICODE
#define UNICODE
#endif
#ifndef _UNICODE
#define _UNICODE
#endif
#ifdef _WIN32_WINNT
#undef _WIN32_WINNT
#endif
#define _WIN32_WINNT 0x0600
// Usage: cmdbkg "file.bmp" [transparency 0-100 (default 33) [includeBorders]]
// Specify no arguments to remove previous background.
// Specify /? as first argument to see this help.
// Compilation: gcc -o cmdbkg.exe cmdbkg.c -lgdi32 -ldwmapi -mwindows
// TODO/Issues:
// 1. Allow centered placement of image, no stretching?
// 2. Background window visible a short time after restoring minimized console window. Minimize background if console window is minimized?
// 3. Inelegant polling. Use SetWinEventHook instead?
// 4. Find a better way to kill the old background window than this horrible title polling hack
// 5. Program blocks if run from a batch script, need to use: start "" cmdbkg
#include <windows.h>
#include <stdio.h>
#include <dwmapi.h>
#include <tlhelp32.h>
#define POLL_INTERVAL 20
#define KILL_TITLE_MESSAGE L"Kill"
// structure to be passed to EnumWindows() and EnumWindowsCallback()
typedef struct tag_CALLBACKDATA
{
DWORD pid;
HWND hwnd;
} CALLBACKDATA;
// callback function that is called from EnumWindows() in order to find the main window of a process
BOOL CALLBACK EnumWindowsCallback(HWND hwnd, LPARAM lParam)
{
CALLBACKDATA *pData = (CALLBACKDATA*)lParam;
DWORD process_id = 0;
GetWindowThreadProcessId(hwnd, &process_id);
if (pData->pid == process_id && !GetWindow(hwnd, GW_OWNER))
{
pData->hwnd = hwnd;
return FALSE;
}
return TRUE;
}
// Functions Fn_LoadBmp and f_SetConsoleTransparency by user "aGerman" at dostips.com
int Fn_LoadBmp(HWND hWnd, wchar_t *szBmpPath, long x, long y, long z, long w, long h)
{
HDC hDc = NULL, hDcBmp = NULL;
HBITMAP hBmp1 = NULL, hBmp2 = NULL;
HGDIOBJ hGdiObj = NULL;
BITMAP bmp = {0};
int iRet = EXIT_FAILURE;
if (hWnd)
{
if ((hDc = GetDC(hWnd)))
{
if ((hDcBmp = CreateCompatibleDC(hDc)))
{
if ((hBmp1 = (HBITMAP)LoadImage(NULL, szBmpPath, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE)))
{
if (GetObject(hBmp1, sizeof(bmp), &bmp))
{
if (w == -1) {
if ((w = bmp.bmWidth * z / 100.0 + 0.5) <= 0 || (h = bmp.bmHeight * z / 100.0 + 0.5) <= 0)
{
w = bmp.bmWidth;
h = bmp.bmHeight;
}
}
if ((hBmp2 = (HBITMAP)CopyImage((HANDLE)hBmp1, IMAGE_BITMAP, w, h, LR_COPYDELETEORG)))
{
if ((hGdiObj = SelectObject(hDcBmp, hBmp2)) && hGdiObj != HGDI_ERROR)
{
if (BitBlt(hDc, (int)x, (int)y, (int)w, (int)h, hDcBmp, 0, 0, SRCCOPY))
iRet = EXIT_SUCCESS;
DeleteObject(hGdiObj);
}
DeleteObject(hBmp2);
}
}
DeleteObject(hBmp1);
}
ReleaseDC(hWnd, hDcBmp);
}
ReleaseDC(hWnd, hDc);
}
}
return iRet;
}
BOOL f_SetConsoleTransparency(HWND hConsoleWnd, long percentage)
{
BYTE bAlpha = 0;
LONG lNewLong = 0;
if (hConsoleWnd && percentage > -1 && percentage < 101)
{
bAlpha = (BYTE)(2.55 * (100 - percentage) + 0.5);
lNewLong = GetWindowLong(hConsoleWnd, GWL_EXSTYLE) | WS_EX_LAYERED;
if (!SetWindowLong(hConsoleWnd, GWL_EXSTYLE, lNewLong)) return FALSE;
return SetLayeredWindowAttributes(hConsoleWnd, 0, bAlpha, LWA_ALPHA);
}
return FALSE;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
int bx, by, bw, bh, ret;
int nbx, nby, nbw, nbh;
int fw = 0, fh = 0, fth = 0;
HWND hConsoleWnd, hBkgWnd, currFgWnd, tempWnd;
RECT bounds, extendBounds;
LPWSTR *szArgList;
int argCount, initialRedraw = 10, compTitle = 1, transparency = 33;
wchar_t titleBuffer[1024];
WNDCLASS wc={0};
UINT_PTR timerId;
BOOL bIsWeirdoBorders = FALSE;
BOOL res;
MSG msg;
// hConsoleWnd = GetForegroundWindow();
//////////////////////////
HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
PROCESSENTRY32 procentry = {0};
procentry.dwSize = sizeof(PROCESSENTRY32);
DWORD pid = GetCurrentProcessId(), ppid = 0;
if (Process32First(hProcessSnap, &procentry))
{
do
{
if (procentry.th32ProcessID == pid)
ppid = procentry.th32ParentProcessID;
} while (ppid == 0 && Process32Next(hProcessSnap, &procentry));
}
if (hProcessSnap != INVALID_HANDLE_VALUE)
CloseHandle(hProcessSnap);
if (ppid == 0)
{
MessageBox(NULL, L"Unable to find cmd.exe process.", L"Error", MB_OK);
return 1;
}
CALLBACKDATA data = {ppid, NULL};
EnumWindows(EnumWindowsCallback, (LPARAM)&data);
hConsoleWnd = data.hwnd;
//////////////////////////
if (hConsoleWnd) {
GetWindowRect(hConsoleWnd, &bounds);
bx = bounds.left;
by = bounds.top;
bw = bounds.right-bounds.left;
bh = bounds.bottom-bounds.top;
} else {
MessageBox(NULL, L"Unable to prepare window creation", L"Error", MB_OK);
return 1;
}
DwmGetWindowAttribute(hConsoleWnd, DWMWA_EXTENDED_FRAME_BOUNDS, &extendBounds, sizeof(extendBounds));
if (extendBounds.left != bounds.left) bIsWeirdoBorders = TRUE;
szArgList = CommandLineToArgvW(GetCommandLine(), &argCount);
if (szArgList == NULL)
{
MessageBox(NULL, L"Unable to get arguments", L"Error", MB_OK);
return 1;
}
if (argCount == 2 && wcscmp(szArgList[1], L"/?") == 0) {
MessageBox(NULL, L"Usage: cmdbkg \"file.bmp\" [transparency 0-100 (default 33) [includeBorders]]\n\nSpecify no arguments to remove previous background.\n\n(C)opyright 2016 Mikael Sollenborn", L"CmdBkg Arguments", MB_OK);
return 0;
}
f_SetConsoleTransparency(hConsoleWnd, 0);
GetWindowText(hConsoleWnd, titleBuffer, 1023);
SetWindowText(hConsoleWnd, KILL_TITLE_MESSAGE);
Sleep(500);
SetWindowText(hConsoleWnd, titleBuffer);
if (argCount < 2) {
f_SetConsoleTransparency(hConsoleWnd, 0);
return 0;
}
if (argCount > 2) {
transparency = wcstol (szArgList[2], NULL, 10);
}
f_SetConsoleTransparency(hConsoleWnd, transparency);
if (argCount <= 3 || (argCount > 3 && bIsWeirdoBorders)) {
fw = GetSystemMetrics(SM_CXSIZEFRAME);
if(argCount <= 3) fth = GetSystemMetrics(SM_CYSIZEFRAME) + GetSystemMetrics(SM_CYCAPTION);
fh = fth + GetSystemMetrics(SM_CYSIZEFRAME);
}
wc.lpszClassName=L"CmdBkgWindowBackground";
wc.lpfnWndProc=DefWindowProc; // Use default Window proc
wc.hInstance=hInstance;
wc.hbrBackground=(HBRUSH)(COLOR_3DFACE+1);
wc.hCursor=LoadCursor(NULL,IDC_ARROW);
if (!RegisterClass(&wc)) { MessageBox(NULL, L"Unable to prepare window creation", L"Error", MB_OK); LocalFree(szArgList); return 1; }
hBkgWnd=CreateWindowEx(0,wc.lpszClassName,0,WS_POPUP|WS_VISIBLE,bx+fw,by+fth,bw-fw*2,bh-fh,0,0,0,0);
if (!hBkgWnd) { MessageBox(NULL, L"Unable to create background window", L"Error", MB_OK); LocalFree(szArgList); return 1; }
ret = Fn_LoadBmp(hBkgWnd, szArgList[1], 0, 0, 100, bw-fw*2, bh-fth);
if (ret == EXIT_FAILURE) { MessageBox(NULL, L"Unable to load bitmap", L"Error", MB_OK); LocalFree(szArgList); return 1; }
ShowWindow(hBkgWnd, SW_HIDE);
SetWindowLongPtr(hBkgWnd, GWL_EXSTYLE, WS_EX_TOOLWINDOW);
ShowWindow(hBkgWnd, SW_SHOW);
SetForegroundWindow(hConsoleWnd);
currFgWnd = GetForegroundWindow();
while((IsWindow(hConsoleWnd) || IsIconic(hConsoleWnd)) && ret == EXIT_SUCCESS && compTitle != 0) {
timerId = SetTimer(NULL, 0, POLL_INTERVAL, NULL);
res = GetMessage(&msg,0,0,0);
KillTimer(NULL, timerId);
if (!(res && msg.message == WM_TIMER && msg.hwnd == NULL && msg.wParam == timerId))
DispatchMessage(&msg);
GetWindowText(hConsoleWnd, titleBuffer, 1023);
compTitle = wcscmp(titleBuffer, KILL_TITLE_MESSAGE);
GetWindowRect(hConsoleWnd, &bounds);
nbx = bounds.left;
nby = bounds.top;
nbw = bounds.right-bounds.left;
nbh = bounds.bottom-bounds.top;
tempWnd = GetForegroundWindow();
if (currFgWnd != tempWnd) {
if (initialRedraw < 5) initialRedraw = 5;
currFgWnd = tempWnd;
}
SetWindowPos(hBkgWnd, hConsoleWnd, nbx+fw, nby+fth, nbw-fw*2, nbh-fh, SWP_SHOWWINDOW);
if (initialRedraw > 0 || nbx != bx || nby != by || nbw != bw || nbh != bh) {
int w = nbw-fw*2, h = nbh-fth;
if (w < 1) w = 1;
if (h < 1) h = 1;
ret = Fn_LoadBmp(hBkgWnd, szArgList[1], 0, 0, 100, w, h);
bx = nbx; by = nby; bw = nbw; bh = nbh;
if (initialRedraw > 0) initialRedraw--;
}
}
LocalFree(szArgList);
return 0;
}
Steffen
//EDIT slightly changed ...
Re: CmdBkg - use bitmap as background to console window
Archive updated.
Should be more stable now, and can be run from a minimized script without issues.
In the end I used a similar approach, but it is essentially the same as aGerman's method:
No more background images for notepad, task manager and Explorer now though... maybe for another tool
Should be more stable now, and can be run from a minimized script without issues.
In the end I used a similar approach, but it is essentially the same as aGerman's method:
Code: Select all
DWORD getParentPID(DWORD pid)
{
HANDLE h = NULL;
PROCESSENTRY32 pe = { 0 };
DWORD ppid = 0;
pe.dwSize = sizeof(PROCESSENTRY32);
h = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if( Process32First(h, &pe))
{
do
{
if (pe.th32ProcessID == pid)
{
ppid = pe.th32ParentProcessID;
break;
}
} while( Process32Next(h, &pe));
}
CloseHandle(h);
return (ppid);
}
HWND FindTopMostWindow(DWORD dwProcID)
{
HWND hWnd = GetTopWindow(GetDesktopWindow());
while(hWnd)
{
DWORD dwWndProcID = 0;
GetWindowThreadProcessId(hWnd, &dwWndProcID);
if(dwWndProcID == dwProcID)
return hWnd;
hWnd = GetNextWindow(hWnd, GW_HWNDNEXT);
}
return NULL;
}
...
DWORD parentPID = getParentPID( GetCurrentProcessId());
HWND hConsoleWnd = FindTopMostWindow(parentPID);
No more background images for notepad, task manager and Explorer now though... maybe for another tool
Re: CmdBkg - use bitmap as background to console window
penpen: There was a lof of C++ "fluff" in your answer so I didn't understand it at first, but actually it's the better and easier solution. This works just fine:
For next release...
Code: Select all
AttachConsole(ATTACH_PARENT_PROCESS);
hConsoleWnd = GetConsoleWindow();
For next release...
Re: CmdBkg - use bitmap as background to console window
I wasn't sure if it would work, so (above) i've written a fast test; i assume you only need:
If you don't use "FreeConsole()", then this could result in a console that doesn't close, because there is one process (== "this one") attached to it.
penpen
Code: Select all
HWND hConsoleWnd = NULL;
if (AttachConsole(ATTACH_PARENT_PROCESS)) {
hConsoleWnd = GetConsoleWindow();
FreeConsole();
} else {
return 1;
}
penpen