Pages

Wednesday, November 11, 2009

Test post from Windows Live Writer

It is a test. Now, the application looks great.

It works. Even from Beijing. :)

Beijing. The map added  with Windows Live Writer

Tuesday, November 10, 2009

String to double

Probably, the C++ standard library is unlimited. There are so many functions I've never heard about. For example, only today I found out that there is strtod function:

C: strtod in stdlib.h
C++: strtod in cstdlib

I knew about atof.

But this function does not work with the strings like "20nn". So, for example, how to check if this conatins a valid double?

I made even a function and tested it in this code:
#include <cstdio>
#include <string>

bool is_valid_double(const char* str)
{
while (str && *str && *str == ' ')
str++;

if (*str && *str == '-')
str++;

while (str && *str)
{
if (!isdigit(*str) && (*str != '.'))
{
if (*str == ' ')
{
while (str && *str && *str == ' ')
str++;
if (!*str)
return true;
}
return false;
}
str++;
}

return true;
}

int main()
{
char sz[16] = { 0 };
sprintf(sz, "%f", 20.0);
bool b = is_valid_double(sz);

strcpy(sz, " 20.00");
b = is_valid_double(sz);

strcpy(sz, " 20.00 ");
b = is_valid_double(sz);

strcpy(sz, " -20.00 ");
b = is_valid_double(sz);

strcpy(sz, " --20.00");
b = is_valid_double(sz);

strcpy(sz, " 20.00 2");
b = is_valid_double(sz);

strcpy(sz, " 20nn ");
b = is_valid_double(sz);

return 0;
}
And then I see (thanks to EE), that there are a set of functions such as strtod, strtof, etc. Actually, I knew about strtol, so I could guess that should be also strtod. But I didn't. :)

Window with a predefined client rectangle

The task was to make a window with an image in the background and do not stretch the image.

The first application version I made just loaded the image into the memory, detected its size and created a popup window (WS_POPUP style) exactly of this size.

The QA said that it will be good, if the window will be moveable and sizeable. Ok. I added a caption and a frame. Now the image in the background is streched.

The first solution I found in Google - detect Window rectangle, then the client rectangle and the difference should be taken into a account when I set a rectangle for MoveWindow function:
HDC hDC = ::GetDC(NULL);
const int w = GetDeviceCaps(hDC, HORZRES);
const int h = GetDeviceCaps(hDC, VERTRES);
::ReleaseDC(NULL, hDC);

RECT rcClient, rcWindow;
GetClientRect(&rcClient);
GetWindowRect(&rcWindow);
m_Diff.x = (rcWindow.right - rcWindow.left) - rcClient.right;
m_Diff.y = (rcWindow.bottom - rcWindow.top) - rcClient.bottom;
cx += m_Diff.x;
cy += m_Diff.y;

RECT rect;
rect.left = (w >> 1) - (cx >> 1);
rect.top = (h >> 1) - (cy >> 1);
rect.right = rect.left + cx;
rect.bottom = rect.top + cy;

MoveWindow(&rect);
Two variables cx and cy are the predefined size of the background image.

This code works. But something's wrong here. Let's use Google again. :)

Of course, there is a Win32 API function that does the job: AdjustWindowRectEx.

And here is the code:
BOOL Center(int cx, int cy, RECT& rect)
{
HDC hDC = ::GetDC(NULL);
const int w = GetDeviceCaps(hDC, HORZRES);
const int h = GetDeviceCaps(hDC, VERTRES);
::ReleaseDC(NULL, hDC);

rect.left = (w >> 1) - (cx >> 1);
rect.top = (h >> 1) - (cy >> 1);
rect.right = rect.left + cx;
rect.bottom = rect.top + cy;

AdjustWindowRectEx(&rect, s_nWndStyle, FALSE, s_nWndStyleEx);
return TRUE;
}
This function above calculates the window rectangle for the predefined client area in the center of the desktop.
The function exists even for Windows Mobile and Windows CE: AdjustWindowRectEx. This MSDN article contains an example.

Now if you need to control the size of your window (do not allow to be smaller or bigger than the predefined size), you need to handle WM_GETMINMAXINFO message. For example, in the ATL-based application it will look as the following:
LRESULT OnGetMinMaxInfo(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
LPMINMAXINFO pInfo = (LPMINMAXINFO)lParam;
if (pInfo != NULL)
{
pInfo->ptMinTrackSize = CPainter::GetMinSize();
}
return 0;
}
ptMinTrackSize in the MINMAXINFO structure is just a point, for example, set it as { 200, 200 } and the window cannot be smaller then 200x200.

More information about this WM_GETMINMAXINFO you can find on The Old New Thing.

Monday, November 9, 2009

The Beginning

If you are the Window programmer, you know that everything begins from here:
#define WIN32_LEAN_AND_MEAN        // Exclude rarely-used stuff from Windows headers

#include <windows.h>

LPCWSTR s_szWndClassName = L"Small Window";

ATOM RegisterWndClass(HINSTANCE hInstance, LPCWSTR lpszWndClassName);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE,
LPWSTR, int nCmdShow)
{
HWND hWnd = NULL;
MSG msg = { 0 };

RegisterWndClass(hInstance, s_szWndClassName);

hWnd = CreateWindow(s_szWndClassName, s_szWndClassName,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
NULL, NULL, hInstance, NULL);

if (hWnd != NULL)
{
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);

// Main message loop:
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}

return (int)msg.wParam;
}


