ActiveX component can't create object when using .net class in vba - vb.net

I am trying to use a custom class exported as a .tlb in vba. I have done the regasm stuff but I keep getting this error when I try to call a subroutine within the class:
Run-time error '429': ActiveX component can't create object
I've referenced the class in vba, I've built the class for 32bit and 64bit CPUs and nothing worked. Anyways, vba code:
Sub test()
Dim test As New Mail.Class1
test.test
End Sub
And the vb.net code:
Imports System.Runtime.InteropServices
Public Class Class1
Public Sub test()
MsgBox("hello")
End Sub
End Class

That class won't be exposed to COM. Simplest way to do this is to Add New Item and select COM Class. This generates a Class skeleton that looks like this:
<ComClass(ComClass1.ClassId, ComClass1.InterfaceId, ComClass1.EventsId)> _
Public Class ComClass1
#Region "COM GUIDs"
' These GUIDs provide the COM identity for this class
' and its COM interfaces. If you change them, existing
' clients will no longer be able to access the class.
Public Const ClassId As String = "e19c541f-8eda-4fdd-b030-abed31518344"
Public Const InterfaceId As String = "e2122f92-5752-4135-a416-4d499d022295"
Public Const EventsId As String = "6b03de7e-90d7-4227-90ec-9121c4ce1288"
#End Region
' A creatable COM class must have a Public Sub New()
' with no parameters, otherwise, the class will not be
' registered in the COM registry and cannot be created
' via CreateObject.
Public Sub New()
MyBase.New()
End Sub
End Class
Also remember to check the "Make assembly COM Visible" in the Assembly Information dialog (Project properties>Application tab>Assembly Information)
Now when you compile this and call RegAsm, it should have an entry point for this class

Related

How to make a VB.NET DLL callable in VB6?

I have made a DLL in VS2017 using VB.NET:
Imports System.Runtime.InteropServices
<ComClass(ComClass1.ClassId, ComClass1.InterfaceId, ComClass1.EventsId)>
Public Class ComClass1
#Region "COM GUIDs"
' These GUIDs provide the COM identity for this class
' and its COM interfaces. If you change them, existing
' clients will no longer be able to access the class.
Public Const ClassId As String = "c67bcd70-54d0-4498-97be-a5f954790dec"
Public Const InterfaceId As String = "7ef1a8ce-bcc1-464e-8dc3-fc164bdb7ea3"
Public Const EventsId As String = "9939eabd-1102-4e34-9735-54664e3536bd"
#End Region
' A creatable COM class must have a Public Sub New()
' with no parameters, otherwise, the class will not be
' registered in the COM registry and cannot be created
' via CreateObject.
Public Sub New()
MyBase.New()
End Sub
Public Xposition As Int32
Public GoX As Int32
Public Function MoveX() As Int32
Dim target As Int32
target = Convert.ToInt32(Rnd() * 1000)
Xposition = target
Return Xposition
End Function
Public Function ReadX() As Int32
Dim target As Int32
target = Convert.ToInt32(Rnd() * 1000 - GoX)
Xposition = target
Return Xposition
End Function
End Class
I compiled the DLL as Administrator on the development Widows 10 PC.
I copied the DLL file to the Windows XP target machine running VB6 and get Run-time error 453.
The VB6 code:
Private Declare Function GSCloseServer Lib "GSWDLL32.DLL" () As Long
Private Declare Function MoveX Lib "C:\Temp\VB_Applications\My_DLL_Test\MyNewDLL.dll" () As Integer
Private Sub btnMove_Click()
Call test
End Sub
Private Sub btnStop_Click()
Dim Running As Integer
' Unload MyGraph
Set Form1 = Nothing
Running = GSCloseServer()
If Running = 0 Then
MsgBox "Close Server OK: " & Running, vbOKOnly
Else
MsgBox "Close Server Error " & Running, vbOKOnly
End If
Unload Me
End Sub
Private Sub test()
'txtbxReadBack.Text = ComClass1.Xposition
txtbxReadBack.Text = MoveX
End Sub
I am running VS2017 as administrator, so the DLL appears to be registered properly on the development PC.
These are the steps. I'll create a very simple "COMVisibleHelloWorld" class library with one class
Imports System.Runtime.InteropServices
'Create a manual interface so we can use a fixed and known guid for the COM interface.
<Guid("3BBA8F8E-6B35-48E0-91D4-124DC6FCE7B9"), InterfaceType(ComInterfaceType.InterfaceIsDual)>
Public Interface IHelloWorld
<DispId(1)> Function SayHello() As String
End Interface
<Guid("BCDCDA5C-DCAF-4C6C-86BA-03908DBEFF4B"), ClassInterface(ClassInterfaceType.None)>
Public Class HelloWorld
Implements IHelloWorld
Public Function SayHello() As String Implements IHelloWorld.SayHello
Return "Hello World"
End Function
End Class
In Visual Studio select "Make Assembly COM-Visible"
In Visual Studio create a strong key
Build the project. This will then create a tlb file.
You can now reference this is VB6
To use in VB6 just create an instance, and call the methods that have a COM interface
Private Sub Command1_Click()
Dim hello As New COMVisibleHelloWorld.HelloWorld
MsgBox (hello.SayHello)
End Sub
Result:

