Archive

Archive for October, 2008

Encription Your String

October 29, 2008 programmervb 2 comments

Function EncDec(InData As variant, Optional inPW As variant=”") as variant

On Error Resume Next

Dim arrSBox(0 to 255) As Integer

Dim arrPW(0 to 255) As Integer

Dim Bi As Integer,Bj as Integer

Dim mKey As Integer

Dim i As Integer, j As Integer

Dim x As Integer, y as Integer

Dim mCode As Byte, mCodeSeries As Variant

EncDec=”"

If Trim(inData)=”"Then

Exit Function

End If

If inPW <> “” Then

j=1

For i=0 to 255

arrPW(i)=Asc(Mid$(inPW),j,1))

j=j+1

if j > Len(inPW) Then

j=1

End if

Next i

Else

For i = 0 to 255

arrPW(i)=0

Next i

End if

For i = 0 to 255

arrSBox(i)=i

Next i

j=0

For i=0 to 255

j=(arrSBox(i) + arrPW(i)) Mod 256

x=arrSBox(i)

arrSBox(i)=arrSBox(j)

arrSBox(j)=x

Next i

mCodeSeries=”"

Bi=0;Bj=0

For i=1 to Len(InData)

Bi=(Bi+1)Mod 256

Bj=(Bj+arrSBox(Bi))Mod 256

‘change

x=arrSBox(Bi)

arrSBox(Bi=arrSBox(Bj)

arrSbox(Bj)=x

‘key for XOR

mKey=arrSBox(arrSBox(Bi)+arrSBox(Bj))Mod 256)

‘use operand XOR

mCode=Asc(Mid$(inData,i,1)) XOR mKey

mCodeSeries=mCodeSeries & Chr(mCode)

Next i

EncDec=mCodeSeries

End Function

If you want to buy something for christmast day don’t worry. Read More

Categories: Application Tags:

Preventing Logoff or Shutdown

October 29, 2008 programmervb Leave a comment

Sometimes if an application is performing a long operation you do not want the user to log-off or shutdown the system until it is complete. An example is when burning a CD, as the CD burn will be terminated and the disc potentially ruined. This sample demonstrates how to respond to the WM_QUERYENDSESSION message to prevent this from occurring.

About WM_QUERYENDSESSION

The WM_QUERYENDSESSION is sent to the top level Window of each application when the user chooses to end a session by either logging off or shutting down Windows (or if a programmatic request to shutdown the system is made). By default, applications return 1 in response to this message, however, if you return 0 then the session is not ended and the system will stop sending the message to any other application.

Note that your application needs to respond to the message loop for this technique to work. That means that the long operation must be non-blocking, otherwise if Windows sends the WM_QUERYENDSESSION but does not get a timely response it will attempt to kill your application after a short delay anyway. Another thing is that you cannot (for example) put up a dialog or MessageBox here asking the user what to do: if you do Windows will again see that it doesn’t get a timely response and will kill your application.

In VB

To respond to this in VB, you need to install a subclass on your applications top-level Window(s). As usual, this sample uses the Subclassing and Timer Assistant to achieve this. Here’s the code:

Private Const WM_QUERYENDSESSION As Long = &H11
Private Const ENDSESSION_LOGOFF As Long = &H80000000

Implements ISubclass

Private Sub Form_Load()
   AttachMessage Me, Me.hWnd, WM_QUERYENDSESSION
End Sub

Private Sub Form_QueryUnload(Cancel As Integer, UnloadMode As Integer)
   DetachMessage Me, Me.hWnd, WM_QUERYENDSESSION
End Sub

Private Property Let ISubclass_MsgResponse(ByVal RHS As SSubTimer.EMsgResponse)
   '
End Property

Private Property Get ISubclass_MsgResponse() As SSubTimer.EMsgResponse
   ISubclass_MsgResponse = emrPreprocess
End Property

Private Function ISubclass_WindowProc( _
      ByVal hWnd As Long, _
      ByVal iMsg As Long, _
      ByVal wParam As Long, _
      ByVal lParam As Long) As Long
Dim lR As Long
   If (iMsg = WM_QUERYENDSESSION) Then
      If (lParam Or ENDSESSION_LOGOFF) = ENDSESSION_LOGOFF Then
         ' user logging off
      Else
         ' shutdown
      End If
      lR = 1
      ' Return 0 to prevent the session being ended,
      ' otherwise return 1:
      ISubclass_WindowProc = lR
   End If
End Function

Wrap Up

This sample demonstrates how to prevent a Windows session from being ended by log-off or shutdown, which can be useful if your application is performing an operation that would otherwise result in data being corrupted.

