VB6 and VB.NET interoperability using com.visible - vb.net

Please see the code below:
Imports System.Runtime.InteropServices
Public Class TestClass
<ComVisible(True)> _
Public Function Hello() As String
Return "Hello Ian"
End Function
Public Function Goodbye() As String
Return "Goodbye Ian"
End Function
End Class
I have created the Type Library using Regasm and I have added a reference to the TLB in a VB6 project. The code for VB6 is below:
Private Sub Form_Load()
Dim tc As TestClass
Set tc = New TestLibrary.TestClass
MsgBox (tc.Hello)
MsgBox (tc.GoodBye)
End Sub
I do not understand why the message prints the value of: tc.Goodbye as this is not visible.
I believe it is because the default value is true. Is there a way to set the default value to false.

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:

Method Call from ASP to .NET COM fails

Background:
I have a legacy application written in ASP (VBScript) which call COM components written in VB6. We are upgrading in phases and need to first update COM components to .NET while maintaining interoperability with ASP.
Situation:
I created a sample .NET class and exposed it to COM system using various attributes as per MSDN documentation
I am able to instantiate the class
I am able to call the desired method on the class
Problem:
Argument values are not being sent to COM method. e.g. all numeric types have value 0 inside the method; all reference types have null value inside the method. Literal strings are passed properly to the method. However, string variables are not passed.
Same issue with return value. Regardless of value returned by the method, the variable on ASP has default value (0 or null as the case may be).
COM Code
<ComVisible(True)>
<InterfaceType(ComInterfaceType.InterfaceIsIDispatch)>
Public Interface IClass1
Function Triple(ByVal input As String) As Integer
End Interface
<ComVisible(True)>
<ProgId("TESTCOM.Class1")>
<ClassInterface(ClassInterfaceType.None)>
Public Class Class1
Inherits ServicedComponent
Implements IClass1
Public Sub New()
' needed for COM
End Sub
Public Function Triple(input As String) As Integer Implements IClass1.Triple
IO.File.WriteAllText("C:\TestCOM.Class1_Triple.log", String.Format("[{0:s}] Input: {1}", Date.Now, input)) ''' this file is updated; so I know method is being called.
Return 97
End Function
End Class
Alternate COM Code
<ComVisible(True)>
<ProgId("TESTCOM.Class1")>
<ComClass("E26FE8A0-8AC7-4824-9776-30ECDD473AA3")>
Public Class Class1
'Inherits ServicedComponent
'Implements IClass1
Public Sub New()
' needed for COM
End Sub
Private Const LogMessageFormat As String = "[{0:s}] Input: {1}, {2}" + vbCrLf
Public Function Triple(csinput As String, nInput As Integer) As Integer 'Implements IClass1.Triple
IO.File.AppendAllText("C:\TestCOM.Class1_Triple.log",
String.Format(LogMessageFormat, Date.Now, if(csinput isnot Nothing, csinput, "**NULL**"), nInput))
Return 97
End Function
End Class
ASP Code
dim testNETCOM
set testNETCOM = Server.CreateObject("TESTCOM.Class1")
' ** use this to check if there was any error during instantiation
If Err.Number <> 0 Then
Response.Redirect("http://"&Err.Description&"/")
'Response.Write (Err.Description& "<br><br>")
else
'Response.Redirect("http://no-error/")
End If
' ** use this to check if object is actually instantiated
if testNETCOM is nothing then
Response.Redirect("http://no-com/")
else
'Response.Redirect("http://yes-com/")
end if
dim nInput
set nInput = 41
dim nOutput
set nOutput = -1
set nOutput = CLng(testNETCOM.Triple("test message")) ' this string is received in the method
set nOutput = CLng(testNETCOM.Triple(CStr(nInput))) ' this string is not received in the method
' ** use this to check if return value is what we expected
if nOutput <> 0 then
Response.Redirect("http://test/")
else
Response.Redirect("http://notest/") ''' **this happens**
end if
The issue is in the syntax on ASP
For simple data types, do not use 'set'
For objects, use 'set'
Because I was using set for simple types, there was an error during the assignment process and the value of the variable for null/nothing/empty.
set nInput = 41 ' this is wrong
nInput = 41 ' this is right
set nOutput = CLng(testNETCOM.Triple(CStr(nInput))) ' this is wrong
nOutput = CLng(testNETCOM.Triple(CStr(nInput))) ' this is right
Give either of these a try:
<ComVisible(True)>
<Guid("79571A9D-2345-48D9-8F86-7F6761A97DBA")>
<InterfaceType(ComInterfaceType.InterfaceIsIDispatch)>
Public Interface IClass1
Function Triple(ByVal input As String) As Integer
End Interface
<ComVisible(True)>
<Guid("E26FE8A0-8AC7-4824-9776-30ECDD473AA3")>
<ProgId("TESTCOM.Class1")>
<ClassInterface(ClassInterfaceType.None)>
Public Class Class1
Inherits ServicedComponent
Implements IClass1
Public Sub New()
' needed for COM
End Sub
Public Function Triple(input As String) As Integer Implements IClass1.Triple
IO.File.WriteAllText("C:\TestCOM.Class1_Triple.log", String.Format("[{0:s}] Input: {1}", Date.Now, input)) ''' this file is updated; so I know method is being called.
Return 97
End Function
End Class
Or:
<ComVisible(True)>
<ProgId("TESTCOM.Class1")>
<ComClass("E26FE8A0-8AC7-4824-9776-30ECDD473AA3", "79571A9D-2345-48D9-8F86-7F6761A97DBA")>
Public Class Class1
'Inherits ServicedComponent
'Implements IClass1
Public Sub New()
' needed for COM
End Sub
Private Const LogMessageFormat As String = "[{0:s}] Input: {1}, {2}" + vbCrLf
Public Function Triple(csinput As String, nInput As Integer) As Integer 'Implements IClass1.Triple
IO.File.AppendAllText("C:\TestCOM.Class1_Triple.log",
String.Format(LogMessageFormat, Date.Now, if(csinput isnot Nothing, csinput, "**NULL**"), nInput))
Return 97
End Function
End Class

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

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

Error. .. Main not accessible by program

I wrote a module and into which a Public Sub Main method. But, when I run the program. It gives " No accessible 'Main' method with an appropriate signature was found in 'abc'."
Could you please suggest possible solutions to the error.
Public Sub Main(ByVal cmdArgs As String)
Dim returnValue As Integer
If cmdArgs.Length > 0 Then
returnValue = Import_Start(cmdArgs, "f9880")
Console.WriteLine("Import end with an error " & returnValue)
Else
Console.WriteLine("parameter failure")
End If
End Sub
End Module
If you want to start your app from a Sub Main, the correct signature is:
Public Sub Main(args As String())
' or
Public Sub Main()
The command line args will be passed as a string array. Yours just declares it as String resulting in the compiler error. You also need to set the StartUp object to Sub Main in Project Properties, but that already seems to have been done.
If you do not want/need to use a module you can add it to a form (it is not clear if this is even a WinForms app) using:
Public Shared Sub Main(args As String())
' or
Public Shared Sub Main()

How should moq's VerifySet be called in VB.net

I am trying to test that a property has been set but when I write this as a unit test:
moqFeed.VerifySet(Function(m) m.RowAdded = "Row Added")
moq complains that "Expression is not a property setter invocation"
My complete code is
Imports Gallio.Framework
Imports MbUnit.Framework
Imports Moq
<TestFixture()> Public Class GUI_FeedPresenter_Test
Private moqFeed As Moq.Mock(Of IFeedView)
<SetUp()> Sub Setup()
moqFeed = New Mock(Of IFeedView)
End Sub
<Test()> Public Sub New_Presenter()
Dim pres = New FeedPresenter(moqFeed.Object)
moqFeed.VerifySet(Function(m) m.RowAdded = "Row Added")
End Sub
End Class
Public Interface IFeedView
Property RowAdded() As String
End Interface
Public Class FeedPresenter
Private _FeedView As IFeedView
Public Sub New(ByVal feedView As IFeedView)
_FeedView = feedView
_FeedView.RowAdded = "Row Added"
End Sub
End Class
I can't find any examples of moq in VB, I would be grateful for any examples.
See my question Using Moq's VerifySet in VB.NET for the solution to this.