Accessing an object over class - vb.net

When reading an old project of mine I found something suspicious where I don't really understand why this part is working:
Public Shared Sub getXMLforProject(QueryString As String)
Dim linkStart As String = "http://example.org"
Dim linkEnd As String = "&tempMax=2000"
Dim target As String = linkStart & QueryString & linkEnd
'replaces parts that need encoding,
'groups(1) is the sign e.g. <= and groups(2) is the text that needs encoding
'groups(0) is the text of the full match (sign and encoding text)
target = rx.Replace(target, Function(m As Match) encodeURLString(m.Groups(1).Value) + encodeURLString(m.Groups(2).Value))
GUI.WebBrowser.Navigate(target)
Return True
End Sub
the respective path that seams suspicious to me is the line
GUI.WebBrowser.Navigate(target)
There is a class called GUI that realises the user interface, but in the file context there is no objects named "GUI" available, so the access must be done by using the class. How is it possible for this to work? Is there an implicit mechanism that redirects the call from the GUI-class to the GUI-object?

You are using VB.NET, it emulates the behavior of the Form class from earlier Visual Basic editions where using the type name was a legal way to refer to an instance of the class. Kinda necessary to give programmers a fighting chance to convert their VB6 projects. Underlying plumbing is the My.Forms object.
So, 99.9% odds are that the GUI class derives from System.Windows.Forms.Form. Especially given that it has a WebBrowser member. The Form is the host window for the browser.

Related

Save a custom Class to a Access Table with VBA

