'我试图制作一个给MDI窗体添加背景的小程序,
'使用钩子监视WM_PAINT消息,在这里调用DC拷贝函数,
'可是每次都非法操作退出
'有谁知道为什么,怎么办?'工程文件
Type=Exe
Reference=*\G{00020430-0000-0000-C000-000000000046}#2.0#0#C:\WINDOWS\SYSTEM\stdole2.tlb#OLE Automation
Form=MDITest.frm
Module=modHook; modHook.bas
Startup="MDITest"
HelpFile=""
Command32=""
Name="MDI画窗体背景"
HelpContextID="0"
CompatibleMode="0"
MajorVer=1
MinorVer=0
RevisionVer=0
AutoIncrementVer=0
ServerSupportFiles=0
VersionCompanyName="HongXiang"
CompilationType=0
OptimizationType=0
FavorPentiumPro(tm)=0
CodeViewDebugInfo=0
NoAliasing=0
BoundsCheck=0
OverflowCheck=0
FlPointCheck=0
FDIVCheck=0
UnroundedFP=0
StartMode=0
Unattended=0
Retained=0
ThreadPerObject=0
MaxNumberOfThreads=1
'文件1
VERSION 5.00
Begin VB.MDIForm MDITest
BackColor = &H8000000C&
Caption = "MDI测试窗体"
ClientHeight = 6165
ClientLeft = 60
ClientTop = 345
ClientWidth = 6480
LinkTopic = "MDIForm1"
StartUpPosition = 3 '窗口缺省
Begin VB.PictureBox picContainer
Align = 1 'Align Top
Height = 1515
Left = 0
ScaleHeight = 1455
ScaleWidth = 6420
TabIndex = 0
Top = 0
Visible = 0 'False
Width = 6480
Begin VB.PictureBox picBackground
Appearance = 0 'Flat
AutoRedraw = -1 'True
AutoSize = -1 'True
BackColor = &H80000005&
ForeColor = &H80000008&
Height = 7230
Left = -75
Picture = "MDITest.frx":0000
ScaleHeight = 480
ScaleMode = 3 'Pixel
ScaleWidth = 640
TabIndex = 1
Top = -75
Width = 9630
End
End
End
Attribute VB_Name = "MDITest"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = True
Attribute VB_Exposed = False
Option Explicit'得到子窗体句柄的常量和函数
Private Const GW_CHILD = 5
Private Declare Function GetWindow Lib "user32" (ByVal hwnd As Long, ByVal wCmd As Long) As LongPublic lngChildWndDC As Long
Public lngBackgroundDC As Long
Public lngBackgroundWidth As Long
Public lngBackgroundHeight As Long
Public lngChildWnd As LongPrivate Sub MDIForm_Load() '得到窗体的子窗体DC句柄
'定义子窗体的句柄
lngChildWnd = GetWindow(Me.hwnd, GW_CHILD)
lngChildWndDC = GetDC(lngChildWnd)
lngBackgroundDC = picBackground.hDC
lngBackgroundHeight = picBackground.ScaleHeight
lngBackgroundWidth = picBackground.ScaleWidth
InitHook lngChildWnd
End SubPrivate Sub MDIForm_Unload(Cancel As Integer) TermHook
End Sub'文件2
Attribute VB_Name = "modHook"
Option ExplicitPrivate Const GWL_WNDPROC = (-4)Private Const WM_RBUTTONUP = &H205
Private Const WM_RBUTTONDOWN = &H204
Private Const WM_RBUTTONDBLCLK = &H206
Private Const WM_PAINT = &HFPrivate Const WM_LBUTTONUP = &H202
Private Const WM_LBUTTONDOWN = &H201
Private Const WM_LBUTTONDBLCLK = &H203Private Const WM_KEYDOWN = &H100
Private Const WM_KEYUP = &H101Public Type RECT
Left As Long
Top As Long
Right As Long
Bottom As Long
End Type'私有属性
Private m_hWnd As Long
Private prevWndProc As Long '窗体缺省过程句柄Private Declare Function GetWindowRect Lib "user32" (ByVal hwnd As Long, lpRect As RECT) As LongPublic Declare Function GetDC Lib "user32" (ByVal hwnd As Long) As Long
Private Declare Function StretchBlt Lib "gdi32" (ByVal sdfhdc As Long, ByVal X As Long, ByVal Y As Long, ByVal nWidth As Long, ByVal nHeight As Long, ByVal hSrcDC As Long, ByVal xSrc As Long, ByVal ySrc As Long, ByVal nSrcWidth As Long, ByVal nSrcHeight As Long, ByVal dwRop As Long) As Long'调用窗体过程
Private Declare Function CallWindowProc Lib "user32" Alias "CallWindowProcA" (ByVal lpPrevWndFunc As Long, ByVal hwnd As Long, ByVal Msg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
'设置窗体
Private Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (ByVal hwnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
'获得窗体参数
Private Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" (ByVal hwnd As Long, ByVal nIndex As Long) As LongPublic Sub InitHook(ByVal hwnd As Long)
'初始化窗口钩子 m_hWnd = hwnd
If m_hWnd <> 0 Then
prevWndProc = GetWindowLong(hwnd, GWL_WNDPROC)
SetWindowLong hwnd, GWL_WNDPROC, AddressOf WindowProc
End If
End SubPublic Sub TermHook()
'撤销窗口钩子 If m_hWnd <> 0 Then
SetWindowLong m_hWnd, GWL_WNDPROC, prevWndProc
End If
End SubFunction WindowProc(ByVal hw As Long, ByVal uMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
'用以接收处理消息的函数 Select Case uMsg
'Case WM_RBUTTONUP
'Case WM_RBUTTONDOWN
'Case WM_RBUTTONDBLCLK
'Case WM_LBUTTONUP
'Case WM_LBUTTONDOWN
'Case WM_LBUTTONDBLCLK 'Case WM_KEYUP
'Case WM_KEYDOWN
Case WM_PAINT
With MDITest
'得到窗体大小
Dim rectChild As RECT
GetWindowRect .lngChildWnd, rectChild
StretchBlt lngChildWndDC, 0, 0, rectChild.Right - rectChild.Left, _
rectChild.Bottom - rectChild.Top, .lngBackgroundDC, 0, 0, .lngBackgroundWidth, _
.lngBackgroundHeight, SRCCOPY
Case Else
WindowProc = CallWindowProc(prevWndProc, hw, uMsg, wParam, lParam)
End Select
End Function
'使用钩子监视WM_PAINT消息,在这里调用DC拷贝函数,
'可是每次都非法操作退出
'有谁知道为什么,怎么办?'工程文件
Type=Exe
Reference=*\G{00020430-0000-0000-C000-000000000046}#2.0#0#C:\WINDOWS\SYSTEM\stdole2.tlb#OLE Automation
Form=MDITest.frm
Module=modHook; modHook.bas
Startup="MDITest"
HelpFile=""
Command32=""
Name="MDI画窗体背景"
HelpContextID="0"
CompatibleMode="0"
MajorVer=1
MinorVer=0
RevisionVer=0
AutoIncrementVer=0
ServerSupportFiles=0
VersionCompanyName="HongXiang"
CompilationType=0
OptimizationType=0
FavorPentiumPro(tm)=0
CodeViewDebugInfo=0
NoAliasing=0
BoundsCheck=0
OverflowCheck=0
FlPointCheck=0
FDIVCheck=0
UnroundedFP=0
StartMode=0
Unattended=0
Retained=0
ThreadPerObject=0
MaxNumberOfThreads=1
'文件1
VERSION 5.00
Begin VB.MDIForm MDITest
BackColor = &H8000000C&
Caption = "MDI测试窗体"
ClientHeight = 6165
ClientLeft = 60
ClientTop = 345
ClientWidth = 6480
LinkTopic = "MDIForm1"
StartUpPosition = 3 '窗口缺省
Begin VB.PictureBox picContainer
Align = 1 'Align Top
Height = 1515
Left = 0
ScaleHeight = 1455
ScaleWidth = 6420
TabIndex = 0
Top = 0
Visible = 0 'False
Width = 6480
Begin VB.PictureBox picBackground
Appearance = 0 'Flat
AutoRedraw = -1 'True
AutoSize = -1 'True
BackColor = &H80000005&
ForeColor = &H80000008&
Height = 7230
Left = -75
Picture = "MDITest.frx":0000
ScaleHeight = 480
ScaleMode = 3 'Pixel
ScaleWidth = 640
TabIndex = 1
Top = -75
Width = 9630
End
End
End
Attribute VB_Name = "MDITest"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = True
Attribute VB_Exposed = False
Option Explicit'得到子窗体句柄的常量和函数
Private Const GW_CHILD = 5
Private Declare Function GetWindow Lib "user32" (ByVal hwnd As Long, ByVal wCmd As Long) As LongPublic lngChildWndDC As Long
Public lngBackgroundDC As Long
Public lngBackgroundWidth As Long
Public lngBackgroundHeight As Long
Public lngChildWnd As LongPrivate Sub MDIForm_Load() '得到窗体的子窗体DC句柄
'定义子窗体的句柄
lngChildWnd = GetWindow(Me.hwnd, GW_CHILD)
lngChildWndDC = GetDC(lngChildWnd)
lngBackgroundDC = picBackground.hDC
lngBackgroundHeight = picBackground.ScaleHeight
lngBackgroundWidth = picBackground.ScaleWidth
InitHook lngChildWnd
End SubPrivate Sub MDIForm_Unload(Cancel As Integer) TermHook
End Sub'文件2
Attribute VB_Name = "modHook"
Option ExplicitPrivate Const GWL_WNDPROC = (-4)Private Const WM_RBUTTONUP = &H205
Private Const WM_RBUTTONDOWN = &H204
Private Const WM_RBUTTONDBLCLK = &H206
Private Const WM_PAINT = &HFPrivate Const WM_LBUTTONUP = &H202
Private Const WM_LBUTTONDOWN = &H201
Private Const WM_LBUTTONDBLCLK = &H203Private Const WM_KEYDOWN = &H100
Private Const WM_KEYUP = &H101Public Type RECT
Left As Long
Top As Long
Right As Long
Bottom As Long
End Type'私有属性
Private m_hWnd As Long
Private prevWndProc As Long '窗体缺省过程句柄Private Declare Function GetWindowRect Lib "user32" (ByVal hwnd As Long, lpRect As RECT) As LongPublic Declare Function GetDC Lib "user32" (ByVal hwnd As Long) As Long
Private Declare Function StretchBlt Lib "gdi32" (ByVal sdfhdc As Long, ByVal X As Long, ByVal Y As Long, ByVal nWidth As Long, ByVal nHeight As Long, ByVal hSrcDC As Long, ByVal xSrc As Long, ByVal ySrc As Long, ByVal nSrcWidth As Long, ByVal nSrcHeight As Long, ByVal dwRop As Long) As Long'调用窗体过程
Private Declare Function CallWindowProc Lib "user32" Alias "CallWindowProcA" (ByVal lpPrevWndFunc As Long, ByVal hwnd As Long, ByVal Msg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
'设置窗体
Private Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (ByVal hwnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
'获得窗体参数
Private Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" (ByVal hwnd As Long, ByVal nIndex As Long) As LongPublic Sub InitHook(ByVal hwnd As Long)
'初始化窗口钩子 m_hWnd = hwnd
If m_hWnd <> 0 Then
prevWndProc = GetWindowLong(hwnd, GWL_WNDPROC)
SetWindowLong hwnd, GWL_WNDPROC, AddressOf WindowProc
End If
End SubPublic Sub TermHook()
'撤销窗口钩子 If m_hWnd <> 0 Then
SetWindowLong m_hWnd, GWL_WNDPROC, prevWndProc
End If
End SubFunction WindowProc(ByVal hw As Long, ByVal uMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
'用以接收处理消息的函数 Select Case uMsg
'Case WM_RBUTTONUP
'Case WM_RBUTTONDOWN
'Case WM_RBUTTONDBLCLK
'Case WM_LBUTTONUP
'Case WM_LBUTTONDOWN
'Case WM_LBUTTONDBLCLK 'Case WM_KEYUP
'Case WM_KEYDOWN
Case WM_PAINT
With MDITest
'得到窗体大小
Dim rectChild As RECT
GetWindowRect .lngChildWnd, rectChild
StretchBlt lngChildWndDC, 0, 0, rectChild.Right - rectChild.Left, _
rectChild.Bottom - rectChild.Top, .lngBackgroundDC, 0, 0, .lngBackgroundWidth, _
.lngBackgroundHeight, SRCCOPY
Case Else
WindowProc = CallWindowProc(prevWndProc, hw, uMsg, wParam, lParam)
End Select
End Function
我感觉好象没有问题,你试一下这样吧,我一向是这样用的,没有出问题。
将
prevWndProc = GetWindowLong(hwnd, GWL_WNDPROC)
SetWindowLong hwnd, GWL_WNDPROC, AddressOf WindowProc
换成:
prevWndProc =SetWindowLong (hwnd, GWL_WNDPROC, AddressOf WindowProc)
子类化不需要这样吧?
SetWindowLong m_hWnd, GWL_WNDPROC, prevWndProc
如果是hook,也不是这样释放hook呀!请其他大吓指教------------
。
。
end with '我怎么没看到这句呀
Private Sub Form_Activate()
Me.ZOrder 1
End Sub
确保该Form出现在所有窗口的最下面。
在MDIForm的Resize事件中调整Form的大小,以保证它和MDIForm的大小相适应。假设MDIMenu是MDI窗体。
Private Sub MDIForm_Resize()
If MDIMenu.WindowState <> 1 Then
FrmCntnr.Height = MDIMenu.ScaleHeight
FrmCntnr.Width = MDIMenu.ScaleWidth
End If
End Sub
由于你可以在普通的Form中设置背景,所以你可以把这个作为背景的Form当作MDIForm的背景。
另一个办法可以参考例子MDI_Background_Demo.zip(http://www.china-askpro.com/download/MDI_Background_Demo.zip),这个例子是从http://www.mvps.org/vbvision/grouped_demos.htm下载的。
如果实在要用 GetDC 的话,每次收到 WM_PAINT 时再调用 GetDC,并且画完后一定要调用 ReleaseDC。
可是不知道为什么一用到hook中就非法操作。那个钩子的模块是我3年前做的,已经不记得了。
rainstormmaster的办法我用过,但是在窗体重绘的时候,
自窗体的背景总是闪烁,如果机器慢一点的话会闪烁的很厉害。
请问WM_REASEBKGND(擦去背景)怎么用的?谢谢rainstormmaster给我的例子,我看看先。
我以前没有这样用过,但现在在学vc,在vc中如果是这个问题,是还需要取得dc的内存句柄,然后将位图放进该内存,最后显示出来!在vb不知道需不需要先取得该设备描述表的内存呢?你的程序我没有办法测试,由于在网吧玩。
关注中--------------
我屏蔽掉
'GetWindowRect .lngChildWnd, rectChild
'StretchBlt lngChildWndDC, 0, 0, rectChild.Right - rectChild.Left, _
rectChild.Bottom - rectChild.Top, .lngBackgroundDC, 0, 0, .lngBackgroundWidth, _
.lngBackgroundHeight, SRCCOPY
两个API的调用,于是就不出现那种错误了。
是不是在钩子中不能调用某些API啊?
StretchBlt不能用
getwindowrect可以用的。
这怎么可能呢?用过api的人都知道,如果你的参数设置不正确(或者其他错误),它可能不报错,而是直接退出的!
我看你的StretchBlt好象也没有问题,晕!你将它的大小参数和起始位置设置偏离一点试一试看,我以前用bitblt函数时,也老是达不到效果,于是我乱试,好象它的参数设置要求很严格,不是我想象中的那样,你设置偏离一点,最后居然就可以了,我也不知道为什么。
看了你的测试结果,我想肯定是参数的原因,你试一下,我也不懂为什么!反正是试一下了!
肯定不是子类化出错,是dc操作出错,我想!
同样的语句我是在窗体的事件中调试成功后粘贴到Hook代码中的。
在窗体中把代码放置在click事件中可以拷贝一次图像。
而且我用API这么长时间,我头一次用DC拷贝出错啊~
钩子里面只要有错误就会报错。
我加上漏掉的end with
.lngChildWndDC前面遗漏的点号也加上。
定义了没定义的SRCCOPY
就一切正常了,结题,
虽然这么做比较容易出错,
但是却比较简单,呵呵不知道大家是否要骂我懒。
可是MDI缩小的时候不会PAINT,也不明白为什么,
再在MDI的Resize中加重绘就好了。
但是rainstormmaster兄所说的WM_ERASEBKGND好像没弄明白,
收到这个消息绘制背景的话并不是一直有背景,
而是只有关闭掉子窗体的时候才有效果,请指教。 Case WM_PAINT
With MDITest
WindowProc = CallWindowProc(prevWndProc, hw, uMsg, wParam, lParam)
'得到窗体大小
Dim rectChild As RECT
GetWindowRect .lngChildWnd, rectChild
StretchBlt .lngChildWndDC, 0, 0, rectChild.Right - rectChild.Left, _
rectChild.Bottom - rectChild.Top, .lngBackgroundDC, 0, 0, .lngBackgroundWidth, _
.lngBackgroundHeight, SRCCOPY
End With
再说一句,我发现在钩子中中断跳出就会产生非法关闭的错误,
比如有一个错误而跳出,on error resume next都不可以。
VB IDE的设置断点,在断点出点Stop也会产生错误,
Hook的代码必须可靠可执行,也就是说代码必须少而逻辑性强,
不能出错,出错也要可以容错不可以产生Err.Raise
其他的好像没发现有限制。收回前言hook中不能使用什么API。