Here is the code from my plugin. It is a VB.NET class library using VS.NET 2005 and .NET 2.0:
Imports System.Windows.Forms
Imports System.Runtime.InteropServices
Public Class EventManager
'This calls the CallFunctionPtr function from the Matrix1Util_20.dll
'NOTE: I have tried this as a cdecl, stdcall, and even a winapi call.
'There was no visible difference in results for any method I used.
<DllImport("Matrix1Util_20.dll", EntryPoint:="CallFunctionPtr", CallingConvention:=CallingConvention.StdCall)> _
Private Shared Sub CallFunctionPtr(ByVal FunctionPointer As Long)
End Sub
'Pretty self explanitory. This is the structure of the message as stored in MessageQueue
Public Structure EventMessage
Public EventType As String
Public Handler As Long
Public ArgumentList As List(Of Object)
End Structure
'This keeps a list of key/value pairs of EventName/FunctionAddress
Private Shared HandlerList As Dictionary(Of String, Long)
'Again it is self explanitory. This is a first-in, first-out collection.
Private Shared MessageQueue As Queue(Of EventMessage) = New Queue(Of EventMessage)
'Initializes what needs to be initialized.
Public Shared Sub InitEventHandling()
HandlerList = New Dictionary(Of String, Long)
End Sub
'Disposes of a few resources just to be safe and eliminate them as a cause.
Public Shared Sub UnloadEventHandling()
HandlerList = Nothing
MessageQueue = Nothing
End Sub
'Adds an EventName and FunctionAddress to the Dictionary list.
Public Shared Sub DeclareDBPEventHandler(ByVal EventName As String, ByVal FunctionAddress As Long)
HandlerList.Add(EventName, FunctionAddress)
End Sub
'Used to raise an event with no arguments in DBPro code.
Public Overloads Shared Sub RaiseDBPEvent(ByVal EventName As String)
'Checks to make sure that the Event has been declared and is in the Dictionary
If HandlerList.ContainsKey(EventName) Then
'makes a new message to go in the queue.
Dim message As New EventMessage
'set the message attributes.
message.EventType = EventName
message.Handler = HandlerList.Item(EventName)
'put the message at the end of the queue
MessageQueue.Enqueue(message)
End If
End Sub
'Handles events by poping them out of the MessageQueue
Public Shared Sub DBPDoEvents()
'This will be the message that gets Dequeued from the MessageQueue
Dim currentmessage As EventMessage = New EventMessage
'Keep handling until the MessageQueue is empty.
Do While MessageQueue.Count > 0
'Just in case to keep things from being locked up.
Application.DoEvents()
'Pop the message out of the MessageQueue
currentmessage = MessageQueue.Dequeue
'So that I don't have to keep adding this onto the beginning of come items
With currentmessage
'This code is in place to later deal with calls that have more arguments.
'As of right now argcount will always be 0 with my example and the code
'is not setup to handle more.
Dim argcount As Integer
If .ArgumentList Is Nothing Then
argcount = 0
Else
argcount = .ArgumentList.Count
End If
'Again, set up to handle more than one argument.
Select Case argcount
Case 0
'NOTE: this is the culprit.
Try
CallFunctionPtr(.Handler)
Catch ex As Exception
'With this line enabled, you can see the error that it is giving
'however you will have to end task it. With this line disabled you can
'see that the code works except for the crash bug at the end.
MessageBox.Show(ex.ToString)
End Try
'CODE THAT IS COMMENTED OUT IS INTENDED FOR LATER HANDLING MULTIPLE ARGUMENTS
'Case 1
' MatrixFunctionPointers.CallFunctionPtr(.Handler, .ArgumentList(0))
'Case 2
' MatrixFunctionPointers.CallFunctionPtr(.Handler, .ArgumentList(0), .ArgumentList(1))
'Case 3
' MatrixFunctionPointers.CallFunctionPtr(.Handler, .ArgumentList(0), .ArgumentList(1), .ArgumentList(2))
'Case 4
' MatrixFunctionPointers.CallFunctionPtr(.Handler, .ArgumentList(0), .ArgumentList(1), .ArgumentList(2), .ArgumentList(3))
'Case 5
' MatrixFunctionPointers.CallFunctionPtr(.Handler, .ArgumentList(0), .ArgumentList(1), .ArgumentList(2), .ArgumentList(3), .ArgumentList(4))
'Case 6
' MatrixFunctionPointers.CallFunctionPtr(.Handler, .ArgumentList(0), .ArgumentList(1), .ArgumentList(2), .ArgumentList(3), .ArgumentList(4), .ArgumentList(5))
'Case 7
' MatrixFunctionPointers.CallFunctionPtr(.Handler, .ArgumentList(0), .ArgumentList(1), .ArgumentList(2), .ArgumentList(3), .ArgumentList(4), .ArgumentList(5), .ArgumentList(6))
'Case 8
' MatrixFunctionPointers.CallFunctionPtr(.Handler, .ArgumentList(0), .ArgumentList(1), .ArgumentList(2), .ArgumentList(3), .ArgumentList(4), .ArgumentList(5), .ArgumentList(6), .ArgumentList(7))
'Case 9
' MatrixFunctionPointers.CallFunctionPtr(.Handler, .ArgumentList(0), .ArgumentList(1), .ArgumentList(2), .ArgumentList(3), .ArgumentList(4), .ArgumentList(5), .ArgumentList(6), .ArgumentList(7), .ArgumentList(8))
End Select
End With
Loop
End Sub
'NOTE: The rest of the code is commented out as it is for calls with arguments.
'Not sure which type I should use for arguments, but figured Object was the most
'likely choice, I could be wrong though.
'Public Overloads Shared Sub RaiseDBPEvent(ByVal EventName As String, ByVal Argument As Object)
' If HandlerList.ContainsKey(EventName) Then
' Dim message As New EventMessage
' message.EventType = EventName
' message.Handler = HandlerList.Item(EventName)
' MessageQueue.Enqueue(message)
' End If
'End Sub
'Public Overloads Shared Sub RaiseDBPEvent(ByVal EventName As String, ByVal Argument1 As Object, ByVal Argument2 As Object)
' If HandlerList.ContainsKey(EventName) Then
' Dim message As New EventMessage
' message.EventType = EventName
' message.Handler = HandlerList.Item(EventName)
' message.ArgumentList.Add(Argument1)
' message.ArgumentList.Add(Argument2)
' MessageQueue.Enqueue(message)
' End If
'End Sub
'Public Overloads Shared Sub RaiseDBPEvent(ByVal EventName As String, ByVal Argument1 As Object, ByVal Argument2 As Object, ByVal Argument3 As Object)
' If HandlerList.ContainsKey(EventName) Then
' Dim message As New EventMessage
' message.EventType = EventName
' message.Handler = HandlerList.Item(EventName)
' message.ArgumentList.Add(Argument1)
' message.ArgumentList.Add(Argument2)
' message.ArgumentList.Add(Argument3)
' MessageQueue.Enqueue(message)
' End If
'End Sub
'Public Overloads Shared Sub RaiseDBPEvent(ByVal EventName As String, ByVal Argument1 As Object, ByVal Argument2 As Object, ByVal Argument3 As Object, ByVal Argument4 As Object)
' If HandlerList.ContainsKey(EventName) Then
' Dim message As New EventMessage
' message.EventType = EventName
' message.Handler = HandlerList.Item(EventName)
' message.ArgumentList.Add(Argument1)
' message.ArgumentList.Add(Argument2)
' message.ArgumentList.Add(Argument3)
' message.ArgumentList.Add(Argument4)
' MessageQueue.Enqueue(message)
' End If
'End Sub
'Public Overloads Shared Sub RaiseDBPEvent(ByVal EventName As String, ByVal Argument1 As Object, ByVal Argument2 As Object, ByVal Argument3 As Object, ByVal Argument4 As Object, ByVal Argument5 As Object)
' If HandlerList.ContainsKey(EventName) Then
' Dim message As New EventMessage
' message.EventType = EventName
' message.Handler = HandlerList.Item(EventName)
' message.ArgumentList.Add(Argument1)
' message.ArgumentList.Add(Argument2)
' message.ArgumentList.Add(Argument3)
' message.ArgumentList.Add(Argument4)
' message.ArgumentList.Add(Argument5)
' MessageQueue.Enqueue(message)
' End If
'End Sub
'Public Overloads Shared Sub RaiseDBPEvent(ByVal EventName As String, ByVal Argument1 As Object, ByVal Argument2 As Object, ByVal Argument3 As Object, ByVal Argument4 As Object, ByVal Argument5 As Object, ByVal Argument6 As Object)
' If HandlerList.ContainsKey(EventName) Then
' Dim message As New EventMessage
' message.EventType = EventName
' message.Handler = HandlerList.Item(EventName)
' message.ArgumentList.Add(Argument1)
' message.ArgumentList.Add(Argument2)
' message.ArgumentList.Add(Argument3)
' message.ArgumentList.Add(Argument4)
' message.ArgumentList.Add(Argument5)
' message.ArgumentList.Add(Argument6)
' MessageQueue.Enqueue(message)
' End If
'End Sub
'Public Overloads Shared Sub RaiseDBPEvent(ByVal EventName As String, ByVal Argument1 As Object, ByVal Argument2 As Object, ByVal Argument3 As Object, ByVal Argument4 As Object, ByVal Argument5 As Object, ByVal Argument6 As Object, ByVal Argument7 As Object)
' If HandlerList.ContainsKey(EventName) Then
' Dim message As New EventMessage
' message.EventType = EventName
' message.Handler = HandlerList.Item(EventName)
' message.ArgumentList.Add(Argument1)
' message.ArgumentList.Add(Argument2)
' message.ArgumentList.Add(Argument3)
' message.ArgumentList.Add(Argument4)
' message.ArgumentList.Add(Argument5)
' message.ArgumentList.Add(Argument6)
' message.ArgumentList.Add(Argument7)
' MessageQueue.Enqueue(message)
' End If
'End Sub
'Public Overloads Shared Sub RaiseDBPEvent(ByVal EventName As String, ByVal Argument1 As Object, ByVal Argument2 As Object, ByVal Argument3 As Object, ByVal Argument4 As Object, ByVal Argument5 As Object, ByVal Argument6 As Object, ByVal Argument7 As Object, ByVal Argument8 As Object)
' If HandlerList.ContainsKey(EventName) Then
' Dim message As New EventMessage
' message.EventType = EventName
' message.Handler = HandlerList.Item(EventName)
' message.ArgumentList.Add(Argument1)
' message.ArgumentList.Add(Argument2)
' message.ArgumentList.Add(Argument3)
' message.ArgumentList.Add(Argument4)
' message.ArgumentList.Add(Argument5)
' message.ArgumentList.Add(Argument6)
' message.ArgumentList.Add(Argument7)
' message.ArgumentList.Add(Argument8)
' MessageQueue.Enqueue(message)
' End If
'End Sub
'Public Overloads Shared Sub RaiseDBPEvent(ByVal EventName As String, ByVal Argument1 As Object, ByVal Argument2 As Object, ByVal Argument3 As Object, ByVal Argument4 As Object, ByVal Argument5 As Object, ByVal Argument6 As Object, ByVal Argument7 As Object, ByVal Argument8 As Object, ByVal Argument9 As Object)
' If HandlerList.ContainsKey(EventName) Then
' Dim message As New EventMessage
' message.EventType = EventName
' message.Handler = HandlerList.Item(EventName)
' message.ArgumentList.Add(Argument1)
' message.ArgumentList.Add(Argument2)
' message.ArgumentList.Add(Argument3)
' message.ArgumentList.Add(Argument4)
' message.ArgumentList.Add(Argument5)
' message.ArgumentList.Add(Argument6)
' message.ArgumentList.Add(Argument7)
' message.ArgumentList.Add(Argument8)
' message.ArgumentList.Add(Argument9)
' MessageQueue.Enqueue(message)
' End If
'End Sub
End Class
Here is the code for my current test project in DBPro:
InitEventHandling
DeclareDBPEventHandler "TestEvent", fp_Test1()
Make Object Cube 1, 50
do
CallingFunction()
DBPDoEvents
If Inkey$() <> "" then Goto ENDPROGRAM
loop
function fp_Test1()
value = Get PTR to Next Function()
endfunction value
function Test1()
color object 1, rgb(0,255,0)
Rotate Object 1, Object Angle X(1) +.001, Object Angle Y(1) +.001, 0
endfunction
function CallingFunction()
color object 1, rgb(255,0,0)
RaiseDBPEvent "TestEvent"
endfunction
ENDPROGRAM:
UnloadEventManager
Attached is a copy of my plugin as it currently stands with one small change:
For the purpose of this testing I am making two versions of the DBPDoEvents function. One call DBPDoEvents and one called DBPDoEventsMessage.
DBPDoEvents bypasses the exception when CallFunctionPtr is called. In this version it will seem to work correctly until the DBPro window is shut down, at which time it will crash.
DBPDoEventsMessage has a messagebox call that returns the exception when CallFunctionPtr is called.
You can change the appropriate line in the DBPro code to use either version. I hope this helps. I would prefer to make this plugin myself, however if it comes down to it and I am unable to do so for whatever reason I hope you will include this functionality in another Matrix1 plugin.
Design documents?!? What design documents??? I thought we were just going to wing it!!!