Use countif to determine boolean in a class module - class

I'm trying to use a Boolean within a class module however the code fails at “MsgBox LOADPROPS.DUPLICATES” with “Public Property Get DUPLICATES() As Range” Please can someone assist?
Sub INITIALIZE_CLASS()
Dim LOADPROPS As cLoadData
Set LOADPROPS = New cLoadData
LOADPROPS.DUPLICATES = PasteLoadingForm.Columns("K")
MsgBox LOADPROPS.DUPLICATES
End Sub
and in the class module
Public Property Get DUPLICATES() As Range
DUPLICATES = pDUPLICATES
End Property
Public Property Let DUPLICATES(Value As Range)
Dim lcount As Long
lcount = Application.WorksheetFunction.CountIf(Value, "DUPLICATE")
'
pDUPLICATES = lcount
Select Case pDUPLICATES
Case Is = 0
pDUPLICATES = False
Case Is >= 0
pDUPLICATES = True
Case Else
MsgBox "Error"
End Select
End Property

From what I can tell, you're mixing up the data type of DUPLICATES. You are declaring it as a range in the get function, yet pDUPLICATES can take on the value of a boolean and long (initially is the count then converted to true or false in the select statement).
The quick fix would be to change them to variants, so your class module becomes:
Private pDUPLICATES As Variant
Public Property Get DUPLICATES() As Variant
DUPLICATES = pDUPLICATES
End Property
Public Property Let DUPLICATES(Value As Variant)
Dim lcount As Long
lcount = Application.WorksheetFunction.CountIf(Value, "DUPLICATE")
'
pDUPLICATES = lcount
Select Case pDUPLICATES
Case Is = 0
pDUPLICATES = False
Case Is >= 0
pDUPLICATES = True
Case Else
MsgBox "Error"
End Select
End Property
EDIT
To avoid variants, a better approach might be to restructure your class module so you're not actually using get/set properties (which you're not really doing here). You could do it with a function as follows:
Class cLoadDataV2
Option Explicit
Public pDUPLICATES As Boolean
Public Function fDuplicates(rngInputRange As Range)
Dim lcount As Long
lcount = Application.WorksheetFunction.CountIf(rngInputRange, "DUPLICATE")
Select Case lcount
Case Is = 0
pDUPLICATES = False
Case Is >= 0
pDUPLICATES = True
Case Else
MsgBox "Error"
End Select
End Function
Regular module
Sub Initialise_v2()
Dim LOADPROPS As cLoadDataV2
Set LOADPROPS = New cLoadDataV2
LOADPROPS.fDuplicates (Sheet1.Columns("K"))
MsgBox LOADPROPS.pDUPLICATES
End Sub

Related

Binary operator '~=' cannot be applied to two UmRet operands

I'm working on integrating an IDTech swiper into my app and I've gotten pretty far along, added the library, registered notifications, unregistered them, and now I'm working on a function that connects the reader. I can't seem to figure out what I'm doing wrong here when I'm attempting to switch cases based on a return value.. could someone please help me?
func displayUmRet(operation: String, returnValue: UmRet) {
var string = ""
do {
switch returnValue {
case UMRET_SUCCESS: string = ""
case UMRET_NO_READER: string="No reader attached"
case UMRET_SDK_BUSY: string="Communication with reader in progress"
case UMRET_MONO_AUDIO: string="Mono audio enabled"
case UMRET_ALREADY_CONNECTED: string="Already connected"
case UMRET_LOW_VOLUME: string="Low volume"
case UMRET_NOT_CONNECTED: string="Not connected"
case UMRET_NOT_APPLICABLE: string="Not applicable to reader type"
case UMRET_INVALID_ARG: string="Invalid argument"
case UMRET_UF_INVALID_STR: string="Invalid firmware update string"
case UMRET_UF_NO_FILE: string="Firmware file not found"
case UMRET_UF_INVALID_FILE: string="Invalid firmware file"
default: string="<unknown code>"
}
} while (0)
// var retStatus = UMRET_SUCCESS==ret
//self.textResponse.text = "\(operation), \(retStatus), \(string)"
self.hexResponse.text = "";
}
You need to put a . before your cases:
enum UmRet {
case UMRET_SUCCESS, UMRET_FAILURE
}
var string = " "
let returnValue = UmRet.UMRET_SUCCESS
switch returnValue {
case .UMRET_SUCCESS: string = "y"
case .UMRET_FAILURE: string = "n"
}
Also, 0 isn't the same as false in Swift, so:
do {
...
} while (0)
Shouldn't work either.
And you don't need semicolons at the end of a line, so this:
self.hexResponse.text = "";
can be this:
self.hexResponse.text = ""
And finally, if your switch statement has every case for every case in your enum, you don't need a default case. (that's why mine didn't have one in the example)
By the way, ~= is just the operator for the pattern-matching function, which is what Swift does in a switch statement. It works kind of like the == function, for instance, Int ~= Int is the same as Int == Int. But it's a bit more versatile: for instance Range ~= Int, eg 0...3 ~= 2 returns whether or not the Int is in the range. (So true in this case) For enums, it matches cases to cases. In my example, it'll match UMRET_SUCCESS, and string will be set to y.

