flyingleaf's blog

象外行一样思考,象专家一样实践。

Show and Hide a Form's Titlebar at run-time

coolbean | 10 九月, 2007 02:16

This tip shows you how to show and hide the title bar of a window at run-time. To make a window's title bar disappear, you have to remove the control box, the maximise box and the minimise box as well as set the caption of the form to blank. Unfortunately, VB's ControlBox, MinButton and MaxButton properties of a form are read-only so you can normally only do this at design time. However, by manipulating the style of the window using API calls, you can get the same thing to happen at run-time.

Start a new project in VB. Add the following code to the project's form:

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 Long 
Private Const GWL_STYLE = (-16) 
Private Const WS_CAPTION = &HC00000 ' WS_BORDER Or WS_DLGFRAME 
Private Const WS_MAXIMIZEBOX = &H10000 
Private Const WS_MINIMIZEBOX = &H20000 
Private Const WS_SYSMENU = &H80000 

Private Declare Function SetWindowPos Lib "user32" _
      (ByVal hwnd As Long, ByVal hWndInsertAfter As Long, _
      ByVal x As Long, ByVal y As Long, _
      ByVal cx As Long, ByVal cy As Long, ByVal wFlags As Long) As Long 
Private Enum ESetWindowPosStyles 
    SWP_SHOWWINDOW = &H40 
    SWP_HIDEWINDOW = &H80 
    SWP_FRAMECHANGED = &H20 ' The frame changed: send WM_NCCALCSIZE 
    SWP_NOACTIVATE = &H10 
    SWP_NOCOPYBITS = &H100 
    SWP_NOMOVE = &H2 
    SWP_NOOWNERZORDER = &H200 ' Don't do owner Z ordering 
    SWP_NOREDRAW = &H8 
    SWP_NOREPOSITION = SWP_NOOWNERZORDER 
    SWP_NOSIZE = &H1 
    SWP_NOZORDER = &H4 
    SWP_DRAWFRAME = SWP_FRAMECHANGED 
    HWND_NOTOPMOST = -2 
End Enum 

Private Declare Function GetWindowRect Lib "user32" ( _
      ByVal hwnd As Long, lpRect As RECT) As Long 
Private Type RECT 
    Left As Long 
    Top As Long 
    Right As Long 
    Bottom As Long 
End Type 

Private Function ShowTitleBar(ByVal bState As Boolean) 
Dim lStyle As Long 
Dim tR As RECT 

   ' Get the window's position: 
   GetWindowRect Me.hwnd, tR 

   ' Modify whether title bar will be visible: 
   lStyle = GetWindowLong(Me.hwnd, GWL_STYLE) 
   If (bState) Then 
      Me.Caption = Me.Tag 
      If Me.ControlBox Then 
         lStyle = lStyle Or WS_SYSMENU 
      End If 
      If Me.MaxButton Then 
         lStyle = lStyle Or WS_MAXIMIZEBOX 
      End If 
      If Me.MinButton Then 
         lStyle = lStyle Or WS_MINIMIZEBOX 
      End If 
      If Me.Caption <> "" Then 
         lStyle = lStyle Or WS_CAPTION 
      End If 
   Else 
      Me.Tag = Me.Caption 
      Me.Caption = "" 
      lStyle = lStyle And Not WS_SYSMENU 
      lStyle = lStyle And Not WS_MAXIMIZEBOX 
      lStyle = lStyle And Not WS_MINIMIZEBOX 
      lStyle = lStyle And Not WS_CAPTION 
   End If 
   SetWindowLong Me.hwnd, GWL_STYLE, lStyle 

   ' Ensure the style takes and make the window the 
   ' same size, regardless that the title bar etc 
   ' is now a different size: 
   SetWindowPos Me.hwnd, _
       0, tR.Left, tR.Top, _
       tR.Right - tR.Left, tR.Bottom - tR.Top, _
       SWP_NOREPOSITION Or SWP_NOZORDER Or SWP_FRAMECHANGED 

   Me.Refresh 

   ' Ensure that your resize code is fired, as the client area 
   ' has changed: 
   Form_Resize 

End Function 

To try out the hiding and showing the title bar, add a CheckBox to the project's form. Set the check box's Value property to 1 (Checked). Then put the following code under the Check box's click event:

Private Sub Check1_Click() 
    If (Check1.Value = Checked) Then 
        ShowTitleBar True 
    Else 
        ShowTitleBar False 
    End If 
End Sub 

When you click on the check box, the form's titlebar will be alternately hidden and shown.

System Internet Connection - Determining How and If Connected

coolbean | 10 九月, 2007 02:11

If you are designing a project which can use an Internet connection, it can be useful to know whether the system is connected or not. There are various methods of doing this, however the most informative and reliable method is to use the WinInet.DLL InternetGetConnectedStateEx API call. The only problem with this call is it is only implemented for the WinInet.DLL version shipped with Internet Explorer version 4.0 or higher.

To test out this function, start a new project and add the following code:

Public Declare Function InternetGetConnectedStateEx Lib "wininet.dll" _
   Alias "InternetGetConnectedStateExA" _
   (ByRef lpdwFlags As Long, _
   ByVal lpszConnectionName As String, _
   ByVal dwNameLen As Long, _
   ByVal dwReserved As Long _
   ) As Long

Public Enum EIGCInternetConnectionState
   INTERNET_CONNECTION_MODEM = &H1&
   INTERNET_CONNECTION_LAN = &H2&
   INTERNET_CONNECTION_PROXY = &H4&
   INTERNET_RAS_INSTALLED = &H10&
   INTERNET_CONNECTION_OFFLINE = &H20&
   INTERNET_CONNECTION_CONFIGURED = &H40&
End Enum

