Wednesday, September 21, 2005

How to create monochrome mask from a bitmap and draw transparent bitmaps to a DC without using TransparentBlt()

To do these, visit this MSDN documentation:

http://support.microsoft.com/default.aspx?scid=kb%3Ben-us%3B79212

Quoted from MSDN, if you wish to create a monochrome bitmap from a color bitmap:
0. Select the bitmap to an 'image DC'
a. Set the background color of the image DC to the color that will be transparent in the bitmap.
b. Create a monochrome DC.
c. BitBlt the image into the monochrome DC.
This will create an AND mask of the bitmap by setting pixels that match the background color to white (1), and setting all other pixels to black (0).

More tutorial on bitmaps and transparent bitmaps:
http://www.codeproject.com/bitmap/gditutorial.asp

More explanation:
When blitting onto a monochrome bitmap from a color, pixels in the source color bitmap that are equal to the background color [of the source color bitmap] are blitted as white. All the remaining pixels are blitted as black.

The code which supports the explanation above:
hbmMask = CreateBitmap(bm.bmWidth,bm.bmHeight,1,1,NULL);
HBITMAP hbmSrcT = SelectObject(hdcSrc,hbmImage);
BITMAP hbmDstT = SelectObject(hdcDst,hbmMask);
SetBkColor(hdcSrc,RGB(255,255,255)); // assume that full white is transparent
BitBlt(hdcDst,0,0,bm.bmWidth,bm.bmHeight,hdcSrc,0,0,SRCCOPY);

Friday, September 16, 2005

Solving mysterious drawing problems associated with ImageList in VC++

Consider the following scenerio - You need combine a system icon and a bitmap together and then insert the composite image to an ImageList ("ImageList" form the MS ImageList API.. i.e., ImageList_Create(), ImageList_AddImage()). What you will probably do is something like this:

1. create a bitmap handle
2. create a memory device context (dc) and then SelectObject() to select the bitmap into the dc
3. call DrawIconEx() to draw the provided HICON to the dc
4. call BitBlt() to blt the provided HBITMAP to the dc
5. call DeleteDC() to free the dc
6. call ImageList_Add() and pass the bitmap handle you created on step 1

Everything seems straightforward; however, there is one crucial part which, if done incorrectly, may fail the whole process, ending up with an ImageList with a corrupted bitmap.

If you initialize the ImageList with anything other than ILC_COLORDDB as the 3rd paramter, such as this:

_imageList = ImageList_Create(width, height, ILC_COLOR32|ILC_MASK, 8, 8);

Then the resulting image will be problematic. However, if you create the ImageList with ILC_COLORDDB instead:

_imageList = ImageList_Create(width, height, ILC_COLORDDB|ILC_MASK, 8, 8);

Then everything works fine. I have yet to figure out exactly why this happens, but I suspect this has something to do with the way Windows handles DIB and DDB differently. Icons might be considered as a "DIB" when being used to DrawIcon() to an HDC. Since your bitmap is a DDB, when you draw a DIB object and a DDB object into the HDC, this will corrupt the image. This is really annoying because the whole process does not flag any errors.

Wednesday, July 20, 2005

Do not treat BSTR as a WCHAR*

Clear and loud - do not treat BSTR as a WCHAR*! For example, if we need to pass a BSTR into a function like the one below:

HRESULT CreateDevice(BSTR bstrDeviceID, IWiaItem **ppWiaItemRoot);

Never, ever do this:

CreateDevice(L"device1", &wiaItem);

By passing a WCHAR* as a BSTR, it may crash the code, because a BSTR also stores the length of the string as an integer immediately before the string buffer, so that if you call SysStringByteLen() on the BSTR, it'll return the number of bytes of the string. If you instead substitute a WCHAR* as BSTR, then SysStringByteLen() will probably fail. So, if the CreateDevice() function above calls SysStringByteLen() to the BSTR passed, the function will fail to work because you are actually passing a WCHAR*, which does not contain string length information before the string buffer.

