以文本方式查看主题 - Foxtable(狐表) (http://foxtable.net/bbs/index.asp) -- 专家坐堂 (http://foxtable.net/bbs/list.asp?boardid=2) ---- [求助]无边窗体任务栏右键菜单关闭能改变么 (http://foxtable.net/bbs/dispbbs.asp?boardid=2&id=40322) |
||||
-- 作者:aalons -- 发布时间:2013/9/15 11:09:00 -- [求助]无边窗体任务栏右键菜单关闭能改变么 如题\'如何做纯粹的无边框窗体\'点任务栏右键的关闭菜单\'只关闭了当前窗体\'然后显示了Foxtable的原窗体\'露出了马甲\'有没能全部关闭的\'且左键点击任务栏上的图标无法最大化、最小化和还原 [此贴子已经被作者于2013-9-15 19:26:59编辑过]
|
||||
-- 作者:aalons -- 发布时间:2013/9/15 11:36:00 -- \'************************************************************************* \'**模 块 名:cSubClass \'**说 明:通用子类化模块,拦截本进程指定句柄的消息 \'**版 本:V1.0 \'**备 注: 内嵌汇编技术实现单个类模块子类化 \' \'************************************************************************* \'======================================================================================== \' Subclasser declarations \'======================================================================================== Private Enum eMsgWhen [MSG_AFTER] = 1 \'Message calls back after the original (previous) WndProc [MSG_BEFORE] = 2 \'Message calls back before the original (previous) WndProc [MSG_BEFORE_AND_AFTER] = MSG_AFTER Or MSG_BEFORE \'Message calls back before and after the original (previous) WndProc End Enum Private Const ALL_MESSAGES As Long = -1 \'All messages added or deleted Private Const CODE_LEN As Long = 197 \'Length of the machine code in bytes Private Const GWL_WNDPROC As Long = -4 \'Get/SetWindow offset to the WndProc procedure address Private Const PATCH_04 As Long = 88 \'Table B (before) address patch offset Private Const PATCH_05 As Long = 93 \'Table B (before) entry count patch offset Private Const PATCH_08 As Long = 132 \'Table A (after) address patch offset Private Const PATCH_09 As Long = 137 \'Table A (after) entry count patch offset Private Type tSubData \'Subclass data type hWnd As Long \'Handle of the window being subclassed nAddrSub As Long \'The address of our new WndProc (allocated memory). nAddrOrig As Long \'The address of the pre-existing WndProc nMsgCntA As Long \'Msg after table entry count nMsgCntB As Long \'Msg before table entry count aMsgTblA() As Long \'Msg after table array aMsgTblB() As Long \'Msg Before table array End Type Private sc_aSubData() As tSubData \'Subclass data array Private sc_aBuf(1 To CODE_LEN) As Byte \'Code buffer byte array Private sc_pCWP As Long \'Address of the CallWindowsProc Private sc_pEbMode As Long \'Address of the EbMode IDE break/stop/running function Private sc_pSWL As Long \'Address of the SetWindowsLong function Private Declare Sub RtlMoveMemory Lib "kernel32" (Destination As Any, Source As Any, ByVal Length As Long) Private Declare Function GetModuleHandleA Lib "kernel32" (ByVal lpModuleName As String) As Long Private Declare Function GetProcAddress Lib "kernel32" (ByVal hModule As Long, ByVal lpProcName As String) As Long Private Declare Function GlobalAlloc Lib "kernel32" (ByVal wFlags As Long, ByVal dwBytes As Long) As Long Private Declare Function GlobalFree Lib "kernel32" (ByVal hMem As Long) As Long Private Declare Function SetWindowLongA Lib "user32" (ByVal hWnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long Private Declare Function VirtualProtect Lib "kernel32" (lpAddress As Any, ByVal dwSize As Long, ByVal flNewProtect As Long, lpflOldProtect As Long) As Long Private Const WM_LBUTTONDOWN = &H201 Private Const WM_SETFOCUS = &O7 Public Event MsgCome(ByVal bBefore As Boolean, ByRef bHandled As Boolean, ByRef lReturn As Long, ByRef lng_hWnd As Long, ByRef uMsg As Long, ByRef wParam As Long, ByRef lParam As Long) Private Sub Class_Terminate() Call Subclass_StopAll End Sub \'======================================================================================== \' Subclass handler: MUST be the first Public routine in this file. \' That includes public properties also. \'======================================================================================== Public Sub zSubclass_Proc(ByVal bBefore As Boolean, ByRef bHandled As Boolean, ByRef lReturn As Long, ByRef lng_hWnd As Long, ByRef uMsg As Long, ByRef wParam As Long, ByRef lParam As Long) \' \'Parameters: \' bBefore - Indicates whether the the message is being processed before or after the default handler - only really needed if a message is set to callback both before & after. \' bHandled - Set this variable to True in a \'before\' callback to prevent the message being subsequently processed by the default handler... and if set, an \'after\' callback \' lReturn - Set this variable as per your intentions and requirements, see the MSDN documentation for each individual message value. \' lng_hWnd - The window handle \' uMsg - The message number \' wParam - Message related data \' lParam - Message related data \' \'Notes: \' If you really know what you\'re doing, it\'s possible to change the values of the \' hWnd, uMsg, wParam and lParam parameters in a \'before\' callback so that different \' values get passed to the default handler.. and optionaly, the \'after\' callback RaiseEvent MsgCome(bBefore, bHandled, lReturn, lng_hWnd, uMsg, wParam, lParam) Debug.Print bBefore; bHandled; lReturn; lng_hWnd; uMsg; wParam; lParam \'消息截获 End Sub \'======================================================================================== \' Methods \'======================================================================================== Public Function AddWindowMsgs(ByVal hWnd As Long) As Boolean \'-- Start subclassing Call Subclass_Start(hWnd) Call Subclass_AddMsg(hWnd, ALL_MESSAGES, MSG_BEFORE) \'-- Success AddWindowMsgs = True End Function Public Function DeleteWindowMsg(ByVal hWnd As Long) As Boolean Debug.Print "de" End Function \'======================================================================================== \' Subclass code - The programmer may call any of the following Subclass_??? routines \'======================================================================================== Private Sub Subclass_AddMsg(ByVal lng_hWnd As Long, ByVal uMsg As Long, Optional ByVal When As eMsgWhen = MSG_AFTER) \'Add a message to the table of those that will invoke a callback. You should Subclass_Start first and then add the messages \'Parameters: \' lng_hWnd - The handle of the window for which the uMsg is to be added to the callback table \' uMsg - The message number that will invoke a callback. NB Can also be ALL_MESSAGES, ie all messages will callback \' When - Whether the msg is to callback before, after or both with respect to the the default (previous) handler With sc_aSubData(zIdx(lng_hWnd)) If (When And eMsgWhen.MSG_BEFORE) Then Call zAddMsg(uMsg, .aMsgTblB, .nMsgCntB, eMsgWhen.MSG_BEFORE, .nAddrSub) End If If (When And eMsgWhen.MSG_AFTER) Then Call zAddMsg(uMsg, .aMsgTblA, .nMsgCntA, eMsgWhen.MSG_AFTER, .nAddrSub) End If End With End Sub Private Sub Subclass_DelMsg(ByVal lng_hWnd As Long, ByVal uMsg As Long, Optional ByVal When As eMsgWhen = MSG_AFTER) \'Delete a message from the table of those that will invoke a callback. \'Parameters: \' lng_hWnd - The handle of the window for which the uMsg is to be removed from the callback table \' uMsg - The message number that will be removed from the callback table. NB Can also be ALL_MESSAGES, ie all messages will callback \' When - Whether the msg is to be removed from the before, after or both callback tables With sc_aSubData(zIdx(lng_hWnd)) If (When And eMsgWhen.MSG_BEFORE) Then Call zDelMsg(uMsg, .aMsgTblB, .nMsgCntB, eMsgWhen.MSG_BEFORE, .nAddrSub) End If If (When And eMsgWhen.MSG_AFTER) Then Call zDelMsg(uMsg, .aMsgTblA, .nMsgCntA, eMsgWhen.MSG_AFTER, .nAddrSub) End If End With End Sub Private Function Subclass_InIDE() As Boolean \'Return whether we\'re running in the IDE. Debug.Assert zSetTrue(Subclass_InIDE) End Function Private Function Subclass_Start(ByVal lng_hWnd As Long) As Long \'Start subclassing the passed window handle \'Parameters: \' lng_hWnd - The handle of the window to be subclassed \'Returns; \' The sc_aSubData() index Dim I As Long \'Loop index Dim J As Long \'Loop index Dim nSubIdx As Long \'Subclass data index Dim sSubCode As String \'Subclass code string Const GMEM_FIXED As Long = 0 \'Fixed memory GlobalAlloc flag Const PAGE_EXECUTE_READWRITE As Long = &H40& \'Allow memory to execute without violating XP SP2 Data Execution Prevention Const PATCH_01 As Long = 18 \'Code buffer offset to the location of the relative address to EbMode Const PATCH_02 As Long = 68 \'Address of the previous WndProc Const PATCH_03 As Long = 78 \'Relative address of SetWindowsLong Const PATCH_06 As Long = 116 \'Address of the previous WndProc Const PATCH_07 As Long = 121 \'Relative address of CallWindowProc Const PATCH_0A As Long = 186 \'Address of the owner object Const FUNC_CWP As String = "CallWindowProcA" \'We use CallWindowProc to call the original WndProc Const FUNC_EBM As String = "EbMode" \'VBA\'s EbMode function allows the machine code thunk to know if the IDE has stopped or is on a breakpoint Const FUNC_SWL As String = "SetWindowLongA" \'SetWindowLongA allows the cSubclasser machine code thunk to unsubclass the subclasser itself if it detects via the EbMode function that the IDE has stopped Const MOD_USER As String = "user32" \'Location of the SetWindowLongA & CallWindowProc functions Const MOD_VBA5 As String = "vba5" \'Location of the EbMode function if running VB5 Const MOD_VBA6 As String = "vba6" \'Location of the EbMode function if running VB6 \'If it\'s the first time through here.. If (sc_aBuf(1) = 0) Then \'Build the hex pair subclass string sSubCode = "5589E583C4F85731C08945FC8945F8EB0EE80000000083F802742185C07424E830000000837DF800750AE838000000E84D0000005F8B45FCC9C21000E826000000EBF168000000006AFCFF7508E800000000EBE031D24ABF00000000B900000000E82D000000C3FF7514FF7510FF750CFF75086800000000E8000000008945FCC331D2BF00000000B900000000E801000000C3E32F09C978078B450CF2AF75248D4514508D4510508D450C508D4508508D45FC508D45F85052B800000000508B00FF501CC3" \'Convert the string from hex pairs to bytes and store in the machine code buffer I = 1 Do While J < CODE_LEN J = J + 1 sc_aBuf(J) = CByte("&H" & Mid$(sSubCode, I, 2)) \'Convert a pair of hex characters to an eight-bit value and store in the static code buffer array I = I + 2 Loop \'Next pair of hex characters \'Get API function addresses If (Subclass_InIDE) Then \'If we\'re running in the VB IDE sc_aBuf(16) = &H90 \'Patch the code buffer to enable the IDE state code sc_aBuf(17) = &H90 \'Patch the code buffer to enable the IDE state code sc_pEbMode = zAddrFunc(MOD_VBA6, FUNC_EBM) \'Get the address of EbMode in vba6.dll If (sc_pEbMode = 0) Then \'Found? sc_pEbMode = zAddrFunc(MOD_VBA5, FUNC_EBM) \'VB5 perhaps End If End If Call zPatchVal(VarPtr(sc_aBuf(1)), PATCH_0A, ObjPtr(Me)) \'Patch the address of this object instance into the static machine code buffer sc_pCWP = zAddrFunc(MOD_USER, FUNC_CWP) \'Get the address of the CallWindowsProc function sc_pSWL = zAddrFunc(MOD_USER, FUNC_SWL) \'Get the address of the SetWindowLongA function ReDim sc_aSubData(0 To 0) As tSubData \'Create the first sc_aSubData element Else nSubIdx = zIdx(lng_hWnd, True) If (nSubIdx = -1) Then \'If an sc_aSubData element isn\'t being re-cycled nSubIdx = UBound(sc_aSubData()) + 1 \'Calculate the next element ReDim Preserve sc_aSubData(0 To nSubIdx) As tSubData \'Create a new sc_aSubData element End If Subclass_Start = nSubIdx End If With sc_aSubData(nSubIdx) .nAddrSub = GlobalAlloc(GMEM_FIXED, CODE_LEN) \'Allocate memory for the machine code WndProc Call VirtualProtect(ByVal .nAddrSub, CODE_LEN, PAGE_EXECUTE_READWRITE, I) \'Mark memory as executable Call RtlMoveMemory(ByVal .nAddrSub, sc_aBuf(1), CODE_LEN) \'Copy the machine code from the static byte array to the code array in sc_aSubData .hWnd = lng_hWnd \'Store the hWnd .nAddrOrig = SetWindowLongA(.hWnd, GWL_WNDPROC, .nAddrSub) \'Set our WndProc in place Call zPatchRel(.nAddrSub, PATCH_01, sc_pEbMode) \'Patch the relative address to the VBA EbMode api function, whether we need to not.. hardly worth testing Call zPatchVal(.nAddrSub, PATCH_02, .nAddrOrig) \'Original WndProc address for CallWindowProc, call the original WndProc Call zPatchRel(.nAddrSub, PATCH_03, sc_pSWL) \'Patch the relative address of the SetWindowLongA api function Call zPatchVal(.nAddrSub, PATCH_06, .nAddrOrig) \'Original WndProc address for SetWindowLongA, unsubclass on IDE stop Call zPatchRel(.nAddrSub, PATCH_07, sc_pCWP) \'Patch the relative address of the CallWindowProc api function End With End Function Private Sub Subclass_StopAll() \'Stop all subclassing Dim I As Long I = UBound(sc_aSubData()) \'Get the upper bound of the subclass data array Do While I >= 0 \'Iterate through each element With sc_aSubData(I) If (.hWnd <> 0) Then \'If not previously Subclass_Stop\'d Call Subclass_Stop(.hWnd) \'Subclass_Stop End If End With I = I - 1 \'Next element Loop End Sub Private Sub Subclass_Stop(ByVal lng_hWnd As Long) \'Stop subclassing the passed window handle \'Parameters: \' lng_hWnd - The handle of the window to stop being subclassed With sc_aSubData(zIdx(lng_hWnd)) Call SetWindowLongA(.hWnd, GWL_WNDPROC, .nAddrOrig) \'Restore the original WndProc Call zPatchVal(.nAddrSub, PATCH_05, 0) \'Patch the Table B entry count to ensure no further \'before\' callbacks Call zPatchVal(.nAddrSub, PATCH_09, 0) \'Patch the Table A entry count to ensure no further \'after\' callbacks Call GlobalFree(.nAddrSub) \'Release the machine code memory .hWnd = 0 \'Mark the sc_aSubData element as available for re-use .nMsgCntB = 0 \'Clear the before table .nMsgCntA = 0 \'Clear the after table Erase .aMsgTblB \'Erase the before table Erase .aMsgTblA \'Erase the after table End With End Sub \'---------------------------------------------------------------------------------------- \'These z??? routines are exclusively called by the Subclass_??? routines. \'---------------------------------------------------------------------------------------- Private Sub zAddMsg(ByVal uMsg As Long, ByRef aMsgTbl() As Long, ByRef nMsgCnt As Long, ByVal When As eMsgWhen, ByVal nAddr As Long) \'Worker sub for Subclass_AddMsg Dim nEntry As Long \'Message table entry index Dim nOff1 As Long \'Machine code buffer offset 1 Dim nOff2 As Long \'Machine code buffer offset 2 If (uMsg = ALL_MESSAGES) Then \'If all messages nMsgCnt = ALL_MESSAGES \'Indicates that all messages will callback Else \'Else a specific message number Do While nEntry < nMsgCnt \'For each existing entry. NB will skip if nMsgCnt = 0 nEntry = nEntry + 1 If (aMsgTbl(nEntry) = 0) Then \'This msg table slot is a deleted entry aMsgTbl(nEntry) = uMsg \'Re-use this entry Exit Sub \'Bail ElseIf (aMsgTbl(nEntry) = uMsg) Then \'The msg is already in the table! Exit Sub \'Bail End If Loop \'Next entry nMsgCnt = nMsgCnt + 1 \'New slot required, bump the table entry count ReDim Preserve aMsgTbl(1 To nMsgCnt) As Long \'Bump the size of the table. aMsgTbl(nMsgCnt) = uMsg \'Store the message number in the table End If If (When = eMsgWhen.MSG_BEFORE) Then \'If before nOff1 = PATCH_04 \'Offset to the Before table nOff2 = PATCH_05 \'Offset to the Before table entry count Else \'Else after nOff1 = PATCH_08 \'Offset to the After table nOff2 = PATCH_09 \'Offset to the After table entry count End If If (uMsg <> ALL_MESSAGES) Then Call zPatchVal(nAddr, nOff1, VarPtr(aMsgTbl(1))) \'Address of the msg table, has to be re-patched because Redim Preserve will move it in memory. End If Call zPatchVal(nAddr, nOff2, nMsgCnt) \'Patch the appropriate table entry count End Sub Private Function zAddrFunc(ByVal sDLL As String, ByVal sProc As String) As Long \'Return the memory address of the passed function in the passed dll zAddrFunc = GetProcAddress(GetModuleHandleA(sDLL), sProc) Debug.Assert zAddrFunc \'You may wish to comment out this line if you\'re using vb5 else the EbMode GetProcAddress will stop here everytime because we look for vba6.dll first End Function Private Sub zDelMsg(ByVal uMsg As Long, ByRef aMsgTbl() As Long, ByRef nMsgCnt As Long, ByVal When As eMsgWhen, ByVal nAddr As Long) \'Worker sub for Subclass_DelMsg Dim nEntry As Long If (uMsg = ALL_MESSAGES) Then \'If deleting all messages nMsgCnt = 0 \'Message count is now zero If When = eMsgWhen.MSG_BEFORE Then \'If before nEntry = PATCH_05 \'Patch the before table message count location Else \'Else after nEntry = PATCH_09 \'Patch the after table message count location End If Call zPatchVal(nAddr, nEntry, 0) \'Patch the table message count to zero Else \'Else deleteting a specific message Do While nEntry < nMsgCnt \'For each table entry nEntry = nEntry + 1 If (aMsgTbl(nEntry) = uMsg) Then \'If this entry is the message we wish to delete aMsgTbl(nEntry) = 0 \'Mark the table slot as available Exit Do \'Bail End If Loop \'Next entry End If End Sub Private Function zIdx(ByVal lng_hWnd As Long, Optional ByVal bAdd As Boolean = False) As Long \'Get the sc_aSubData() array index of the passed hWnd \'Get the upper bound of sc_aSubData() - If you get an error here, you\'re probably Subclass_AddMsg-ing before Subclass_Start zIdx = UBound(sc_aSubData) Do While zIdx >= 0 \'Iterate through the existing sc_aSubData() elements With sc_aSubData(zIdx) If (.hWnd = lng_hWnd) Then \'If the hWnd of this element is the one we\'re looking for If (Not bAdd) Then \'If we\'re searching not adding Exit Function \'Found End If ElseIf (.hWnd = 0) Then \'If this an element marked for reuse. If (bAdd) Then \'If we\'re adding Exit Function \'Re-use it End If End If End With zIdx = zIdx - 1 \'Decrement the index Loop If (Not bAdd) Then Debug.Assert False \'hWnd not found, programmer error End If \'If we exit here, we\'re returning -1, no freed elements were found End Function Private Sub zPatchRel(ByVal nAddr As Long, ByVal nOffset As Long, ByVal nTargetAddr As Long) \'Patch the machine code buffer at the indicated offset with the relative address to the target address. Call RtlMoveMemory(ByVal nAddr + nOffset, nTargetAddr - nAddr - nOffset - 4, 4) End Sub Private Sub zPatchVal(ByVal nAddr As Long, ByVal nOffset As Long, ByVal nValue As Long) \'Patch the machine code buffer at the indicated offset with the passed value Call RtlMoveMemory(ByVal nAddr + nOffset, nValue, 4) End Sub Private Function zSetTrue(ByRef bValue As Boolean) As Boolean \'Worker function for Subclass_InIDE zSetTrue = True bValue = True End Function [此贴子已经被作者于2013-9-15 19:24:22编辑过]
|
||||
-- 作者:aalons -- 发布时间:2013/9/15 12:13:00 -- \'************************************************************************* \'**模 块 名:cSetStyle \'**说 明:为无标题栏窗口模拟默认窗体行为的类 \'**备 注: 需要单类模块子类化类cSubclass.cls支持 \'************************************************************************* 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 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 DrawFocusRect Lib "user32.dll" ( _ ByVal hdc As Long, _ ByRef lpRect As RECT) As Long Private Declare Function GetWindowRect Lib "user32.dll" ( _ ByVal hWnd As Long, _ ByRef lpRect As RECT) As Long Private Declare Function GetDC Lib "user32.dll" ( _ ByVal hWnd As Long) As Long Private Declare Function ReleaseDC Lib "user32.dll" ( _ ByVal hWnd As Long, _ ByVal hdc As Long) As Long Private Declare Function GetCursorPos Lib "user32.dll" ( _ ByRef lpPoint As POINTAPI) As Long Private Declare Function MoveWindow Lib "user32.dll" ( _ ByVal hWnd As Long, _ ByVal X As Long, _ ByVal Y As Long, _ ByVal nWidth As Long, _ ByVal nHeight As Long, _ ByVal bRepaint As Long) As Long Private Type RECT Left As Long Top As Long Right As Long Bottom As Long End Type Private Type POINTAPI X As Long Y As Long End Type Private Const GWL_STYLE = (-16) Private Const WS_SYSMENU = &H80000 Private Const WS_MINIMIZEBOX = &H20000 Private Const SC_CLOSE = &HF060& Private Const SC_MOVE = &HF010& Private Const WM_SYSCOMMAND = &H112 Private Const WM_SETTINGCHANGE As Long = &H1A Private Const WM_MOUSEMOVE As Long = &H200 Private Const WM_LBUTTONDOWN As Long = &H201 Private Const WM_LBUTTONUP As Long = &H202 Private Const MK_LBUTTON As Long = &H1 Dim WithEvents objSubclass As cSubclass Dim WithEvents m_objForm As Form \'窗体对象,方便事件封装 Dim m_theHWND As Long, theMonDC As Long, DrawRECT As Boolean, tST As Boolean Dim MouseStyle As Long \'鼠标方位,顺时钟0到7,-1表示不在窗体边缘 Dim mX As Long, mY As Long \'鼠标按下时的座标 Public Event CloseClick() \'菜单关闭项被点击 Public Event SettingChange() \'WM_SETTINGCHANGE消息出现,可重绘窗体 Public Event MouseSize(ByVal theMouseStyle As Long) \'鼠标位置 Public Function SetWindowStyle(ByVal hWnd As Long) \'添加任务栏右键菜单,以及启动子类化. \'目标窗体的ShowInTaskbar属性需要设置为True. m_theHWND = hWnd Call SetWindowLong(m_theHWND, GWL_STYLE, WS_SYSMENU Or WS_MINIMIZEBOX) \'添加任务栏菜单样式 Call objSubclass.AddWindowMsgs(m_theHWND) End Function Public Function CallWindowStateChange() As Boolean \'最大化与还原效果处理,每次调用切换至另一状态 \'返回值为窗体状态. \' TRUE为最大化状态,FALSE为非最大化状态 Static tW As Long, tH As Long, tX As Long, tY As Long With m_objForm If tST Then \'TRUE,最大化状态 .Move tX, tY, tW, tH tST = False Else tX = .Left: tY = .Top: tW = .Width: tH = .Height \'记忆原位置,用于还原 .Move 0, 0, Screen.Width, Screen.Height - GetTaskbarHeight tST = True End If End With CallWindowStateChange = tST End Function Private Sub objSubclass_MsgCome(ByVal bBefore As Boolean, bHandled As Boolean, lReturn As Long, lng_hWnd As Long, uMsg As Long, wParam As Long, lParam As Long) Static MS As Long, PT As POINTAPI, RT As RECT, LastRT As RECT, NewRT As RECT If bBefore = False Then Exit Sub Select Case uMsg Case WM_SYSCOMMAND If wParam = SC_CLOSE Then \'点击了关闭按钮 RaiseEvent CloseClick End If Case WM_SETTINGCHANGE \'WM_SETTINGCHANGE消息出现,比如任务栏被改变等 RaiseEvent SettingChange Call CallSettingChange Case WM_LBUTTONDOWN \'鼠标按下. Call GetCursorPos(PT) MS = GetMouseStyle(lng_hWnd, PT.X, PT.Y) If MS <> -1 And DrawRECT = False And tST = False Then \'如果在调整区内按下鼠标左键 DrawRECT = True \'开始画框 Call GetWindowRect(lng_hWnd, RT) mX = PT.X: mY = PT.Y \'记录鼠标按下时的座标 LastRT = RT: NewRT = RT Call DrawMutiRect(theMonDC, LastRT) End If Case WM_MOUSEMOVE \'鼠标移动 Call GetCursorPos(PT) If DrawRECT = False And tST = False Then MS = GetMouseStyle(lng_hWnd, PT.X, PT.Y) RaiseEvent MouseSize(MS) \'窗体中用于改变鼠标样式 Call CallMouseSize(MS) End If Debug.Print " MouseStyle = " & MS If (wParam And MK_LBUTTON) And DrawRECT = True Then \'如果按下鼠标在移动 If theMonDC = 0 Then theMonDC = GetDC(0) \'取桌面DC With PT Select Case MS Case 0 NewRT.Top = .Y Case 1 NewRT.Top = .Y: NewRT.Right = .X Case 2 NewRT.Right = .X Case 3 NewRT.Bottom = .Y: NewRT.Right = .X Case 4 NewRT.Bottom = .Y Case 5 NewRT.Bottom = .Y: NewRT.Left = PT.X Case 6 NewRT.Left = PT.X Case 7 NewRT.Top = .Y: NewRT.Left = .X End Select End With Call DrawMutiRect(theMonDC, LastRT) \'先擦除原来的框 Call DrawMutiRect(theMonDC, NewRT) \'画新的框 LastRT = NewRT Debug.Print " " & NewRT.Top, NewRT.Bottom, NewRT.Left, NewRT.Right End If Case WM_LBUTTONUP If DrawRECT = True Then DrawRECT = False Call DrawMutiRect(theMonDC, LastRT) \'先擦除原来的框 With LastRT \'改变窗体大小与位置 Call MoveWindow(lng_hWnd, .Left, .Top, .Right - .Left, .Bottom - .Top, True) End With Call ReleaseDC(0, theMonDC): theMonDC = 0 \'再释放DC End If End Select End Sub Private Sub DrawMutiRect(ByVal DestDC As Long, ByRef RT As RECT) \'画框. \'画三次是为了粗一点(如果觉得细了的话就去掉注释) Dim I As RECT I = RT DrawFocusRect DestDC, I \' With I \' .Left = .Left + 1 \' .Top = .Top + 1 \' .Right = .Right - 1 \' .Bottom = .Bottom - 1 \' End With \' DrawFocusRect DestDC, I \' With I \' .Left = .Left - 2 \' .Top = .Top - 2 \' .Right = .Right + 2 \' .Bottom = .Bottom + 2 \' End With \' DrawFocusRect DestDC, I End Sub Private Function GetMouseStyle(ByVal hWnd As Long, ByVal X As Long, ByVal Y As Long) As Long \'取鼠标方位,0到7,分成8个方位. \'7 0 1 \'6 2 \'5 4 3 Const WC As Long = 5 \'5个象素以内有效 Dim RT As RECT, XOK As Long, YOK As Long GetMouseStyle = -1 Call GetWindowRect(hWnd, RT) With RT Do If Abs(.Left - X) <= WC And Abs(.Bottom - Y) <= WC Then \'左下角判定 GetMouseStyle = 5 Exit Do End If If Abs(.Left - X) <= WC And Abs(.Top - Y) <= WC Then \'左上角判定 GetMouseStyle = 7 Exit Do End If If Abs(.Right - X) <= WC And Abs(.Bottom - Y) <= WC Then \'右下角判定 GetMouseStyle = 3 Exit Do End If If Abs(.Right - X) <= WC And Abs(.Top - Y) <= WC Then \'右上角判定 GetMouseStyle = 1 Exit Do End If If X > .Left + WC And X < .Right - WC And Abs(.Top - Y) <= WC Then \'上边判定 GetMouseStyle = 0 Exit Do End If If Abs(.Right - X) <= WC And Y < .Bottom - WC And Y > .Top + WC Then \'右边判定 GetMouseStyle = 2 Exit Do End If If X > .Left + WC And X < .Right - WC And Abs(.Bottom - Y) <= WC Then \'下边判定 GetMouseStyle = 4 Exit Do End If If Abs(.Left - X) <= WC And Y < .Bottom - WC And Y > .Top + WC Then \'左边判定 GetMouseStyle = 6 Exit Do End If Exit Do Loop End With End Function Private Sub CallSettingChange() \'任务栏改变等事件 If tST And m_objForm.WindowState <> vbMinimized Then m_objForm.Move 0, 0, Screen.Width, Screen.Height - GetTaskbarHeight End Sub Private Sub CallMouseSize(ByVal theMouseStyle As Long) \'边缘位置时的鼠标样式改变 With m_objForm Select Case theMouseStyle Case 0, 4 \'上下边 .MousePointer = vbSizeNS Case 1, 5 \'左下右上角 .MousePointer = vbSizeNESW Case 2, 6 \'左右边 .MousePointer = vbSizeWE Case 3, 7 \'左上右下角 .MousePointer = vbSizeNWSE Case Else .MousePointer = vbArrow End Select End With End Sub Private Function LoWord(ByVal DWord As Long) As Integer \'低字 If DWord And &H8000& Then LoWord = DWord Or &HFFFF0000 Else LoWord = DWord And &HFFFF& End If End Function Private Function HiWord(ByVal DWord As Long) As Integer \'高字 HiWord = (DWord And &HFFFF0000) \\ 65536 End Function Private Sub Class_Initialize() Set objSubclass = New cSubclass End Sub Private Sub Class_Terminate() objSubclass.DeleteWindowMsg m_theHWND Set objSubclass = Nothing End Sub Public Property Get objForm() As Form Set objForm = m_objForm End Property Public Property Let objForm(ByVal vNewValue As Form) Set m_objForm = vNewValue End Property [此贴子已经被作者于2013-9-15 19:25:52编辑过]
|
||||
-- 作者:aalons -- 发布时间:2013/9/15 15:14:00 -- 顶3 |
||||
-- 作者:aalons -- 发布时间:2013/9/15 19:17:00 --
|
||||
-- 作者:有点甜 -- 发布时间:2013/9/15 20:13:00 -- 如果要关闭主窗口,用代码 basemainform.close 没有闲心做任务栏的右键菜单。楼主自己想办法吧。
|
||||
-- 作者:有点甜 -- 发布时间:2013/9/15 20:49:00 -- 感觉有趣,还是做了一下,楼主参考下。
|
||||
-- 作者:aalons -- 发布时间:2013/9/16 1:09:00 -- 以下是引用有点甜在2013-9-15 20:49:00的发言:
感觉有趣,还是做了一下,楼主参考下。
|
||||
-- 作者:lsy -- 发布时间:2013/9/16 7:35:00 -- 楼主的坚持,有点甜的敬业,都让人敬佩。 |
||||
-- 作者:aalons -- 发布时间:2013/9/16 8:28:00 -- 只是好像右键关闭还是只关闭当前窗口,露出后面的Foxtable窗口??有点甜??你那边是吗? |