VB.NET Class with COM Interop, missing most properties when used from VBA (Access)

I have written a VB.NET class that has COM Interop enabled so it can be utilized in VBA - specifically, MS Access.
The class works fine in VB.NET.
With Accees, I can add the reference to it, instantiate the main object and set and return some properties.
But Access does not recognize anything relating to the sub-classes underneath the main class. VB.NET has no problem exposing these classes, but not VBA.
Is this simply a limitation of COM Interop and/or VBA?
Is there a work-around?
No you can’t get interop to generate the sub classes for you (to appear in VBA)
However, keep in mind that nested classes are really the same as non-nested. That sub class instance HAS to be initialized anyway. And there is nothing you can't do if the classes were to be separated. And you can well place many classes in one code module.
So this is purely a syntax preference you are looking for.
However, what you can do declare a pubic instance of any sub class in the main class (variables area as public).
Take this simple example.
Imports System.Runtime.InteropServices
<ClassInterface(ClassInterfaceType.AutoDual)>
Public Class Class1
Private m_Company As String = ""
Public Function MyHello()
MsgBox("Hello world")
End Function
Public Property Company As String
Get
Return m_Company
End Get
Set(value As String)
m_Company = value
End Set
End Property
<ClassInterface(ClassInterfaceType.AutoDual)>
Public Class Class2
Private m_FirstValue As Integer = 2
Public Property V1 As Integer
Get
Return m_FirstValue
End Get
Set(value As Integer)
m_FirstValue = value
End Set
End Property
Public Function MyTimes2() As Integer
Return m_FirstValue * 2
End Function
End Class
End Class
NOTE above the nested class “class2” in above.
Ok, so check the make com assembly visible = True, and for testing check the “register for com interop”
Compile the above, set the reference in Access. (Note how you don’t have to build a custom interface either!!!).
Now, in VBA you get this in intel-sense.
NOTE carefully how the sub class Class2 does not appear.
If you really want the intel-sense and sub class to appear, then to the above vb.net class, simple add this;
Public Class Class1
Private m_Company As String = ""
Public SClass2 As New Class2 <--- add this line to expose as public
Private m_Company As String = ""
.etc. etc. etc.
Now I put a “S” in from of the name – you unfortunately can’t use the same name as the nested class. (so either put something in front of the nested class, or something in front of the public instance of that class (that is what I did in above).
Now if we compile, then in VBA you get this:
Note the class2 DOES appear as a sub class
And if I hit a “dot” in VBA editor, then the sub class methods show like this:
So quite sure the above is the only way to get the sub-classes working with COM interop

Visual Studio - Autocomplete words for custom Objects / Libraries

When typing code in Visual Studio 2015 it automatically gives me alternatives on what I can do with my code:
I was wondering if there is a way to enable autocomplete on imported Objects / Libraries (.dll) files?
I am using IBM 3270 and have imported all of the libraries and added them as references to my project, but I still don't get any alternatives.
Here is an example of the code and what I believe it should do:
' The Imports are already referenced to the project.
' I have added them here to visually display that I am using the libraries.
Imports AutConnListTypeLibrary
Imports AutConnMgrTypeLibrary
Imports AutOIATypeLibrary
Imports AutPSTypeLibrary
Imports AutSystemTypeLibrary
Public Class IBM3270
Public Shared autECLConnList As Object
autECLConnList = CreateObject("PCOMM.autECLConnList")
autECLConnList. ' <- I believe that I should get autocomplete words here, but I don't.
' Some of the alternatives that should pop up are:
' autECLConnList.Count
' autECLConnList.Refresh
End Class
Link to IBM Knowledge Center
EDIT: I have rewritten the code and the Autocomplete words appears, but I can't figure out how I can execute it now. Previously I used CreateObject("PCOMM.autECLConnList") to access the object.
Here is what I have:
Imports AutConnListTypeLibrary
Public Class IBM3270
Implements AutConnList
Default Public ReadOnly Property ConnInfo(Index As Object) As Object Implements IAutConnList.ConnInfo
Get
Throw New NotImplementedException()
End Get
End Property
Public ReadOnly Property Count As Integer Implements IAutConnList.Count
Get
Throw New NotImplementedException()
End Get
End Property
Public Sub Refresh() Implements IAutConnList.Refresh
Throw New NotImplementedException()
End Sub
Public Shared Sub Test() ' Here is where I am stuck.
Dim autECLConnList As AutConnList ' I believe I can't reference AutConnList. ("PCOMM.autECLConnList") should probably be added someplace.
autECLConnList.Refresh
End Sub
End Class

In vb.net, how do I access a public function in a module from within a user control?

How can I access public functions from a user control the same way I can access public functions from a form? The public functions are in a utility module.
Let's assume that you have a module named SpecialUtility which is in SomeProject.Utilities namespace.
' Without "Global." prefix the "SomeProject.Utilities" namespace
' would be placed under the project's default namespace.
' If your project's default namespace was "SomeProject" then
' you could write only "Namespace Utilities" in this case.
Namespace Global.SomeProject.Utilities
Module SpecialUtility
Public Sub DoSomething()
Console.WriteLine("Doing something")
End Sub
End Module
End Namespace
You should import the namespace of that utility module in the user control.
Imports SomeProject.Utilities
Public Class SomeUserControl
Private Sub SomeAction()
' Now you can call DoSomething() method from SpecialUtility module.
DoSomething()
End Sub
End Class

Windows service, create class with 'global scope'

VB.NET 2010, Framework 3.5
Question / problem with global scope in a Windows Service
Barebones Window Service with two default Classes, Class1 and Class2
Class1 looks like this.
Public Class Class1
Public Hi As String = "Hi"
End Class
The main Service Class 'OnStart' below. obj1 looks like it should have global scope
Public Class Service
Public obj1 As New Class1 ' need obj1 to have global scope
Protected Overrides Sub OnStart(ByVal args() As String)
End Sub
End Class
However, trying to access the global obj1 within Class2 generates the error "obj1 is not declared. It may be inaccessible due to its protection level"
Public Class Class2
Public Sub SayHi()
MsgBox(obj1.Hi) ' error here, obj1 is out of scope
End Sub
End Class
In a non-Service app, where Sub Main replaces Sub OnStart, obj1 is visible everywhere. All the other classes can see obj1 until flow goes out of Sub Main.
Does anyone know how to get around this?
This only works in a non service app if the Sub Main is in a module.
The best solution is probably to create a separate class with a shared member
Public Class CommonObjects
Public Shared obj1 As New Class1
End Class
Then you can use obj1 like this:
Public Class Class2
Public Sub SayHi()
MsgBox(CommonObjects.obj1.Hi)
End Sub
End Class
Alternatively just create a module and put Public obj1 As New Class1 in it to use the object without having to specify the fully qualified name
See the answers to this question Classes vs. Modules in VB.NET for more information before deciding which route to take