FREE hit counter and Internet traffic statistics from freestats.com

Wednesday, July 07, 2004

Pocket PC Notification

One of the interesting functionalities of the Pocket PC is its Notification API. Unfortunately for .NET Compact Framework developers for now this API is in managed code only. As a result, a developer could use the OpenNETCF Smart Device Framework which contains a Notification class. However, if you don't wish to download and reference the entire SDF you could create a wrapper in the Compact Framework to add notification bubbles to your applications. Just such a class was written in C# by Seth Dempsey of the CF team and then posted by Gokhan Altinoren on the web. Well, here is the class rewritten in VB .NET.



Option Strict On

Imports System.Runtime.InteropServices
Imports Atomic.CF.InteropServices

Namespace Atomic.CF.PocketPC

Public Class Notification

#Region "Private Properties"

Private m_clsid As Guid
Private m_baseID As Integer
Private m_NotificationCount As Integer

#End Region

#Region "API Declarations"

'Default system icons
'private const System.Int32 MB_ICONINFORMATION = 0x00000040

'Notification priority'

'bubble shown for duration, then goes away
Private Const SHNP_INFORM As Integer = &H1B1
'no bubble, icon shown for duration then goes away
Private Const SHNP_ICONIC As Integer = &H0

'Notification update mask

Private Const SHNUM_PRIORITY As Integer = &H1
Private Const SHNUM_DURATION As Integer = &H2
Private Const SHNUM_ICON As Integer = &H4
Private Const SHNUM_HTML As Integer = &H8
Private Const SHNUM_TITLE As Integer = &H10

'Flags

'For SHNP_INFORM priority and above, don't display the notification
'bubble when it's initially added the icon will display for the
'duration then it will go straight into the tray. The user can
'view the icon / see the bubble by opening the tray.
Private Const SHNF_STRAIGHTTOTRAY As Integer = &H1
'Critical information - highlights the border and title of the bubble.
Private Const SHNF_CRITICAL As Integer = &H2
'Force the message (bubble) to display even if settings says not to.
Private Const SHNF_FORCEMESSAGE As Integer = &H8


_
Private Structure SHNOTIFICATIONDATA
Public cbStruct As UInt32 'For verification and versioning
Public dwID As UInt32 'Identifier for this particular notification
Public npPriority As UInt32 'Priority
Public csDuration As UInt32 'Duration of the notification
Public hicon As IntPtr 'Icon for the notification
Public grfFlags As UInt32 'Flags
Public clsid As Guid 'Unique identifier for the notification class
Public hwndSink As IntPtr 'Window to receive command choices
Public pszHTML As StringPtr 'HTML content for the bubble
Public pszTitle As StringPtr 'Optional title for bubble
Public lParam As UInt32 'User-defined parameter
End Structure

'Add
_
Private Shared Function SHNotificationAdd( _
ByRef shinfo As SHNOTIFICATIONDATA) As Integer
End Function

'Remove
_
Private Shared Function SHNotificationRemove( _
ByRef clsid As Guid, ByVal dwID As UInt32) As Integer
End Function


'Update
_
Private Shared Function SHNotificationUpdate( _
ByVal grnumUpdateMask As UInt32, _
ByRef shinfo As SHNOTIFICATIONDATA) As Integer
End Function

#End Region

#Region "Constructor & Destructor"

Public Sub New()
m_clsid = New Guid("{A6EF1E8E-5BC0-49d9-B03F-08BC163DADC8}")
m_baseID = 0
m_NotificationCount = 0
End Sub

#End Region

#Region "Private Methods"

Private Sub NotificationAdd(ByVal message As String, _
ByVal title As String, ByVal duration As Integer, _
ByVal hIcon As IntPtr, ByVal ID As Integer, ByVal clsid As Guid)

Dim shinfo As New SHNOTIFICATIONDATA

'Initialize and/or null-terminate the strings
If message Is Nothing Then message = "'"

If title Is Nothing Then title = ""

'Duration of the message
If duration <> 0 Then
shinfo.csDuration = Convert.ToUInt32(duration)
Else
shinfo.csDuration = Convert.ToUInt32(20)
End If

'ID
If ID <> 0 Then
shinfo.dwID = Convert.ToUInt32(ID)
Else
m_baseID += 1
shinfo.dwID = Convert.ToUInt32(m_baseID)
End If

'Guid
If clsid.Equals(Nothing) Then
shinfo.clsid = clsid
Else
shinfo.clsid = m_clsid
End If

'Fill the structure's remaining members
shinfo.cbStruct = Convert.ToUInt32(Marshal.SizeOf(shinfo))
shinfo.npPriority = Convert.ToUInt32(SHNP_INFORM)
shinfo.hicon = hIcon

Dim pTitle As New StringPtr(title)
Dim pHTML As New StringPtr(message)

shinfo.pszTitle = pTitle
shinfo.pszHTML = pHTML