The RIGHT way to do it is to use SysAllocString() to allocate for a BSTR from a WCHAR*, and then call SysFreeString() when it's done:

BSTR bstr = SysAllocString(L"device1");
CreateDevice(bstr, &wiaItem);
SysFreeString(bstr);

For more information, please consult MSDN Library's documentation for SysAllocString() and SysFreeString() and its associated section.

Thursday, February 24, 2005

Throwing Exception from a DLL and be caught by the application which loads the DLL

First of all, give it up, it just doesn't work. Well, it kind of works and you can catch the exception from the application, but once your application finishes processing the exception, the application will crash. This is true no matter what exception you throw. Even if you just throw an integer like this:

throw 1;

Then the application, after processing this exception, will still crash no matter what. I haven yet to figure out a solution for this problem. If you have a solution, please send me a message!

Thursday, January 13, 2005

Tackling mysterious crashing problems when calling run-time libraries from DLL modules within a C++ application

If your application has LoadLibrary() calls which hooks a DLL to your application, and you call a function within a DLL function which in turn calls C++ run-time library functions such as fopen() and fread(), the application may sometimes fail with an unhandled exception. Why?

This is probably because the linked run-time library with the application is different from the linked run-time library with the DLL module. For instance, if you specify your application to link to static run-time library, and then you specify your DLL module to link to dynamic run-time library, the application will crash when it attempts to call the function on the DLL module which access the run-time library, simply because they are referring to different libraries with completely separated code and memory management!

In order to fix this, during compilation, you must set both the application and the DLL module to use "Multi-threaded DLL (/MD)" run-time library. Do NOT use "Multi-threaded (/MT)" run-time library as it does not consider the case when the DLL is loaded dynamically by an application and thus would crash under such circumstances.

You may use "Multi-threaded Debug DLL (/MDd)" run-time library too, but again, you must make sure that both the application and DLL module are using the same "Multi-threaded Debug DLL (/MDd)" run-time library. Mixing and matching "Multi-threaded Debug DLL" and "Multi-threaded DLL" only gives you more headaches.

Compile your C++ Windows applications faster with pre-compiled header files

I guess I should be shameful by not knowing about this Visual Studio feature until now.

Most of the time, your C++ Windows applications requires several Windows-specific header files in order to use WINAPI functions, such as windows.h, tchar.h, or wininet.h. These header files significantly increases compilation time. Moreover, if you have more than one cpp files which all reference to the same header files such as windows.h, the compiler compiles the same header files again and again for each cpp files when producing object code. This is such a waste of time. Typically, compiling a single such cpp file takes about 2 seconds on my P4-2.6GHz PC.

Fortunately, Visual Studio C++ allows you to "cache" these common header during compilation by using the "pre-compiled header files" feature. For most programmers who use ATL or MFC to create new projects, the pre-compiled header feature is configured automatically for them. In case if you are a native WinAPI lover like me, or if you are just curious, below are the steps for configuring this feature manually:

1. Create a .cpp file and a .h file and name it whatever you want. These files are usually called StdAfx.cpp and StdAfx.h if they are generated automatically by VC++.

2. Suppose we name the files as StdAfx.cpp and StdAfx.h. For StdAfx.cpp file, type in this single line of code:
#include "StdAfx.h"

3. for StdAfx.h, type in all Windows header files you wish to include in all your source files in the project. For example:
#ifndef __STDAFX_H__
#define __STDAFX_H__
#include "tchar.h"
#include "windows.h"
#include "wininet.h"
#endif

4. On the left-hand project pane, right-click the project and click "Properties". Then click "C/C++" and then "Precompiled Headers".

5. Setup the options like this:
Create/Use Precompiled Header -- Use Precompiled Header (/Yu)
Create/Use PCH Through File -- StdAfx.h
Precompiled Header File -- $(IntDir)/$(TargetName).pch
... This will "link" all other source files to the pre-compiled header file during compilation to avoid multiple compilation of the same Windows header file.

6. Next, on the left-hand project pane, right-click StdAfx.cpp and click "Properties". Then again click "C/C++" and then "Precompiled Headers".

