HOW TO Pass VB .NET Arrays Hashes to VBA via COM? - vb.net

I am currently trying to pass an array of objects as a result to VBA but Excel is complaining the following:
'Function or interface marked as restricted, or the function uses an automation type not supported in Visual Basic.'
I have looked around on the internet and have seen similar issues but not to one I am trying to do.
My function is declared as follows:
<ComClass(Class1.ClassId, Class1.InterfaceId, Class1.EventsId)> _
Public Class Class1
#Region "COM GUIDs"
...
#End Region
Public Sub New()
MyBase.New()
End Sub
Private Const AT_HOME As Boolean = True
Public Function hello() As System.String()
Dim a(10) As String
a(0) = "hello"
a(1) = "Goodbye"
Return a
End Function
Public Function CallArray(ByVal serviceName As String, _
ByVal effectiveDate As String,
ByVal serviceParams() As System.Object,
ByRef ArrayRes() As System.Object) As System.Boolean
VBA is complaining about the System.Objects my question is how the heck are you supposed to pass and return arrays from .NET to VBA. Also the data that will be in the ArrayRes() is mixed type, its not just strings etc, its an array of arrays that can contain strings, integers doubles etc.
I chose VB .NET because I assumed it would be possible to map to VBA easier.
One last thing, if System.Object and Arrays in VB .NET is not the way, ie maybe theres another way via collections or lists, then Im open to that. I just need to get this data into VBA. Also I could move over to C# if needs be.
Thanks for any help or recommendations

Fixed it was the data coming in wasnt in the format needed so I created a function to format and process the data

Related

Set a Property Value From Database of Properties

My database has the formname, control, and control property type value stored.
I would like to have a line of code like this.
Forms(i%).Controls(ControlName$)).controlpropertytype$ = NewValue
I am currently using a select case structure to handle the various property types. It would be much simpler to have a single statement take care of it.
Using a helper function, you can achieve this with one line of code. Here's an example of setting a TextBox on Form1 to the value 'aaa':
Option Explicit
Private Sub Test()
CallByName FindForm("Form1").Controls("Text1"), "Text", VbLet, "aaa"
End Sub
Public Function FindForm(ByVal Name As String) As Form
Dim f As Form
For Each f In Forms
If UCase(f.Name) = UCase(Name) Then
Set FindForm = f
Exit Function
End If
Next
End Function
While this is an interesting exercise, I would not recommend this approach. It assumes the form and the control can both be found, but if they can't be found this one-liner will crash your app.
Here's documentation for CallByName.

Custom Function Disable in Excel

I have created a custom function that I am using excplicitly in another module in VBA. Function looks something like this:
Function Coverts(ByVal inputString As String) As String
'Coverts code here
End Function
It works perfectly fine in both VBA and Excel UI. However, I do not want it to work or appear in Excel UI as I only want to use it in VBA. Is there a way to do this?
Thanks
Put
Option Private Module
at the top of the module containing your function.
From MSDN:
When a module contains Option Private Module, the public parts, for example, variables, objects, and user-defined types declared at module level, are still available within the project containing the module, but they are not available to other applications or projects.
you can add the keywords Public or Private to your functions, subs or global variable to have that specified.
so if you want to only want this function to be accessible by your code and not in excel sheets you can add private:
Private Function Coverts(ByVal inputString As String) As String
'Coverts code here
End Function
You can make the function inoperable if called from the worksheet by identifying the Application.Caller. If called as a function from the XL UI, this will be Range (i.e. the cell the function is within).
Function Coverts(ByVal inputString As String) As String
If TypeName(Application.Caller) = "Range" then
Coverts = cverr(xlerrna)
exit function
end if
'Coverts code here
End Function

Emit Reflection (vb.net) Dynamically Reflect dropbox results in property grid

