vbAccelerator - Contents of code file: SimpleInterprocessCommunicationsVB_CopyData.vbNamespace vbAccelerator.Components.Win32
Public Delegate Sub DataReceivedEventHandler(ByVal sender As Object, ByVal
e As DataReceivedEventArgs)
''' <summary>
''' A class which wraps using Windows native WM_COPYDATA
''' message to send interprocess data between applications.
''' This is a simple technique for interprocess data sends
''' using Windows. The alternative to this is to use
''' Remoting, which requires a network card and a way
''' to register the Remoting name of an object so it
''' can be read by other applications.
''' </summary>
Public Class CopyData
Inherits NativeWindow
Implements IDisposable
''' <summary>
''' Event raised when data is received on any of the channels
''' this class is subscribed to.
''' </summary>
Public Event DataReceived As DataReceivedEventHandler
<System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServi
ces.LayoutKind.Sequential)> _
Private Structure COPYDATASTRUCT
Public dwData As IntPtr
Public cbData As Integer
Public lpData As Integer
End Structure
Private Const WM_COPYDATA As Integer = &H4A
Private Const WM_DESTROY As Integer = &H2
#Region "Member Variables"
Private m_channels As CopyDataChannels = Nothing
Private m_disposed As Boolean = False
#End Region
''' <summary>
''' Override for a form's Window Procedure to handle WM_COPYDATA
''' messages sent by other instances of this class.
''' </summary>
''' <param name="m">The Windows Message information.</param>
Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
If (m.Msg = WM_COPYDATA) Then
Dim cds As COPYDATASTRUCT = New COPYDATASTRUCT()
cds =
System.Runtime.InteropServices.Marshal.PtrToStructure(m.LParam,
cds.GetType())
If (cds.cbData > 0) Then
Dim data(cds.cbData - 1) As Byte
Dim source As IntPtr = New IntPtr(cds.lpData)
Dim length As Integer = cds.cbData
System.Runtime.InteropServices.Marshal.Copy(source, data,
0, length)
Dim stream As System.IO.MemoryStream = New
System.IO.MemoryStream(data)
Dim b As
System.Runtime.Serialization.Formatters.Binary.BinaryFormat
ter = New
System.Runtime.Serialization.Formatters.Binary.BinaryFormat
ter()
Dim cdo As CopyDataObjectData = b.Deserialize(stream)
If (channels.Contains(cdo.Channel)) Then
Dim d As DataReceivedEventArgs = New
DataReceivedEventArgs(cdo.Channel, cdo.Data, cdo.Sent)
OnDataReceived(d)
m.Result = New IntPtr(1)
End If
End If
ElseIf (m.Msg = WM_DESTROY) Then
' WM_DESTROY fires before OnHandleChanged and is
' a better place to ensure that we've cleared
' everything up.
channels.OnHandleChange()
MyBase.OnHandleChange()
End If
MyBase.WndProc(m)
End Sub
''' <summary>
''' Raises the DataReceived event from this class.
''' </summary>
''' <param name="e">The data which has been received.</param>
Overridable Sub OnDataReceived(ByVal e As DataReceivedEventArgs)
RaiseEvent DataReceived(Me, e)
End Sub
''' <summary>
''' If the form's handle changes, the properties associated
''' with the window need to be cleared up. This override ensures
''' that it is done. Note that the CopyData class will then
''' stop responding to events and it should be recreated once
''' the new handle has been assigned.
''' </summary>
Protected Overrides Sub OnHandleChange()
' need to clear up everything we had set.
channels.OnHandleChange()
MyBase.OnHandleChange()
End Sub
''' <summary>
''' Gets the collection of channels.
''' </summary>
Public ReadOnly Property Channels() As CopyDataChannels
Get
Return m_channels
End Get
End Property
''' <summary>
''' Clears up any resources associated with this object.
''' </summary>
Public Sub Dispose() _
Implements IDisposable.Dispose
If Not (m_disposed) Then
Channels.Clear()
m_channels = Nothing
m_disposed = True
GC.SuppressFinalize(Me)
End If
End Sub
''' <summary>
''' Constructs a new instance of the CopyData class
''' </summary>
Public Sub New()
m_channels = New CopyDataChannels(Me)
End Sub
''' <summary>
''' Finalises a CopyData class which has not been disposed.
''' There may be a minor resource leak if this class is finalised
''' after the form it is associated with.
''' </summary>
Protected Overrides Sub Finalize()
MyBase.Finalize()
Dispose()
End Sub
End Class
''' <summary>
''' Contains data and other information associated with data
''' which has been sent from another application.
''' </summary>
Public Class DataReceivedEventArgs
Private m_channelName As String = ""
Private m_data As Object = Nothing
Private m_sent As DateTime
Private m_received As DateTime
''' <summary>
''' Gets the channel name that this data was sent on.
''' </summary>
Public ReadOnly Property ChannelName() As String
Get
Return m_channelName
End Get
End Property
''' <summary>
''' Gets the data object which was sent.
''' </summary>
Public ReadOnly Property Data() As Object
Get
Return m_data
End Get
End Property
''' <summary>
''' Gets the date and time which at the data was sent
''' by the sending application.
''' </summary>
Public ReadOnly Property Sent() As DateTime
Get
Return m_sent
End Get
End Property
''' <summary>
''' Gets the date and time which this data item as
''' received.
''' </summary>
Public ReadOnly Property Received() As DateTime
Get
Return m_received
End Get
End Property
''' <summary>
''' Constructs an instance of this class.
''' </summary>
''' <param name="channelName">The channel that the data was received
from</param>
''' <param name="data">The data which was sent</param>
''' <param name="sent">The date and time the data was sent</param>
Friend Sub New(ByVal channelName As String, ByVal data As Object, ByVal
sent As DateTime)
m_channelName = channelName
m_data = data
m_sent = sent
m_received = DateTime.Now
End Sub
End Class
''' <summary>
''' A strongly-typed collection of channels associated with the CopyData
''' class.
''' </summary>
Public Class CopyDataChannels
Inherits DictionaryBase
Private m_owner As NativeWindow = Nothing
''' <summary>
''' Returns an enumerator for each of the CopyDataChannel objects
''' within this collection.
''' </summary>
''' <returns>An enumerator for each of the CopyDataChannel objects
''' within this collection.</returns>
Public Shadows Function GetEnumerator() As
System.Collections.IEnumerator
Return Me.Dictionary.Values.GetEnumerator()
End Function
''' <summary>
''' Returns the CopyDataChannel at the specified 0-based index.
''' </summary>
Default Public ReadOnly Property Item(ByVal Index As Integer) As
CopyDataChannel
Get
Dim ret As CopyDataChannel = Nothing
Dim i As Integer = 0
Dim cdc As CopyDataChannel
For Each cdc In Me.Dictionary.Values
i += 1
If (i = index) Then
ret = cdc
Exit For
End If
Next
Return ret
End Get
End Property
''' <summary>
''' Returns the CopyDataChannel for the specified channelName
''' </summary>
Default Public ReadOnly Property Item(ByVal channelName As String) As
CopyDataChannel
Get
Return Me.Dictionary(channelName)
End Get
End Property
''' <summary>
''' Adds a new channel on which this application can send and
''' receive messages.
''' </summary>
Public Sub Add(ByVal channelName As String)
Dim cdc As CopyDataChannel = New CopyDataChannel(m_owner,
channelName)
Me.Dictionary.Add(channelName, cdc)
End Sub
''' <summary>
''' Removes an existing channel.
''' </summary>
''' <param name="channelName">The channel to remove</param>
Public Sub Remove(ByVal channelName As String)
Me.Dictionary.Remove(channelName)
End Sub
''' <summary>
''' Gets/sets whether this channel contains a CopyDataChannel
''' for the specified channelName.
''' </summary>
Public Function Contains(ByVal channelName As String) As Boolean
Return Me.Dictionary.Contains(channelName)
End Function
''' <summary>
''' Ensures the resources associated with a CopyDataChannel
''' object collected by this class are cleared up.
''' </summary>
Protected Overrides Sub OnClear()
Dim cdc As CopyDataChannel
For Each cdc In Me.Dictionary.Values
cdc.Dispose()
Next
MyBase.OnClear()
End Sub
''' <summary>
''' Ensures any resoures associated with the CopyDataChannel object
''' which has been removed are cleared up.
''' </summary>
''' <param name="key">The channelName</param>
''' <param name="data">The CopyDataChannel object which has
''' just been removed</param>
Protected Overrides Sub OnRemoveComplete(ByVal key As Object, ByVal
data As Object)
Dim cdc As CopyDataChannel
cdc = data
cdc.Dispose()
MyBase.OnRemove(key, data)
End Sub
''' <summary>
''' If the form's handle changes, the properties associated
''' with the window need to be cleared up. This override ensures
''' that it is done. Note that the CopyData class will then
''' stop responding to events and it should be recreated once
''' the new handle has been assigned.
''' </summary>
Public Sub OnHandleChange()
Dim cdc As CopyDataChannel
For Each cdc In Me.Dictionary.Values
cdc.OnHandleChange()
Next
End Sub
''' <summary>
''' Constructs a new instance of the CopyDataChannels collection.
''' Automatically managed by the CopyData class.
''' </summary>
''' <param name="owner">The NativeWindow this collection
''' will be associated with</param>
Friend Sub New(ByVal owner As NativeWindow)
m_owner = owner
End Sub
End Class
''' <summary>
''' A channel on which messages can be sent.
''' </summary>
Public Class CopyDataChannel
Implements IDisposable
#Region " Unmanaged Code"
Private Declare Auto Function GetProp Lib "user32" ( _
ByVal hwnd As IntPtr, _
ByVal lpString As String) As Integer
Private Declare Auto Function SetProp Lib "user32" ( _
ByVal hwnd As IntPtr, _
ByVal lpString As String, _
ByVal hData As IntPtr) As Integer
Private Declare Auto Function RemoveProp Lib "user32" ( _
ByVal hwnd As IntPtr, _
ByVal lpString As String) As Integer
Private Declare Auto Function SendMessage Lib "user32" ( _
ByVal hwnd As IntPtr, _
ByVal wMsg As Integer, _
ByVal wParam As Integer, _
ByRef lParam As COPYDATASTRUCT _
) As Integer
<System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServi
ces.LayoutKind.Sequential)> _
Private Structure COPYDATASTRUCT
Public dwData As IntPtr
Public cbData As Integer
Public lpData As IntPtr
End Structure
Private Const WM_COPYDATA As Integer = &H4A
#End Region
#Region " Member Variables"
Private m_channelName As String = ""
Private m_disposed As Boolean = False
Private m_owner As NativeWindow = Nothing
Private m_recreateChannel As Boolean = False
#End Region
''' <summary>
''' Gets the name associated with this channel.
''' </summary>
Public ReadOnly Property ChannelName() As String
Get
Return m_channelName
End Get
End Property
''' <summary>
''' Sends the specified object on this channel to any other
''' applications which are listening. The object must have the
''' SerializableAttribute set, or must implement ISerializable.
''' </summary>
''' <param name="obj">The object to send</param>
''' <returns>The number of recipients</returns>
Public Function Send(ByVal obj As Object) As Integer
Dim recipients As Integer = 0
If (m_disposed) Then
Throw New InvalidOperationException("Object has been disposed")
Else
If (m_recreateChannel) Then ' handle has changed
addChannel()
End If
Dim cdo As CopyDataObjectData = New CopyDataObjectData(obj,
m_channelName)
' Try to do a binary serialization on obj.
' This will throw and exception if the object to
' be passed isn't serializable.
Dim b As
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
= New
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter(
)
Dim stream As System.IO.MemoryStream = New
System.IO.MemoryStream()
b.Serialize(stream, cdo)
stream.Flush()
' Now move the data into a pointer so we can send
' it using WM_COPYDATA:
' Get the length of the data:
Dim dataSize As Integer = stream.Length
If (dataSize > 0) Then
' This isn't very efficient if your data is very large.
' First we copy to a byte array, then copy to a CoTask
' Mem object... And when we use WM_COPYDATA windows will
' make yet another copy! But if you're talking about 4K
' or less of data then it doesn't really matter.
Dim data(dataSize) As Byte
stream.Seek(0, System.IO.SeekOrigin.Begin)
stream.Read(data, 0, dataSize)
Dim ptrData As IntPtr =
System.Runtime.InteropServices.Marshal.AllocCoTaskMem(dataS
ize)
System.Runtime.InteropServices.Marshal.Copy(data, 0,
ptrData, dataSize)
' Enumerate all windows which have the
' channel name, send the data to each one
Dim ew As EnumWindows = New EnumWindows()
ew.GetWindows()
' Send the data to each window identified on
' the channel:
Dim window As EnumWindowsItem
For Each window In ew.Items
If Not (window.Handle.Equals(m_owner.Handle)) Then
If (GetProp(window.Handle, Me.ChannelName) <> 0)
Then
Dim cds As COPYDATASTRUCT = New COPYDATASTRUCT()
cds.cbData = dataSize
cds.dwData = IntPtr.Zero
cds.lpData = ptrData
Dim res As Integer = SendMessage(window.Handle,
WM_COPYDATA, m_owner.Handle.ToInt32(), cds)
recipients +=
IIf(System.Runtime.InteropServices.Marshal.GetL
astWin32Error() = 0, 1, 0)
End If
End If
Next
' Clear up the data:
System.Runtime.InteropServices.Marshal.FreeCoTaskMem(ptrData
)
End If
stream.Close()
End If
Return recipients
End Function
Private Sub addChannel()
' Tag this window with property "channelName"
SetProp(m_owner.Handle, m_channelName, m_owner.Handle)
End Sub
Private Sub removeChannel()
' Remove the "channelName" property from this window
RemoveProp(m_owner.Handle, m_channelName)
End Sub
''' <summary>
''' If the form's handle changes, the properties associated
''' with the window need to be cleared up. This method ensures
''' that it is done. Note that the CopyData class will then
''' stop responding to events and it should be recreated once
''' the new handle has been assigned.
''' </summary>
Public Sub OnHandleChange()
removeChannel()
m_recreateChannel = True
End Sub
''' <summary>
''' Clears up any resources associated with this channel.
''' </summary>
Public Sub Dispose() _
Implements IDisposable.Dispose
If Not (m_disposed) Then
If (ChannelName.Length > 0) Then
removeChannel()
End If
m_channelName = ""
m_disposed = True
GC.SuppressFinalize(Me)
End If
End Sub
''' <summary>
''' Constructs a new instance of a CopyData channel. Called
''' automatically by the CopyDataChannels collection.
''' </summary>
''' <param name="owner">The owning native window</param>
''' <param name="channelName">The name of the channel to
''' send messages on</param>
Friend Sub New(ByVal owner As NativeWindow, ByVal channelName As String)
m_owner = owner
m_channelName = channelName
addChannel()
End Sub
Protected Overrides Sub Finalize()
Dispose()
MyBase.Finalize()
End Sub
End Class
''' <summary>
''' A class which wraps the data being copied, used
''' internally within the CopyData class objects.
''' </summary>
<Serializable()> _
Friend Class CopyDataObjectData
''' <summary>
''' The Object to copy. Must be Serializable.
''' </summary>
Public Data As Object
''' <summary>
''' The date and time this object was sent.
''' </summary>
Public Sent As DateTime
''' <summary>
''' The name of the channel this object is being sent on
''' </summary>
Public Channel As String
''' <summary>
''' Constructs a new instance of this object
''' </summary>
''' <param name="data">The data to copy</param>
''' <param name="channel">The channel name to send on</param>
''' <exception cref="ArgumentException">If data is not
serializable.</exception>
Public Sub New(ByVal theData As Object, ByVal theChannel As String)
Data = theData
If Not (Data.GetType().IsSerializable) Then
Throw New ArgumentException("Data object must be
serializable.", "data")
End If
Channel = theChannel
Sent = DateTime.Now
End Sub
End Class
end namespace
|
|