Referencing reusable forms (2 deep) in MS Access

I have multiple Members, and each one has a record which contains several memo fields:
Member ID Entry A Entry B
1 [memo text] [memo text]
2 [memo text] [memo text]
3 [memo text] [memo text]
In Access 2007, I am creating a Memo Entry form that is the equivalent of Shift-F2 -- A dedicated window to review and edit the content. Unlike Shift-F2, this must be reusable.
I must mention that the form for showing details about a Member is also reusable. Following the plan above (which is abbreviated), I could have up to three Member forms and six Memo Entry forms open at once.
The solution below works well EXCEPT that the critical event UpdateComment() is only triggered once per Member-form instead of once per Memo-form. So if I open A and B for the same member and make edits, only one edit will be passed to the calling form.
Somehow I am failing to provide the calling form, Member Detail, with a method to treat the Memo forms as unique. How do I solve that?
Member Detail form -- this does the spawning
Dim strFieldName As String, varValue
Private WithEvents frmZoom As Form_frmMemberInputZoom
Private Sub btnView_A_Click()
strFieldName = "boxQuality_A"
varValue = Me(strFieldName)
Call OpenMemberInput(Me!boxID, strFieldName, varValue, True)
End Sub
Private Sub btnView_B_Click()
strFieldName = "boxQuality_B"
varValue = Me(strFieldName)
Call OpenMemberInput(Me!boxID, strFieldName, varValue, True)
End Sub
Private Sub frmZoom_UpdateComment(lngID As Long, _
strAssessStage As String, varReturn)
Dim intCount As Integer
For intCount = 1 To collectnZooms.Count
If collectnZooms(intCount)![boxID] = lngID _
And collectnZooms(intCount)![boxAssessStage] = strAssessStage _
Then
Me(strAssessStage) = varReturn
Me.Dirty = False
Exit Sub
End If
Next
End Sub
Private Sub Form_Close()
Dim obj As Object
For Each obj In collectnMembers
If obj.Hwnd = Me.Hwnd Then
collectnMembers.Remove CStr(Me.Hwnd)
End If
Next
End Sub
Sub OpenMemberInput(lngID As Long, strStage As String, _
varComment, booEdit As Boolean)
Set frmZoom = New Form_frmMemberInputZoom
frmZoom.Caption = CStr(frmZoom.Hwnd)
frmZoom.ID = lngID
frmZoom.Stage = strStage
frmZoom.ProviderName = "Dr " & CStr(lngID)
frmZoom.Comment = varComment
frmZoom.Visible = True
collectnZooms.Add Item:=frmZoom, Key:=CStr(frmZoom.Hwnd)
End Sub
Memo Entry form -- this one is spawned
Public Event UpdateComment(lngID As Long, strAssessStage As String, _
varReturn)
Private lngAssess_ID As Long
Private strAssessStage As String
Private strProviderName As String
Private varComment
Public Property Let ID(ByVal MyAssessID As Long)
lngAssess_ID = MyAssessID
Me.boxID = lngAssess_ID
End Property
Public Property Let Stage(ByVal MyAssessStage As String)
strAssessStage = MyAssessStage
Me.boxAssessStage = strAssessStage
End Property
Public Property Let ProviderName(ByVal MyProviderName As String)
strProviderName = MyProviderName
Me.boxProviderName = strProviderName
End Property
Public Property Let Comment(ByVal varExisting)
varComment = varExisting
Me.boxComment = varComment
End Property
Public Property Get Comment()
Comment = varComment
End Property
Private Sub boxComment_AfterUpdate()
varComment = Me.boxComment
Comment = varComment
End Sub
Private Sub Form_Close()
'#################################################################
' Line below will be called for only ONE of the multiple instances
'#################################################################
RaiseEvent UpdateComment(lngAssess_ID, strAssessStage, varComment)
Dim obj As Object
For Each obj In collectnZooms
If obj.Hwnd = Me.Hwnd Then
collectnZooms.Remove CStr(Me.Hwnd)
End If
Next
End Sub
As recommended in this post:
Dismantle the RaiseEvent function.
Use .Visible to avoid closing out the Memo Entry form (which throws things off).
Member Detail form -- this does the spawning
Dim strFieldName As String, varValue
'Private WithEvents ... NAH, WE WON'T GO THERE
Private Sub btnView_A_Click()
strFieldName = "boxQuality_A"
varValue = Me(strFieldName)
Call OpenMemberInput(Me!boxID, strFieldName, varValue, True)
End Sub
Private Sub btnView_B_Click()
strFieldName = "boxQuality_B"
varValue = Me(strFieldName)
Call OpenMemberInput(Me!boxID, strFieldName, varValue, True)
End Sub
'Private Sub frmZoom_UpdateComment(lngID As Long, strAssessStage As String, varReturn)
' ... NAH, we won't be using this...
'End Sub
Private Sub Form_Close()
Dim obj As ObjecT
For Each obj In collectnMembers
If obj.Hwnd = Me.Hwnd Then
collectnMembers.Remove CStr(Me.Hwnd)
End If
Next
End Sub
Sub OpenMemberInput(lngID As Long, strStage As String, _
varComment, booEdit As Boolean)
Dim FoundMe As Boolean
FoundMe = v_MemberComment.FetchForm(lngID, strStage)
If FoundMe Then Exit Sub
Dim frmZoom As New Form_frmMemberInputZoom
Set frmZoom = New Form_frmMemberInputZoom
frmZoom.ID = lngID
frmZoom.Stage = strStage
frmZoom.Comment = varComment
frmZoom.Visible = True
collectnZooms.Add Item:=frmZoom, Key:=CStr(frmZoom.Hwnd)
End Sub
Memo Entry form -- this one is spawned
'' NAH, no use of the Public Event
'Public Event UpdateComment(lngID As Long, strAssessStage As String, varReturn)
Private lngAssess_ID As Long
Private strAssessStage As String
Private varComment
Public Property Let ID(ByVal MyAssessID As Long)
lngAssess_ID = MyAssessID
Me.boxID = lngAssess_ID
End Property
Public Property Let Stage(ByVal MyAssessStage As String)
strAssessStage = MyAssessStage
Me.boxAssessStage = strAssessStage
End Property
Public Property Let Comment(ByVal varExisting)
varComment = varExisting
Me.boxComment = varComment
End Property
Public Property Get Comment()
Comment = varComment
End Property
Private Sub boxComment_AfterUpdate()
varComment = Me.boxComment
Comment = varComment
End Sub
Private Sub CmdCancel_Click()
''TODO revert to before-update text if Cancel is selected
Me.Tag = "Cancel"
Me.Visible = False
End Sub
Private Sub CmdOK_Click()
Dim intCount As Integer, frm As Form, lngHwnd As Long
For intCount = 1 To collectnMembers.Count
Set frm = collectnMembers(intCount)
If frm![boxID] = lngAssess_ID Then
frm(strAssessStage) = varComment
frm.Requery
End If
Next
Me.Visible = False
End Sub
Private Sub Form_Close()
'RaiseEvent .... NAH, DON'T BOTHER
Dim obj As Object
For Each obj In collectnZooms
If obj.Hwnd = Me.Hwnd Then
collectnZooms.Remove CStr(Me.Hwnd)
End If
Next
End Sub
VBA Standard Module to support above form
Public collectnZooms As New Collection
Public Function FetchForm(lngID As Long, strStage As String) As Boolean
Dim intCount As Integer
For intCount = 1 To collectnZooms.Count
If collectnZooms(intCount)![boxID] = lngID _
And collectnZooms(intCount)![boxAssessStage] = strStage Then
FetchForm = True
collectnZooms(intCount).Tag = ""
collectnZooms(intCount).Visible = True
Exit Function
End If
Next
End Function

