'我试图制作一个给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

解决方案 »

  1.   

    这是子类化吧?
    我感觉好象没有问题,你试一下这样吧,我一向是这样用的,没有出问题。

    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呀!请其他大吓指教------------
      

  2.   

    Case Else  什么意思/?????
      

  3.   

    With MDITest
       。
       。
    end with '我怎么没看到这句呀
      

  4.   

    在Windows中标准的做法应该是处理MDI父窗口的WM_ERASEBKGND,但这对VB来说太麻烦了。一个比较简单的办法是先建立一个Form,假设叫FrmCntnr,设置MDIChild为True,Border为0(无)。然后,在Form中加入如下代码: 
        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下载的。 
         
      

  5.   

    GetDC 不是那么用的吧?收到 WM_PAINT 后可以调用 BeginPaint 来获得 HDC。然后,在画完之后调用 EndPaint。
    如果实在要用 GetDC 的话,每次收到 WM_PAINT 时再调用 GetDC,并且画完后一定要调用 ReleaseDC。
      

  6.   

    我在程序中,而不是在hook中用上述办法就可以添加背景,
    可是不知道为什么一用到hook中就非法操作。那个钩子的模块是我3年前做的,已经不记得了。
    rainstormmaster的办法我用过,但是在窗体重绘的时候,
    自窗体的背景总是闪烁,如果机器慢一点的话会闪烁的很厉害。
    请问WM_REASEBKGND(擦去背景)怎么用的?谢谢rainstormmaster给我的例子,我看看先。
      

  7.   

    James0001(See-Plus-Plus)说的也是,你应该要释放dc的!
    我以前没有这样用过,但现在在学vc,在vc中如果是这个问题,是还需要取得dc的内存句柄,然后将位图放进该内存,最后显示出来!在vb不知道需不需要先取得该设备描述表的内存呢?你的程序我没有办法测试,由于在网吧玩。
    关注中--------------
      

  8.   

    在钩子里面不用API就好了
    我屏蔽掉
                    'GetWindowRect .lngChildWnd, rectChild
                    'StretchBlt lngChildWndDC, 0, 0, rectChild.Right - rectChild.Left, _
                        rectChild.Bottom - rectChild.Top, .lngBackgroundDC, 0, 0, .lngBackgroundWidth, _
                        .lngBackgroundHeight, SRCCOPY
    两个API的调用,于是就不出现那种错误了。
    是不是在钩子中不能调用某些API啊?
      

  9.   

    说错了
    StretchBlt不能用
    getwindowrect可以用的。
      

  10.   

    feihong0233(泓):是不是在钩子中不能调用某些API啊?
    这怎么可能呢?用过api的人都知道,如果你的参数设置不正确(或者其他错误),它可能不报错,而是直接退出的!
    我看你的StretchBlt好象也没有问题,晕!你将它的大小参数和起始位置设置偏离一点试一试看,我以前用bitblt函数时,也老是达不到效果,于是我乱试,好象它的参数设置要求很严格,不是我想象中的那样,你设置偏离一点,最后居然就可以了,我也不知道为什么。
    看了你的测试结果,我想肯定是参数的原因,你试一下,我也不懂为什么!反正是试一下了!
    肯定不是子类化出错,是dc操作出错,我想!
      

  11.   

    efei4000(何浩然) 
    同样的语句我是在窗体的事件中调试成功后粘贴到Hook代码中的。
    在窗体中把代码放置在click事件中可以拷贝一次图像。
    而且我用API这么长时间,我头一次用DC拷贝出错啊~
      

  12.   

    哈哈,知道了,
    钩子里面只要有错误就会报错。
    我加上漏掉的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
      

  13.   

    刚才又测试了一下,
    再说一句,我发现在钩子中中断跳出就会产生非法关闭的错误,
    比如有一个错误而跳出,on error resume next都不可以。
    VB IDE的设置断点,在断点出点Stop也会产生错误,
    Hook的代码必须可靠可执行,也就是说代码必须少而逻辑性强,
    不能出错,出错也要可以容错不可以产生Err.Raise
    其他的好像没发现有限制。收回前言hook中不能使用什么API。