We are a group of users of Blackbaud products and are not affiliated with Blackbaud. We'd love to have you join our community to help and be helped in getting the most from your Blackbaud software.
Register now to join us to get independant advice on your system, connect with 3rd party consultants to help you maximize your database and have a real alternative to the official Blackbaud website.
I'm using VB.NET and the RE api to read and delete all
Notepads of a certain type. The problem is that sometimes the
program tries to read into protected storage, meaning (I think) that it
is trying to read too many notepads. The 'Catch' never happens; the program just halts. The stmt given is always the first 'Next'.
The program will run without halting in Debug mode, but I'd like to find out
what's causing the problem.
Ideas anyone?
Here is the code:
Code:
Dim oRecords As CRecords
oRecords = New CRecords
oRecords.Init(RE7.SessionContext)
Dim oRecord As CRecord
oRecord = New CRecord
' scroll through each record..
For Each oRecord In oRecords
' scroll through all the Notepads....
If oRecord.Notepads.Count > 0 Then
For x = 1 To oRecord.Notepads.Count
Try
If oRecord.Notepads.Item(x).Fields(ENotepadFields.NOTEPAD_fld_NotepadType) = "AS400 Daily Feed" Then
oRecord.Notepads.Remove(x)
oRecord.Save()
Exit For
End If
Catch ex As Exception
End Try
Next
End If
Next
Last edited by Peter Gulka; 01-10-2007 at 01:40 PM.
Reason: added CODE tags
There is no Close() method for oRecord, only CloseDown(), which distroys the object.
Here is the message text:
AS400 Daily Feed...
Unhandled Exception: System.Runtime.InteropServices.COMException (0x800A005B): O
bject variable or With block variable not set
at REDotNetAPI._CRecord.get_Notepads()
at REDailyFeed.Module1.Main() in C:\Documents and Settings\dwalker\My Documen
ts\Docs\data\Visual Studio Projects in VSS\vbwork\blackbaud\REDailyFeed\REDailyF
eed\Module1.vb:line 62
Press any key to continue . . .
When you iterate through the CRecord classes from CRecords, you
need to call the CRecord.Init() function each time. This isn't stated in the documentation, but my program started working properly when I did this.
Did you try doing a for each for the notepads instead of counting them?
as follows:
Code:
Dim oNotepad as IBBNotepad
for each oNotepad in oRecord.Notepads
'Remainder of your code
Next oNotepad
It is also possible that it does not like you remove notepads as you try to iterate them. Try commenting out remove line and see if you get the same error.
You do need to closedown top level objects when iterating through them in a collection.
OK, that seems to make sense, but how do I delete the notepad?
I get confused by the interface classes.
When I use the following code I do not get a problem. Having said that I am using VBA so maybe it is more tolerant of such things:
Code:
Dim oRecord As New CRecord
Dim oNote As IBBNotepad
oRecord.Init SessionContext
oRecord.LoadByField uf_Record_CONSTITUENT_ID, "3"
For Each oNote In oRecord.Notepads
If oNote.Fields(NOTEPAD_fld_NotepadType) = "Delete" Then
oRecord.Notepads.Remove oNote
End If
Next oNote
However if there is an issue with deleting while iterating, you could store the notepad ids (oNote.Fields(NOTEPAD_fld_Id) ) in a collection and then later loop through the collection and remove them. The remove method takes both a notepad object and an id, that way you do not need to actually loop the collection of notepads.
I just recently got the program working. The main problem was the .NET interface. It just doesn't seem to work right. I dug up an old box from storage, blew off the dust, and got out my old VB 6.0 CDs, then went to the thrift shop and bought a VB 6.0 book for 35 cents...
I ended up saving the indexes of the notepads to delete, then accessing them later in a loop to remove them. The .NET interface really doesn't like removing an item during an iteration of the collection. It seems to get lost and read beyond the collection boundry. VB 6.0 handles it ok.
At first, I wasn't happy about needing VB 6, but it actually made me see really how cumbersome .NET is!
If anyone is having strange problems using the API through the .NET interface, switch to VB 6.
I totally hear ya about the overhead of .Net vs. VB6. I like to think that .Net is worth all the fuss, but I have to admit I get a great feeling of relief when I open an old VB6 project - the simplicity is refreshing. Then as soon as my code lines don't indent themselves automatically I'm ready to go back. ;-)
Anyway, I'll bet dollars to donuts that the issue you experienced is a GarbageCollection issue related to the fact that (as Drew mentioned) you are not closing down the objects. This bit me hard once with .Net. The error did not "respect" my try...catch block error handling and seemed to happen at random times. Looks like you've solved your issue using VB6, but I'll post it here in case it can help anyone in the future. There are some cases where you don't close down an object you're using (like when you're handed an object in a VBA event), but most of the time the rule of thumb is "if you opened it, you close it". In your case, you must call CloseDown on each and every record you process in your For Each loop. Also, when dealing with COM objects from .Net, it's usually a good idea to call this on your COM object when you're done with it:
I've created a proc that I send all my top-level RE COM objects to when I'm done with them. It handles the CloseDown for you on Top Level objects (Record, Gift, etc). I should probably add a few other interfaces to look for too. Anyway, it looks like this:
Code:
PublicSub CloseREObject(ByVal o AsObject)
'Dispose of COM objects (works on any COM objects, especially ibbTobObjects)
If o IsNothingThenExitSub
Dim oTO As Blackbaud.PIA.RE7.BBREAPI.IBBTopObject = TryCast(o, Blackbaud.PIA.RE7.BBREAPI.IBBTopObject)
If oTO IsNotNothingThen
oTO.CloseDown()
oTO = Nothing
EndIf
Try
System.Runtime.InteropServices.Marshal.ReleaseComObject(o)
Catch
Finally
o = Nothing
EndTry
EndSub
A couple other things from above:
Nope, there is no need to call "Init" on the record object when you're getting it from a collection you init'ed. I believe that will "flash" your object to a brand spanking new object, which is why you saw it "clear the oRecord.Notepads.count to zero"
There is no need to set the record object = New Record before using it in the For Each loop
I recommend the For Each loop for your notepads. There shouldn't be an issue removing them while you're iterating through the loop, but it doesn't look like that would be an issue anyway since you're assuming there's only one of that notepad type per record (or at least I'm assuming that you're assuming, based on your "Exit For" in there).
Make sure you closedown your Records collection
VBA is ~great~ for this sort of stuff. Talk about low overhead!