trying to add a dropdown into a property grid
Im using VS2010 VB.net with reflection
For my full solution - Download it here
https://www.nyvault.com/files/reflection/xml_propgrid_reflect_sk.zip
Password is
1
The point of this project is to populate a propertyGrid
I dont want to create a custom grid, i just want to populate the already made out of the box generic microsoft thing.
basically, I get the class to be created with reflection. using data from XML
Some of the fields use a custom type (this will be used for the dropdown)
This is what happens when I run the solution:
So i make the class, and it looks fine
It creates everything and sets it up. then when I make an instance of this class in my MAIN()
in calls a default constructor [new()] for the type(which is hardcoded dropdown items) instead of the custom constructor I wanted [new(byval test as integer)]
basically here are the class constructors for the custom type (located in customlist.vb)
Public Sub New()
' Gather all the localized strings currently loaded
' Gather all the strintTables from the current project.
For i As Integer = 0 To 4
myStringCollection.Add(New MyString(100 + i, "Test " & i))
Next
End Sub
Public Sub New(ByVal val As Integer)
For i As Integer = 0 To val
myStringCollection.Add(New MyString(100 + i, "Testy " & i))
Next
End Sub
it calls the Public Sub New()
but i want to call the Public Sub New(ByVal val As Integer)
please help, this has been making me rip my hair out for two months.

Restrict type in a Collection inside a class module

