Related
I'm using libvlc to display the video stream after capturing the ts fragments exposed by m3u8 index files. I have never used libvlc before. According some tutorials, the following lines:
libvlc_instance_t* inst;
libvlc_media_player_t* mp;
libvlc_media_t* m;
inst=libvlc_new(0,NULL);
m=libvlc_media_new_path(inst,"...");
mp=libvlc_media_player_new_from_media(m);
libvlc_media_release(m);
//For windows. binding the displaying window.
libvlc_media_player_set_hwnd(mp,hwnd);
libvlc_media_player_play(mp);
Sleep(3000);
libvlc_media_player_stop(mp);
libvlc_media_player_release(mp);
libvlc_release(inst);
It seems you just need to call few functions and set some parameters for some initialization and then binding the window to display the video resource, libvlc should know how to do the coding and decoding works and display the stream. After all of those works done, release the resource. Sound pretty convenient.
After following those codes, the window just flickered once and exited. I don't know why yet.
My code is something like this:
int test(HWND hwnd)
{
//const char* version=libvlc_get_version();
//const char* compiler=libvlc_get_compiler();
//const char* changeset=libvlc_get_changeset();
//printf("Libvlc.\n");
//printf("Version:%s\n",version);
//printf("Compiler:%s\n",compiler);
//printf("Changeset:%s\n",changeset);
libvlc_instance_t* inst;
libvlc_media_player_t* mp;
libvlc_media_t* m;
inst=libvlc_new(0,NULL);
m=libvlc_media_new_path(inst,"my video resource file path...");
mp=libvlc_media_player_new_from_media(m);
libvlc_media_release(m);
//For windows. binding the displaying window.
libvlc_media_player_set_hwnd(mp,hwnd);
libvlc_media_player_play(mp);
Sleep(3000);
libvlc_media_player_stop(mp);
libvlc_media_player_release(mp);
libvlc_release(inst);
return 0;
}
int WINAPI WinMain(HINSTANCE hinstance, HINSTANCE hprevinstance, PSTR szcmdLine, int icmdshow)
{
HWND hwnd=NULL;
InitCommonControls();
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
if(Frame_Register(hinstance)!=0) {
MessageBox(NULL,"window register failed","Sorry",MB_OK|MB_ICONERROR);
return 0;
}
if(NULL==(hwnd=Frame_Create(hinstance,1200,600))) return 0;
Frame_Test(hwnd);
return Frame_Run(hwnd);
}
int regist_window(char* class_name,HINSTANCE instance,WNDPROC proc)
{
WNDCLASSEX wndclass;
wndclass.cbSize=sizeof(wndclass);
wndclass.style = CS_DBLCLKS|CS_HREDRAW|CS_VREDRAW;
wndclass.lpfnWndProc = proc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = instance;
wndclass.hIcon = NULL;
wndclass.hCursor = LoadCursor(NULL,IDC_ARROW);
wndclass.hbrBackground = (HBRUSH)CreateSolidBrush(RGB(0,0,0));
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = class_name;
wndclass.hIconSm = NULL;
if(!RegisterClassEx(&wndclass)) return -1;
return 0;
}
HWND Frame_Create(HINSTANCE instance,int width,int height)
{
int cx,cy;
POINT pt;
cx=GetSystemMetrics(SM_CXSCREEN);
cy=GetSystemMetrics(SM_CYSCREEN);
pt.x=(cx-width)>>1;
pt.y=(cy-height)>>1;
HWND hwnd=CreateWindowEx(NULL,
WINDOW_CLASS_NAME,
"Docker Frame Capture",
WS_OVERLAPPEDWINDOW|WS_VISIBLE|WS_HSCROLL|WS_CLIPCHILDREN,
pt.x,pt.y,width,height,NULL,
(HMENU)NULL,
instance,NULL);
// Some owner-drawing and data referring to the customized window.
Frame_InitialSettings(hwnd);
return hwnd;
}
int Frame_Run(HWND frame)
{
MSG msg;
while(GetMessage(&msg,NULL,0,0)) {
if(!IsDialogMessage(frame,&msg)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
GdiplusShutdown(gdiplusToken);
return(msg.wParam);
}
int Frame_Test(HWND hwnd)
{
//create a child window , as a container for vlc player.
RECT rc;
GetClientRect(hwnd,&rc);
int width=rc.right-rc.left,height=rc.bottom-rc.top;
HWND vlc_hwnd=CreateWindowEx(NULL,
WC_STATIC,
"",
WS_CHILD|WS_VISIBLE,
0,0,width,height,hwnd,
(HMENU)NULL,//LoadMenu(instance,"IDR_FRMENU"),
(HINSTANCE)GetWindowLongPtr(hwnd,GWLP_HINSTANCE),NULL);
test(vlc_hwnd);
return 0;
}
New addings:
the function libvlc_new return null, libvlc_errmsg() return null string.
Modified 7th July:
Problem figured out, You must copy the plugsin folder of libvlc to your project.
I'm trying to autosize some date-time picker controls with DTM_GETIDEALSIZE, but I don't quite get how to use this message properly. While widths returned seem to be fine if treated as pixels, heights seem to be not only way off, but sometimes change over time!
The sample program below creates three date-time pickers. The first shows both date and time with a custom format, the second shows dates only with DTS_SHORTDATECENTURYFORMAT, and the third shows times only with DTS_TIMEFORMAT. Resizing the window resizes all three pickers using DTM_GETIDEALSIZE. The widths seem to be fine. However, the heights of the first two are always 100, and the height of the last (the time picker) seems to start at 98 or so and decreases by two with each resize until it reaches 16, at which point it stays at 16.
This is tested both with Windows Vista and with Windows 7. Error checking in the below program is inconsistent (but some errors are not checked for the purposes of keeping this a simple test example). However, I will note that MSDN explicitly documents DTM_GETIDEALSIZE as only ever returning TRUE with no error condition specified.
Thanks.
// 3 june 2015
#define UNICODE
#define _UNICODE
#define STRICT
#define STRICT_TYPED_ITEMIDS
#define CINTERFACE
#define WINVER 0x0600
#define _WIN32_WINNT 0x0600
#define _WIN32_WINDOWS 0x0600
#define _WIN32_IE 0x0700
#define NTDDI_VERSION 0x06000000
#include <windows.h>
#include <commctrl.h>
#include <stdint.h>
#include <uxtheme.h>
#include <string.h>
#include <wchar.h>
#include <windowsx.h>
#include <vsstyle.h>
#include <vssym32.h>
#include <stdarg.h>
#include <oleacc.h>
#include <stdio.h>
void die(char *s)
{
// TODO
}
HWND mainwin;
HWND dtp1, dtp2, dtp3;
void idealsize(HWND dtp, char *n, int *x, int *y)
{
SIZE s;
s.cx = 0;
s.cy = 0;
printf("%s | %I32d ", n, SendMessageW(dtp, DTM_GETIDEALSIZE, 0, (LPARAM) (&s)));
printf("%I32d %I32d\n", s.cx, s.cy);
MoveWindow(dtp, *x, *y, s.cx, s.cy, TRUE);
*y += s.cy;
}
LRESULT CALLBACK wndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
int x, y;
SIZE s;
switch (uMsg) {
case WM_CLOSE:
PostQuitMessage(0);
return 0;
case WM_SIZE:
x = 10;
y = 10;
idealsize(dtp1, "1", &x, &y);
idealsize(dtp2, "2", &x, &y);
idealsize(dtp3, "3", &x, &y);
break;
}
return DefWindowProcW(hwnd, uMsg, wParam, lParam);
}
static HWND makedtp(DWORD style, WCHAR *format)
{
HWND hwnd;
hwnd = CreateWindowExW(WS_EX_CLIENTEDGE,
DATETIMEPICK_CLASSW, L"",
style | WS_TABSTOP | WS_CHILD | WS_VISIBLE,
0, 0, 100, 100,
mainwin, NULL, GetModuleHandle(NULL), NULL);
if (format != NULL)
if (SendMessageW(hwnd, DTM_SETFORMAT, 0, (LPARAM) format) == 0)
die("error applying format string to date/time picker in finishNewDateTimePicker()");
return hwnd;
}
#define GLI(what, buf, n) GetLocaleInfoEx(LOCALE_NAME_USER_DEFAULT, what, buf, n)
HWND makeDateTimePicker(void)
{
WCHAR *date, *time, *datetime;
int ndate, ntime;
int n;
HWND hwnd;
// TODO verify that this always returns a century year
ndate = GLI(LOCALE_SSHORTDATE, NULL, 0);
if (ndate == 0)
die("error getting date string length in uiNewDateTimePicker()");
date = (WCHAR *) malloc(ndate * sizeof (WCHAR));
if (GLI(LOCALE_SSHORTDATE, date, ndate) == 0)
die("error geting date string in uiNewDateTimePicker()");
ntime = GLI(LOCALE_STIMEFORMAT, NULL, 0);
if (ndate == 0)
die("error getting time string length in uiNewDateTimePicker()");
time = (WCHAR *) malloc(ntime * sizeof (WCHAR));
if (GLI(LOCALE_STIMEFORMAT, time, ntime) == 0)
die("error geting time string in uiNewDateTimePicker()");
n = _scwprintf(L"%s %s", date, time);
datetime = (WCHAR *) malloc((n + 1) * sizeof (WCHAR));
snwprintf(datetime, n + 1, L"%s %s", date, time);
hwnd = makedtp(0, datetime);
free(datetime);
free(time);
free(date);
return hwnd;
}
HWND makeDatePicker(void)
{
return makedtp(DTS_SHORTDATECENTURYFORMAT, NULL);
}
HWND makeTimePicker(void)
{
return makedtp(DTS_TIMEFORMAT, NULL);
}
static void makeWindows(void)
{
mainwin = CreateWindowExW(0,
L"mainwin", L"Full Window",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
500, 500,
NULL, NULL, GetModuleHandle(NULL), NULL);
dtp1 = makeDateTimePicker();
dtp2 = makeDatePicker();
dtp3 = makeTimePicker();
}
void initCommonControls(BOOL);
int main(int argc, char *argv[])
{
WNDCLASSW wc;
MSG msg;
HBRUSH b;
initCommonControls(TRUE);
ZeroMemory(&wc, sizeof (WNDCLASSW));
wc.lpszClassName = L"mainwin";
wc.lpfnWndProc = wndProc;
wc.hInstance = GetModuleHandle(NULL);
wc.hIcon = LoadIconW(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursorW(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH) (COLOR_BTNFACE + 1);
RegisterClassW(&wc);
makeWindows();
ShowWindow(mainwin, SW_SHOWDEFAULT);
UpdateWindow(mainwin);
while (GetMessageW(&msg, NULL, 0, 0) > 0) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
static const char manifest[] = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n<assembly xmlns=\"urn:schemas-microsoft-com:asm.v1\" manifestVersion=\"1.0\">\n<assemblyIdentity\n version=\"1.0.0.0\"\n processorArchitecture=\"*\"\n name=\"CompanyName.ProductName.YourApplication\"\n type=\"win32\"\n/>\n<description>Your application description here.</description>\n<dependency>\n <dependentAssembly>\n <assemblyIdentity\n type=\"win32\"\n name=\"Microsoft.Windows.Common-Controls\"\n version=\"6.0.0.0\"\n processorArchitecture=\"*\"\n publicKeyToken=\"6595b64144ccf1df\"\n language=\"*\"\n />\n </dependentAssembly>\n</dependency>\n</assembly>\n";
static ULONG_PTR comctlManifestCookie;
static HMODULE comctl32;
void initCommonControls(BOOL comctl6)
{
WCHAR temppath[MAX_PATH + 1];
WCHAR filename[MAX_PATH + 1];
HANDLE file;
DWORD nExpected, nGot;
ACTCTX actctx;
HANDLE ac;
INITCOMMONCONTROLSEX icc;
FARPROC f;
// this is listed as WINAPI in both Microsoft's and MinGW's headers, but not on MSDN for some reason
BOOL (*WINAPI ficc)(const LPINITCOMMONCONTROLSEX);
if (comctl6) {
if (GetTempPathW(MAX_PATH + 1, temppath) == 0)
die("getting temporary path for writing manifest file");
if (GetTempFileNameW(temppath, L"manifest", 0, filename) == 0)
die("getting temporary filename for writing manifest file");
file = CreateFileW(filename, GENERIC_WRITE,
0, // don't share while writing
NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (file == NULL)
die("creating manifest file");
nExpected = (sizeof manifest / sizeof manifest[0]) - 1; // - 1 to omit the terminating null character)
if (WriteFile(file, manifest, nExpected, &nGot, NULL) == 0)
die("writing manifest file");
if (nGot != nExpected)
die("short write to manifest file");
if (CloseHandle(file) == 0)
die("closing manifest file (this IS an error here because not doing so will prevent Windows from being able to use the manifest file in an activation context)");
ZeroMemory(&actctx, sizeof (ACTCTX));
actctx.cbSize = sizeof (ACTCTX);
actctx.dwFlags = ACTCTX_FLAG_SET_PROCESS_DEFAULT;
actctx.lpSource = filename;
ac = CreateActCtx(&actctx);
if (ac == INVALID_HANDLE_VALUE)
die("creating activation context for synthesized manifest file");
if (ActivateActCtx(ac, &comctlManifestCookie) == FALSE)
die("activating activation context for synthesized manifest file");
}
ZeroMemory(&icc, sizeof (INITCOMMONCONTROLSEX));
icc.dwSize = sizeof (INITCOMMONCONTROLSEX);
icc.dwICC = ICC_STANDARD_CLASSES | ICC_PROGRESS_CLASS | ICC_TAB_CLASSES | ICC_LISTVIEW_CLASSES | ICC_UPDOWN_CLASS | ICC_BAR_CLASSES | ICC_DATE_CLASSES;
comctl32 = LoadLibraryW(L"comctl32.dll");
if (comctl32 == NULL)
die("loading comctl32.dll");
f = GetProcAddress(comctl32, "InitCommonControlsEx");
if (f == NULL)
die("loading InitCommonControlsEx()");
ficc = (BOOL (*WINAPI)(const LPINITCOMMONCONTROLSEX)) f;
if ((*ficc)(&icc) == FALSE)
die("initializing Common Controls (comctl32.dll)");
}
I'm using state images to fake having checkboxes in every column of a standard list view control, and I've come across one weird anomaly on Windows XP that I'm not understanding: the top-left pixel of state image 1 is white, regardless of what that image actually is.
Here's a (zoomed in) example. You'll notice that the top-left pixel of the first image is blank.
It does not matter which state I draw for the first state image (change the first element of the themestates array in the same program below to see for yourself); all that seems to matter is that this only happens to state image 1 (index 0 in the HIMAGELIST).
IIRC this doesn't happen unthemed, and IIRC it doesn't happen on Windows 7.
The sample program below demonstrates this behavior (so I know it's not me accidentally overwriting memory in another part of my library, at least???).
What's going on? Thanks.
// 27 august 2014
// modified from winstateimglist.c 17 august 2014
// scratch Windows program by pietro gagliardi 17 april 2014
// fixed typos and added toWideString() 1 may 2014
// borrows code from the scratch GTK+ program (16-17 april 2014) and from code written 31 march 2014 and 11-12 april 2014
#define _UNICODE
#define UNICODE
#define STRICT
#define _GNU_SOURCE // needed to declare asprintf()/vasprintf()
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <windows.h>
#include <commctrl.h> // needed for InitCommonControlsEx() (thanks Xeek in irc.freenode.net/#winapi for confirming)
#include <uxtheme.h>
#include <vsstyle.h>
#include <vssym32.h>
#ifdef _MSC_VER
#error sorry! the scratch windows program relies on mingw-only functionality! (specifically: asprintf())
#endif
HMODULE hInstance;
HICON hDefaultIcon;
HCURSOR hDefaultCursor;
HFONT controlfont;
void panic(char *fmt, ...);
TCHAR *toWideString(char *what);
void init(void);
LRESULT CALLBACK wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
NMHDR *nmhdr = (NMHDR *) lparam;
NMLVDISPINFOW *fill = (NMLVDISPINFO *) lparam;
switch (msg) {
case WM_NOTIFY:
if (nmhdr->code == LVN_GETDISPINFO) {
if (fill->item.iSubItem == 0) {
fill->item.state = INDEXTOSTATEIMAGEMASK(fill->item.iItem + 1);
fill->item.stateMask = LVIS_STATEIMAGEMASK;
} else
fill->item.pszText = L"No State Image Here";
return 0;
}
return DefWindowProc(hwnd, msg, wparam, lparam);
case WM_CLOSE:
PostQuitMessage(0);
return 0;
default:
return DefWindowProc(hwnd, msg, wparam, lparam);
}
panic("oops: message %ud does not return anything; bug in wndproc()", msg);
}
HWND makeMainWindow(void)
{
WNDCLASS cls;
HWND hwnd;
ZeroMemory(&cls, sizeof (WNDCLASS));
cls.lpszClassName = L"mainwin";
cls.lpfnWndProc = wndproc;
cls.hInstance = hInstance;
cls.hIcon = hDefaultIcon;
cls.hCursor = hDefaultCursor;
cls.hbrBackground = (HBRUSH) (COLOR_BTNFACE + 1);
if (RegisterClass(&cls) == 0)
panic("error registering window class");
hwnd = CreateWindowEx(0,
L"mainwin", L"Main Window",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
300, 300,
NULL, NULL, hInstance, NULL);
if (hwnd == NULL)
panic("opening main window failed");
return hwnd;
}
void buildUI(HWND mainwin)
{
#define CSTYLE (WS_CHILD | WS_VISIBLE)
#define CXSTYLE (0)
#define SETFONT(hwnd) SendMessage(hwnd, WM_SETFONT, (WPARAM) controlfont, (LPARAM) TRUE);
HWND lv;
LVCOLUMN column;
HIMAGELIST imglist;
lv = CreateWindowEx(WS_EX_CLIENTEDGE | CXSTYLE,
WC_LISTVIEW, L"",
LVS_REPORT | LVS_OWNERDATA | LVS_NOSORTHEADER | LVS_SHOWSELALWAYS | WS_HSCROLL | WS_VSCROLL | WS_TABSTOP | CSTYLE,
10, 10, 250, 250,
mainwin, (HMENU) 100, hInstance, NULL);
if (lv == NULL)
panic("error making list view");
SETFONT(lv);
SendMessageW(lv, LVM_SETEXTENDEDLISTVIEWSTYLE,
LVS_EX_FULLROWSELECT | LVS_EX_SUBITEMIMAGES,
LVS_EX_FULLROWSELECT | LVS_EX_SUBITEMIMAGES);
// error checking elided from this point to where otherwise noted
HTHEME theme = NULL;
HIMAGELIST makeCheckboxImageList(HWND hwnddc, HTHEME *theme);
SendMessageW(lv, LVM_SETIMAGELIST, LVSIL_STATE, (LPARAM) makeCheckboxImageList(lv, &theme));
ZeroMemory(&column, sizeof (LVCOLUMN));
column.mask = LVCF_FMT | LVCF_TEXT | LVCF_SUBITEM | LVCF_ORDER;
column.fmt = LVCFMT_LEFT;
column.pszText = L"State Image";
column.iSubItem = 0;
column.iOrder = 0;
SendMessageW(lv, LVM_INSERTCOLUMN, 0, (LPARAM) (&column));
ZeroMemory(&column, sizeof (LVCOLUMN));
column.mask = LVCF_FMT | LVCF_TEXT | LVCF_SUBITEM | LVCF_ORDER;
column.fmt = LVCFMT_LEFT;
column.pszText = L"No State Image";
column.iSubItem = 1;
column.iOrder = 1;
SendMessageW(lv, LVM_INSERTCOLUMN, 1, (LPARAM) (&column));
// end of error eliding
if (SendMessageW(lv, LVM_SETCALLBACKMASK, LVIS_STATEIMAGEMASK, 0) == FALSE)
panic("error marking state image list as application-managed");
if (SendMessageW(lv, LVM_SETITEMCOUNT, 8, 0) == 0)
panic("error setting number of items in list view");
}
void firstShowWindow(HWND hwnd);
int main(void)
{
HWND mainwin;
MSG msg;
init();
mainwin = makeMainWindow();
buildUI(mainwin);
firstShowWindow(mainwin);
for (;;) {
BOOL gmret;
gmret = GetMessage(&msg, NULL, 0, 0);
if (gmret == -1)
panic("error getting message");
if (gmret == 0)
break;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
DWORD iccFlags =
// ICC_ANIMATE_CLASS | // animation control
// ICC_BAR_CLASSES | // toolbar, statusbar, trackbar, tooltip
// ICC_COOL_CLASSES | // rebar
// ICC_DATE_CLASSES | // date and time picker
// ICC_HOTKEY_CLASS | // hot key
// ICC_INTERNET_CLASSES | // IP address entry field
// ICC_LINK_CLASS | // hyperlink
ICC_LISTVIEW_CLASSES | // list-view, header
// ICC_NATIVEFNTCTL_CLASS | // native font
// ICC_PAGESCROLLER_CLASS | // pager
// ICC_PROGRESS_CLASS | // progress bar
// ICC_STANDARD_CLASSES | // "one of the intrinsic User32 control classes"
// ICC_TAB_CLASSES | // tab, tooltip
// ICC_TREEVIEW_CLASSES | // tree-view, tooltip
// ICC_UPDOWN_CLASS | // up-down
// ICC_USEREX_CLASSES | // ComboBoxEx
// ICC_WIN95_CLASSES | // some of the above
0;
void init(void)
{
INITCOMMONCONTROLSEX icc;
NONCLIENTMETRICS ncm;
hInstance = GetModuleHandle(NULL);
if (hInstance == NULL)
panic("error getting hInstance");
hDefaultIcon = LoadIcon(NULL, MAKEINTRESOURCE(IDI_APPLICATION));
if (hDefaultIcon == NULL)
panic("error getting default window class icon");
hDefaultCursor = LoadCursor(NULL, MAKEINTRESOURCE(IDC_ARROW));
if (hDefaultCursor == NULL)
panic("error getting default window cursor");
icc.dwSize = sizeof (INITCOMMONCONTROLSEX);
icc.dwICC = iccFlags;
if (InitCommonControlsEx(&icc) == FALSE)
panic("error initializing Common Controls");
ncm.cbSize = sizeof (NONCLIENTMETRICS);
if (SystemParametersInfo(SPI_GETNONCLIENTMETRICS,
sizeof (NONCLIENTMETRICS), &ncm, 0) == 0)
panic("error getting non-client metrics for getting control font");
controlfont = CreateFontIndirect(&ncm.lfMessageFont);
if (controlfont == NULL)
panic("error getting control font");
}
void panic(char *fmt, ...)
{
char *msg;
TCHAR *lerrmsg;
char *fullmsg;
va_list arg;
DWORD lasterr;
DWORD lerrsuccess;
lasterr = GetLastError();
va_start(arg, fmt);
if (vasprintf(&msg, fmt, arg) == -1) {
fprintf(stderr, "critical error: vasprintf() failed in panic() preparing panic message; fmt = \"%s\"\n", fmt);
abort();
}
// according to http://msdn.microsoft.com/en-us/library/windows/desktop/ms680582%28v=vs.85%29.aspx
lerrsuccess = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, lasterr,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lerrmsg, 0, NULL);
if (lerrsuccess == 0) {
fprintf(stderr, "critical error: FormatMessage() failed in panic() preparing GetLastError() string; panic message = \"%s\", last error in panic(): %ld, last error from FormatMessage(): %ld\n", msg, lasterr, GetLastError());
abort();
}
// note to self: use %ws instead of %S (thanks jon_y in irc.oftc.net/#mingw-w64)
if (asprintf(&fullmsg, "panic: %s\nlast error: %ws\n", msg, lerrmsg) == -1) {
fprintf(stderr, "critical error: asprintf() failed in panic() preparing full report; panic message = \"%s\", last error message: \"%ws\"\n", msg, lerrmsg);
abort();
}
fprintf(stderr, "%s\n", fullmsg);
va_end(arg);
exit(1);
}
void firstShowWindow(HWND hwnd)
{
// we need to get nCmdShow
int nCmdShow;
STARTUPINFO si;
nCmdShow = SW_SHOWDEFAULT;
GetStartupInfo(&si);
if ((si.dwFlags & STARTF_USESHOWWINDOW) != 0)
nCmdShow = si.wShowWindow;
ShowWindow(hwnd, nCmdShow);
if (UpdateWindow(hwnd) == 0)
panic("UpdateWindow(hwnd) failed in first show");
}
TCHAR *toWideString(char *what)
{
TCHAR *buf;
int n;
size_t len;
len = strlen(what);
if (len == 0) {
buf = (TCHAR *) malloc(sizeof (TCHAR));
if (buf == NULL)
goto mallocfail;
buf[0] = L'\0';
} else {
n = MultiByteToWideChar(CP_UTF8, 0, what, -1, NULL, 0);
if (n == 0)
panic("error getting number of bytes to convert \"%s\" to UTF-16", what);
buf = (TCHAR *) malloc((n + 1) * sizeof (TCHAR));
if (buf == NULL)
goto mallocfail;
if (MultiByteToWideChar(CP_UTF8, 0, what, -1, buf, n) == 0)
panic("erorr converting \"%s\" to UTF-16", what);
}
return buf;
mallocfail:
panic("error allocating memory for UTF-16 version of \"%s\"", what);
}
#define checkboxnStates 8
static int themestates[checkboxnStates] = {
CBS_UNCHECKEDNORMAL, // 0
CBS_CHECKEDNORMAL, // checked
CBS_UNCHECKEDHOT, // hot
CBS_CHECKEDHOT, // checked | hot
CBS_UNCHECKEDPRESSED, // pushed
CBS_CHECKEDPRESSED, // checked | pushed
CBS_UNCHECKEDPRESSED, // hot | pushed
CBS_CHECKEDPRESSED, // checked | hot | pushed
};
static SIZE getStateSize(HDC dc, int cbState, HTHEME theme)
{
SIZE s;
HRESULT res;
res = GetThemePartSize(theme, dc, BP_CHECKBOX, themestates[cbState], NULL, TS_DRAW, &s);
if (res != S_OK)
panic("error getting theme part size; HRESULT: %x", res);
return s;
}
static void themeImage(HDC dc, RECT *r, int cbState, HTHEME theme)
{
HRESULT res;
res = DrawThemeBackground(theme, dc, BP_CHECKBOX, themestates[cbState], r, NULL);
if (res != S_OK)
panic("error drawing checkbox image; HRESULT: %x", res);
}
static void themeSize(HDC dc, int *width, int *height, HTHEME theme)
{
SIZE size;
int cbState;
size = getStateSize(dc, 0, theme);
for (cbState = 1; cbState < checkboxnStates; cbState++) {
SIZE against;
against = getStateSize(dc, cbState, theme);
if (size.cx != against.cx || size.cy != against.cy)
panic("size mismatch in checkbox states");
}
*width = (int) size.cx;
*height = (int) size.cy;
}
static HBITMAP makeCheckboxImageListEntry(HDC dc, int width, int height, int cbState, void (*drawfunc)(HDC, RECT *, int, HTHEME), HTHEME theme)
{
BITMAPINFO bi;
VOID *ppvBits;
HBITMAP bitmap;
RECT r;
HDC drawDC;
HBITMAP prevbitmap;
r.left = 0;
r.top = 0;
r.right = width;
r.bottom = height;
ZeroMemory(&bi, sizeof (BITMAPINFO));
bi.bmiHeader.biSize = sizeof (BITMAPINFOHEADER);
bi.bmiHeader.biWidth = (LONG) width;
bi.bmiHeader.biHeight = -((LONG) height); // negative height to force top-down drawing;
bi.bmiHeader.biPlanes = 1;
bi.bmiHeader.biBitCount = 32;
bi.bmiHeader.biCompression = BI_RGB;
bi.bmiHeader.biSizeImage = (DWORD) (width * height * 4);
bitmap = CreateDIBSection(NULL, &bi, DIB_RGB_COLORS, &ppvBits, 0, 0);
if (bitmap == NULL)
panic("error creating HBITMAP for unscaled ImageList image copy");
drawDC = CreateCompatibleDC(dc);
if (drawDC == NULL)
panic("error getting DC for checkbox image list bitmap");
prevbitmap = SelectObject(drawDC, bitmap);
if (prevbitmap == NULL)
panic("error selecting checkbox image list bitmap into DC");
(*drawfunc)(drawDC, &r, cbState, theme);
if (SelectObject(drawDC, prevbitmap) != bitmap)
panic("error selecting previous bitmap into checkbox image's DC");
if (DeleteDC(drawDC) == 0)
panic("error deleting checkbox image's DC");
return bitmap;
}
static HIMAGELIST newCheckboxImageList(HWND hwnddc, void (*sizefunc)(HDC, int *, int *, HTHEME), void (*drawfunc)(HDC, RECT *, int, HTHEME), HTHEME theme)
{
int width, height;
int cbState;
HDC dc;
HIMAGELIST il;
dc = GetDC(hwnddc);
if (dc == NULL)
panic("error getting DC for making the checkbox image list");
(*sizefunc)(dc, &width, &height, theme);
il = ImageList_Create(width, height, ILC_COLOR32, 20, 20); // should be reasonable
if (il == NULL)
panic("error creating checkbox image list");
for (cbState = 0; cbState < checkboxnStates; cbState++) {
HBITMAP bitmap;
bitmap = makeCheckboxImageListEntry(dc, width, height, cbState, drawfunc, theme);
if (ImageList_Add(il, bitmap, NULL) == -1)
panic("error adding checkbox image to image list");
if (DeleteObject(bitmap) == 0)
panic("error deleting checkbox bitmap");
}
if (ReleaseDC(hwnddc, dc) == 0)
panic("error deleting checkbox image list DC");
return il;
}
HIMAGELIST makeCheckboxImageList(HWND hwnddc, HTHEME *theme)
{
if (*theme != NULL) {
HRESULT res;
res = CloseThemeData(*theme);
if (res != S_OK)
panic("error closing theme; HRESULT: %x", res);
*theme = NULL;
}
// ignore error; if it can't be done, we can fall back to DrawFrameControl()
if (*theme == NULL) // try to open the theme
*theme = OpenThemeData(hwnddc, L"button");
if (*theme != NULL) // use the theme
return newCheckboxImageList(hwnddc, themeSize, themeImage, *theme);
panic("error loading theme");
return NULL; // unreached
}
I have a HBITMAP in my pure Win32 project (no external libraries are used). Can I export it to a *.bmp file using only Winapi and/or CRT functions so I don't have to add dependencies to the project?
There is no API to save into file directly because, generally, having a bitmap handle does not mean you have direct access to bitmap data. Your solution is to copy bitmap into another bitmap with data access (DIB) and then using it data to write into file.
You typically either create another bitmap using CreateDIBSection, or you get bitmap data with GetDIBits.
CreateFile, WriteFile writes data into file.
You write: BITMAPFILEHEADER, then BITMAPINFOHEADER, then palette (which you typically don't have when bits/pixel is over 8), then data itself.
See also:
C++: Hbitmap/BITMAP into .bmp file (this answer)
Saving .bmp file using hBitmap = CreateDIBSection() in C Win32
Storing an Image on MSDN
The Code
This is the code from the MSDN article (Note that you need to define the errhandler() function):
PBITMAPINFO CreateBitmapInfoStruct(HWND hwnd, HBITMAP hBmp)
{
BITMAP bmp;
PBITMAPINFO pbmi;
WORD cClrBits;
// Retrieve the bitmap color format, width, and height.
if (!GetObject(hBmp, sizeof(BITMAP), (LPSTR)&bmp))
errhandler("GetObject", hwnd);
// Convert the color format to a count of bits.
cClrBits = (WORD)(bmp.bmPlanes * bmp.bmBitsPixel);
if (cClrBits == 1)
cClrBits = 1;
else if (cClrBits <= 4)
cClrBits = 4;
else if (cClrBits <= 8)
cClrBits = 8;
else if (cClrBits <= 16)
cClrBits = 16;
else if (cClrBits <= 24)
cClrBits = 24;
else cClrBits = 32;
// Allocate memory for the BITMAPINFO structure. (This structure
// contains a BITMAPINFOHEADER structure and an array of RGBQUAD
// data structures.)
if (cClrBits < 24)
pbmi = (PBITMAPINFO) LocalAlloc(LPTR,
sizeof(BITMAPINFOHEADER) +
sizeof(RGBQUAD) * (1<< cClrBits));
// There is no RGBQUAD array for these formats: 24-bit-per-pixel or 32-bit-per-pixel
else
pbmi = (PBITMAPINFO) LocalAlloc(LPTR,
sizeof(BITMAPINFOHEADER));
// Initialize the fields in the BITMAPINFO structure.
pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
pbmi->bmiHeader.biWidth = bmp.bmWidth;
pbmi->bmiHeader.biHeight = bmp.bmHeight;
pbmi->bmiHeader.biPlanes = bmp.bmPlanes;
pbmi->bmiHeader.biBitCount = bmp.bmBitsPixel;
if (cClrBits < 24)
pbmi->bmiHeader.biClrUsed = (1<<cClrBits);
// If the bitmap is not compressed, set the BI_RGB flag.
pbmi->bmiHeader.biCompression = BI_RGB;
// Compute the number of bytes in the array of color
// indices and store the result in biSizeImage.
// The width must be DWORD aligned unless the bitmap is RLE
// compressed.
pbmi->bmiHeader.biSizeImage = ((pbmi->bmiHeader.biWidth * cClrBits +31) & ~31) /8
* pbmi->bmiHeader.biHeight;
// Set biClrImportant to 0, indicating that all of the
// device colors are important.
pbmi->bmiHeader.biClrImportant = 0;
return pbmi;
}
void CreateBMPFile(HWND hwnd, LPTSTR pszFile, PBITMAPINFO pbi,
HBITMAP hBMP, HDC hDC)
{
HANDLE hf; // file handle
BITMAPFILEHEADER hdr; // bitmap file-header
PBITMAPINFOHEADER pbih; // bitmap info-header
LPBYTE lpBits; // memory pointer
DWORD dwTotal; // total count of bytes
DWORD cb; // incremental count of bytes
BYTE *hp; // byte pointer
DWORD dwTmp;
pbih = (PBITMAPINFOHEADER) pbi;
lpBits = (LPBYTE) GlobalAlloc(GMEM_FIXED, pbih->biSizeImage);
if (!lpBits)
errhandler("GlobalAlloc", hwnd);
// Retrieve the color table (RGBQUAD array) and the bits
// (array of palette indices) from the DIB.
if (!GetDIBits(hDC, hBMP, 0, (WORD) pbih->biHeight, lpBits, pbi,
DIB_RGB_COLORS))
{
errhandler("GetDIBits", hwnd);
}
// Create the .BMP file.
hf = CreateFile(pszFile,
GENERIC_READ | GENERIC_WRITE,
(DWORD) 0,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
(HANDLE) NULL);
if (hf == INVALID_HANDLE_VALUE)
errhandler("CreateFile", hwnd);
hdr.bfType = 0x4d42; // 0x42 = "B" 0x4d = "M"
// Compute the size of the entire file.
hdr.bfSize = (DWORD) (sizeof(BITMAPFILEHEADER) +
pbih->biSize + pbih->biClrUsed
* sizeof(RGBQUAD) + pbih->biSizeImage);
hdr.bfReserved1 = 0;
hdr.bfReserved2 = 0;
// Compute the offset to the array of color indices.
hdr.bfOffBits = (DWORD) sizeof(BITMAPFILEHEADER) +
pbih->biSize + pbih->biClrUsed
* sizeof (RGBQUAD);
// Copy the BITMAPFILEHEADER into the .BMP file.
if (!WriteFile(hf, (LPVOID) &hdr, sizeof(BITMAPFILEHEADER),
(LPDWORD) &dwTmp, NULL))
{
errhandler("WriteFile", hwnd);
}
// Copy the BITMAPINFOHEADER and RGBQUAD array into the file.
if (!WriteFile(hf, (LPVOID) pbih, sizeof(BITMAPINFOHEADER)
+ pbih->biClrUsed * sizeof (RGBQUAD),
(LPDWORD) &dwTmp, ( NULL)))
errhandler("WriteFile", hwnd);
// Copy the array of color indices into the .BMP file.
dwTotal = cb = pbih->biSizeImage;
hp = lpBits;
if (!WriteFile(hf, (LPSTR) hp, (int) cb, (LPDWORD) &dwTmp,NULL))
errhandler("WriteFile", hwnd);
// Close the .BMP file.
if (!CloseHandle(hf))
errhandler("CloseHandle", hwnd);
// Free memory.
GlobalFree((HGLOBAL)lpBits);
}
One function code for HBITMAP to *.bmp file.
BOOL SaveHBITMAPToFile(HBITMAP hBitmap, LPCTSTR lpszFileName)
{
HDC hDC;
int iBits;
WORD wBitCount;
DWORD dwPaletteSize = 0, dwBmBitsSize = 0, dwDIBSize = 0, dwWritten = 0;
BITMAP Bitmap0;
BITMAPFILEHEADER bmfHdr;
BITMAPINFOHEADER bi;
LPBITMAPINFOHEADER lpbi;
HANDLE fh, hDib, hPal, hOldPal2 = NULL;
hDC = CreateDC(TEXT("DISPLAY"), NULL, NULL, NULL);
iBits = GetDeviceCaps(hDC, BITSPIXEL) * GetDeviceCaps(hDC, PLANES);
DeleteDC(hDC);
if (iBits <= 1)
wBitCount = 1;
else if (iBits <= 4)
wBitCount = 4;
else if (iBits <= 8)
wBitCount = 8;
else
wBitCount = 24;
GetObject(hBitmap, sizeof(Bitmap0), (LPSTR)&Bitmap0);
bi.biSize = sizeof(BITMAPINFOHEADER);
bi.biWidth = Bitmap0.bmWidth;
bi.biHeight = -Bitmap0.bmHeight;
bi.biPlanes = 1;
bi.biBitCount = wBitCount;
bi.biCompression = BI_RGB;
bi.biSizeImage = 0;
bi.biXPelsPerMeter = 0;
bi.biYPelsPerMeter = 0;
bi.biClrImportant = 0;
bi.biClrUsed = 256;
dwBmBitsSize = ((Bitmap0.bmWidth * wBitCount + 31) & ~31) / 8
* Bitmap0.bmHeight;
hDib = GlobalAlloc(GHND, dwBmBitsSize + dwPaletteSize + sizeof(BITMAPINFOHEADER));
lpbi = (LPBITMAPINFOHEADER)GlobalLock(hDib);
*lpbi = bi;
hPal = GetStockObject(DEFAULT_PALETTE);
if (hPal)
{
hDC = GetDC(NULL);
hOldPal2 = SelectPalette(hDC, (HPALETTE)hPal, FALSE);
RealizePalette(hDC);
}
GetDIBits(hDC, hBitmap, 0, (UINT)Bitmap0.bmHeight, (LPSTR)lpbi + sizeof(BITMAPINFOHEADER)
+ dwPaletteSize, (BITMAPINFO *)lpbi, DIB_RGB_COLORS);
if (hOldPal2)
{
SelectPalette(hDC, (HPALETTE)hOldPal2, TRUE);
RealizePalette(hDC);
ReleaseDC(NULL, hDC);
}
fh = CreateFile(lpszFileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL);
if (fh == INVALID_HANDLE_VALUE)
return FALSE;
bmfHdr.bfType = 0x4D42; // "BM"
dwDIBSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + dwPaletteSize + dwBmBitsSize;
bmfHdr.bfSize = dwDIBSize;
bmfHdr.bfReserved1 = 0;
bmfHdr.bfReserved2 = 0;
bmfHdr.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) + (DWORD)sizeof(BITMAPINFOHEADER) + dwPaletteSize;
WriteFile(fh, (LPSTR)&bmfHdr, sizeof(BITMAPFILEHEADER), &dwWritten, NULL);
WriteFile(fh, (LPSTR)lpbi, dwDIBSize, &dwWritten, NULL);
GlobalUnlock(hDib);
GlobalFree(hDib);
CloseHandle(fh);
return TRUE;
}
Yet another minimalistic option is to use OLE's IPicture. It's always been around, still a part of Win32 API:
#define _S(exp) (([](HRESULT hr) { if (FAILED(hr)) _com_raise_error(hr); return hr; })(exp));
PICTDESC pictdesc = {};
pictdesc.cbSizeofstruct = sizeof(pictdesc);
pictdesc.picType = PICTYPE_BITMAP;
pictdesc.bmp.hbitmap = hBitmap;
CComPtr<IPicture> picture;
_S( OleCreatePictureIndirect(&pictdesc, __uuidof(IPicture), FALSE, (LPVOID*)&picture) );
// Save to a stream
CComPtr<IStream> stream;
_S( CreateStreamOnHGlobal(NULL, TRUE, &stream) );
LONG cbSize = 0;
_S( picture->SaveAsFile(stream, TRUE, &cbSize) );
// Or save to a file
CComPtr<IPictureDisp> disp;
_S( picture->QueryInterface(&disp) );
_S( OleSavePictureFile(disp, CComBSTR("C:\\Temp\\File.bmp")) );
i'll leave this self contained proof of concept here since I'll probably need to look it up later since it's not obvious. It takes a screenshot of the desktop window and saves it into bitmap.bmp:
#include <Windows.h>
#include <stdio.h>
#include <assert.h>
/* forward declarations */
PBITMAPINFO CreateBitmapInfoStruct(HBITMAP);
void CreateBMPFile(LPTSTR pszFile, HBITMAP hBMP);
int main(int argc, char **argv);
PBITMAPINFO CreateBitmapInfoStruct(HBITMAP hBmp)
{
BITMAP bmp;
PBITMAPINFO pbmi;
WORD cClrBits;
// Retrieve the bitmap color format, width, and height.
assert(GetObject(hBmp, sizeof(BITMAP), (LPSTR)&bmp));
// Convert the color format to a count of bits.
cClrBits = (WORD)(bmp.bmPlanes * bmp.bmBitsPixel);
if (cClrBits == 1)
cClrBits = 1;
else if (cClrBits <= 4)
cClrBits = 4;
else if (cClrBits <= 8)
cClrBits = 8;
else if (cClrBits <= 16)
cClrBits = 16;
else if (cClrBits <= 24)
cClrBits = 24;
else cClrBits = 32;
// Allocate memory for the BITMAPINFO structure. (This structure
// contains a BITMAPINFOHEADER structure and an array of RGBQUAD
// data structures.)
if (cClrBits < 24)
pbmi = (PBITMAPINFO) LocalAlloc(LPTR,
sizeof(BITMAPINFOHEADER) +
sizeof(RGBQUAD) * (1<< cClrBits));
// There is no RGBQUAD array for these formats: 24-bit-per-pixel or 32-bit-per-pixel
else
pbmi = (PBITMAPINFO) LocalAlloc(LPTR,
sizeof(BITMAPINFOHEADER));
// Initialize the fields in the BITMAPINFO structure.
pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
pbmi->bmiHeader.biWidth = bmp.bmWidth;
pbmi->bmiHeader.biHeight = bmp.bmHeight;
pbmi->bmiHeader.biPlanes = bmp.bmPlanes;
pbmi->bmiHeader.biBitCount = bmp.bmBitsPixel;
if (cClrBits < 24)
pbmi->bmiHeader.biClrUsed = (1<<cClrBits);
// If the bitmap is not compressed, set the BI_RGB flag.
pbmi->bmiHeader.biCompression = BI_RGB;
// Compute the number of bytes in the array of color
// indices and store the result in biSizeImage.
// The width must be DWORD aligned unless the bitmap is RLE
// compressed.
pbmi->bmiHeader.biSizeImage = ((pbmi->bmiHeader.biWidth * cClrBits +31) & ~31) /8
* pbmi->bmiHeader.biHeight;
// Set biClrImportant to 0, indicating that all of the
// device colors are important.
pbmi->bmiHeader.biClrImportant = 0;
return pbmi;
}
void CreateBMPFile(LPTSTR pszFile, HBITMAP hBMP)
{
HANDLE hf; // file handle
BITMAPFILEHEADER hdr; // bitmap file-header
PBITMAPINFOHEADER pbih; // bitmap info-header
LPBYTE lpBits; // memory pointer
DWORD dwTotal; // total count of bytes
DWORD cb; // incremental count of bytes
BYTE *hp; // byte pointer
DWORD dwTmp;
PBITMAPINFO pbi;
HDC hDC;
hDC = CreateCompatibleDC(GetWindowDC(GetDesktopWindow()));
SelectObject(hDC, hBMP);
pbi = CreateBitmapInfoStruct(hBMP);
pbih = (PBITMAPINFOHEADER) pbi;
lpBits = (LPBYTE) GlobalAlloc(GMEM_FIXED, pbih->biSizeImage);
assert(lpBits) ;
// Retrieve the color table (RGBQUAD array) and the bits
// (array of palette indices) from the DIB.
assert(GetDIBits(hDC, hBMP, 0, (WORD) pbih->biHeight, lpBits, pbi,
DIB_RGB_COLORS));
// Create the .BMP file.
hf = CreateFile(pszFile,
GENERIC_READ | GENERIC_WRITE,
(DWORD) 0,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
(HANDLE) NULL);
assert(hf != INVALID_HANDLE_VALUE) ;
hdr.bfType = 0x4d42; // 0x42 = "B" 0x4d = "M"
// Compute the size of the entire file.
hdr.bfSize = (DWORD) (sizeof(BITMAPFILEHEADER) +
pbih->biSize + pbih->biClrUsed
* sizeof(RGBQUAD) + pbih->biSizeImage);
hdr.bfReserved1 = 0;
hdr.bfReserved2 = 0;
// Compute the offset to the array of color indices.
hdr.bfOffBits = (DWORD) sizeof(BITMAPFILEHEADER) +
pbih->biSize + pbih->biClrUsed
* sizeof (RGBQUAD);
// Copy the BITMAPFILEHEADER into the .BMP file.
assert(WriteFile(hf, (LPVOID) &hdr, sizeof(BITMAPFILEHEADER),
(LPDWORD) &dwTmp, NULL));
// Copy the BITMAPINFOHEADER and RGBQUAD array into the file.
assert(WriteFile(hf, (LPVOID) pbih, sizeof(BITMAPINFOHEADER)
+ pbih->biClrUsed * sizeof (RGBQUAD),
(LPDWORD) &dwTmp, ( NULL)));
// Copy the array of color indices into the .BMP file.
dwTotal = cb = pbih->biSizeImage;
hp = lpBits;
assert(WriteFile(hf, (LPSTR) hp, (int) cb, (LPDWORD) &dwTmp,NULL));
// Close the .BMP file.
assert(CloseHandle(hf));
// Free memory.
GlobalFree((HGLOBAL)lpBits);
}
int main(int argc, char **argv)
{
HWND hwnd;
HDC hdc[2];
HBITMAP hbitmap;
RECT rect;
hwnd = GetDesktopWindow();
GetClientRect(hwnd, &rect);
hdc[0] = GetWindowDC(hwnd);
hbitmap = CreateCompatibleBitmap(hdc[0], rect.right, rect.bottom);
hdc[1] = CreateCompatibleDC(hdc[0]);
SelectObject(hdc[1], hbitmap);
BitBlt (
hdc[1],
0,
0,
rect.right,
rect.bottom,
hdc[0],
0,
0,
SRCCOPY
);
CreateBMPFile("bitmap.bmp", hbitmap);
return 0;
}
Yes, this is possible, using the Windows Imaging Component (WIC). WIC offers built-in encoders, so that you don't have to manually write out bitmap headers and data. It also allows you to choose a different encoder (e.g. PNG), by changing as little as one line of code.
The process is fairly straight forward. It consists of the following steps:
Retrieve properties from the source HBITMAP using GetObject (dimensions, bit depth).
Create an IWICImagingFactory instance.
Create an IWICBitmap instance from the HBITMAP (IWICImagingFactory::CreateBitmapFromHBITMAP).
Create an IWICStream instance (IWICImagingFactory::CreateStream), and attach it to a filename (IWICStream::InitializeFromFilename).
Create an IWICBitmapEncoder instance (IWICImagingFactory::CreateEncoder), and associate it with the stream (IWICBitmapEncoder::Initialize).
Create an IWICBitmapFrameEncode instance (IWICBitmapEncoder::CreateNewFrame), and initialize it in compliance with the source HBITMAP (IWICBitmapFrameEncode::Initialize, IWICBitmapFrameEncode::SetSize, IWICBitmapFrameEncode::SetPixelFormat).
Write bitmap data to the frame (IWICBitmapFrameEncode::WriteSource).
Commit frame and data to stream (IWICBitmapFrameEncode::Commit, IWICBitmapEncoder::Commit).
Translated to code:
#define COBJMACROS
#include <Objbase.h>
#include <wincodec.h>
#include <Windows.h>
#include <Winerror.h>
#pragma comment(lib, "Windowscodecs.lib")
HRESULT WriteBitmap(HBITMAP bitmap, const wchar_t* pathname) {
HRESULT hr = S_OK;
// (1) Retrieve properties from the source HBITMAP.
BITMAP bm_info = { 0 };
if (!GetObject(bitmap, sizeof(bm_info), &bm_info))
hr = E_FAIL;
// (2) Create an IWICImagingFactory instance.
IWICImagingFactory* factory = NULL;
if (SUCCEEDED(hr))
hr = CoCreateInstance(&CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER,
&IID_IWICImagingFactory, &factory);
// (3) Create an IWICBitmap instance from the HBITMAP.
IWICBitmap* wic_bitmap = NULL;
if (SUCCEEDED(hr))
hr = IWICImagingFactory_CreateBitmapFromHBITMAP(factory, bitmap, NULL,
WICBitmapIgnoreAlpha,
&wic_bitmap);
// (4) Create an IWICStream instance, and attach it to a filename.
IWICStream* stream = NULL;
if (SUCCEEDED(hr))
hr = IWICImagingFactory_CreateStream(factory, &stream);
if (SUCCEEDED(hr))
hr = IWICStream_InitializeFromFilename(stream, pathname, GENERIC_WRITE);
// (5) Create an IWICBitmapEncoder instance, and associate it with the stream.
IWICBitmapEncoder* encoder = NULL;
if (SUCCEEDED(hr))
hr = IWICImagingFactory_CreateEncoder(factory, &GUID_ContainerFormatBmp, NULL,
&encoder);
if (SUCCEEDED(hr))
hr = IWICBitmapEncoder_Initialize(encoder, (IStream*)stream,
WICBitmapEncoderNoCache);
// (6) Create an IWICBitmapFrameEncode instance, and initialize it
// in compliance with the source HBITMAP.
IWICBitmapFrameEncode* frame = NULL;
if (SUCCEEDED(hr))
hr = IWICBitmapEncoder_CreateNewFrame(encoder, &frame, NULL);
if (SUCCEEDED(hr))
hr = IWICBitmapFrameEncode_Initialize(frame, NULL);
if (SUCCEEDED(hr))
hr = IWICBitmapFrameEncode_SetSize(frame, bm_info.bmWidth, bm_info.bmHeight);
if (SUCCEEDED(hr)) {
GUID pixel_format = GUID_WICPixelFormat24bppBGR;
hr = IWICBitmapFrameEncode_SetPixelFormat(frame, &pixel_format);
}
// (7) Write bitmap data to the frame.
if (SUCCEEDED(hr))
hr = IWICBitmapFrameEncode_WriteSource(frame, (IWICBitmapSource*)wic_bitmap,
NULL);
// (8) Commit frame and data to stream.
if (SUCCEEDED(hr))
hr = IWICBitmapFrameEncode_Commit(frame);
if (SUCCEEDED(hr))
hr = IWICBitmapEncoder_Commit(encoder);
// Cleanup
if (frame)
IWICBitmapFrameEncode_Release(frame);
if (encoder)
IWICBitmapEncoder_Release(encoder);
if (stream)
IWICStream_Release(stream);
if (wic_bitmap)
IWICBitmap_Release(wic_bitmap);
if (factory)
IWICImagingFactory_Release(factory);
return hr;
}
Here's a companion test application to showcase the usage. Make sure to #define OEMRESOURCE prior to including any system headers to allow use of the OBM_ images.
int wmain(int argc, wchar_t** argv) {
HRESULT hr = S_OK;
hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
if (FAILED(hr))
return -1;
HBITMAP bitmap = LoadImage(NULL, MAKEINTRESOURCE(OBM_ZOOM), IMAGE_BITMAP, 0, 0,
LR_DEFAULTCOLOR);
hr = WriteBitmap(bitmap, argv[1]);
// Cleanup
if (bitmap)
DeleteObject(bitmap);
CoUninitialize();
return 0;
}
This will load a system-provided bitmap, and save it to the pathname specified as an argument on the command line.
Limitations:
No support for alpha channels. Although bitmaps version 5 support alpha channels, I am not aware of any way to find out, whether an HBITMAP refers to a bitmap with an alpha channel, nor would I know, how to determine, whether it is pre-multiplied. If you do want to support an alpha channel, make sure to set the EnableV5Header32bppBGRA property to VARIANT_TRUE (see BMP Format: Encoding).
No support for palletized bitmaps (bpp <= 8). If you are dealing with palletized bitmaps, make sure to supply an appropriate HPALETTE in the call to IWICImagingFactory::CreateBitmapFromHBITMAP.
The encoder is initialized with the GUID_WICPixelFormat24bppBGR pixel format constant. A more versatile implementation would deduce a compatible pixel format from the source HBITMAP.
I've just started using MinGW and I'm having an issue where it isn't outputting an executable or an object file, or anything really. After fixing a few errors everything compiles fine, but no executable is being outputted. When I first installed MinGW I tested it on a simple hello world program and everything worked correctly, but I'm trying to write a basic windows application and it's not working. I've worked with gcc before, but only briefly, I don't really know anything about it.
C:\Users\Cole\Dev\Hello Windows\>gcc win_main.c -o win_main
Here's the win_main.c file:
#include <windows.h>
#include <stdio.h>
/*
* Global Variables
*/
HWND g_hwnd = NULL;
HINSTANCE g_hinst = NULL;
/*
* Forward Declarations
*/
LRESULT CALLBACK win_proc(HWND h_wnd, UINT message, WPARAM w_param, LPARAM l_param);
HRESULT init_window(HINSTANCE h_instance, int cmd_show);
/*
* Main entry point to the application.
*/
int WINAPI WinMain(HINSTANCE h_instance, HINSTANCE h_previnstance, LPSTR cmd_line, int cmd_show) {
if(FAILED(init_window(h_instance, cmd_show)))
return -1;
MSG msg = {0};
while(GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int)msg.wParam;
}
/*
* Register window class and create the window.
*/
HRESULT init_window(HINSTANCE h_instance, int cmd_show) {
/* Register window class. */
WNDCLASSEX wcx;
wcx.cbSize = sizeof(WNDCLASSEX);
wcx.style = CS_VREDRAW | CS_HREDRAW;
wcx.lpfnWndProc = win_proc;
wcx.cbClsExtra = 0;
wcx.cbWndExtra = 0;
wcx.hInstance = h_instance;
wcx.hIcon = NULL;
wcx.hIconSm = NULL;
wcx.hCursor = LoadCursor(NULL, IDC_ARROW);
wcx.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wcx.lpszMenuName = NULL;
wcx.lpszClassName = "BasicWindowClass";
if(!RegisterClassEx(&wcx)) {
printf("Failed to register window class.\n");
return E_FAIL;
}
/* Create the window. */
g_hinst = h_instance;
RECT rc = {0, 0, 640, 480};
AdjustWindowRect(&rc, WS_OVERLAPPEDWINDOW, FALSE);
g_hwnd = CreateWindow("BasicWindowClass", "Windows Application", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
rc.right - rc.left, rc.bottom - rc.top, NULL, NULL, g_hinst, NULL);
if(g_hwnd == NULL) {
printf("Failed to create the window.\n");
return E_FAIL;
}
ShowWindow(g_hwnd, cmd_show);
return S_OK;
}
LRESULT CALLBACK win_proc(HWND h_wnd, UINT message, WPARAM w_param, LPARAM l_param) {
PAINTSTRUCT ps;
HDC hdc;
switch(message) {
case WM_PAINT:
hdc = BeginPaint(h_wnd, &ps);
EndPaint(h_wnd, &ps);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(h_wnd, message, w_param, l_param);
}
return 0;
}
You should add -mwindows gcc -mwindows win_main.c -o win_main .I guess your first program was using a 'main' function as entry point...