Try
Dim err As Integer = SHNotificationAdd(shinfo)
If err <> 0 Then
Throw (New Exception("Invalid return from SHNotificationAdd"))
Else
m_NotificationCount += 1
End If
Catch ex As Exception
'Trace the error out
End Try

End Sub

'Remove a notification
Private Sub NotificationRemove(ByVal clsid As Guid, ByVal ID As Integer)
If m_NotificationCount > 0 Then
If ID = 0 Then ID = m_baseID

Dim err As Integer = SHNotificationRemove(clsid, Convert.ToUInt32(ID))

If err <> 0 Then
Throw (New Exception("Invalid return from SHNotificationRemove"))
Else
m_NotificationCount -= 1
End If
End If
End Sub

#End Region

#Region "Public Methods"

#Region "Add(...)"

'/
'/ This function adds a notification (asynchronously) to the notification tray.
'/

'/ HTML content for the notification bubble.
Public Sub Add(ByVal message As String)
NotificationAdd(message, "", 0, IntPtr.Zero, 0, m_clsid)
End Sub

'/
'/ This function adds a notification (asynchronously) to the notification tray.
'/

'/ HTML content for the notification bubble.
'/ Optional title for notification bubble.
Public Sub Add(ByVal message As String, ByVal title As String)
NotificationAdd(message, title, 0, IntPtr.Zero, 0, m_clsid)
End Sub

'/
'/ This function adds a notification (asynchronously) to the notification tray.
'/

'/ HTML content for the notification bubble.
'/ Optional title for notification bubble.
'/ Duration of the notification.
Public Sub Add(ByVal message As String, ByVal title As String, _
ByVal duration As Integer)
NotificationAdd(message, title, duration, IntPtr.Zero, 0, m_clsid)
End Sub

'/
'/ This function adds a notification (asynchronously) to the notification tray.
'/

'/ HTML content for the notification bubble.
'/ Optional title for notification bubble.
'/ The icon for the notification.
Public Sub Add(ByVal message As String, ByVal title As String, _
ByVal hIcon As IntPtr)
NotificationAdd(message, title, 0, hIcon, 0, m_clsid)
End Sub

'/
'/ This function adds a notification (asynchronously) to the notification tray.
'/

'/ HTML content for the notification bubble.
'/ Optional title for notification bubble.
'/ Duration of the notification.
'/ The icon for the notification.
Public Sub Add(ByVal message As String, ByVal title As String, _
ByVal duration As Integer, ByVal hIcon As IntPtr)
NotificationAdd(message, title, duration, hIcon, 0, m_clsid)
End Sub

'/
'/ This function adds a notification (asynchronously) to the notification tray.
'/

'/ HTML content for the notification bubble.
'/ Optional title for notification bubble.
'/ Duration of the notification.
'/ Identifier for this particular notification.
Public Sub Add(ByVal message As String, ByVal title As String, _
ByVal duration As Integer, ByVal messageID As Integer)
NotificationAdd(message, title, duration, IntPtr.Zero, messageID, m_clsid)
End Sub

'/
'/ This function adds a notification (asynchronously) to the notification tray.
'/

'/ HTML content for the notification bubble.
'/ Optional title for notification bubble.
'/ Duration of the notification.
'/ Unique identifier for the notification class.
Public Sub Add(ByVal message As String, ByVal title As String, _
ByVal duration As Integer, ByVal clsid As String)
m_clsid = New Guid(clsid)
NotificationAdd(message, title, duration, IntPtr.Zero, 0, m_clsid)
End Sub

'/
'/ This function adds a notification (asynchronously) to the notification tray.
'/

'/ HTML content for the notification bubble.
'/ Optional title for notification bubble.
'/ Duration of the notification.
'/ Identifier for this particular notification.
'/ Unique identifier for the notification class.
Public Sub Add(ByVal message As String, ByVal title As String, _
ByVal duration As Integer, ByVal messageID As Integer, ByVal clsid As String)
m_clsid = New Guid(clsid)
NotificationAdd(message, title, duration, IntPtr.Zero, messageID, m_clsid)
End Sub

#End Region

#Region "Remove(...)"

'/
'/ This function removes a notification.
'/

'/ The class of the notification to remove.
'/ The unique identifier of the notification to remove.
'/ Do not set the value of this parameter to 0 that option is not implemented.
'/ If the value of this parameter is set to 0, the function will return an
'/ error.
Public Sub Remove(ByVal clsid As String, ByVal ID As Integer)
NotificationRemove(New Guid(clsid), ID)
End Sub

'/
'/ This function removes a notification by ID. The value of the guid property
'/ will be used as the class of the notification.
'/

'/ The unique identifier of the notification to remove.
'/ Do not set the value of this parameter to 0 that option is not implemented.
'/ If the value of this parameter is set to 0, the function will return an
'/ error.
Public Sub Remove(ByVal ID As Integer)
NotificationRemove(m_clsid, ID)
End Sub