7. Setup the options like this:
Create/Use Precompiled Header -- Create Precompiled Header (/Yc)
Create/Use PCH Through File -- StdAfx.h
Precompiled Header File -- $(IntDir)/$(TargetName).pch
... This will notify the compiler to look for StdAfx.cpp and create the pre-compiled header file (.pch) before it compiles any other source files.

8. On all your other source files, you need to add this line as the first line of the code:
#include "StdAfx.h"

8. Save the project and rebuild.

During the rebuild, you'll notice that the compiler first picks up StdAfx.cpp, compiles it, and then creates the .pch file. After that, the other source files should compile much faster than before. To me, the compilation speeds up by at least 80%.

Wednesday, December 29, 2004

Weird crashing problems with Windows C/C++ multi-threading applications

Have you ever wondered why your C/C++ multi-threaded Windows applications crash mysteriously and randomly with memory corruption errors, even though you are absolutely sure that all your shared resources have been protected by some sorts of Semaphores or CriticalSection objects?

Maybe it's because you forgot to turn a specific compiler option.

There is a compiler option flag called "Runtime Library" which needs to be turned to either "Multi-threaded Debug (/MTd)" or "Multi-threaded (/MT)" when you write multi-threaded applications. By default, Microsoft Visual Studio .Net 2003 set this option to "Single-threaded Debug (/MLd)".

If you write multi-threaded applications with "Single-threaded Debug" option, numerous common Win32 or C++ runtime routines will fail with memory corruption errors. This is because the linker uses LIBCD.lib to resolve external symbols when you are in "Single-threaded Debug" mode; that library is not designed to be thread-safe. "Mutli-threaded Debug" mode uses LIBCMTD.lib, which is designed to be thread-safe.

In Microsoft Visual Studio .Net 2003, you can change this option by the following steps:
1. Right-click on your project file and click "Properties".
2. Expand "C/C++" folder.
3. Click "Code-generation".
4. Under "Runtime Library" on the right, select "Multi-threaded Debug (/MTd)" or "Multi-threaded (/MT)".
5. Note that you must select "Multi-threaded Debug (/MTd)" when your configuration is "Debug", and you must select "Multi-threaded (/MT)" when your configuration is "Release". Otherwise you'll run into various linker errors.

Sunday, December 12, 2004

Using Javascript XMLHttpRequest object to connect to any hosts on Mozilla/Firefox

XMLHttpRequest is a great client-side Javascript API to create rich-client dynamic webpages. Being supported by both Mozilla/Firefox and Internet Explorer, you can dynamically feed a webpage with new contents without refreshing the page. This creates a much better user experience under many situations, especially on resource-extensive websites such as chatrooms or message boards.

For those who are unfamiliar with Javascript XMLHttpRequest, Jim Ley has written a very good introductory article here.

A common problem faced by Mozilla/Firefox Javascript developers is that, the browser's default security permission does not allow your XMLHttpRequest object to access any hosts other than the host where the script is originated from. For instance, when you access the script from http://www.foo.com/test.html, your XMLHttpRequest object can only make connections to www.foo.com but not any other hosts such as www.foo.com:81 or www.bar.com.

A solution to this is to request UniversalBrowserRead privilege from the browser:

netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead");

When Mozilla/Firefox runs this line, it will pop up a security dialog box asking the user to "Allow" or "Deny" this request. If the user clicks "Allow", your script will then be allowed to access any hosts.

Note, however, that this privilege is granted only within the scope of the function. If you use XMLHttpRequest on a different function, you must ask for permission again, like this:

function func1()
{
netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead"); xmlhttp.open("GET", "http://www.bar.com/",true); xmlhttp.onreadystatechange = fx;
xmlhttp.send(null);
}

function func2()
{
// Ask again because the privilege is granted on a per-function basis
netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead"); xmlhttp.open("GET", "http://www.bar2.com/",true); xmlhttp.onreadystatechange = fx;
xmlhttp.send(null);
}

For more information, please consult Mozilla's javascript security documentation here.