Categories: Tutorial, forms

Change The Width of Item in a ListBox

October 29, 2008 programmervb Leave a comment

Normally in a VB ListBox, if a ListItem is too long to fit, it is clipped by the display. This tip demonstrates how to modify a ListBox so it displays a longer items with a horizontal scroll bar.

Basic Sizing

The ListBox control API includes the LB_SETHORIZONTALEXTENT message which allows the actual width of the items to be set independently of the size of the control (which is the default behaviour). To see this working, start a new project in VB. Add a module, and then add the following code:

Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" _
   (ByVal hwnd As Long, ByVal nIndex As Long) As Long
Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" _
   (ByVal hwnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
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

Public Const GWL_STYLE = (-16)
Public Const SWP_NOACTIVATE = &H10
Public Const SWP_NOCOPYBITS = &H100
Public Const SWP_NOMOVE = &H2
Public Const SWP_NOOWNERZORDER = &H200      '  Don't do owner Z ordering
Public Const SWP_NOREDRAW = &H8
Public Const SWP_NOREPOSITION = SWP_NOOWNERZORDER
Public Const SWP_NOSIZE = &H1
Public Const SWP_NOZORDER = &H4
Public Const SWP_SHOWWINDOW = &H40
Public Const SWP_FRAMECHANGED = &H20        '  The frame changed: send WM_NCCALCSIZE

Public Const WS_HSCROLL = &H100000

Public Const LB_SETHORIZONTALEXTENT = &H194
Public Const LB_GETHORIZONTALEXTENT = &H193

Declare Function SendMessageLong Lib "user32" Alias "SendMessageA" _
   (ByVal hwnd As Long, ByVal wMsg As Long, _
    ByVal wParam As Long, ByVal lParam As Long) As Long

Now add a Listbox to your project’s main form, and then add this code to the Form_Load procedure:

Dim i As Long
Dim lS As Long
Dim lW As Long

   For i = 1 To 100
      List1.AddItem _
         "An application sends an LB_GETHORIZONTALEXTENT message to retrieve" & _
         " from a list box the width, in pixels, by which the list box can" &
         " be scrolled horizontally (the scrollable width) if the list box" &
         " has a horizontal scroll bar."
   Next i

   i = SendMessageLong(List1.hwnd, LB_SETHORIZONTALEXTENT, 3000, 0)
   lS = GetWindowLong(List1.hwnd, GWL_STYLE)
   lS = lS Or WS_HSCROLL
   SetWindowLong List1.hwnd, GWL_STYLE, lS
   SetWindowPos List1.hwnd, _
      0, 0, 0, 0, 0, _
      SWP_NOMOVE Or SWP_NOOWNERZORDER Or SWP_NOSIZE Or SWP_FRAMECHANGED

Run the project. You will see that the ListBox has a horizontal scroll bar and the long text items which have been added can be accessed.

More Sophisticated Sizing

In the simple example, I’ve just set the horizontal extent to 3000 pixels. However, in real life use you’d ideally like to be able to set the width to the exact width needed to display the largest item. The download, provided by 10Tec.com, does exactly this, so on to their description of how it works:

The 10Tec CListBoxHScroll class

The attached code contains a class that can be used to manipulate (add, remove or change caption) items in the ListBox control; when you add/remove/change an item, it adds or hides the horizontal scroll bar if required. Optionally it can scroll down the list box so you can see the last added item.

The code uses the SendMessage API function to add a horizontal scroll bar dynamically to a list box using the LB_SETHORIZONTALEXTENT message. To precisely calculate the width of a new item, the class uses the DrawText API function with the DT_CALCRECT flag. The IFont interface and its hFont property are used to retrieve the handle of the font used in the listbox which ensures that the size is calculated correctly regardless of the font selected. The class can also calculate the minimum width of the list box required to make the horizontal scroll bar disappear. This takes into account the visibility of the vertical scroll bar in the listbox by retrieving the listbox style flags and testing for the WS_VSCROLL flag.

Using CListBoxHScroll in real-world applications is easy. All you need to do is to initialize the class using the Attach method which takes a reference to the list box you want to populate. Then you add, remove or change item caption using its methods rather than the standard ListBox method (they have the same names).

To see how this class works, create a new exe project in VB and place a CommandButton and a ListBox control on the form. Put the following code in the Command1_Click event procedure:

Private Sub cmdPopulate_Click()
   Dim LBHS As New CListBoxHScroll
   Dim i As Long, lStrLen As Long

   With List1.Font
      .Name = "Arial"
      .Size = 12
      .Italic = True
   End With

   LBHS.Attach List1

   For i = 1 To 30
      lStrLen = Int(Rnd * 50) + 1
      LBHS.AddItem String(lStrLen, "W") & "!"
   Next
End Sub

Run the project and press the Command1 button. You will see that the listbox named List1 has been populated with 30 random length strings, has the horizontal scroll bar and displays the last added string.

The class can be useful in many real-world situations. For instance, if you perform context search in files, you can use this class to add found files to a list box at the screen as they are found. 10Tec uses this code in demo applications of the xDir library that allows you to enumerate files and folders in a specified folder and all of its subfolders using various filter criteria (file and folder mask; file size; attributes; date and time of creation, last access and modification, etc.) You can visit the 10Tec.com web-site to download this and other demos.

Categories: Tutorial, forms

Working with Multiple Monitors

October 29, 2008 programmervb 1 comment

Windows 98/2000 systems and above provide support for multiple monitors. This is a great thing except that it messes up old programs which attempt to do things like centre Windows or otherwise restrict their position to the visible area of the screen. This tip provides some simple code to allow you to work with multiple monitors.

About Multiple Monitors

Multiple monitor support in Windows is achieved by providing a rectangular Virtual Screen area onto which the physical monitors are mapped. The diagram below, taken directly from the MSDN documentation, demonstrates the kind of setup that could result if multi-monitor support were to be used by a madman or madwoman with plenty of old hardware and some bizarrely shapped monitors:

Idiotic Monitor Layout

Idiotic but possible monitor layout

The useful things to note are that the monitors may or may not contain all of the actual virtual screen information and that a screen may have coordinates which are offset from the origin in either direction. Finally, the size of the virtual screen is currently not supposed to exceed +/-32767, since there are a number of old-school API calls and messages in Win32 which follow the sort of sharp thinking that led to the idea in DOS that no-one would ever have more than 64k memory in their PC. (Unfortunately, I feel this strategy is already pretty much kcufed since 200dpi screens are on their way. Although I can’t afford any of them, never mind a 30″ version…)

Using Multiple Monitors

The only times you really need to know about multiple monitors are:

  1. When positioning objects on screen
  2. When drawing objects which span multiple monitors, the monitors have different display characteristics and you want the display to render with the highest quality achievable.

Unfortunately, the second case is pretty much beyond the scope of VB unless you choose to do all painting by intercepting WM_PAINT messages. This would really be making life extraordinarily hard for yourself, even by vbAccelerator standards.

In any case, most multiple monitor systems are driven from a single graphics card and have the same colour depth in each monitor. When that doesn’t occur, your users shouldn’t really expect to be able to drag your lovely alpha-blended window onto their amber-screen EGA debugging display and expect everything will still look perfect.

So back to centring and sizing, which is relatively simple. You need to be able to determine the size of the virtual screen area and each of the monitors within that area.

Determing the Size of the Virtual Screen

In the same way that the usable screen area used to be provided through the SystemParametersInfo API call, the Virtual Screen size is also, as well as the number of physical monitors and whether each has the same colour depth. The code below demonstrates how to retrieve this information:

Private Const SM_CXVIRTUALSCREEN = 78
Private Const SM_CYVIRTUALSCREEN = 79
Private Const SM_CMONITORS = 80
Private Const SM_SAMEDISPLAYFORMAT = 81

Private Declare Function GetSystemMetrics Lib "user32" ( _
   ByVal nIndex As Long) As Long

Public Property Get VirtualScreenWidth() As Long
   VirtualScreenWidth = GetSystemMetrics(SM_CXVIRTUALSCREEN)
End Property
Public Property Get VirtualScreenHeight() As Long
   VirtualScreenHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN)