Lua custom linked list for class creation practice fails to set node.next

So I've been trying to figure out how to mimic classes in lua. So I could start with some basic code that I already have written in other languages, I started with a linked list. I have what seems to be a working node class, but when i try to print out the list from my LList class, I cant seem to get it to print.
There seems to be a problem where after the first node is added to the list, either the second node that is added gets overwritten by the third, or it simply doesn't actually get added. This could be a problem with a while loop I'm using, but I have no idea why it would be an issue in this case.
Here is the Linked List "class"
LList = {}
LList.__index = LList
function LList.create()
local list = {} -- our new object
setmetatable(list,LList) -- make LList handle lookup
list.count = 0 -- initialize our object
list.head = nil
return list
end
function LList:add(newNode)
print("DEBUG PRINT: LList:add(): newNode.data: "..newNode:getData().." LList.count: "..self.count)
if(self.head) then
local curr = self.head
print("DEBUG PRINT: LList:add(): self.head:toString(): "..self.head:toString())
print("DEBUG PRINT: LList:add(): curr:toString(): "..curr:toString())
print("DEBUG PRINT: LList:add(): newNode:toString: "..newNode:toString())
while curr.nextNode do --this is the while loop in question
print("DEBUG PRINT: LList:add(): in while:"..curr:toString())
curr = curr.nextNode
end
curr:setNext(newNode)
print("DEBUG PRINT: LList:add(): curr.nextNode:toString(): "..curr.nextNode:toString())
self.count = self.count + 1
else
self.head = newNode
self.count = 1
print("DEBUG PRINT: LList:add(): self.count" .. self.count .." self.head:getData(): ".. self.head:getData())
end
print("DEBUG PRINT: LList:add(): EXITING\n")
end
function LList:getLen()
return self.count
end
function LList:toString()
print("Stubbed toString()")
if(head)then
print(self:toStringHelper(head))
else
print("emptyList")
end
end
function LList:toStringHelper(currNode)
if(currNode.nextNode)then
return currNode:toString() .. toStringHelper(currNode.nextNode)
else
return currNode:toString()
end
end
This here is the Node "class"
Node = {}
Node.__index = Node
function Node.create(newData)
local tNode = {}
setmetatable(tNode, Node)
tNode.data = newData
return tNode
end
function Node:getData()
return self.data
end
function Node:getNext()
return self.nextNode
end
function Node:setNext(newNode)
self.nextNode = newNode
print("DEBUG PRINT: Node:setNext(): self.nextNode:toString(): "..self.nextNode:toString())
end
function Node:hasNext()
if self.nextNode then
return true
else
return false
end
end
function Node:toString()
return tostring(self.data)
end
This is the tester code. Again, all code is in one file for ease of testing.
testerList = LList.create()
print(testerList:getLen())
tNode1=Node.create(5)
tNode2=Node.create(7)
tNode3=Node.create(2)
testerList:add(tNode1)
testerList:add(tNode2)
testerList:add(tNode3)
print(testerList:getLen())
print(testerList:toString())
I believe that my problem is either in the while loop implementation, or in the way I'm setting the Node.nextNode data.
Works for me after fixing some typos in your code:
In LList:toString() you need to replace the references to head with self:head and in LList:toStringHelper(currNode) you need to replace the references to toStringHelper with self:toStringHelper. After this the code prints out the correct list.

