HOME | プログラミングTClock |
TClock Lightは、Windows 95以降のどのWindowsでも動かせるようにしつつ、新しいWindowsの機能も使う、という方針です。そのため、このページに書かれているような対策をとります。
Windowsのバージョンを考慮せずにプログラムを書いてコンパイルすると、Windows 98やMe以降でしか動かないものができてしまうことがあります。
マイクロソフトのSDKに含まれるヘッダファイル(BCCでもインストールされます)では、マクロWINVER、_WIN32_WINNT、_WIN32_IEが使われています。それぞれ、Windowsのバージョン、Windows NT(2000、XP)のバージョン、IEのバージョンを表します。これらのマクロの数値が新しければ、Windowsの新しい機能のためのマクロ、構造体、APIの関数宣言がインクルードされます。
しかし、そうした新しい機能がインクルードされると、古いWindowsでは動かないプログラムになることがあります。TClock Lightでは、各ヘッダファイル(tclock.h、tcdll.h、tcprop.h、common.h)の先頭で次のように定義して、Windowsのバージョンをわざと古くします。
#define _WIN32_IE 0x0200 #define _WIN32_WINNT 0x0400 #define WINVER 0x0400
つまり、IEのバージョンは2.0、NTは4.0、Windowsは4.0(=Windows 95)とします。
しかし、マクロWINVER、_WIN32_WINNT、_WIN32_IEの値を古くすると、今度は新しい機能を使いたくても使えなくなってしまいます。そこで、
新しいマクロや構造体を使う場合は、SDKのヘッダファイルから必要な部分だけコピペする。
という手を使います。必要なマクロや構造体があったら、インクルードディレクトリでgrepをかけて探します。BCCではC:\borland\bcc55\Include、Visual C++ 6.0ではC:\Program Files\Microsoft Visual Studio\VC98\Include、Visual C++ .NETではC:\Program Files\Microsoft Visual Studio .NET\Vc7\PlatformSDK\Includeです。
たとえば、タスクバーの「取っ手を消す」に必要なメッセージと構造体は、commctrl.hから取ってきて、TClock Lightのソースに加えます(dll/newapi.h)。
#define CCM_FIRST 0x2000
#define CCM_SETCOLORSCHEME (CCM_FIRST + 2)
#define RB_SETCOLORSCHEME CCM_SETCOLORSCHEME
typedef struct tagCOLORSCHEME {
DWORD dwSize;
COLORREF clrBtnHighlight;
COLORREF clrBtnShadow;
} COLORSCHEME, *LPCOLORSCHEME;
古いWindowsにはない新しいAPIを使うと、古いWindowsではTClockを起動することさえできなくなります。そこで、
新しいAPIは静的にリンクせずに、必要なときに動的にロードする。
ようにプログラムします。
今のところ、新しいAPIを使っているのはtcdll.tclockだけなので、dllの下のソースを例に説明します。ウィンドウを半透明化するAPI SetLayeredWindowAttributesの使い方を見てみましょう。このAPIは、Windows 2000とXPだけがサポートしています。
まず、SDKのヘルプでSetLayeredWindowAttributesを探し(Googleで検索すれば早い)、APIがどのDLLに含まれるか調べます。「Import library User32.lib」とあるので、user32.dllに含まれていることが分かります。
SetLayeredWindowAttributes Function (MSDN)
ソースにuser32.dllのモジュールハンドルを加えます(dll/newapi.c)。
static HMODULE g_hmodUser32 = NULL;
SDKのヘッダファイル(winuser.h)から、SetLayeredWindowAttributesの宣言を持ってきます。
WINUSERAPI
BOOL
WINAPI
SetLayeredWindowAttributes(
HWND hwnd,
COLORREF crKey,
BYTE bAlpha,
DWORD dwFlags);
これを元にして、SetLayeredWindowAttributesへの関数ポインタを作ります(dll/newapi.c)。
static BOOL (WINAPI *g_pSetLayeredWindowAttributes)(HWND,COLORREF,BYTE,DWORD) = NULL;
次のように、関数InitUser32を作ります。LoadLibraryでuser32.dllを動的にロードして外部変数に保存し、GetProcAddressでAPIの関数ポインタを取り出て外部変数に保存します(dll/newapi.c)。
void InitUser32(void)
{
if(g_hmodUser32) return;
g_hmodUser32 = LoadLibrary("user32.dll");
if(g_hmodUser32 != NULL)
{
(FARPROC)g_pSetLayeredWindowAttributes
= GetProcAddress(g_hmodUser32, "SetLayeredWindowAttributes");
}
}
SetLayeredWindowAttributesのラッパーとなる関数MySetLayeredWindowAttributesを作ります。この関数は、最初に呼ばれるとInitUser32を呼び出します(dll/newapi.c)。
BOOL MySetLayeredWindowAttributes(HWND hwnd, COLORREF crKey,
BYTE bAlpha, DWORD dwFlags)
{
if(g_hmodUser32 == NULL) InitUser32();
if(g_pSetLayeredWindowAttributes)
return g_pSetLayeredWindowAttributes(hwnd, crKey,
bAlpha, dwFlags);
else return FALSE;
}
半透明化の機能を使いたいときは、ラッパー関数MySetLayeredWindowAttributesを呼び出します(dll/taskbar.cのSetLayeredTaskbar)。
MySetLayeredWindowAttributes(hwndTaskbar, 0, (BYTE)alpha, LWA_ALPHA);
TClockを終了するときは、関数EndNewAPIを呼び出します(dll/main2.cのEndClock)。
EndNewAPI(); // newapi.c
EndNewAPIには、user32.dllを解放する処理を加えておきます(dll/newapi.c)。
void EndNewAPI(void)
{
(中略 ほかのDLLの解放)
if(g_hmodUser32 != NULL) FreeLibrary(g_hmodUser32);
g_hmodUser32 = NULL;
g_pSetLayeredWindowAttributes = NULL;
}
TClock Lightを改良するときに、tcdll.tclockで新しいAPIを使いたい場合は、この例に沿ってdll/newapi.hとdll/newapi.cを拡張するとよいでしょう。
TClock Lightでは、Windowsのバージョンをチェックして処理を振り分けたり(tcdll.tclock)、ダイアログの部品の有効/無効を切り替えたり(tcprop_ja.exe)しています。
common/utl.cの中には、Windowsのバージョンを取り出す関数CheckWinVersionがあります。この関数の戻り値は次のようなビットフラグでチェックできます。
#define WIN95 0x01 // 95,98,Me #define WIN98 0x02 // 98,Me #define WINME 0x04 // Me #define WINNT 0x08 // NT4,2000,XP #define WIN2000 0x10 // 2000,XP #define WINXP 0x20 // XP
次の例は、Windows Me、2000、XPの場合に処理をするものです。
int ver = CheckWinVersion(); if((ver & WINME) || (ver & WIN2000)) DoSomething();
また、common/utl.cの中には、IE 4以降のタスクバーかどうかを調べる関数IsIE4と、Windows XPのテーマが使われているかどうかを調べる関数IsXPVisualStyleがあります。この2つの関数はBOOLを返します。この2つの関数の戻り値も、処理の振り分けやダイアログの部品の有効/無効の切り替えに使われます。