End Property
Public Property Get DisplayMonitorCount() As Long
   DisplayMonitorCount = GetSystemMetrics(SM_CMONITORS)
End Property
Public Property Get AllMonitorsSame() As Long
   AllMonitorsSame = GetSystemMetrics(SM_SAMEDISPLAYFORMAT)
End Property

Evaluating Monitor Size and Work Area

Each monitor has an overall size and a work area, which represents the size with any explorer bars (like the task bar, or the misguided Office docking help thingies) subtracted. The EnumDisplayMonitors API call is provided to allow this to be performed. This is a callback method which provides details of all attached monitors, along with a hMonitor handle which can be used to interrogate the monitor.

As with any callback function, unless you implement a Machine Code Thunk you must implement the function which gets called within a VB module. As my personal knowledge of machine code can be approximated as (or at least tends to) none, I’ve used module, which means the enumeration works as follows:

Private Declare Function EnumDisplayMonitors Lib "user32" ( _
      ByVal hDC As Long, _
      lprcClip As Any, _
      ByVal lpfnEnum As Long, _
      ByVal dwData As Long _
   ) As Long

Private Function MonitorEnumProc( _
      ByVal hMonitor As Long, _
      ByVal hDCMonitor As Long, _
      ByVal lprcMonitor As Long, _
      ByVal dwData As Long _
   ) As Long
   ' Do something with the monitor here...
   MonitorEnumProc = 1
