I've run into what seems to be an odd quirk with VB6. I'm passing a checkbox to a method with the signature MyMethod(ByRef object) and calling it as myClass.MyMethod chkMyCheckbox. VB6, however, refuses to pass the checkbox itself, but instead passes a reference to 1 to my method. I'm guessing that this has something to do with how VB6 specifies an object's default properties. How can I get the entire object to be passed, not just .Value? I cannot turn off default properties, as a large amount of the legacy code relies heavily on them.
As you can see, ChkCalFault is a Checkbox and not an integer, but is being evaluated and passed as its integer value. (Which is in this screenshot 0.)
I can't see what your trying to do with the code so I apologize if this is off-base, but would modifying your methods signature to
MyMethod(ByRef MyCheckBox as CheckBox)
work for you? If not, the undocumented VarPtr will get the address of your checkbox object, but I don't know if that points you in the right direction either.
Related
The documentation for -[NSObject scriptingValueForSpecifier:] says:
You can override this method to customize the evaluation of object specifiers…
I want to do this to make AppleScript lookups in my app more efficient. There are certain types of whose tests where I could do a hash lookup instead of having AppleScript ask for every single object and then query their properties one-by-one. The problem is that if I receive a NSWhoseSpecifier, I can get its test, which is a NSSpecifierTest, but there doesn’t seem to be a way to look inside the NSSpecifierTest to figure out the property being tested. Everything but the initializers and the -isTrue method are private.
Let's make a distinction. If you merely want to customize the values returned by a whose clause — in the sense of fine-tuning results — then you would override scriptingValueForSpecifier for the object in question. For example, say you have an object called 'box' that has a property called 'color,' and you want a statement like every box whose color is blue to also return boxes whose color is 'aqua,' 'teal,' or 'navy' (which are all shades of blue), then you would override scriptingValueForSpecifier to implement that.
However, if your goal is to make a more efficient form of whose test, you're probably going to have to implement appropriate methods from the NSScriptingComparisonMethods protocol. These are the routines that AppleScript uses to evaluate scripting object comparisons through NSSpecifierTest (which is what NSWhoseSpecifier) relies on). Again, you'd override these methods on your object(s) to provide different methods for doing evaluations.
As a last resort, you might try subclassing NSWhoseSpecifier or NSSpecifierTest, with the understanding that these are opaque classes for which Apple discourages subclassing. I'm not entirely certain how you would implement those subclasses; you'd probably have to register your subclass with NSScriptSuiteRegistry commands.
It looks like it will be possible to override -scriptingValueForSpecifier: and then ask the NSScriptObjectSpecifier for its descriptor. This produces an NSAppleEventDescriptor that has the needed information, but it has to be interpreted manually.
Of the two choices I have to access the value of a control which is the most efficient?
getComponent("ControlName").getValue();
or
dataSource.getItemValue("FieldName");
I find that on occasion the getComponent does not seem to return the current value, but accessing the dataSource seems to be more reliable. So does it make much difference from a performance perspective which one is used?
The dataSource.getValue seems to work everywhere that I have tried it. However, when working with rowData I still seem to need to do a rowData.getColumnValue("Something"). rowData.getValue("Something") fails.
Neither. The fastest syntax is dataSource.getValue ("FieldName"). The getItemValue method is only reliable on the document data source, whereas the getValue method is not only also available on view entries accessed via a view data source (although in that context you would pass it the programmatic name of a view column, which is not necessarily the same name as a field), but will also be available on any custom data sources that you develop or install (e.g. third-party extension libraries). Furthermore, it does automatic type conversion that you'd have to do yourself if you used getItemValue instead.
Even on very simple pages, dataSource.getValue ("FieldName") is 5 times as fast as getComponent ("id").getValue (), because, as Fredrik mentions, first it has to find the component, and then ask it what the value is... which, behind the scenes, just asks the data source anyway. So it will always be faster to just ask the data source yourself.
NOTE: the corresponding write method is dataSource.setValue ("FieldName", "NewValue"), not dataSource.replaceItemValue ("FieldName", "NewValue"). Both will work, but setValue also does the same type conversion that getValue does, so you can pass it data that doesn't strictly conform to the old Domino Java API and it usually just figures out what the value needs to be converted to in order to be "safe" for Domino to store.
I would say that the most efficient way is to get the value directly from the datasource.
Because if you use getComponent("ControlName").getValue(); you will do a get on the component first and then a getValue from that. So do a single get from the datasource is more efficient if you ask me.
I have been trying to use BLToolkit to activate an Oracle stored procedure which takes a User Defined Type as an argument as an output parameter and changes it.
I have managed to do this on a primitive type, and and also by manually calling SetSpCommamd however I would like to use the abstract class generation method but can't seem to get it to work.
I'm pretty sure the code I wrote is correct (works for the primitive). When debugging I found the SetSpCommamd called by the generated code gets wierd parameters instead of the ones I provided as opposed to when I call the method manually (the it gets the exact parameters I'd like). I wish I could see the code generated by the reflection emit to see what's wrong there.
Can anyone please help me figure out why this is not working?
Found the problem (Potentially a bug in BLToolkit).
BLToolkit does not pass the UDT Class as is to the procedure (instead it tries to flatten it or something and pass the insides of the object). I Changed the object to a Struct instead of a Class and that fixed it.
Later on I also changed it back to class and made a patch in the 'IsScaler()' method in BLToolkits code.
I will report this as a Bug I hope they fix it.
What's the point of binding the value of a NSProgressIndicator to your controller? It never seems to ask the controller for the value, except on startup. The only way to move the NSProgressIndicator seems to be by sending it #increaseBy:, which bypasses my binding. So, why would I bind?!
If your UI's bound value not updating, that means you either bungled the binding or your controller code is not modifying the bound value in a key-value-observing–compliant way. The most common problem is doing fooIvar = val rather than [self setFooIvar:val] or self.fooIvar = val.
Apple's answer to your problem:
[What to do if] Changing the value of a model property programmatically is not reflected in the user interface
If changes made to a model value programmatically are not being reflected in the user interface, this typically indicates that the model object is not key-value-observing compliant for the property, or that you are modifying the value in a manner that is bypassing key-value observing. You should ensure that:
The model class has automatic key-value observing enabled or implements manual key-value observing for the property.
That you are changing the value using an accessor method, or using a key-value-coding compliant method. Changing the value of an instance variable directly does not provide key-value observing change notifications.
If your model property is a collection, that you're modifying the content in a key-value-observing compliant manner. See “My collection controller isn’t displaying the current data” for more information.
For that answer and answers other common problems, see "Troubleshooting Cocoa Bindings."
You should also look at the examples provided by mmalc. They are a valuable resource.
In one of my VB6 forms, I create several other Form objects and store them in member variables.
Private m_frm1 as MyForm
Private m_frm2 as MyForm
// Later...
Set m_frm1 = New MyForm
Set m_frm2 = New MyForm
I notice that I'm leaking memory whenever this (parent) form is created and destroyed. Is it necessary for me to assign these member variables to Nothing in Form_Unload()?
In general, when is that required?
SOLVED: This particular memory leak was fixed when I did an Unload on the forms in question, not when I set the form to Nothing. I managed to remove a few other memory leaks by explicitly setting some instances of Class Modules to Nothing, as well.
Actually, VB6 implements RAII just like C++ meaning that locally declared references automatically get set to Nothing at the end of a block. Similarly, it should automatically reset member class variables after executing Class_Terminate. However, there have been several reports that this is not done reliably. I don't remember any rigorous test but it has always been best practice to reset member variables manually.
#Matt Dillard - Did setting these to nothing fix your memory leak?
VB6 doesn't have a formal garbage collector, more along the lines of what #Konrad Rudolph said.
Actually calling unload on your forms seems to me to be the best way to ensure that the main form is cleaned up and that each subform cleans up their actions.
I tested this with a blank project and two blank forms.
Private Sub Form_Load()
Dim frm As Form2
Set frm = New Form2
frm.Show
Set frm = Nothing
End Sub
After running both forms are left visible. setting frm to nothing did well... nothing.
After settign frm to nothing, the only handle open to this form is via the reference.
Unload Forms(1)
Am I seeing the problem correctly?
Josh
Objects in VB have reference counting. This means that an object keeps a count of how many other object variables hold a reference to it. When there are no references to the object, the object is garbage collected (eventually). This process is part of the COM specification.
Usually, when a locally instantiated object goes out of scope (i.e. exits the sub), its reference count goes down by one, in other words the variable referencing the object is destroyed. So in most instances you won't need to explicitly set an object equal to Nothing on exiting a Sub.
In all other instances you must explicitly set an object variable to Nothing, in order to decrease its reference count (by one). Setting an object variable to Nothing, will not necessarily destroy the object, you must set ALL references to Nothing. This problem can become particularly acute with recursive data structures.
Another gotcha, is when using the New keyword in an object variable declaration. An object is only created on first use, not at the point where the New keyword is used. Using the New keyword in the declaration will re-create the object on first use every time its reference count goes to zero. So setting an object to Nothing may destroy it, but the object will automatically be recreated if referenced again. Ideally you should not declare using the New keyword, but by using the New operator which doesn't have this resurrection behaviour.
#Martin
VB6 had a "With/End With" statement that worked "like" the Using() statement in C#.NET. And of course, the less global things you have, the better for you.
With/End With does not working like the Using statement, it doesn't "Dispose" at the end of the statement.
With/End With works in VB 6 just like it does in VB.Net, it is basically a way to shortcut object properties/methods call. e.g.
With aCustomer
.FirstName = "John"
.LastName = "Smith"
End With
Strictly speaking never, but it gives the garbage collector a strong hint to clean things up.
As a rule: do it every time you're done with an object that you've created.
Setting a VB6 reference to Nothing, decreases the refecences count that VB has for that object. If and only if the count is zero, then the object will be destroyed.
Don't think that just because you set to Nothing it will be "garbage collected" like in .NET
VB6 uses a reference counter.
You are encouraged to set to "Nothing" instanciated objects that make referece to C/C++ code and stuff like that. It's been a long time since I touched VB6, but I remember setting files and resources to nothing.
In either case it won't hurt (if it was Nothing already), but that doesn't mean that the object will be destroyed.
VB6 had a "With/End With" statement that worked "like" the Using() statement in C#.NET. And of course, the less global things you have, the better for you.
Remember that, in either case, sometimes creating a large object is more expensive than keeping a reference alive and reusing it.
I had a problem similar to this a while back. I seem to think it would also prevent the app from closing, but it may be applicable here.
I pulled up the old code and it looks something like:
Dim y As Long
For y = 0 To Forms.Count -1
Unload Forms(x)
Next
It may be safer to Unload the m_frm1. and not just set it to nothing.
One important point that hasn't yet been mentioned here is that setting an object reference to Nothing will cause the object's destructor to run (Class_Terminate if the class was written in VB) if there are no other references to the object (reference count is zero).
In some cases, especially when using a RAII pattern, the termination code can execute code that can raise an error. I believe this is the case with some of the ADODB classes. Another example is a class that encapsulates file i/o - the code in Class_Terminate might attempt to flush and close the file if it's still open, which can raise an error.
So it's important to be aware that setting an object reference to Nothing can raise an error, and deal with it accordingly (exactly how will depend on your application - for example you might ignore such errors by inserting "On Error Resume Next" just before "Set ... = Nothing").