I have a collection inside a class module. I'd like to restrict the object type that is "addable" to this collection, i.e. collection should only ever accept objects of one given type and nothing else.
Is there any way to enforce the type of objects added to a collection?
From what I can tell, there is no built-in way to do this. Is the solution then to make this collection private, and build wrapper functions for the methods usually accessible for Collections, i.e. Add, Remove, Item, and Count?
I kinda hate having to write 3 wrapper functions that add no functionality, just to be able to add some type enforcement to the Add method. But if that's the only way, then that's the only way.
There is no way to avoid wrapper functions. That's just inherent in the "specialization through containment/delegation" model that VBA uses.
You can build a "custom collection class", though. You can even make it iterable with For...Each, but that requires leaving the VBA IDE and editing source files directly.
First, see the "Creating Your Own Collection Classes" section of the old Visual Basic 6.0 Programmer's Guide:
http://msdn.microsoft.com/en-us/library/aa262340(v=VS.60).aspx
There is also an answer here on stackoverflow that describes the same thing:
vb6 equivalent to list<someclass>
However, those are written for VB6, not VBA. In VBA you can't do the "procedure attributes" part in the IDE. You have to export the class module as text and add it in with a text editor. Dick Kusleika's website Daily Dose of Excel (Dick is a regular stackoverflow contributer as you probably know) has a post from Rob van Gelder showing how to do this:
http://www.dailydoseofexcel.com/archives/2010/07/04/custom-collection-class/
In your case, going to all that trouble - each "custom collection" class needs its own module - might not be worth it. (If you only have one use for this and it is buried in another class, you might find that you don't want to expose all of the functionality of Collection anyway.)
This is what I did. I liked Rob van Gelder's example, as pointed to by #jtolle, but why should I be content with making a "custom collection class" that will only accept one specific object type (e.g. People), forever? As #jtolle points out, this is super annoying.
Instead, I generalized the idea and made a new class called UniformCollection that can contain any data type -- as long as all items are of the same type in any given instance of UniformCollection.
I added a private Variant that is a placeholder for the data type that a given instance of UniformCollection can contain.
Private mvarPrototype As Variant
After making an instance of UniformCollection and before using it, it must be initialized by specifying which data type it will contain.
Public Sub Initialize(Prototype As Variant)
If VarType(Prototype) = vbEmpty Or VarType(Prototype) = vbNull Then
Err.Raise Number:=ERR__CANT_INITIALIZE, _
Source:=TypeName(Me), _
Description:=ErrorDescription(ERR__CANT_INITIALIZE) & _
TypeName(Prototype)
End If
' Clear anything already in collection.
Set mUniformCollection = New Collection
If VarType(Prototype) = vbObject Or VarType(Prototype) = vbDataObject Then
' It's an object. Need Set.
Set mvarPrototype = Prototype
Else
' It's not an object.
mvarPrototype = Prototype
End If
' Collection will now accept only items of same type as Prototype.
End Sub
The Add method will then only accept new items that are of the same type as Prototype (be it an object or a primitive variable... haven't tested with UDTs yet).
Public Sub Add(NewItem As Variant)
If VarType(mvarPrototype) = vbEmpty Then
Err.Raise Number:=ERR__NOT_INITIALIZED, _
Source:=TypeName(Me), _
Description:=ErrorDescription(ERR__NOT_INITIALIZED)
ElseIf Not TypeName(NewItem) = TypeName(mvarPrototype) Then
Err.Raise Number:=ERR__INVALID_TYPE, _
Source:=TypeName(Me), _
Description:=ErrorDescription(ERR__INVALID_TYPE) & _
TypeName(mvarPrototype) & "."
Else
' Object is of correct type. Accept it.
' Do nothing.
End If
mUniformCollection.Add NewItem
End Sub
The rest is pretty much the same as in the example (plus some error handling). Too bad RvG didn't go the whole way! Even more too bad that Microsoft didn't include this kind of thing as a built-in feature...
I did almost the same code of Jean-François Corbett, but I adapted because for some reason wasn't working.
Option Explicit
Public pParametro As String
Private pColecao As New Collection
Public Sub Inicializar(ByVal parametro As String)
pParametro = parametro
End Sub
Public Sub Add(NewItem As Object)
If TypeName(NewItem) <> pParametro Then
MsgBox "Classe do objeto não é compatível à coleção"
Else
pColecao.Add NewItem
End If
End Sub
Public Property Get Count() As Long
Count = pColecao.Count
End Property
Public Property Get Item(NameOrNumber As Variant) As Variant
Set Item = pColecao(NameOrNumber)
End Property
Sub Remove(NameOrNumber As Variant)
pColecao.Remove NameOrNumber
End Sub
Then, when i want to create an instance from CCollection I make like the code bellow:
Set pFornecedores = New CCollection
pFornecedores.Inicializar ("CEmpresa")
Where CEmpresa is the class type from the object I want
Yes. The solution is to make your collection private and then make public wrapper functions to add, remove, getitem and count etc.
It may seem like hassle to write the additional code but it is a more robust solution to encapsulate the collection like this.

Dynamic properties for classes in visual basic

I am a vb.net newbie, so please bear with me. Is it possible to create properties (or attributes) for a class in visual basic (I am using Visual Basic 2005) ? All web searches for metaprogramming led me nowhere. Here is an example to clarify what I mean.
public class GenericProps
public sub new()
' ???
end sub
public sub addProp(byval propname as string)
' ???
end sub
end class
sub main()
dim gp as GenericProps = New GenericProps()
gp.addProp("foo")
gp.foo = "Bar" ' we can assume the type of the property as string for now
console.writeln("New property = " & gp.foo)
end sub
So is it possible to define the function addProp ?
Thanks!
Amit
It's not possible to modify a class at runtime with new properties1. VB.Net is a static language in the sense that it cannot modify it's defined classes at runtime. You can simulate what you're looking for though with a property bag.
Class Foo
Private _map as New Dictionary(Of String, Object)
Public Sub AddProperty(name as String, value as Object)
_map(name) = value
End Sub
Public Function GetProperty(name as String) as Object
return _map(name)
End Function
End Class
It doesn't allow direct access in the form of myFoo.Bar but you can call myFoo.GetProperty("Bar").
1 I believe it may be possible with the profiling APIs but it's likely not what you're looking for.
Came across this wondering the same thing for Visual Basic 2008.
The property bag will do me for now until I can migrate to Visual Basic 2010:
http://blogs.msdn.com/vbteam/archive/2010/01/20/fun-with-dynamic-objects-doug-rothaus.aspx
No - that's not possible. You'd need a Ruby like "method_missing" to handle the unknown .Foo call. I believe C# 4 promises to offer something along these lines.