Public Property Get InternetConnected( _
     Optional ByRef eConnectionInfo As EIGCInternetConnectionState, _
     Optional ByRef sConnectionName As String _
   ) As Boolean
Dim dwFlags As Long
Dim sNameBuf As String
Dim lR As Long
Dim iPos As Long
   sNameBuf = String$(513, 0)
   lR = InternetGetConnectedStateEx(dwFlags, sNameBuf, 512, 0&)
   eConnectionInfo = dwFlags
   iPos = InStr(sNameBuf, vbNullChar)
   If iPos > 0 Then
     sConnectionName = Left$(sNameBuf, iPos - 1)
   ElseIf Not sNameBuf = String$(513, 0) Then
     sConnectionName = sNameBuf
   End If
     InternetConnected = (lR = 1)
End Property

To try out the code, add a CommandButton and a Multi-Line TextBox to your test project's main form. Then add the following code to try the function:

Private Sub Command1_Click()
Dim eR As EIGCInternetConnectionState
Dim sMsg As String
Dim sName As String
Dim bConnected As Boolean

   ' Determine whether we have a connection:
   bConnected = InternetConnected(eR, sName)

   ' The connection state info parameter provides details
   ' about how we connect:
   If (eR And INTERNET_CONNECTION_MODEM) = INTERNET_CONNECTION_MODEM Then
     sMsg = sMsg & "Connection uses a modem." & vbCrLf
   End If
   If (eR And INTERNET_CONNECTION_LAN) = INTERNET_CONNECTION_LAN Then
     sMsg = sMsg & "Connection uses LAN." & vbCrLf
   End If
   If (eR And INTERNET_CONNECTION_PROXY) = INTERNET_CONNECTION_PROXY Then
     sMsg = sMsg & "Connection is via Proxy." & vbCrLf
   End If
   If (eR And INTERNET_CONNECTION_OFFLINE) = INTERNET_CONNECTION_OFFLINE Then
     sMsg = sMsg & "Connection is Off-line." & vbCrLf
   End If
   If (eR And INTERNET_CONNECTION_CONFIGURED) = INTERNET_CONNECTION_CONFIGURED Then
     sMsg = sMsg & "Connection is Configured." & vbCrLf
   Else
     sMsg = sMsg & "Connection is Not Configured." & vbCrLf
   End If
   If (eR And INTERNET_RAS_INSTALLED) = INTERNET_RAS_INSTALLED Then
     sMsg = sMsg & "System has RAS installed." & vbCrLf
   End If
   
   ' Display the connection name and info:
   If bConnected Then
     Text1.Text = "Connected: " & sName & vbCrLf & vbCrLf & sMsg
   Else
     Text1.Text = "Not Connected: " & sName & vbCrLf & vbCrLf & sMsg
   End If

End Sub

Run the project. When you click the command button, the text box will be updated with the status of the current connection, the name of RAS dial-up connection used (if applicable) and also various information about how the connection is being achieved (i.e. by modem or LAN, via a proxy and whether the connection is configured or not).

VB6 Async File Find Component

coolbean | 10 九月, 2007 02:02

Download code:

VB6_Async_File_Find_Component.zip(10K)

Multi-Threaded VB Using ActiveX EXEs

coolbean | 10 九月, 2007 01:42

Run code asynchronously with this simple and elegant design model

No more Hourglasses

Ever since VB5 service pack 2, there has been the possibility of running VB applications multi-threaded by using ActiveX EXEs. However, if you try and research this you will find it is fiddly to get working. This article presents a method which makes running operations asynchronously really easy.

Multi-Threading

Although ActiveX EXEs can run in a new thread, I've never really understood how to get it working: you need to use CreateObject to make the object on a new thread, and then you need to be very careful as to how you start the work you want to do asynchronously. Most times you find that VB blocks until the method call is complete regardless of whether the object is in a new thread or not.

What you want to be able to do is to say I want to perform some operation asynchronously and I would like to be notified when it is complete. This project demonstrates a tiny code module and a type library you can add into your own ActiveX projects to do just that. It is based around a part of the MSDN "CodeFlow sample" (see downloads).

Asynchronous Running - Just to get it Started

The main problem with getting a multi-threaded application up and running is how to call a method in VB without the caller being blocked. It turns out there is a simple solution to this problem. The steps are as follows:

  1. Put the method call into an ActiveX EXE, and make it private to the EXE.
  2. Provide a new wrapper method to call the method you want to run asynchronously. This wrapper method enables a timer. When the timer fires, the original method is called.

This solves the problem because the method you call immediately yields control back to the caller, and then it is left to Windows pre-emptive multi-tasking to raise the timer event and kick off the process within the ActiveX EXE. There is no further interference because the ActiveX EXE is running in a different process to the caller.

In More Detail

To stop having to have a form in the ActiveX EXE, this solution is based on a Win32 API timer. Win32 API timers come in two flavours: either they notify the application when they tick by posting a WM_TIMER message to a window, or they fire a callback interface. This solution uses the callback interface, and as a consequence must be implemented within a module (because VB will not provide the address of a function to callback to for any function within an object, only one in a module).

Because the code to start the object is implemented in a module, the module must have a reference to the object instance it has to start when the timer fires. To achieve this without the possibility for errors, an interface is defined that the object can implement and the module will only use this for communication. In this sample, the interface is defined in a Type Library called Runnable. This allows you to reference the Type Library without having to declare it as a public class from the ActiveX executable.

The final implementation feature is the use of the OLE/COM API call CoLockObjectExternal to ensure that the object being started asynchronously isn't inadvertently terminated by the caller before the timer has had a chance to be fired.

Here is the code in the mStart.bas module:

 (查看全文)

 
Accessible and Valid XHTML 1.0 Strict and CSS
Powered by LifeType - Design by BalearWeb