I'm writing a simple vb6 button which tests the access of the registry values.
I have the following:
Private Function registry_read(key_path, key_name) as variant
Dim registry as object
set registry = CreateObject("WScript.shell")
registry_read = registry.regread(key_path & key_name)
End function
Private Sub Command1_Click()
MsgBox registry_read("HKEY_LOCAL_MACHINE\SOFTWARE\PROCESS\frmMain_Values\", "Version")
end Sub
I have Project Menu -> References
and select Microsoft WMI Scripting V1.1 Library selected
and Windows Script Host Object Model referenced
however my msgbox is still coming up blank. I did check the registry path and it is correct. any ideas?
thanks in advance.
You need to comment out the line 'on error resume next' while you are developing. If an error is occurring, you will not be able to see the details. It could be not found or access denied etc.
Also there are two ways to reference an object. Early binding ie Dim rs as new adobdb.recordset and late binding set rs = CreateObject("Adodb.recordset"). The first method (early binding) forces you to declare a reference and the second (late) does not. There are advantages and disadvantages to both (ie early binding is faster, gives intellisense, easier debugging, etc) http://word.mvps.org/faqs/interdev/earlyvslatebinding.htm
#bugtussle While your statements are correct, wqw's statements are also. Whether you use the New keyword or CreateObject actually hasn't anything to do with whether an object is early or late bound. What does matter is whether you declare the object variable with a registered type or not. I believe that you actually explain this correctly in your article.
I'd like to mention also that your article is well-written and has good information, but IMHO contains also a couple of minor inaccuracies. What you call "Dual Interface" binding in your article (and explain well) is generally referred to as "vTable" or "very early" binding. VB6 supports vTable binding where possible.
Now, as you have said, the sole requirement to be a COM class is that the class must implement iUnknown. A "dual interface" simply means a COM class that implements both iUnknown and iDispatch: a COM class that supports late binding must implement the latter. VB doesn't directly support COM objects that don't implement iDispatch (having some COM classes that don't support late binding and some that do would pretty clearly be problematical in VB); in other words VB only supports COM classes that implement a dual interface. (However, there are tricks using SendMessage's GETOLEINTERFACE message that bypass the requirement.)
Also, it isn't quite that iUnknown is bypassed altogether, it is that iUnknown.QueryInterface() is bypassed, instead going directly to the virtual table. iUnknown.AddRef() is still called, of course.
Regarding New vs. CreateObject: VB has an optimization strategy for classes defined within a project that are instantiated within that project using the New keyword. However, there are also important differences between the two if you are using a class outside of a project context; this page http://msdn.microsoft.com/en-us/library/Aa241758 does a good job of summarizing them.
I'm curious as well to know what error the OP got. :)
Related
I am using a program which makes it possible to customize dialogues via vbscript. Now, the form has an attribute which seems to be a vbDataObject (VarType returns 13).
How can I read the content and create a new vbDataObject to assign it to the attribute?
Don't know if this is exactly the answer, but it seems that vbDataObject's are indeed COM objects, but they just do not expose an IDispatch interface (so they're not true automation objects). As such, VBScript just isn't capable of accessing their content, since it needs an IDispatch interface for it. It just has an IUnknown interface which it doesn't know what to do with.
Passing them around should be possible, though, so if another true automation COM object can manipulate them for you (maybe there is such an object in the app designed to do this?), you could use that to make an object with the correct settings and pass that to the form.
Another option is to contact the authors of the app and ask them if they're willing to give this one object an IDispatch interface as well. Since they presumably went the length to put IDispatch interfaces on most of their objects, this omission might just be an oversight on their part.
I guess my question is relatively easy for those of you who spent time dealing with Win32 API.
So my question is:
After initializing a WNDCLASSEX instance we need to "register" it using the "RegisterClassEx" function, why? Why do we do that? What's the meaning of this registration and in what cases I need to register things?
The ATOM returned by RegisterClassEx uniquely identifies your "window class" which can then be referred to in other windows APIs. [MSDN]
Effectively it is a hash so as to reduce the amount of data processed each time a window is created or looked for. It does also mean that multiple windows with same features can be easily created and identified.
I was addressing the practical reasons above. Hans Passant's answer correctly explains this is the OO class concept provided for C. Further MSDN example.
The word Class in the function name is significant. When you write code in an object oriented language, like C++, Delphi, Java or C# etcetera, then you use the class keyword to create objects that have behavior. But the winapi was designed to be used from C, a language that doesn't have such functionality. The RegisterClassEx() function is an emulation of that, it lets you create a window that "derives" its behavior from a named class, behavior that you can override. With every window that you create using that class name behaving identically.
The WNDCLASSEX structure you pass gives a window its default behavior. The most significant members of this structure are:
lpszClassName. That's the equivalent of the C++ class name. You can later call CreateWindowEx() and pass that name to get a window that behaves a certain way. Windows itself calls RegisterClassEx() to register several of its built-in window classes that you then can readily re-use in your own code. "EDIT", "BUTTON" and "LISTBOX" are good examples of that.
lpfnWndProc. This is what gives a window class its specific default behavior. The address of its window procedure that implement message handlers for specific messages. You can further customize the default behavior, in other words "derive" your own class from the base class, by specifying another window procedure in the CreateWindowEx() call. Such a window procedure must always call DefWindowProc(), the equivalent of calling the base class method. Or in other words, a window has one virtual method.
hIcon, etcetera. These are the equivalent of properties of the base class, they set default values that affect the default message handlers. Helping you to keep your window procedure simple. It is for example rarely necessary to write a message handler for WM_ERASEBKGND, the hbrBackground member sets the default background for a window.
Windows requires you to call RegisterClassEx() even if you don't plan on re-using a window. Which is by far the most common usage of the function in your own code. You don't start to really take advantage of it until you write a library that implements controls, windows that other code can use. Like "EDIT".
I come from a Java and .NET background.
In VB6 it appears that you do not always have to create an instance of a class when using it. For example, when using the Printer class you can simply say Printer.print instead of having to create an instance first i.e. Dim printer As New Printer and then running printer.Print. I know that Printer is a system object in VB6, but why don't you have to create an instance?
Visual Basic traditionally had a large number of pre-defined identifiers that are directly recognized by the compiler. The Printer object is one of those. Under the hood, this is implemented with the [appobject] attribute but that's carefully hidden in the language. The runtime creates an instance of the COM coclass automatically, much like the As New syntax. The DAO DBEngine object would be an example of one that isn't predefined in the language parser.
This is over and done with in VB.NET, a truly object oriented language with a large class library, much like Java. There is no Printer object anymore, you're supposed to use the PrintDocument class. The Printer object is still supported for legacy code, available in the Microsoft.VisualBasic.PowerPacks.Printing.Compatibility.VB6 namespace. It however requires the New keyword to create an instance of it.
Be careful investing a lot of time and energy in VB6, it is in all respects a badly outdated language.
VB6 isn't an object oriented language in the same way as you would expect if you are used to newer languages. VB6 will do implicit instantiation and you can treat certain things as if it were static. For example, you can declare a variable of a form, but you don't have to. You can directly call a form and manipulate it without declaring it. In the case of the printer, it can't be explicitly declared and instantiated, but VB6 already has one available.
With a view to avoiding the construction of further barriers to migration whilst
enhancing an existing vb6 program.
Is there a way to achieve the same functionality as control arrays in vb6 without using them?
In .NET you have a tag property. You can also have the same delegate handle events raised by multiple controls. Set the Tag property of the new control to the Index.
Private Sub MyButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click,Button2.Click
Dim Btn As Button = CType(sender, Button)
Dim Index As Integer = CType(Btn.Tag, Integer)
' Do whatever you were doing in VB6 with the Index property
End Sub
You also should look at the classes that inherit from BaseControlArray in the VB6.Compatibility which automates some of the work. I find the use of Tag to be less error prone in the conversion process than relying on the control name. However don't thank this as an absolute. You will have to decide whether the control name approach is best or the tag as index approach.
In either case you can easily setup .NET to funnel the events raised by multiple controls into one handler.
Well, you could always create your own array of controls in code :) Perhaps a better container, though, is a Collection or Dictionary object. Depending on what you want to do, you could perhaps create a wrapper class for the control with a custom collection class... but creating an object model is far nicer using generics in .NET so probably best to keep it simple in VB6 for now.
VBA Userforms lack support for control arrays, so why not Google for suggestions on how to mimic control arrays with VBA, Userforms, Excel, etc.
BTW have you tried migrating control arrays from VB6 to VB.NET? Just a guess but considering they are commonly used in VB I imagine they are handled quite well.
I did a bit of reading and experimentation over the last couple of days and it seems that there is no other way in vb6 to be able to do the things control arrays do.
If you already know the number of controls you'll be creating at runtime, at design time, then you can declare Private control object variables 'with events' and instance these at dynamically at runtime. If you need to create any more then you can do so but these won't have any code to fire in response to events.
This as far as I can see is the nub of the problem. there is no way to dynamically associate code with an event of a dynamically created control in vb6.
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").