I have a requirement to store text messages for sending in a sequential fashion. However, due to reasons of program structure I am using a timer to pick up the messages and process them for sending on.
I am using a quick a dirty method of passing the message to the timer using the timer's .tag property. I am not ashamed! It is just a method to pass a parameter for the moment and is temporary.
The trouble is of course, that if two messages are sent to the timer sequentially close in time then there is a chance that one message will overwrite the .tag before the first has been processed.
I think I need to create a message queue and I could do that with an array, popping each message onto the array 'stack' and reading the stack sequentially, afterwards clearing the array. It is unlikely that more than three or four messages could make it into the 'stack' simultaneously.
So, that is what I had planned to implement but I thought I'd ask first as there is probably a better method of queueing messages or handling an event such as a message arriving.
My reasoning for creating a timer and an array is that is just how my brain works and the limit of my BASIC knowledge.
The messages are text messages typically up to 255 chars.
Skillset: VMS,DOS,Windows Sysadmin from 1985, fault-tolerance, VaxCluster, Alpha,Sparc. DCL,QB,VBDOS- VB6,.NET, PHP,NODE.JS, Graphic Design, Project Manager, CMS, Quad Electronics. classic cars & m'bikes. Artist in water & oils. Historian.
By the power invested in me, all the threads I start are battle free zones - no arguing about the benefits of VB6 over .NET here please. Happiness must reign.
The vb6-native way of doing a queue without getting too fancy would be to use a collection of strings.
Code:
Dim colMessages As Collection
Set colMessages = New Collection
colMessages.Add "Message1" ' Add to the end of the collection
colMessages.Add "Message2" ' Add to the end of the collection
Debug.Print colMessages(1) ' Read colMessages(1) to get the first item in the list
colMessages.Remove 1 ' Remove the first item in the collection (Message1)
Yes, I've used collections before but they aren't the first construct that pops into my mind being so 8-bit BASIC in nature. I will consider a collection.
Can you easily push items into a collection and then pop some off the other end or does the whole collection need to be rewritten in full every time? I've encountered some memory consumption issues in the past when creating and recreating collections.
Skillset: VMS,DOS,Windows Sysadmin from 1985, fault-tolerance, VaxCluster, Alpha,Sparc. DCL,QB,VBDOS- VB6,.NET, PHP,NODE.JS, Graphic Design, Project Manager, CMS, Quad Electronics. classic cars & m'bikes. Artist in water & oils. Historian.
By the power invested in me, all the threads I start are battle free zones - no arguing about the benefits of VB6 over .NET here please. Happiness must reign.
You could roll your own Queue object... that's probably what I do in this case if Collections aren't to your liking. Create a linked list and then the Queue would have push & pop (or enqueue & dequeue as would be appropriate for a queue) methods that add nodes to the linked list at the tail, and remove items from the head.
Create a linked list and then the Queue would have push & pop (or enqueue & dequeue as would be appropriate for a queue) methods that add nodes to the linked list at the tail, and remove items from the head.
I like that. I do not have knowledge of creating linked lists in VB6 though I have created structures with push/pop methods in other languages a long time ago (using DCL). Not come across linked lists in VB6 before, can you point me to a good example if it is something that already exists?
I'm not against collection per se, I have just had experiences with them being a little awkward to manipulate and sometimes more restrictive than an preserved array where you can just insert or reindex without having to recreate the whole array. I might be wrong about collections, I just want the advice.
Skillset: VMS,DOS,Windows Sysadmin from 1985, fault-tolerance, VaxCluster, Alpha,Sparc. DCL,QB,VBDOS- VB6,.NET, PHP,NODE.JS, Graphic Design, Project Manager, CMS, Quad Electronics. classic cars & m'bikes. Artist in water & oils. Historian.
By the power invested in me, all the threads I start are battle free zones - no arguing about the benefits of VB6 over .NET here please. Happiness must reign.
...can you point me to a good example if it is something that already exists?
RC5- or RC6-based:
Code:
Option Explicit
Private MQ As cArrayList, WithEvents tmrDecouple As cTimer
Private Sub Form_Load()
Set MQ = New_c.ArrayList(vbString)
Set tmrDecouple = New_c.Timer(1000, True)
End Sub
Private Sub Form_Click()
Static Counter: Counter = Counter + 1
MQ.Queue "Msg " & Counter
End Sub
Private Sub tmrDecouple_Timer()
Do While MQ.Count
Debug.Print MQ.DeQueue
Loop
End Sub
Originally Posted by yereverluvinuncleber
I'm not against collection per se, ...
The example, as posted by ahenry in #2, should work reliable and fast as well.
Skillset: VMS,DOS,Windows Sysadmin from 1985, fault-tolerance, VaxCluster, Alpha,Sparc. DCL,QB,VBDOS- VB6,.NET, PHP,NODE.JS, Graphic Design, Project Manager, CMS, Quad Electronics. classic cars & m'bikes. Artist in water & oils. Historian.
By the power invested in me, all the threads I start are battle free zones - no arguing about the benefits of VB6 over .NET here please. Happiness must reign.
I've written a VERY basic Stack (Push/Pop) as a single linked list. Should be somewhere here in this Sub-Forum.
Nota Bene: That stack is for LIFO-Principle, but i think could be adapted to FIFO (what you'd need)
Last edited by Zvoni; Tomorrow at 31:69 PM.
----------------------------------------------------------------------------------------
One System to rule them all, One Code to find them,
One IDE to bring them all, and to the Framework bind them,
in the Land of Redmond, where the Windows lie
---------------------------------------------------------------------------------
People call me crazy because i'm jumping out of perfectly fine airplanes.
---------------------------------------------------------------------------------
Code is like a joke: If you have to explain it, it's bad
I like that. I do not have knowledge of creating linked lists in VB6 though..
It's not really that hard. A linked list is a very simple data structure and it's easy to implement one with basic functionality. Here's a queue class implemented using a linked list internally:-
Code:
Private g_firstLink As LinkedListNodeOfString
Private g_lastLink As LinkedListNodeOfString
Public Sub Enqueue(ByVal message As String)
'If the last link has nothing then
'it means the queue has no items
If g_lastLink Is Nothing Then
'At this point the linked list would contain one item
'so the reference to the first and last link
'would be the same object
Set g_firstLink = New LinkedListNodeOfString
Set g_lastLink = g_firstLink
g_firstLink.Value = message
Else
Dim newLink As New LinkedListNodeOfString
newLink.Value = message
'Let our last link reference our new link
'as it's next node
Set g_lastLink.NextNode = newLink
'Let reference to the last link
'to our new node
Set g_lastLink = newLink
End If
End Sub
Public Function Dequeue() As String
If g_firstLink Is Nothing Then
Err.Raise 10000, , "Queue empty"
Else
Dequeue = g_firstLink.Value
Set g_firstLink = g_firstLink.NextNode
'The first link reference was set to the next node
'so if that is Nothing then it means we have no more
'items in the list
If g_firstLink Is Nothing Then
Set g_lastLink = Nothing
End If
End If
End Function
Public Function HasItems() As Boolean
HasItems = Not (g_firstLink Is Nothing)
End Function
And the node class:-
Code:
Public Value As String
Public NextNode As LinkedListNodeOfString
It might be better to use a UDT instead of a class to represent nodes of the linked list so you can properly encapsulate the internal implementation of the Queue. I used a class here because I absolutely hate UDTs in VB6. However, nesting privates classes inside of a public class is not allowed in VB6 so the Queue's internal implementation is not properly encapsulated in this example.
EDIT:
Never mind using UDTs. I tried to change the implementation and it would have complicated it way too much. A UDT is just not suitable for a linked list implementation. It would be better to live with the poor encapsulation.
C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter
There's just no reason to use garbage like InputBox. - jmcilhinney
The threads I start are Niya and Olaf free zones. No arguing about the benefits of VB6 over .NET here please. Happiness must reign. - yereverluvinuncleber
I have a requirement to store text messages for sending in a sequential fashion. However, due to reasons of program structure I am using a timer to pick up the messages and process them for sending on.
Your help appreciated in this.
It is feasible to use an array, just like a warehouse. If it will cause up to 100 messages to be stored and cached. Then design a 200-capacity array. Add another current processing position. For example, there are now 30 messages, and 10 have been processed.
After a period of time, there are 190 messages in total, and 180 messages have been processed.
20 more messages are received.
The memory can indicate that the number of messages is 210 and the current position is 180. After 200 messages are processed, the status becomes the number of messages 10 and the current position is 5.
Finally, the 210 messages were processed.
Private g_firstLink As MessageObj, g_lastLink As MessageObj
Public MessageCount As Long
Public Sub AddData(ByVal message As String)
If g_lastLink Is Nothing Then
Set g_firstLink = New MessageObj
Set g_lastLink = g_firstLink
g_firstLink.Value = message
Else
Dim newLink As New MessageObj
newLink = message
Set g_lastLink.NextNode = newLink
Set g_lastLink = newLink
End If
MessageCount = MessageCount + 1
End Sub
Public Function GetMessage() As String
If Not g_firstLink Is Nothing Then
GetMessage = g_firstLink.Value
Set g_firstLink = g_firstLink.NextNode
If g_firstLink Is Nothing Then Set g_lastLink = Nothing
MessageCount = MessageCount - 1
End If
End Function
Public Function HasItems() As Boolean
HasItems = Not (g_firstLink Is Nothing)
End Function
CLASS:MessageObj.CLS
Code:
Public Value As String 'Set as the default property of the class
Public NextNode As MessageObj
Attribute Value.VB_VarUserMemId = 0
---------------------------------
VERSION 1.0 CLASS
BEGIN
MultiUse = -1 'True
Persistable = 0 'NotPersistable
DataBindingBehavior = 0 'vbNone
DataSourceBehavior = 0 'vbNone
MTSTransactionMode = 0 'NotAnMTSObject
END
Attribute VB_Name = "MessageObj"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = True
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = False
Option Explicit
Public Value As String
Attribute Value.VB_VarUserMemId = 0
Public NextNode As MessageObj
-------------------------------------
sub Main TEST:
Code:
SUB MAIN()
Dim MessageList1 As New MessageList
For i = 1 To 5
MessageList1.AddData "Message " & CStr(i)
Next
While MessageList1.HasItems
Debug.Print "MessageCount=" & MessageList1.MessageCount & ",Get Message:" & MessageList1.GetMessage
Wend
END SUB
MessageList:
or use messagecount check for fast without object check(g_lastLink Is Nothing)
Code:
Private g_firstLink As MessageObj, g_lastLink As MessageObj
Public MessageCount As Long
Public Sub AddMessage(ByVal message As String)
If MessageCount = 0 Then
Set g_firstLink = New MessageObj
Set g_lastLink = g_firstLink
g_firstLink = message
Else
Dim newLink As New MessageObj
newLink = message
Set g_lastLink.NextNode = newLink
Set g_lastLink = newLink
End If
MessageCount = MessageCount + 1
End Sub
Public Function GetMessage() As String
If MessageCount > 0 Then
GetMessage = g_firstLink
Set g_firstLink = g_firstLink.NextNode
If g_firstLink Is Nothing Then Set g_lastLink = Nothing
MessageCount = MessageCount - 1
End If
End Function
Public Function HasItems() As Boolean
HasItems = MessageCount > 0
End Function
Last edited by xiaoyao; Sep 3rd, 2021 at 05:31 PM.
This code can also run, I wonder if there will be memory leaks and other problems?
MessageList.cls
Code:
Private g_firstLink As MessageObj, g_lastLink As MessageObj
Public MessageCount As Long
Private Sub Class_Initialize()
Set g_lastLink = New MessageObj
End Sub
Public Sub AddMessage(ByVal message As String)
Dim newLink As New MessageObj
newLink = message
Set g_lastLink.NextNode = newLink
Set g_lastLink = newLink
If MessageCount = 0 Then
Set g_firstLink = g_lastLink
End If
MessageCount = MessageCount + 1
End Sub
Public Function GetMessage() As String
If MessageCount > 0 Then
GetMessage = g_firstLink
Set g_firstLink = g_firstLink.NextNode
MessageCount = MessageCount - 1
End If
End Function
Public Function HasItems() As Boolean
HasItems = MessageCount > 0
End Function
As for a selfwritten Class...
The fastest performing approaches are based on "Fixed-Size-RingBuffers" (avoiding unnecessary mem-allocations) -
and do not require much code (no "extra-side-classes" will be needed):
Code:
Option Explicit 'O.Schmidt (a Msg-Queue, based on a "FixedSize-RingBuffer"-approach)
Private Const QLEN As Long = 4096 'define the Max-Capacity here
Private mCnt As Long, mIdx As Long, mArr(0 To QLEN - 1) As String
Public Property Get Count() As Long
Count = mCnt
End Property
Public Sub Enqueue(Msg As String)
If mCnt < QLEN Then mCnt = mCnt + 1 Else Err.Raise vbObjectError, , "Queue is full"
mArr((mIdx + mCnt) Mod QLEN) = Msg
End Sub
Public Function Dequeue() As String
If mCnt > 0 Then mCnt = mCnt - 1 Else Err.Raise vbObjectError, , "Queue is empty"
mIdx = (mIdx + 1) Mod QLEN
Dequeue = mArr(mIdx): mArr(mIdx) = vbNullString 'return - and free the Slot
End Function
Skillset: VMS,DOS,Windows Sysadmin from 1985, fault-tolerance, VaxCluster, Alpha,Sparc. DCL,QB,VBDOS- VB6,.NET, PHP,NODE.JS, Graphic Design, Project Manager, CMS, Quad Electronics. classic cars & m'bikes. Artist in water & oils. Historian.
By the power invested in me, all the threads I start are battle free zones - no arguing about the benefits of VB6 over .NET here please. Happiness must reign.
Thankyou everyone. I have lots of grist for my mill. I will attempt to implement and test each method.
You can also try the analog pointer method
You can test each kind of CPU operating speed and memory usage.
When using a linked list, if the amount of data is too large, tens of thousands of hundreds of thousands of messages, it may cause errors or a lot of system overhead.
I wanted to replace Dictionary and Collection before, but I finally found that it was totally impossible.
At least DictionaryI will not crash even if you add millions of data, of course, the speed will get slower and slower.
Using these methods, it can also be used for memory mapping, where multiple processes extract messages or add messages, just like an in-memory database.
The main thing is to perform a lock or trigger event when adding or (reading or deleting one), you can write a total amount of data, or the address of each message, and write it to the previous message. Until the last one is read.
Last edited by xiaoyao; Sep 3rd, 2021 at 05:50 PM.
Public BufferSize As Long
Public MessageList() As String
Public ListCount As Long
Dim ListIndex As Long, WriteIndex As Long
Sub Main()
IntMessageList 5
Dim i As Long
For i = 1 To 4
AddMessage "data-" & i
Next
Dim Message As String
While GetMessage(Message)
Debug.Print Message
Wend
AddMessage "data-" & 5
AddMessage "data-" & 6
AddMessage "data-" & 7
While GetMessage(Message)
Debug.Print Message
Wend
End Sub
Sub IntMessageList(BufferSizeA As Long)
BufferSize = BufferSizeA
ReDim MessageList(BufferSize)
End Sub
Sub AddMessage(MessageA As String)
If WriteIndex = BufferSize Then WriteIndex = 0
MessageList(WriteIndex) = MessageA
WriteIndex = WriteIndex + 1
ListCount = ListCount + 1
Debug.Print "Save ok,ListCount=" & ListCount
End Sub
Function GetMessage(Message As String) As Boolean
If ListIndex <> WriteIndex Then
If ListIndex = BufferSize Then ListIndex = 0
Message = MessageList(ListIndex)
GetMessage = True
ListIndex = ListIndex + 1
ListCount = ListCount - 1
Debug.Print "Read ok,ListCount=" & ListCount
'Else 'NoMessage
End If
End Function
Public BufferSize As Long
Public MessageList() As String
Public ListCount As Long
Dim ListIndex As Long, WriteIndex As Long
Sub Main()
IntMessageList 5
Dim i As Long
For i = 1 To 8
AddMessage "data-" & i
Next
Dim Message As String
While GetMessage(Message)
Debug.Print Message
Wend
AddMessage "data-" & 15
AddMessage "data-" & 16
AddMessage "data-" & 17
While GetMessage(Message)
Debug.Print Message
Wend
End Sub
Sub IntMessageList(BufferSizeA As Long)
BufferSize = BufferSizeA
ReDim MessageList(BufferSize)
End Sub
Sub AddMessage(MessageA As String)
If WriteIndex = BufferSize Then WriteIndex = 0
MessageList(WriteIndex) = MessageA
WriteIndex = WriteIndex + 1
ListCount = ListCount + 1
If ListCount > BufferSize Then
' ListCount = BufferSize
GetMessage ("") 'SKIP ONE MESSAGE
End If
Debug.Print "Save ok,ListCount=" & ListCount
End Sub
Function GetMessage(Message As String) As Boolean
If ListCount > 0 Then
If ListIndex = BufferSize Then ListIndex = 0
Message = MessageList(ListIndex)
GetMessage = True
ListIndex = ListIndex + 1
ListCount = ListCount - 1
Debug.Print "Read ok,ListCount=" & ListCount
End If
End Function
Last edited by xiaoyao; Sep 3rd, 2021 at 07:08 PM.
The vb6-native way of doing a queue without getting too fancy would be to use a collection of strings.
Code:
Dim colMessages As Collection
Set colMessages = New Collection
colMessages.Add "Message1" ' Add to the end of the collection
colMessages.Add "Message2" ' Add to the end of the collection
Debug.Print colMessages(1) ' Read colMessages(1) to get the first item in the list
colMessages.Remove 1 ' Remove the first item in the collection (Message1)
Thankyou for your help chaps. In the end I used a VB6 collection similar to the manner described above.
Skillset: VMS,DOS,Windows Sysadmin from 1985, fault-tolerance, VaxCluster, Alpha,Sparc. DCL,QB,VBDOS- VB6,.NET, PHP,NODE.JS, Graphic Design, Project Manager, CMS, Quad Electronics. classic cars & m'bikes. Artist in water & oils. Historian.
By the power invested in me, all the threads I start are battle free zones - no arguing about the benefits of VB6 over .NET here please. Happiness must reign.
That makes sense. It is a very common way to do it, quite versatile since it takes Variants including those wrapping Objects, and performance is usually not the problem some would make it out to be when they are trying to sell you on some random 3rd party library.
If you had a special need such as extremely high volume it might be worth exploring something else. If you need things like persistence to disk, inter-process, or inter-machine queues there is always MSMQ too. MSMQ Machine Queues don't require a Domain or server. You just need ensure that this optional Windows component has been installed.
The vb6-native way of doing a queue without getting too fancy would be to use a collection of strings.
Code:
Dim colMessages As Collection
Set colMessages = New Collection
colMessages.Add "Message1" ' Add to the end of the collection
colMessages.Add "Message2" ' Add to the end of the collection
Debug.Print colMessages(1) ' Read colMessages(1) to get the first item in the list
colMessages.Remove 1 ' Remove the first item in the collection (Message1)
Unless we know for sure that removal from a Collection by index is less than an O(n) operation, I'd go with Olaf's method. It is the best method in this thread barring it's limitation of having a fixed number of maximum items.
C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter
There's just no reason to use garbage like InputBox. - jmcilhinney
The threads I start are Niya and Olaf free zones. No arguing about the benefits of VB6 over .NET here please. Happiness must reign. - yereverluvinuncleber
At most, I will have two or three messages queueing at any time. Messages come frequently but they are almost always synchronous. Only in one case will they be asynchronous, so this is just to check for that eventuality. I always check the collection count is non zero before I perform any operations using its contents.
This solution is very simple, it is elegant and just slots in with the absolute minimum of code. I am rather fond of collections, a programming construct I was only made aware of three years ago by Olaf. Thankyou Olaf.
I am amazed at how feature-rich this language is, I was only vaguely aware of linked lists previously but that is something else I can say I now have stored in the brain. Once again, thankyou chaps.
Skillset: VMS,DOS,Windows Sysadmin from 1985, fault-tolerance, VaxCluster, Alpha,Sparc. DCL,QB,VBDOS- VB6,.NET, PHP,NODE.JS, Graphic Design, Project Manager, CMS, Quad Electronics. classic cars & m'bikes. Artist in water & oils. Historian.
By the power invested in me, all the threads I start are battle free zones - no arguing about the benefits of VB6 over .NET here please. Happiness must reign.