ページ 1 / 3
「DLL側からコントロールの色変更」について
Posted: 2006年4月04日(火) 16:27
by KICO
何時も、お世話に成っています。
DLL側から指定したコントロールの色変更が出来ないのですが、どの様にすれば良いのでしょうか?
コード (一部抜粋) [ここをクリックすると内容が表示されます] [ここをクリックすると非表示にします]
DLL中でWH_CALLWNDPROCをグローバルフック
hSTHook=SetWindowsHookEx(WH_CALLWNDPROC, AddressOf(CallWndProc), hInst, 0)
hInst: "Function Export DllMain(hinstDLL As DWord, ・・・"のhinstDLL値
STATIC1:DLL呼び出し側のウィンドウに貼り付けて在るスタティックコントロールのハンドル
hbST:背景用枠ブラシ CreateSolidBrush(&HFFFF)
コード: 全て選択
Function CallWndProc(code As DWord, wParam As DWord, lParam As *CWPSTRUCT) As DWord
Dim f As Byte
Dim bc As DWord
Dim tc As DWord
If code=HC_ACTION Then
Select Case lParam->uMsg
Case WM_CTLCOLORSTATIC
If lParam->lParam=STATIC1 Then f=1: tc=&HFF: bc=&HFFFF
End Select
End If
If f Then
SetBkMode(lParam->wParam, OPAQUE)
SetBkColor(lParam->wParam, bc)
SetTextColor(lParam->wParam, tc)
CallWndProc=hbST '背景用枠ブラシ
CallNextHookEx(hSTHook, code, wParam, lParam)
Else
'次のフックを呼び出す
CallWndProc=CallNextHookEx(hSTHook, code, wParam, lParam)
End If
End Function
宜しくお願いします。
Re: 「DLL側からコントロールの色変更」について
Posted: 2006年4月04日(火) 17:29
by ノッチ
あのコードだと多分自前で描画した後に通常の描画が行われているのでは
ないかと思います。
WH_CALLWNDPROCをWH_CALLWNDPROCRETに変えてみたらどうでしょうか?
それともフックじゃできないのかな?
「DLL側からコントロールの色変更」について
Posted: 2006年4月04日(火) 18:07
by KICO
ノッチ様、何時もありがとう御座います。
あのコードだと多分自前で描画した後に通常の描画が行われているのでは
ないかと思います。
WH_CALLWNDPROCをWH_CALLWNDPROCRETに変えてみたらどうでしょうか?
WH_CALLWNDPROCRETに変えてみましたが、ダメでした。
If lParam->lParam=STATIC1 Then f=1: tc=&HFF: bc=&HFFFF:
Beep(10000,100)で
確認したところWH_CALLWNDPROCではSTATIC1を捕らえる事は出来ますが、
WH_CALLWNDPROCRETでは、捕らえられませんでした。
宜しくお願いします。
Re: 「DLL側からコントロールの色変更」について
Posted: 2006年4月04日(火) 21:41
by ノッチ
> If lParam->lParam=STATIC1 Then f=1: tc=&HFF: bc=&HFFFF: Beep(10000,100)で
> 確認したところWH_CALLWNDPROCではSTATIC1を捕らえる事は出来ますが、
> WH_CALLWNDPROCRETでは、捕らえられませんでした。
WH_CALLWNDPROCでそれぞれの関数の戻り値をmsgbox等で確認して
関数が成功しているかを確認して見て下さい。
WH_CALLWNDPROCのMSDNの説明に「CallWndProcフックプロシージャは、
メッセージを調べることはできますが、メッセージの修正はできません。」
という文があったのでそこで処理を行うことができないのかもしれません。
他にもWH_GETMESSAGEというフックもあり、それには「GetMsgProcフック
プロシージャは、メッセージを調べたり修正したりできます。」とあるので
こちらであれば処理が可能かもしれません。
「DLL側からコントロールの色変更」について
Posted: 2006年4月04日(火) 22:16
by KICO
ノッチ様、ありがとう御座います。
WH_CALLWNDPROCでそれぞれの関数の戻り値をmsgbox等で確認して
関数が成功しているかを確認して見て下さい。
「それぞれの関数」とは、SetBkMode(), SetBkColor(), SetTextColor()等の事でしょうか?
WH_CALLWNDPROCのMSDNの説明に「CallWndProcフックプロシージャは、
メッセージを調べることはできますが、メッセージの修正はできません。」
という文があったのでそこで処理を行うことができないのかもしれません。
解説に、そう書いてありますね。見落としていました。
他にもWH_GETMESSAGEというフックもあり、それには「GetMsgProcフック
プロシージャは、メッセージを調べたり修正したりできます。」とあるので
こちらであれば処理が可能かもしれません。
「GetMsgProcフック」も試しましたが、WM_CTLCOLORSTATICが捕らえられませんでした。
宜しくお願いします。
Re: 「DLL側からコントロールの色変更」について
Posted: 2006年4月04日(火) 23:15
by ノッチ
試しもせずに想像で答えたのがことごとく外れたようです。
確実ではないですが、フックプロシージャからの変更はできそうにないです。
そこで裏技的な方法で、WH_CALLWNDPROCでフックをインストールする。
フックプロシージャでlParam->hwndに対しサブクラス化する。
と言う方法があります。(実際にそういったソフトがあります)
この方法であれば別プロセスであってもサブクラス化が可能になります。
【注意点1】
フックインストール時にGetWindowThreadProcessIdで
取得したスレッドIDを使用してSetWindowsHookExを呼び出す。
コード: 全て選択
例:
ThreadID=GetWindowThreadProcessId(STATIC1, NULL)
SetWindowsHookEx(WH_CALLWNDPROC,AddressOf(CallWndProc),hInst,ThreadID)
【注意点2】
CallWndProcで何度もサブクラス化しないようにグローバルにフラグ用の
変数を用意して1度だけ処理を行うようにする。
これでどうでしょう?
「DLL側からコントロールの色変更」について
Posted: 2006年4月04日(火) 23:46
by KICO
ノッチ様、ありがとう御座います。
そこで裏技的な方法で、WH_CALLWNDPROCでフックをインストールする。
フックプロシージャでlParam->hwndに対しサブクラス化する。
と言う方法があります。(実際にそういったソフトがあります)
この方法であれば別プロセスであってもサブクラス化が可能になります。
サブクラス化だと確か対象コントロール分用意するはずで、この場合「STATIC1」
一つですが、複数個に成る場合どうすれば良いのでしょうか?
宜しくお願いします。
Re: 「DLL側からコントロールの色変更」について
Posted: 2006年4月10日(月) 00:03
by ノッチ
サブクラス化だと確か対象コントロール分用意するはずで、この場合「STATIC1」
一つですが、複数個に成る場合どうすれば良いのでしょうか?
宜しくお願いします。
大変長らくお待たせしました。
内容は自由に改変してかまいませんので。といってもバグとかまだありそう
なので改変しないとだめだと思いますが。
KICOさんがそれぞれのコントロールでどういった処理をしたいのかが
不明ですが、単純にサブクラス化するコントロールが決まっているので
あれば内容を少しいじるだけで大丈夫だと思います。
DLLのコード
[ここをクリックすると内容が表示されます] [ここをクリックすると非表示にします]コード: 全て選択
#include "dll.idx"
'-------------------------------------------------------------------
' メモ - このファイルには、DLLの構成要素を記述します。
' (例:関数定義、グローバル変数、定数定義など)
'
' エクスポートが必要な関数には、"Export" 修飾子を指定します。
' (例:Function Export FuncName() As Long)
'-------------------------------------------------------------------
'下のNUM_SUBCLASS_WNDを変更すると同時にサブクラス化できるウィンドウ数を変更できる
Const NUM_SUBCLASS_WND=16
Const SUBCLASS_SET=1234567890 As WPARAM
Const SUBCLASS_UNSET=987654321 As WPARAM
Type SUBCLASSSTRUCT
hWnd As HWND
hHook As HANDLE
pProc As VoidPtr
bFlag As BOOL
End Type
Dim hInst As HINSTANCE
Dim scs(NUM_SUBCLASS_WND-1) As SUBCLASSSTRUCT
Declare Function SetWindowsHookEx Lib "user32" Alias "SetWindowsHookExA"(idHook As Long,lpfn As VoidPtr,hMod As HINSTANCE,dwThreadId As DWord) As HANDLE
Declare Function CallNextHookEx Lib "user32"(hhk As HANDLE,nCode As Long,wParam As WPARAM,lParam As LPARAM) As LRESULT
Declare Function UnhookWindowsHookEx Lib "user32"(hhk As HANDLE) As BOOL
Declare Function ReplyMessage Lib "user32"(lResult As LRESULT) As BOOL
Const WH_CALLWNDPROC=4
Const HC_ACTION =0
Type CWPSTRUCT
lParam As LPARAM
wParam As WPARAM
message As DWord
hWnd As HWND
End Type
Function Export DllMain(hinstDLL As HINSTANCE, fdwReason As DWord, lpvReserved As VoidPtr) As Long
'DLLエントリポイント
Select Case fdwReason
Case DLL_PROCESS_ATTACH
'DLLがプロセス空間にロードされた時に実行されます。
_System_StartupProgram()
hInst=hinstDLL
Dim i As Long
For i=0 To NUM_SUBCLASS_WND-1
scs(0).hWnd=0
Next
DllMain=1
End Select
End Function
Function Export SetSubClass(hWnd As HWND,pProc As VoidPtr) As Long
SetSubClass=-1
Dim i As Long
i=0
Do
If scs(i).hWnd=0 Then Goto *SetSubClassNext
i++
Loop While i<NUM_SUBCLASS_WND-1
Exit Function
*SetSubClassNext
scs(i).bFlag=0
scs(i).hWnd=hWnd
Dim ThreadID As DWord
ThreadID=GetWindowThreadProcessId(scs(i).hWnd,NULL)
scs(i).hHook=SetWindowsHookEx(WH_CALLWNDPROC,AddressOf(CallWndProc),hInst,ThreadID)
If scs(i).hHook=0 Then Exit Function
scs(i).pProc=SendMessage(scs(i).hWnd,WM_NULL,SUBCLASS_SET,AddressOf(SubClassProc) As LPARAM) As VoidPtr
SetSubClass=i
End Function
Function Export EndSubClass(ID As Long) As Long
EndSubClass=-1
If ID<0 or ID>NUM_SUBCLASS_WND-1 Then Exit Function
If scs(ID).hWnd=0 Then Exit Function
scs(ID).bFlag=0
SendMessage(scs(ID).hWnd,WM_NULL,SUBCLASS_UNSET,VarPtr(scs(ID)) As LPARAM)
scs(ID).hWnd=0
UnhookWindowsHookEx(scs(ID).hHook)
EndSubClass=1
End Function
Function CallWndProc(code As DWord,wParam As DWord,lParam As *CWPSTRUCT) As DWord
If code=HC_ACTION Then
Dim p As VoidPtr
p=CallSubClass(lParam)
End If
CallWndProc=CallNextHookEx(p,code,wParam,lParam As LPARAM)
End Function
Function CallSubClass(pcwp As *CWPSTRUCT) As VoidPtr
Dim i As Long
For i=0 To NUM_SUBCLASS_WND-1
If scs(i).hWnd=pcwp->hWnd Then Exit For
Next
If scs(i).bFlag=0 Then
If pcwp->message=WM_NULL Then
If pcwp->wParam=SUBCLASS_SET Then
scs(i).bFlag=1
scs(i).pProc=SetWindowLong(pcwp->hWnd,GWL_WNDPROC,pcwp->lParam) As VoidPtr
End If
End If
End If
CallSubClass=scs(i).pProc
End Function
Function SubClassProc(hwnd As HWND,message As DWord,wParam As WPARAM,lParam As LPARAM) As LRESULT
Dim i As Long
For i=0 To NUM_SUBCLASS_WND-1
If scs(i).hWnd=hwnd Then Exit For
Next
If message=WM_NULL Then
If wParam=SUBCLASS_UNSET Then
SetWindowLong(hwnd,GWL_WNDPROC,scs(i).pProc As LPARAM)
Exit Function
End If
ElseIf message=WM_DESTROY Then
EndSubClass(i)
End If
SubClassProc=CallWindowProc(scs(i).pProc,hwnd,message,wParam,lParam)
If message=WM_CTLCOLORSTATIC Then
SetTextColor(wParam As HDC,&HFF)
End If
End Function
呼び出しのexe側
[ここをクリックすると内容が表示されます] [ここをクリックすると非表示にします]コード: 全て選択
Declare Function SetSubClass Lib "dll"(hWnd As HWND,pProc As VoidPtr) As Long
Declare Function EndSubClass Lib "dll"(hWnd As HWND) As Long
Sub MainWnd_Create(ByRef CreateStruct As CREATESTRUCT)
hTarget=FindWindow("NORMAL0","Test")
subid=SetSubClass(hTarget,0)
End Sub
Sub MainWnd_QueryClose(ByRef cancel As Integer)
EndSubClass(subid)
End Sub
また出張でホテル生活なのでしばらくはネット環境ありの予定です。
「DLL側からコントロールの色変更」について
Posted: 2006年4月10日(月) 22:28
by KICO
ノッチ様、ありがとう御座います。(休日中考えて頂いていたんですネ)
コード拝見しましたが、凄く難しくて理解するのに時間が掛かりそうです。
後ほど色々と質問させて頂きますが、取敢えず一つ、FindWindow()で指定されていますが、
特定のコントロールの指定は出来るのでしょうか?
「NUM_SUBCLASS_WNDの同時にサブクラス化できるウィンドウ数」とは、
"コントロールのウィンドウ"という事ではないのですか?
KICOさんがそれぞれのコントロールでどういった処理をしたいのかが
不明ですが、単純にサブクラス化するコントロールが決まっているので
あれば内容を少しいじるだけで大丈夫だと思います。
指定したコントロールの色変更をDLL側からしたいのですが例えば、「STATIC1~STATIC10は、青色。STATIC11~STATIC20は、黄色。 EDIT1~EDIT20は、灰色。」という様に。
「また出張でホテル生活・・・。」 よく出張されているみたいで大変ですね、
お仕事ガンバって下さい。 (^o^)
宜しくお願いします。
Re: 「DLL側からコントロールの色変更」について
Posted: 2006年4月11日(火) 00:29
by ノッチ
特定ウィンドウのコントロールだけであれば、
下記コードを使用してください。
[ここをクリックすると内容が表示されます] [ここをクリックすると非表示にします]コード: 全て選択
#include "dll.idx"
'-------------------------------------------------------------------
' メモ - このファイルには、DLLの構成要素を記述します。
' (例:関数定義、グローバル変数、定数定義など)
'
' エクスポートが必要な関数には、"Export" 修飾子を指定します。
' (例:Function Export FuncName() As Long)
'-------------------------------------------------------------------
'下のNUM_SUBCLASS_WNDを変更すると同時にサブクラス化できるウィンドウ数を変更できる
Const SUBCLASS_SET=1234567890 As WPARAM
Const SUBCLASS_UNSET=987654321 As WPARAM
Dim hInst As HINSTANCE
Dim hTarget As HWND
Dim hHook As HANDLE
Dim pProc As VoidPtr
Dim bFlag As BOOL
Declare Function SetWindowsHookEx Lib "user32" Alias "SetWindowsHookExA"(idHook As Long,lpfn As VoidPtr,hMod As HINSTANCE,dwThreadId As DWord) As HANDLE
Declare Function CallNextHookEx Lib "user32"(hhk As HANDLE,nCode As Long,wParam As WPARAM,lParam As LPARAM) As LRESULT
Declare Function UnhookWindowsHookEx Lib "user32"(hhk As HANDLE) As BOOL
Declare Function ReplyMessage Lib "user32"(lResult As LRESULT) As BOOL
Const WH_CALLWNDPROC=4
Const HC_ACTION =0
Type CWPSTRUCT
lParam As LPARAM
wParam As WPARAM
message As DWord
hWnd As HWND
End Type
Const Static1=1000
Const Static2=1001
Const Static3=1002
Const Static4=1003
Const Static5=1004
Function Export DllMain(hinstDLL As HINSTANCE, fdwReason As DWord, lpvReserved As VoidPtr) As Long
'DLLエントリポイント
Select Case fdwReason
Case DLL_PROCESS_ATTACH
'DLLがプロセス空間にロードされた時に実行されます。
_System_StartupProgram()
hInst=hinstDLL
hTarget=0
DllMain=1
End Select
End Function
Function Export SetSubClass(hWnd As HWND) As Long
SetSubClass=0
If hTarget Then Exit Function
bFlag=0
hTarget=hWnd
Dim ThreadID As DWord
ThreadID=GetWindowThreadProcessId(hTarget,NULL)
hHook=SetWindowsHookEx(WH_CALLWNDPROC,AddressOf(CallWndProc),hInst,ThreadID)
If hHook=0 Then Exit Function
pProc=SendMessage(hTarget,WM_NULL,SUBCLASS_SET,AddressOf(SubClassProc) As LPARAM) As VoidPtr
SetSubClass=1
End Function
Function Export EndSubClass() As Long
EndSubClass=0
If hTarget=0 Then Exit Function
bFlag=0
SendMessage(hTarget,WM_NULL,SUBCLASS_UNSET,pProc As LPARAM)
hTarget=0
UnhookWindowsHookEx(hHook)
EndSubClass=1
End Function
Function CallWndProc(code As DWord,wParam As DWord,lParam As *CWPSTRUCT) As DWord
If code=HC_ACTION Then
If bFlag=0 Then
If lParam->message=WM_NULL Then
If lParam->wParam=SUBCLASS_SET Then
bFlag=1
pProc=SetWindowLong(lParam->hWnd,GWL_WNDPROC,lParam->lParam) As VoidPtr
ReplyMessage(pProc As LRESULT)
End If
End If
End If
End If
CallWndProc=CallNextHookEx(pProc,code,wParam,lParam As LPARAM)
End Function
Function (hWnd As HWND,message As DWord,wParam As WPARAM,lParam As LPARAM) As LRESULT
Select Case message
Case WM_CTLCOLORSTATIC
SubClassProc=CallWindowProc(pProc,hWnd,message,wParam,lParam)
'ここに色変更の処理を追加
Select Case GetWindowLong(lParam As HWND,GWL_ID)
Case Static1'これはID
SetTextColor(wParam As HDC,&H000000FF)
'SubClassProcにブラシハンドルを指定すると背景色も変更できます
Case Static2
SetTextColor(wParam As HDC,&H0000FF00)
Case Static3
SetTextColor(wParam As HDC,&H00FF0000)
Case Static4
SetTextColor(wParam As HDC,&H0000FFFF)
Case Static5
SetTextColor(wParam As HDC,&H00FFFF00)
End Select
'こんな感じで
Exit Function
Case WM_NULL
If wParam=SUBCLASS_UNSET Then
SetWindowLong(hWnd,GWL_WNDPROC,pProc)
Exit Function
End If
Case WM_DESTROY
EndSubClass()
End Select
SubClassProc=CallWindowProc(pProc,hWnd,message,wParam,lParam)
End Function
[ここをクリックすると内容が表示されます] [ここをクリックすると非表示にします]Exe側
コード: 全て選択
Declare Function SetSubClass Lib "dll"(hWnd As HWND) As Long
Declare Function EndSubClass Lib "dll"() As Long
Sub MainWnd_Create(ByRef CreateStruct As CREATESTRUCT)
hTarget=FindWindow("NORMAL0","Test")
SetSubClass(hTarget)
End Sub
Sub MainWnd_QueryClose(ByRef cancel As Integer)
EndSubClass()
End Sub
前回使用方法を記述してなかったのですが、
変更したい対象の「親」ウィンドウハンドルを指定して
SetSubClass を呼び出します。
サブクラスを終了したい時には
EndSubClass を呼び出します。
上記DLLのコードではABで作成した"test"というキャプションのウィンドウに
配置されているStaticコントロール5つの色をそれぞれ変更します。
別プロセス間のデータはグローバルであっても使用できないようなので、
GetWindowLong(GWL_ID)でコントロールのIDを取得し、そのIDを使用して
色分けを行っています。
> 後ほど色々と質問させて頂きますが
とのことですが、遠慮なくどうぞ!
「DLL側からコントロールの色変更」について
Posted: 2006年4月11日(火) 12:46
by KICO
ノッチ様の方では正常に表示されているみたいですが、私の方では
DLL内でグルグル回っているみたいでウィンドウが表示されません?
それと、以前から機会があったらお聞きしたかったのですが、
「構造体を指定した配列変数」の初期化についてなのですが
例えば、"Dim scs[10] As SUBCLASSSTRUCT"を全て初期化しようとする場合、
"For i=0 to 10: ZeroMemory(VarPtr(scs), Len(scs)): Next" としていますが
"ZeroMemory(VarPtr(scs[0]), Len(scs[0])*11)" としては、
マズイ(必ずしも連続しているとは限らない為)でしょうか?
又、配列変数のDim宣言時には常にNULL(0)に成っているとは限らないのでしょうか?
と言うのもDim宣言の直後に "ZeroMemory()"等で初期化されているのを時々見受けられるからです。
宜しくお願いします。
Re: 「DLL側からコントロールの色変更」について
Posted: 2006年4月11日(火) 23:15
by ノッチ
> ノッチ様の方では正常に表示されているみたいですが、私の方では
> DLL内でグルグル回っているみたいでウィンドウが表示されません?
前のDLLはあくまでもDLLです。
前のコードをそのまま使うには、ABでtestという名前で新規プロジェクトを
作成してウィンドウにStaticを5つ配置、F7でコンパイルしたexeを実行して
おきます。->①
別のプロジェクトを新規作成して、プロジェクトのフォルダ内に作成したDLLを
置いて、WM_CREATE部分にSetSubClass部分、WM_CLOSEにEndSubClass部分を
追加します。->②
するとすでに実行しているtest.exe(①)のウィンドウに配置したStaticの文字色が
変化します。
②を終了させて①の画面を更新(画面を隠して再表示)させると文字色が黒になります。(サブクラス化解除)
これでうまくいかないようであればもう一度質問してください。
> それと、以前から機会があったらお聞きしたかったのですが、
> 「構造体を指定した配列変数」の初期化についてなのですが
> 例えば、"Dim scs[10] As SUBCLASSSTRUCT"を全て初期化しようとする場合、
> "For i=0 to 10: ZeroMemory(VarPtr(scs), Len(scs)): Next" としていますが
> "ZeroMemory(VarPtr(scs[0]), Len(scs[0])*11)" としては、
> マズイ(必ずしも連続しているとは限らない為)でしょうか?
これは構造体と配列から考えると、構造体の順番通り、配列の順番通りに
メモリが確保されるので問題ないはずです。
が、もしかしたら4Byte境界(64bit環境では8Byte?)で丸めるという処理が入る
かもしれません。
そうすると、構造体の中にByteやWordが含まれているときれいに初期化されない
かもしれません。
恐らくは問題ないとは思いますが、確実性を求めるのであれば上記のように
Forで初期化するのが良いと思います。
(この程度で速度的に問題があるのであればそもそも仕様を考え直した方が
早いかと)
> 又、配列変数のDim宣言時には常にNULL(0)に成っているとは限らないのでしょうか?
> と言うのもDim宣言の直後に "ZeroMemory()"等で初期化されているのを時々見受けられるからです。
ABの変数確保方法は、スタックと呼ばれるメモリ領域を使用したごく一般的な
もので、サブルーチン(関数)を呼び出したり変数を確保することで
さまざまな用途に使用されるメモリである為、宣言時は内容が不確定です。
(HeapAlloc APIやalloc関数を使用したメモリのようなもの)
HEAP_ZERO_MEMORYやcallocを使用して
Dim scs As *SUBCLASSSTRUCT
scs=calloc(***)
のようにすると0で初期化されています。(これは説明するまでもないと思いますが)
「DLL側からコントロールの色変更」について
Posted: 2006年4月12日(水) 00:45
by KICO
ノッチ様、ありがとう御座います。
①(test.exe)を実行した後②を実行、色が変化しないので①のウィンドウにカーソルを置く
又は、アクティブにすると①がエラーに成り強制終了します。
「配列変数」の初期化についてですが、良く解りました。
宜しくお願いします。
変数の初期値
Posted: 2006年4月12日(水) 19:06
by イグトランス
ABの場合,ZeroMemoryせずとも全ての変数は初めから0になっています。
ただ,明記された仕様ではないので,それを当てにしなかったり知らなかったりで,私も含め明示的にZeroMemoryを使う人もいるのです。
Re: 変数の初期値
Posted: 2006年4月12日(水) 19:28
by KICO
イグトランス様、何時もありがとう御座います。
> ABの場合,ZeroMemoryせずとも全ての変数は初めから0になっています。
ABでは基本的に、Dim宣言時にNULL(0)になっているという事ですネ。
良く解りました。
また、宜しくお願いします。(^_-)