Releasing recursive class module in VBA

I've been playing around with a class module which contains multiple versions of itself to build up a tree structure.
I've noticed the process of building the tree is very fast. Roughly 2 seconds for a 7 level tree with 6-8 branches per subtree. Unfortunately the program runs very slowly. This seems to be caused by the release of the memory used by the tree, which takes at least 60 seconds.
Initially I did not release the class module, and allowed VB to do it at the end of the program, but replacing this with set myTree = nothing makes no difference to the speed.
I also tried writing a sub routine to destroy the tree. This recursively went through each layer and set the sub trees to nothing. Oddly this seemed to save aroung 0.5 of a second, but nothing significant.
Is there anything else I can do to reduce the unload time?
The code is really long but the excert below gives the idea. I'm happy that the tree structure works, but the gap between the final two timer statements is very large
Class treeNode
private aCurrentDepth as integer
private aNodeObject as myObject
private aNodes(maxNodeCount) as treeNode
end class
public function creatreTree(m as myObject,depth as integer) as treeNode
Dim x As Integer
Set createTree = New treeNode
createTree.initialise
createTree.cNodeObject = m
createTree.cCurrentDepth = depth
If depth <> 1 Then
For x = 0 To maxNodeCount
createTree.tNode(x) = createTree(getObject(m,x), depth - 1)
Next x
End If
end function
sub testTree
Dim t as treeNode
dim g as myObject
Set t = New treeNode
g.initialise
t.initialise
set g = startObject
Cells(1, "A") = Timer
Set t = createTree(g, 7)
Cells(1, "B") = Timer
Set t = Nothing
Cells(1, "C") = Timer
end sub
I just created a debugExcelLog class last week. You might find it helpful to track what is happening in your classes. I used it to locate a bug that was happening only occasionally. (Turned out UserRoutine2 was trying to use a global class that UserRoutine1 was in the middle of clearing.)
'------------------------------------------------------------------------------
'| Class Name: debugExcelLog
'| Programmer: Mischa Becker
'| Date: 11/4/2011
'| Purpose: Creates an Excel Workbook log
'------------------------------------------------------------------------------
'| Notes:
'| + Add a DEBUG_PROJECT compiler constant to your project
'| + Add Public Const g_PROJECT_NAME As String = "[ProjectName]" to a module.
'| + sName and sCalledBy are expected to be in one of the following formats:
'| Project.Class.Routine
'| Class.Routine
'| Routine
'------------------------------------------------------------------------------
Option Explicit
Private Const m_CLASS_NAME As String = g_PROJECT_NAME & ".debugExcelLog"
Private Const m_OFFSET As Integer = 3
Private m_wbk As Workbook
Private m_r As Range
Private m_bLogged As Boolean
Private m_iIndent As Integer
Private m_bOkToLog As Boolean
Private m_lInstanceID As Long
'------------------------------------------------------------------------------
'------------------------------------------------------------------------------
Private Sub Class_Initialize()
m_bOkToLog = False
m_lInstanceID = CLng(Rnd * 10 ^ 6)
#If DEBUG_PROJECT Then
Debug.Print m_CLASS_NAME; ".Class_Initialize", "Id:"; m_lInstanceID
Me.TurnOn
#End If
End Sub
Private Sub Class_Terminate()
If Not (m_bLogged Or m_wbk Is Nothing) Then
m_wbk.Close False
End If
Set m_wbk = Nothing
Set m_r = Nothing
#If DEBUG_PROJECT Then
Debug.Print m_CLASS_NAME; ".Class_Terminate", "Id:"; m_lInstanceID
#End If
End Sub
'------------------------------------------------------------------------------
'------------------------------------------------------------------------------
Public Sub TurnOn()
Set m_wbk = Application.Workbooks.Add
Set m_r = m_wbk.Sheets(1).Range("A1")
m_iIndent = 0
SetTitle
m_bOkToLog = True
End Sub
Public Sub TurnOff()
m_bOkToLog = False
End Sub
'------------------------------------------------------------------------------
'------------------------------------------------------------------------------
Public Sub Log_Start(sName As String, lInstance As Long _
, Optional sCalledBy As String = "" _
, Optional sComment As String = "")
Const MY_NAME As String = m_CLASS_NAME & ".Log_Start"
On Error GoTo ErrorHandler
If Not m_bOkToLog Then Exit Sub
m_bLogged = True
m_iIndent = m_iIndent + 1
BreakApartAndLogName sName, ".Start"
Instance = lInstance
TimeStamp = Now
CalledBy = sCalledBy
Comment = sComment
MoveNextRow
Exit Sub
ErrorHandler:
Debug.Print MY_NAME, Err.Number; " - "; Err.Description
Stop
Resume
End Sub
Public Sub Log_End(sName As String, lInstance As Long _
, Optional sCalledBy As String = "" _
, Optional sComment As String = "")
Const MY_NAME As String = m_CLASS_NAME & ".Log_End"
On Error GoTo ErrorHandler
If Not m_bOkToLog Then Exit Sub
BreakApartAndLogName sName, ".End"
Instance = lInstance
TimeStamp = Now
CalledBy = sCalledBy
Comment = sComment
MoveNextRow
m_iIndent = m_iIndent - 1
Exit Sub
ErrorHandler:
Debug.Print MY_NAME, Err.Number; " - "; Err.Description
Stop
Resume
End Sub
Public Sub Log_Other(sName As String, lInstance As Long _
, Optional sCalledBy As String = "" _
, Optional sComment As String = "")
Const MY_NAME As String = m_CLASS_NAME & ".Log_Other"
On Error GoTo ErrorHandler
If Not m_bOkToLog Then Exit Sub
m_bLogged = True
If m_iIndent < 0 Then m_iIndent = 0
BreakApartAndLogName sName
Instance = lInstance
TimeStamp = Now
CalledBy = sCalledBy
Comment = sComment
MoveNextRow
Exit Sub
ErrorHandler:
Debug.Print MY_NAME, Err.Number; " - "; Err.Description
Stop
Resume
End Sub
'------------------------------------------------------------------------------
'------------------------------------------------------------------------------
Private Sub SetTitle()
Const MY_NAME As String = m_CLASS_NAME & ".SetTitle"
On Error GoTo ErrorHandler
m_r = "Debug Excel Log Created on " & Date
MoveNextRow
Project = "Project"
Module = "Module"
Routine = "Routine"
Instance = "Instance"
TimeStamp = "TimeStamp"
CalledBy = "Called By"
Comment = "Comment"
With Range(m_r, m_r.End(xlToRight))
.Font.Bold = True
.BorderAround XlLineStyle.xlContinuous, xlMedium
End With
MoveNextRow
m_iIndent = -1
Exit Sub
ErrorHandler:
Debug.Print MY_NAME, Err.Number; " - "; Err.Description
Stop
Resume
End Sub
Private Sub MoveNextRow()
Set m_r = m_r.Offset(1)
End Sub
Private Sub BreakApartAndLogName(ByVal sName As String _
, Optional sExtra As String = "")
Const MY_NAME As String = m_CLASS_NAME & ".BreakApartAndLogName"
On Error GoTo ErrorHandler
Routine = SplitOffLastSection(sName) & sExtra
If Len(sName) > 0 Then
Module = SplitOffLastSection(sName)
If Len(sName) > 0 Then
Project = SplitOffLastSection(sName)
End If
End If
Exit Sub
ErrorHandler:
Debug.Print MY_NAME, Err.Number; " - "; Err.Description
Stop
Resume
End Sub
Private Function SplitOffLastSection(ByRef sName As String) As String
' Passed sName is returned without the Last Section.
Const MY_NAME As String = m_CLASS_NAME & ".SplitOffLastSection"
Dim i As Integer
i = InStrRev(sName, ".")
If i > 0 Then
SplitOffLastSection = Mid(sName, i + 1)
sName = Left(sName, i - 1)
Else
SplitOffLastSection = sName
sName = ""
End If
Exit Function
ErrorHandler:
Debug.Print MY_NAME, Err.Number; " - "; Err.Description
Stop
Resume
End Function
'------------------------------------------------------------------------------
'------------------------------------------------------------------------------
Private Property Let Project(sText As String)
m_r = sText
End Property
Private Property Get Project() As String
Project = m_r.Text
End Property
Private Property Let Module(sText As String)
m_r.Offset(0, 1) = sText
End Property
Private Property Get Module() As String
Module = m_r.Offset(0, 1).Text
End Property
Private Property Let Routine(sText As String)
If m_iIndent < 0 Then
m_r.Offset(0, 2) = sText
Else
m_r.Offset(0, 2) = Space(m_OFFSET * m_iIndent) & sText
End If
End Property
Private Property Let Instance(lInstance As Variant)
m_r.Offset(0, 3) = lInstance
End Property
Private Property Let TimeStamp(dTimeStamp As Variant)
m_r.Offset(0, 4) = dTimeStamp
End Property
Private Property Let CalledBy(ByVal sText As String)
' remove Project and Module from sText if same as running Routine
sText = Replace(sText, Project & "." & Module & ".", "")
sText = Replace(sText, Project & ".", "")
m_r.Offset(0, 5) = sText
End Property
Private Property Let Comment(sText As String)
m_r.Offset(0, 6) = sText
End Property
'------------------------------------------------------------------------------
To use:
Add the class to your project and name it debugExcelLog
Add DEBUG_PROJECT=-1 as a conditional compiler constant to your project
Create a global variable of the class. ie Public g_XlLog As debugExcelLog
Logging can be turned on and off with
g_xlLog.TurnOn
g_xlLog.TurnOff
If DEBUG_PROJECT is True, you don't need to call TurnOn, the class will auto turn on when it initializes.
Use the following in any routine you want to track.
g_XlLog.Log_Start "[Class.Routine]", m_lInstanceIdOrZero
g_XlLog.Log_Other "[Class.Routine]", m_lInstanceIdOrZero, ,"Comment"
g_XlLog.Log_End "[Class.Routine]", m_lInstanceIdOrZero
I suggest altering your testTree as follows.
sub testTree
Dim t as treeNode, g as myObject
Dim iLevel as Integer
iLevel = 7
Set g_XlLog = New debugExcelLog
g_XlLog.Log_Start "testTree", 0, , "Initializing test variables"
Set t = New treeNode
g.initialise
t.initialise
set g = startObject
g_XlLog.Log_Other "testTree", 0, , "Create a " & iLevel & " level tree"
Set t = createTree(g, iLevel)
g_XlLog.Log_Other "testTree", 0, , "Terminate a " & iLevel & " level tree"
Set t = Nothing
g_XlLog.Log_End "testTree", 0
set g_XlLog = Nothing
end sub
I would recommend adding logging to Class_Initialize and Class_Terminate for both treeNode and MyObject. If Class_Terminate is calling other routines you can either add logging to them, or use Log_Other to track when each one starts.
If you haven't done so already, I really recommend adding some sort of instance id to treeNode so you will know which instance is being created\terminated. If you aren't worried about Rnd creating duplicate IDs it can be as simple as what I have in the above class.
You will also notice the optional sCalledBy and sComment parameters. sComment should be obvious but sCalledBy is there because the Excel VBE's Call Stack leaves a lot to be desired. For debugging purposes, some of my methods require the routine calling them to pass their name in as a parameter. If you have this info, you can send it to the logger.
Once you have a more precise idea of where the slow-down is happening, it will be a lot easier to figure out how to fix it.