'/
'/ This function removes a notification by class. The value of the ID property
'/ will be used as the ID of the notification.
'/

'/ The class of the notification to remove.
Public Sub Remove(ByVal clsid As String)
NotificationRemove(New Guid(clsid), 0)
End Sub

#End Region

#Region "Others"

'/
'/ This function removes the last notification added, if any.
'/

Public Sub RemoveLast()
NotificationRemove(m_clsid, 0)
End Sub

#End Region

#End Region

#Region "Public Properties"

'/
'/ A number when tagging notifications with an ID. If no ID
'/ specified during an Add(), ID property is incremented by one, used as ID
'/ and saved back into ID property.
'/

Public Property ID() As Integer
Get
Return m_baseID
End Get
Set(ByVal Value As Integer)
m_baseID = Value
End Set
End Property

'/
'/ A String that contains a GUID
'/ If this property is not set,
'/ {A6EF1E8E-5BC0-49d9-B03F-08BC163DADC8} is used as GUID.

'/

Public Property clsid() As String
Get
Return m_clsid.ToString()
End Get
Set(ByVal Value As String)
m_clsid = New Guid(Value)
End Set
End Property
#End Region

End Class
End Namespace


In rewriting this code in VB .NET one of the major differences is that the C# code
used the unsafe and fixed keywords to handle string pointers within the notification
structure. These keywords are not available in VB and so a managed string pointer
class was created (as documented in our whitepaper on MSDN) as shown here.


Imports System.Runtime.InteropServices
Imports System.ComponentModel

Namespace Atomic.CF.InteropServices

Public Class Memory

_
Private Shared Function LocalAlloc(ByVal uFlags As Integer, _
ByVal uBytes As Integer) As IntPtr
End Function

_
Private Shared Function LocalFree(ByVal hMem As IntPtr) As IntPtr
End Function

_
Private Shared Function LocalReAlloc(ByVal hMem As IntPtr, _
ByVal uBytes As Integer, ByVal fuFlags As Integer) As IntPtr
End Function

Private Const LMEM_FIXED As Integer = 0
Private Const LMEM_MOVEABLE As Integer = 2
Private Const LMEM_ZEROINIT As Integer = &H40
Private Const LPTR = (LMEM_FIXED Or LMEM_ZEROINIT)

' Allocates a block of memory using LocalAlloc
Public Shared Function AllocHLocal(ByVal cb As Integer) As IntPtr
Return LocalAlloc(LPTR, cb)
End Function

' Frees memory allocated by AllocHLocal
Public Shared Sub FreeHLocal(ByVal hlocal As IntPtr)
If Not hlocal.Equals(IntPtr.Zero) Then
If Not IntPtr.Zero.Equals(LocalFree(hlocal)) Then
Throw New Win32Exception(Marshal.GetLastWin32Error())
End If
hlocal = IntPtr.Zero
End If
End Sub

' Resizes a block of memory previously allocated with AllocHLocal
Public Shared Function ReAllocHLocal(ByVal pv As IntPtr, _
ByVal cb As Integer) As IntPtr

Dim newMem As IntPtr = LocalReAlloc(pv, cb, LMEM_MOVEABLE)
If newMem.Equals(IntPtr.Zero) Then
Throw New OutOfMemoryException
End If
Return newMem
End Function

' Copies the contents of a managed string to unmanaged memory
Public Shared Function StringToHLocalUni(ByVal s As String) As IntPtr
If s Is Nothing Then
Return IntPtr.Zero
Else
Dim nc As Integer = s.Length
Dim len As Integer = 2 * (1 + nc)
Dim hLocal As IntPtr = AllocHLocal(len)
If hLocal.Equals(IntPtr.Zero) Then
Throw New OutOfMemoryException
Else
Marshal.Copy(s.ToCharArray(), 0, hLocal, s.Length)
Return hLocal
End If
End If
End Function

End Class

Public Structure StringPtr

Private szString As IntPtr

Public Sub New(ByVal s As String)
Me.szString = Memory.StringToHLocalUni(s)
End Sub

Public Overrides Function ToString() As String
Return Runtime.InteropServices.Marshal.PtrToStringUni(Me.szString)
End Function

Public Sub Free()
Memory.FreeHLocal(Me.szString)
End Sub
End Structure

End Namespace

The end result is the ability to add the following lines of code to your application
in order to show a notification bubble.
Dim n as New Atomic.CF.PocketPC.Notification()

n.Add("My Message","My Title",3,hIcon)

Where 3 is the duration of the bubble and hIcon is a pointer to an icon extracted
from the executable typically using the ExtractIconEx unmanaged function.

2 comments:

Unknown said...

the above written code is given me error n since iam new in programming iam not able to rectify it. it will be of gr8 help if iam assisted for this error
Error : "Out of memory Exception"

Anonymous said...

Who knows where to download XRumer 5.0 Palladium?
Help, please. All recommend this program to effectively advertise on the Internet, this is the best program!