ATOM RegisterWndClass(HINSTANCE hInstance, LPCWSTR lpszWndClassName)
{
WNDCLASSEX wcex = { 0 };
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.hInstance = hInstance;
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wcex.lpszMenuName = NULL;
wcex.lpszClassName = lpszWndClassName;
return RegisterClassEx(&wcex);
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
PAINTSTRUCT ps;
HDC hdc;

switch (message)
{
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
EndPaint(hWnd, &ps);
break;

case WM_DESTROY:
PostQuitMessage(0);
break;

    default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}

Do you know who's this?






Sunday, November 8, 2009

ATL, GDI+. PNG,...

After a half of a day of a dramatic struggle with CImage, I failed to make it working with the png images - the images that contain the alpha chanel. I found the solution, but with GDI+ and I will show it later.

Firstly, I have to say, that the CImage converts any image into 32 bit per pixel format, but it is just RGB (pre-muliplay alpha?). When CImage loads an image it copies the image data into an internal buffer. You can see everything yourself, if you will debug CImage methods such as Load from the stream, for example.

Secondly, I just included atlimage.h file header into my source code and got a bunch of dlls from which my application depends now. Including the dynamic run-time libraries, and GDI+, and something from the Windows Installer, and...

I very like ATL, I just love the style, but, probably, I will try to avoid using CImage in my projects.
For a fast and dirty job that does not require to load any data with alpha..., maybe, it is possible. CImage even has AlphaBlend function - it's just a wrapper for the Win API function. From my point view, it is just one more evidence of a bad design made for this class - from one side the class uses GDI+, from other side it uses GDI function to draw the image.

The original task was to load the PNG-file from the resources in an ATL-based application. I thought I solved it. It looked great - I made a template - CImageResource, that contain only one function -  LoadFromResource. The objects I created in my application were like

CImageResource<CImage> m_Image;

I added a window background image to the resources and saw it's drawn in the application window. AlphaBlend method was used and everything was just fine.

The problems begun when I added a button image with the real visible alpha.

So finally the class CImageResource is not a template :). It looks so:
#pragma once

class CImageResource
{
Bitmap* m_pBitmap;
HGLOBAL m_hBlock;

public:
CImageResource() : m_pBitmap(NULL), m_hBlock(NULL) {}
~CImageResource() { Clear(); }

inline BOOL IsNull() const { return m_pBitmap == NULL; }

UINT GetWidth()
{
if (IsNull())
return 0;
return m_pBitmap->GetWidth();
}

UINT GetHeight()
{
if (IsNull())
return 0;
return m_pBitmap->GetHeight();
}

BOOL Draw(HDC hDC, int x, int y)
{
if (IsNull())
return FALSE;
Graphics graphics(hDC);
return graphics.DrawImage(m_pBitmap, x, y,
m_pBitmap->GetWidth(), m_pBitmap->GetHeight()) == Ok;
}

BOOL Draw(HDC hDC, RECT& rect)
{
if (IsNull())
return FALSE;
Graphics graphics(hDC);
return graphics.DrawImage(m_pBitmap, rect.left, rect.top,
rect.right - rect.left, rect.bottom - rect.top) == Ok;
}

BOOL Load(LPCWSTR lpszFile)
{
Clear();
m_pBitmap = Bitmap::FromFile(lpszFile);
return m_pBitmap->GetLastStatus() == Ok;
}

BOOL LoadFromResource(UINT nResID)
{
Clear();
HMODULE hModule = GetModuleHandle(NULL);
HRSRC hResource = FindResource(hModule,
MAKEINTRESOURCE(nResID), L"IMAGES");
if (hResource == NULL)
return FALSE;

HGLOBAL hImage = LoadResource(hModule, hResource);
if (hImage == NULL)
return FALSE;
LPVOID pImage = LockResource(hImage);
if (pImage == NULL)
return FALSE;

HRESULT hr = E_FAIL;
int size = SizeofResource(hModule, hResource);
m_hBlock = GlobalAlloc(GMEM_MOVEABLE, size);
if (m_hBlock == NULL)
return FALSE;

LPVOID pBlock = GlobalLock(m_hBlock);
if (pBlock != NULL)
{
memmove(pBlock, pImage, size);
IStream* pStream = NULL;
if (CreateStreamOnHGlobal(m_hBlock, FALSE, &pStream) == S_OK)
{
m_pBitmap = Bitmap::FromStream(pStream);
pStream->Release();
if (m_pBitmap != NULL)
{
if (m_pBitmap->GetLastStatus() == Ok)
return TRUE;
}
delete m_pBitmap;
m_pBitmap = NULL;
}
GlobalUnlock(m_hBlock);
}
GlobalFree(m_hBlock);
m_hBlock = NULL;
return FALSE;
}

void Clear()
{
delete m_pBitmap;
m_pBitmap = NULL;
if (m_hBlock != NULL)
{
GlobalUnlock(m_hBlock);
GlobalFree(m_hBlock);
}
}
};

Now all images are loaded and drawn with the alpha. For drawing I use the GDI+ as well.
Here are few helpful links from CodeProject about this topic:
Joe Woodbury. Loading JPG & PNG resources using GDI+: http://www.codeproject.com/KB/GDI-plus/cgdiplusbitmap.aspx
Christian Graus. Starting with GDI+: http://www.codeproject.com/KB/GDI-plus/startinggdiplus.aspx
 Darren Sessions. A user draw button that supports PNG files with transparency, for Visual C++ 6.0 and VS2005: http://www.codeproject.com/KB/buttons/GdipButton.aspx

Wednesday, November 4, 2009

Очередной Android

От Sony Ericsson (4" capacitive display, 8 с чем-то фотокамера и так далее):




Ну... что Вам сказать... По-моему пока все андроиды хуже телефонов с Windows Mobile.


Наверное, безплатный Google Map Navigation это большой плюс. Зря Microsoft не довели свою навигационную историю до ума - мыкались кого купить, свои проэкты начали (NavReady), в конце все проспали.