How to send a request and get a response using system.io.pipes

I'm trying to write a client and server that communicate through named pipes. I need the the client to be able to send a query to the server and have the server send a response. All of the samples on the web for System.IO.pipes either send a single string to the client or a single string to the server. I have found some examples of how to do that with the old Win32 pipes. But, I can't use that.
Here is what I have so far. I know it is a lot of code. But, I think the issue may just be in one of the constructors for the pipe streams.
Client Code
Private Sub cmdSend_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmdSend.Click
Dim strMsg As String = ""
Dim strRequest As String = "Send Key"
Dim strErrMsg As String = ""
PrintText("Creating new pipe client")
'pipeStream = New NamedPipeClientStream(strPipeName)
'pipeStream = New NamedPipeClientStream(strPipeName, PipeDirection.InOut, 1, PipeTransmissionMode.Message, PipeOptions.None)
pipeStream = New NamedPipeClientStream(strServerName, strPipeName, PipeDirection.InOut, PipeOptions.None, Security.Principal.TokenImpersonationLevel.None)
PrintText("Connecting to server")
pipeStream.Connect()
pipeStream.ReadMode = PipeTransmissionMode.Message
PrintText("Sending request")
'Send Request
If SendPipeMessage(pipeStream, strRequest, strErrMsg) Then
Else
PrintText(strErrMsg)
End If
PrintText("Receiving response")
'Process Response
If ReadPipeMessage(pipeStream, strMsg, strErrMsg) Then
PrintText(strMsg)
Else
PrintText(strErrMsg)
End If
pipeStream.Dispose()
pipeStream = Nothing
End Sub
Private Function SendPipeMessage(ByRef pipeStream As NamedPipeClientStream, ByVal strMsg As String, ByRef strErrMsg As String) As Boolean
Dim blnRetVal As Boolean = True
Dim bytMessage() As Byte = Nothing
Dim encoding As UTF8Encoding = Nothing
Try
encoding = New UTF8Encoding
bytMessage = encoding.GetBytes(strMsg)
pipeStream.Write(bytMessage, 0, bytMessage.Length)
Catch ex As Exception
blnRetVal = False
strErrMsg = ex.ToString
End Try
Return blnRetVal
End Function
Private Function ReadPipeMessage(ByRef pipeStream As NamedPipeClientStream, ByRef strPipeText As String, ByRef strErrMsg As String) As Boolean
Dim blnRetVal As Boolean = True
Dim strTextChunck As String = ""
Dim intNumBytes As Integer = 0
Dim intNumChars As Integer = 0
Dim bytMessage(10) As Byte
Dim chars(10) As Char
Dim decoder As Decoder = Nothing
Try
decoder = Encoding.UTF8.GetDecoder
strPipeText = ""
Do
strTextChunck = ""
Do
intNumBytes = pipeStream.Read(bytMessage, 0, bytMessage.Length)
intNumChars = decoder.GetChars(bytMessage, 0, intNumBytes, chars, 0)
strTextChunck = strTextChunck & New String(chars, 0, intNumChars)
Loop While Not pipeStream.IsMessageComplete
strPipeText = strPipeText & strTextChunck
Loop While intNumBytes <> 0
Catch ex As Exception
blnRetVal = False
strErrMsg = ex.ToString
End Try
Return blnRetVal
End Function
Server Code
Private Sub cmdListen_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmdListen.Click
bw.RunWorkerAsync()
End Sub
Private Sub bw_DoWork(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles bw.DoWork
Dim strRequest As String = ""
Dim strErrMsg As String = ""
bw.ReportProgress(0, "Create new pipe server stream")
pipeStream = New NamedPipeServerStream(strPipeName, PipeDirection.InOut, 1, PipeTransmissionMode.Message, PipeOptions.None)
'Wait for connection
bw.ReportProgress(0, "Listening for connection")
pipeStream.WaitForConnection()
bw.ReportProgress(0, "Receiving request")
'Receive Request
If ReadPipeMessage(pipeStream, strRequest, strErrMsg) Then
bw.ReportProgress(0, "Request : " & strRequest)
Else
bw.ReportProgress(0, strErrMsg)
End If
'Get response
strResponse = GetResponse(strRequest)
bw.ReportProgress(0, "Sending response")
'Send Response
If SendPipeMessage(pipeStream, strResponse, strErrMsg) Then
bw.ReportProgress(0, "Sent response")
Else
bw.ReportProgress(0, strErrMsg)
End If
bw.ReportProgress(0, "Done!")
pipeStream.Disconnect()
pipeStream.Dispose()
pipeStream = Nothing
End Sub
Private Sub bw_ProgressChanged(ByVal sender As Object, ByVal e As System.ComponentModel.ProgressChangedEventArgs) Handles bw.ProgressChanged
PrintText(e.UserState)
End Sub
Private Sub bw_RunWorkerCompleted(ByVal sender As Object, ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles bw.RunWorkerCompleted
PrintText(e.Result)
End Sub
Private Function SendPipeMessage(ByRef pipeStream As NamedPipeServerStream, ByVal strMsg As String, ByRef strErrMsg As String) As Boolean
Dim blnRetVal As Boolean = True
Dim bytMessage() As Byte = Nothing
Dim encoding As UTF8Encoding = Nothing
Try
encoding = New UTF8Encoding
bytMessage = encoding.GetBytes(strMsg)
pipeStream.Write(bytMessage, 0, bytMessage.Length)
Catch ex As Exception
blnRetVal = False
strErrMsg = ex.ToString
End Try
Return blnRetVal
End Function
Private Function ReadPipeMessage(ByRef pipeStream As NamedPipeServerStream, ByRef strPipeText As String, ByRef strErrMsg As String) As Boolean
Dim blnRetVal As Boolean = True
Dim strTextChunck As String = ""
Dim intNumBytes As Integer = 0
Dim intNumChars As Integer = 0
Dim bytMessage(10) As Byte
Dim chars(10) As Char
Dim decoder As Decoder = Nothing
Try
decoder = Encoding.UTF8.GetDecoder
strPipeText = ""
Do
strTextChunck = ""
Do
intNumBytes = pipeStream.Read(bytMessage, 0, bytMessage.Length)
intNumChars = decoder.GetChars(bytMessage, 0, intNumBytes, chars, 0)
strTextChunck = strTextChunck & New String(chars, 0, intNumChars)
Loop While Not pipeStream.IsMessageComplete
strPipeText = strPipeText & strTextChunck
Loop While intNumBytes <> 0
Catch ex As Exception
blnRetVal = False
strErrMsg = ex.ToString
End Try
Return blnRetVal
End Function
When I start the client, it hangs on ReadPipMessage() and the server hangs on ReceivingRequest(). If I kill the client, the server reads in the request sent by the client. But, it bombs on sending the response because the client is no longer running.
Is it not possible for the server to send and receive a message in the same connection? I thought that was what PipeDirection.InOut meant.
Thanks,
Mike
You may want to look at the named pipes binding for WCF. There are many good examples of how to use it available out there, for instance: http://www.jmedved.com/2010/03/named-pipes-in-wcf/