I have a really weird one that I'm not quite clear if it's possible. I have an Access database that I've built that stores documents basically. And in it, you are able to produce these documents in Excel. As it stands today, I have it storing values I want to put into a cell as a table value, such as a string. But I'd like to possibly store an attributed string so that I can also store formatting.
I have an idea of how I would make a custom class that would basically be an attributed string, but then I still have the problem that it would need to be an object that could be stored in an Access table.
I was thinking to make an OLEObject field in the table, and save it there, but it gives me an error when I try saving my custom class in that field.
Run-time error '438':
Object doesn't support this property or method
I tried making an object variable and then setting my custom class to that, but still same error.
Dim attStr As New AttributedStringClass
attStr.Value = "Test Test"
Dim oleObj As Object
Set oleObj = attStr
Dim rst As Recordset: Set rst = CurrentDb.OpenRecordset("tblTest")
rst.AddNew
rst("attributeString") = oleObj
rst.Update
AttributedStringClass
Option Compare Database
Option Explicit
Dim zValue As String
Property Get Value() As String
Value = zValue
End Property
Property Let Value(dValue As String)
zValue = dValue
End Property
I kept it really simple to test if I could store the custom class, just in case it wasn't possible.
Is what I'm trying to do even possible? Or am I barking up the wrong tree?
Thanks in advance!
Unfortunately, what you looking for is object serialization. .net supports serialization, and thus you can convert a object into XML, or these days the much tighter and shorter format used is JSON.
You could however, make your own serializer. So, you would have to take the custom class you make, and call a routine (passing the class object) to spit out all the values as text. Perhaps the format could be comma delimited, or I suppose even JSON format (but we don't have a good JSON serlizer/de-serlizer like we do in .net).
You then save the text in a standard memo column. You could then read/pull that data, and call a routine to de-serialize the text back into the object.
But, since you do know the class, you can expose each property, and use a for/each loop. this trick is outline here:
https://www.mrexcel.com/forum/excel-questions/466141-use-custom-class-each-loop.html
However, what I would simply do is make your class, add all the "members", and then simply add a routine called serialize, and de serializer.
So:
dim clsMyClass as new clsTour
clsMyClass.HotelName = "Greenwood Inn"
.etc. etc. etc.
' now get all of the values as a string
dim strCsv as string
strCsv = clsMyClass.Serlize
' now, the comma delimited strCsv as all the values of the class as a string
rstData!Cdata = strCsv
rstData.update
Now, at this point,the memo field is saved (as noted, xml, json, or csv format is fine).
To pull + load (de-serialize) the class, we now go:
dim rstData as DAO.Recordset
' code to load up reocord set
set rstData = currentdb.OpenRecordSet("Select * from tblTours where id =2")
strCsv = rstData!CData
dim clsMyClass as new clsTour
clsMyclass.Serialize = strCsv
' at this point, your class is now loaded with all the correct values.
eg:
msgbox "Hotel name = " & clsMyClass.HotelName
So, in the .net world, the idea of serializing a class ito a string, passing to a web service, and then on that end, they de-serialize the object back into a class/object.
In .net, this generating is built into the frame work. So, when you call a SOAP or these days more common a REST service, then the data is sent to you as xml (or json). On your end, you now call the de-serialize method, and you have the object now ready for use in your code. So, this idea of converting a class into some kind of "string" or something that can be saved as text, or pass (or pulled) from a web site is rather common these days.
So, your idea and question is rather normal, especially if you coming from any of the modern systems and frameworks that support serialization.
As noted, if your class only has say 5-10 values to save, then a simple method to serialize and de-serialize all values to/from a string from the values the class holds is not hard at all. But for complex objects, then of course one would want a development platform that supports this automatic. In .net, you can pass any object to a serializer, and it will spit back the xml (or json) string. Now that string can be saved, sent to a web site, or some program. And to get the object, you de-serialize that string back to the object for use in your code.
Do keep in mind that this whole concept only works well for a well defined class, and if the class is not dynamic, then the concept works well.

Marshalling a .Net function that returns Double() to consume in VBA

Here is my function in .Net:
<Runtime.InteropServices.ComVisibleAttribute(True)>
Public Function Unhex(hex As String) As Double()
Dim GetArr As Double() = HexStringToDoubleArray(hex)
Return GetArr
End Function
Here is how I would like to use it in VBA:
Dim ret() As Double
ret = LinkToComLib.Unhex("EDC531...")
There are hundreds of examples of how to pass arrays into .Net (eg), but the only one I found showing the opposite is this MS page, and it doesn't show it being used on the VBA (or even COM) side. Perhaps I am using the wrong search terms. In any event:
Can I use the MarshalAs to export the Double() from .Net, or will I need to use Marshal.Copy or similar (as I suspect, as it is managed)?
If I do have to Copy, is the proper return type then IntPtr?
Am I correct in thinking that Dim ret() As Double is a pointer to a malloc'ed array or perhaps SAFEARRAY? Is that the proper type to use in VBA in this case?
Would creating the array with the proper size (it's always 492!) in VBA and then passing that to the function help in any way? Deallocing perhaps?
If anyone has a pointer to an example of this - a double (or int) array being passed out of .Net along with the corresponding VBA code, I can likely take it from there. But if someone has answers for the above, VB.Net or C# as they like, I'd appreciate it.
You need to decorate the return with <MarshalAs(UnmanagedType.SafeArray)> attribute.
VB.Net Example:
Imports System.Runtime.InteropServices
<ComClass(ArrayExample.ClassId, ArrayExample.InterfaceId)> _
Public Class ArrayExample
' These GUIDs provide the COM identity for this class and its COM interfaces.
Public Const ClassId As String = "e510d899-dad1-412b-94ea-6c726fe9f9da"
Public Const InterfaceId As String = "ef3498f0-22b4-4c2a-aeb1-22936c9757eb"
Public Function Unhex(hex As String) As <MarshalAs(UnmanagedType.SafeArray)> Double()
Dim GetArr As Double() = {2.0R, 5.0R}
Return GetArr
End Function
End Class
VBA Usage:
Sub t()
Dim c As ExampleComArrayReturn.ArrayExample
Set c = New ExampleComArrayReturn.ArrayExample
Dim arr() As Double
arr = c.Unhex("AABB")
End Sub
Edit: Forgot to mention that this uses the ComClassAttribute Class to have the compiler generate the interfaces for your class.
Edit 2 in response to follow-up question.
To debug your COM library project, go to the Debug tab of project properties. Select "Start External Program" and set it to run Excel. You can also specify the Workbook to open in the "Command line Arguments". Now when you click on the "Start" button, Excel will be launched and break points in your code will be triggered.
Edit 3:
To address the issue of targeting .Net 3.5, you can use a slightly less convenient method of attaching the debugger to the Excel process. If you are using VS2008, the method described above will work. New VS versions will need to attach to the process. There may be a way to specify this info in the vproj.user file, but I have not found the magic property type to allow direct launching using a specific framework version.
Depending on your VS version the "Attach To Process" item will either be under the Tools (VS2013) or the Debug (VS2017) menu or you can use the shortcut cntrl-alt-p.
Obviously start Excel and load your Workbook. Then in VS launch the Attach to Process dialog. Click the "Select" button and then click on the "Debug these type" radiobutton. Select the "Managed (v3.5, v3.0, v2.0) code" type and click the "OK" button. Then select the Excel process and click "Attach".

In VB.NET does MS require the fully qualified function name for the Right or Left string functions?

According to the Microsoft documentation, to determine the number of characters in str, use the Len function. If used in a Windows Form, or any other class that has a Right property, you must fully qualify the function with "Microsoft.VisualBasic.Strings.Right".
If I set "Imports Microsoft.VisualBasic" at the top of the form I still have to use the fully qualified name in my code. Why does MS require this?
Because, without the fully qualified name, if there are two methods with the same name, the compiler cannot choose one over the other. So you should take care of the problem giving the correct hint
To ease your typing you could add at the top of your code file this version of the Imports statement
Imports VB6 = Microsoft.VisualBasic
and then you could type
Dim stringLen = VB6.Len(yourStringVariable)
This is the MSDN introduction to Namespaces in VB.NET, in particular, in the first lines of the article is explained your problem Avoiding Namespaces Collisions
NET Framework namespaces address a problem sometimes called namespace
pollution, in which the developer of a class library is hampered by
the use of similar names in another library. These conflicts with
existing components are sometimes called name collisions.
For example, if you create a new class named ListBox, you can use it
inside your project without qualification. However, if you want to use
the .NET Framework ListBox class in the same project, you must use a
fully qualified reference to make the reference unique. If the
reference is not unique, Visual Basic produces an error stating that
the name is ambiguous.
And by the way, start to use the equivalent framework methods for Right, Left, and Len.
They are still available only to help the porting of old VB6 application, (and sometime they work differently). In new applications I suggest to use
string.Substring(start, len)
string.Length
A winform, Form (derived from Control), have properties named Right and Left.
Public Class Form1
Inherits Form
Public Sub Test()
Dim location_left As Integer = Me.Left
Dim location_right As Integer = Me.Right
'Or simply:
location_left = Left '<- (Referring to Me.Left, not Microsoft.VisualBasic.Strings.Left)
location_right = Right '<- (Referring to Me.Right, not Microsoft.VisualBasic.Strings.Right)
End Sub
End Class
Therefore you'll need the use the full qualify name.

How to select an image from resources via a string?

I'm coding a pokedex type deal as practice for my class.
Basically, I have a class titled "pokemon". One of the properties of the class is "ImgName" Which I want to use to display an image from the resources with the same name.
VB doesn't allow me to call the ImgName as a string and then use 'My.Resources.ImgName'
How can i do this, or what are some alternative options to it? I want it to be determined by a property in the pokemon object, and i don't want to have to hard code in an if-elseif statement for every single pokemon.
One way is you can have a resource file added to your project. Then drop the resource into it. You will be able to address it like this:
My.Resources.Resource1.ImgName
Resource1 is your resource file name, and ImgName is the resource name here. But you need to do hard code for every call. However, you get full intellisense support with type checking.
If you don't want hard code, here is a stripped down version of my production code:
Imports System.Reflection
Imports System.Xml.Linq
Public Class EmbeddedResourceManager
Private Class EmbeddedResourceManagerCore
Private Shared _executingAssembly As Assembly
Private Shared _resourcePrefix As String
Shared Sub New()
_executingAssembly = Assembly.GetExecutingAssembly
_resourcePrefix = _executingAssembly.GetName.Name & "."
End Sub
Public Shared Function GetStream(resourceRelName As String) As IO.Stream
Return _executingAssembly.GetManifestResourceStream(_resourcePrefix & resourceRelName)
End Function
End Class
Public Shared Function GetImage(ByVal resourceName As String) As Bitmap
Return New Bitmap(EmbeddedResourceManagerCore.GetStream(resourceName))
End Function
End Class
So whenever you need, just call EmbeddedResourceManager.GetImage and pass the resource name, as it appears in your project (your image file needs to be attached to a project). You need to have Build Action for an image in question to be set to Embedded Resource.
This piles up all your resource into an executable, which has both benefits and drawbacks, depending on the situation. Still, it should work for your needs, since I am assuming number of different pokemons is limited and does not change throughout the game (i.e. downloaded from a 3rd party server in real time etc.).
BackgroundImage = My.Resources.ResourceManager.GetObject(aString)
10 time easier than previous answer imho

is there a better way of retrieving my settings?

I'm not an IT professional so apologies if I've missed something obvious.
When writing a program I add a class SettingsIni that reads a text file of keys and values. I find this method really flexible as settings can be added or changed without altering any code, regardless of what application I have attached it to.
Here's the main code.
Public Shared Sub Load()
Using settingsReader As StreamReader = New StreamReader(System.AppDomain.CurrentDomain.BaseDirectory & "settings.ini")
Do While settingsReader.Peek > -1
Dim line As String = settingsReader.ReadLine
Dim keysAndValues() As String = line.Split("="c)
settingsTable.Add(keysAndValues(0).Trim, keysAndValues(1).Trim)
Loop
End Using
End Sub
Public Shared Function GetValue(ByVal key As String)
Dim value As String = settingsTable(key)
Return value
End Function
This allows you to use a setting within your code by calling the SettingsIni.GetValue method.
For example:
watcher = New FileSystemWatcher(SettingsIni.GetValue("inputDir"), "*" & SettingsIni.GetValue("extn")).
I find this makes my code esay to read.
My problem is the values in this case, inputDir and extn, are typed freehand and not checked by intellisense. I'm always worried that I may make a typo in an infrequently used branch of an application and miss it during testing.
Is there a best practice method for retrieving settings? or a way around these unchecked freehand typed values?
A best practice for your code example would be to use Constants for the possible settings.
Class Settings
Const inputDir as String = "inputDir"
Const extn as String = "extn"
End Class
watcher = New FileSystemWatcher(SettingsIni.GetValue(Settings.inputDir), "*" & SettingsIni.GetValue(Settings.extn))
I assume you are using VB.NET?
If so, there is the handy "Settings"-menu under "my project". It offers a way to store the settings for your program and retrieve them via "my.settings.YOURKEY". The advantage is, that type securtiy is enforced on this level.
Additionally, you can also store "resources" almost the same way - but resources are better suited for strings / pictures etc. But they are expecially good if you want to translate your program.
As for your current problem:
Store the path in the settings, this way you do not need to change alll your code immidiately but you can use your system and never misspell anything.
If it's a number you could do these 3 things:
Check if is numeric - using IsNumeric function
Check if it is whole number - using Int function, like: if Int(number)=number
Check for the valid range, like: if number>=lowerbound and number<=upperbound
It totally depends on you. You are the one to check almost all the things inside quotes, not the intellisense.
But you still use Try-Catch block:
Try
Dim value As String = settingsTable(key)
Return value
Catch ex As Exception
MsgBox(ex.ToString)
Return ""
End Try
So you will get an message box if you are trying to access a non-existing setting that you may have mistyped.