End Function

Public Sub EnumMonitors(cM As cMonitors)
   EnumDisplayMonitors 0, ByVal 0&, AddressOf MonitorEnumProc, 0
End Sub

The parameters returned by the MonitorEnumProc are primarily aimed at responding to a WM_PAINT message. More usefully, the hMonitor handle can be converted into the details of the monitor in question: its name, its size and work area size and whether it is the primary monitor or not. To do this you use the GetMonitorInfo API function. This function is provided in both Unicode and ANSI versions to account for monitor names in either language. Both versions are translated in this code:

Private Type RECT
   Left As Long
   Top As Long
   right As Long
   bottom As Long
End Type

Private Const CCHDEVICENAME = 32

Private Type MONITORINFOEXA
   cbSize As Long
   rcMonitor As RECT
   rcWork As RECT
   dwFlags As Long
   b(0 To CCHDEVICENAME - 1) As Byte
End Type

Private Type MONITORINFOEXW
   cbSize As Long
   rcMonitor As RECT
   rcWork As RECT
   dwFlags As Long
   b(0 To CCHDEVICENAME * 2 - 1) As Byte
End Type

Private Declare Function GetMonitorInfoA Lib "user32" ( _
      ByVal hMonitor As Long, _
      lpmi As MONITORINFOEXA _
   ) As Long
Private Declare Function GetMonitorInfoW Lib "user32" ( _
      ByVal hMonitor As Long, _
      lpmi As MONITORINFOEXW _
   ) As Long

Private Const MONITORINFOF_PRIMARY = &H1

Private m_hMonitor As Long
Private m_sName As String
Private m_rcMonitor As RECT
Private m_rcWork As RECT
Private m_bIsPrimary As Boolean

...

   If (IsNt) Then
      Dim tMIW As MONITORINFOEXW
      tMIW.cbSize = Len(tMIW)
      GetMonitorInfoW hMonitor, tMIW
      With tMIW
         LSet m_rcMonitor = .rcMonitor
         LSet m_rcWork = .rcWork
         m_bIsPrimary = _
            ((.dwFlags And MONITORINFOF_PRIMARY) = MONITORINFOF_PRIMARY)
         sName = .b
         iPos = InStr(sName, vbNullChar)
      End With
   Else
      Dim tMIA As MONITORINFOEXA
      tMIA.cbSize = Len(tMIA)
      GetMonitorInfoA hMonitor, tMIA
      With tMIA
         LSet m_rcMonitor = .rcMonitor
         LSet m_rcWork = .rcWork
         m_bIsPrimary = _
            ((.dwFlags And MONITORINFOF_PRIMARY) = MONITORINFOF_PRIMARY)
         sName = StrConv(.b, vbUnicode)
      End With
   End If
   iPos = InStr(sName, vbNullChar)
   If (iPos > 0) Then
      m_sName = Left(sName, iPos - 1)
   Else
      m_sName = sName
   End If

Note that if you don’t need the monitor name, then you can simplify this call by missing out the name parameter entirely. In this case you only ever need the ANSI version of the function and the declares look like this:

Private Type MONITORINFO
   cbSize As Long
   rcMonitor As RECT
   rcWork As RECT
   dwFlags As Long
End Type

Private Declare Function GetMonitorInfoA Lib "user32" ( _
      ByVal hMonitor As Long, _
      lpmi As MONITORINFO _
   ) As Long

Wrapping It Up

With this code its easy to create wrapper for the function. The class cMonitors is responsible for providing information about the Virtual Screen area, and to store a collection of monitors which contain information about the individual monitors. As VB provides no easily accessible support to a strongly typed collection, the implemented collection is an index only version, even though access by the hMonitor handle would be sensible. The code is arranged so that cMonitors class is responsible for responding to callbacks from the mMonitors module containing the callback and the creates an array of cMonitor objects which can be used to access the position, name and primary information about the monitors.

The sample code demonstrates some of the types of functions you might want to achieve in a multiple monitor system, such as ensuring your form is on a specific monitor, centred on a specific monitor or maximised there.

Conclusion

Working with multiple monitor systems is generally trivial unless you have to save or restore a screen position. In this case you can easily get access to the details of the current monitor arrangement using the simple classes provided with this tip.

Categories: Tutorial, forms