共通コントロールのバージョン6.0(comctl32.dll)は、古いバージョンのWindowsでは使用できないコントロールをサブクラス化するための新しいアプローチを実装しています。いずれかのバージョンの共通コントロールライブラリをサポートするシステムで機能するようにサブクラス化を実装するための最良の方法は何ですか?
1 に答える
まず、MSDNに、バージョン6.0以前のバージョン6.0とそれ以前のコントロールのサブクラス化で発生した変更について説明している記事があります。
下位互換性を維持するための最良の方法は、コントロールをサブクラス化するためのラッパー関数を作成することです。これには、comctl32.dllのバージョン6でコントロールをサブクラス化するために必要な関数を動的にロードする必要があります。これは、それを行う方法の大まかな例です。
typedef BOOL (WINAPI *LPFN_SETWINDOWSUBCLASS)(HWND, SUBCLASSPROC, UINT_PTR, DWORD_PTR);
typedef LRESULT (WINAPI *LPFN_DEFSUBCLASSPROC)(HWND, UINT, WPARAM, LPARAM);
typedef BOOL (WINAPI *LPFN_REMOVEWINDOWSUBCLASS)(HWND, SUBCLASSPROC, UINT_PTR);
typedef BOOL (WINAPI *LPFN_INITCOMMONCONTROLSEX)(LPINITCOMMONCONTROLSEX);
typedef struct SubclassStruct {
WNDPROC Proc;
} SubclassStruct;
LPFN_SETWINDOWSUBCLASS SetWindowSubclassPtr = NULL;
LPFN_REMOVEWINDOWSUBCLASS RemoveWindowSubclassPtr = NULL;
LPFN_DEFSUBCLASSPROC DefSubclassProcPtr = NULL;
LPFN_INITCOMMONCONTROLSEX InitCommonControlsExPtr = NULL;
HMODULE ComCtlModule = NULL;
int Subclasser_Init(void)
{
INITCOMMONCONTROLSEX CommonCtrlEx = {0};
ComCtlModule = LoadLibrary("comctl32.dll");
if (ComCtlModule == NULL)
return FALSE;
SetWindowSubclassPtr = (LPFN_SETWINDOWSUBCLASS)GetProcAddress(ComCtlModule, "SetWindowSubclass");
RemoveWindowSubclassPtr = (LPFN_REMOVEWINDOWSUBCLASS)GetProcAddress(ComCtlModule, "RemoveWindowSubclass");
DefSubclassProcPtr = (LPFN_DEFSUBCLASSPROC)GetProcAddress(ComCtlModule, "DefSubclassProc");
InitCommonControlsExPtr = (LPFN_INITCOMMONCONTROLSEX)GetProcAddress(ComCtlModule, "InitCommonControlsEx");
if (InitCommonControlsExPtr != NULL)
{
CommonCtrlEx.dwSize = sizeof(CommonCtrlEx);
InitCommonControlsExPtr(&CommonCtrlEx);
}
return TRUE;
}
int Subclasser_Uninit(void)
{
if (ComCtlModule != NULL)
FreeLibrary(ComCtlModule);
return TRUE;
}
LRESULT CALLBACK Subclasser_SharedSubclassProc(HWND hWnd, UINT Message, WPARAM wParam, LPARAM lParam, UINT_PTR SubclassId, DWORD_PTR RefData)
{
SubclassStruct *Subclass = (SubclassStruct *)SubclassId;
return CallWindowProc(Subclass->Proc, hWnd, Message, wParam, lParam);
}
int Subclasser_SetProc(HWND hWnd, WNDPROC Proc, WNDPROC *OriginalProc, void *Param)
{
SubclassStruct *Subclass = NULL;
int Result = TRUE;
SetLastError(0);
if (SetWindowLongPtr(hWnd, GWLP_USERDATA, (__int3264)(UINT_PTR)Param) == 0)
{
if (GetLastError() > 0)
return FALSE;
}
if (SetWindowSubclassPtr!= NULL)
{
Subclass = (SubclassStruct*)malloc(sizeof(SubclassStruct));
Subclass->Proc = Proc;
*OriginalProc = (WNDPROC)Subclass;
Result = SetWindowSubclassPtr(hWnd, Subclasser_SharedSubclassProc, (UINT_PTR)Subclass, NULL);
}
else
{
*OriginalProc = (WNDPROC)(void *)GetWindowLongPtr(hWnd, GWLP_WNDPROC);
SetLastError(0);
if (SetWindowLongPtr(hWnd, GWLP_WNDPROC, (__int3264)(intptr)Proc) == 0)
{
if (GetLastError() > 0)
Result = FALSE;
}
}
if (Result == FALSE)
return FALSE;
return TRUE;
}
int Subclasser_UnsetProc(HWND hWnd, WNDPROC Proc, WNDPROC *OriginalProc)
{
SubclassStruct *Subclass = NULL;
int Result = TRUE;
if (RemoveWindowSubclassPtr != NULL)
{
if (*OriginalProc != NULL)
{
Subclass = (SubclassStruct *)*OriginalProc;
Proc = Subclass->Proc;
}
Result = RemoveWindowSubclassPtr(hWnd, Subclasser_SharedSubclassProc, (UINT_PTR)Subclass);
free(Subclass);
}
else
{
SetLastError(0);
if (SetWindowLongPtr(hWnd, GWLP_WNDPROC, (__int3264)(UINT_PTR)*OriginalProc) == 0)
{
if (GetLastError() > 0)
Result = FALSE;
}
}
SetLastError(0);
if (SetWindowLongPtr(hWnd, GWLP_USERDATA, 0) == 0)
{
if (GetLastError() > 0)
Result = FALSE;
}
*OriginalProc = NULL;
if (Result == FALSE)
return FALSE;
return TRUE;
}
LRESULT Subclasser_DefProc(WNDPROC OriginalProc, HWND hWnd, UINT Message, WPARAM wParam, LPARAM lParam)
{
if (OriginalProc == NULL)
return DefWindowProc(hWnd, Message, wParam, lParam);
if (DefSubclassProcPtr != NULL)
return DefSubclassProcPtr(hWnd, Message, wParam, lParam);
return CallWindowProc(OriginalProc, hWnd, Message, wParam, lParam);
}
他の唯一の例はOpenJDKにあります。それの1つの欠点は、同じWindowProc関数を持つダイアログで複数のコントロールをサブクラス化するとクラッシュするサブクラスIDとしてWindowProcを使用することです。上記の例では、SubclassStructという新しいメモリ構造を割り当て、そのアドレスをサブクラスIDとして渡します。これにより、サブクラス化するコントロールの各インスタンスが一意のサブクラスIDを持つことが保証されます。
複数のアプリケーションでサブクラス化関数を使用している場合(comctl32.dll<6を使用するものとcomctl32.dll>= 6を使用するもの)、comctl32.dllのファイルバージョン情報を取得することで、ロードされた共通制御ライブラリのバージョンを検出できます。 。GetModuleFileName
これは、とを使用して実行できますGetFileVersionInfo
。
さらに、comctl32.dll6.0のサブクラスコールバックでSetWindowWord/ GetWindowWordを使用する場合( Windowsカスタムコントロールの作成に関する次のDr. Dobbsの記事など)、 comctl32.dll <6の場合、これらのコードブロックを条件付きで使用する必要があります。 、バージョン6以降では動作せず、アプリケーションがクラッシュするためです。