VB.NET - Module Interpreted as Class in VBE - vb.net

I am building a VB.NET class library that will be used for VBA Projects. In my library, I have a module called "Globals" I wish to store some global (Library level) properties and methods in:
Imports System.Runtime.InteropServices
<ComVisible(True)>
Public Module Globals
<ComVisible(True)>
Public Const TestGlobal As Integer = 0
End Module
However, when I reference my library in the Excel VBE, my module shows up as a Class and I can't see my constant variable in the Object Browser:
What am I missing or doing incorrectly that's causing my module to show up as a class?

There's somewhat of an "impedance mismatch" between VB.NET and COM. COM works primarily with interfaces, and .NET builds the .tlb files from VB projects in a way that makes them more accessible to COM. A Module in VB.NET is basically just a static class in the managed world. When the interop assembly is built from your code, it sees a class, wraps it in an interface, and attaches the interface to the class:
Note that you can't provide your own interface implementation for a Module, because static classes in .NET can't implement interfaces.
The other thing that you can't do in .NET is create member procedures that aren't wrapped in a class. That is basically what a Module is in the COM world. For example, take a look at the IDL for the VBA Strings module:
module Strings {
[entry(516), helpcontext(0x000f665f)]
short _stdcall Asc([in] BSTR String);
[entry(537), helpcontext(0x000f6e9f)]
BSTR _stdcall _B_str_Chr([in] long CharCode);
[entry(608), helpcontext(0x000f64e2)]
VARIANT _stdcall _B_var_Chr([in] long CharCode);
[entry(517), helpcontext(0x000f6ea0)]
Note that all of the entries have a .dll entry point specified. That's basically because you're making raw function calls of the variety you'd find in the C world. If an analogue existed in the .NET world, it would be a Sub or Function declared at the namespace level instead of the class (or module) level...
<ComVisible(True)>
Namespace SomeClass.Functions
<ComVisible(True)>
Public Function Foo() As String
Return "Foo"
End Function
End Namespace
...which obviously isn't valid .NET code.
On a related note, keep in mind that you aren't seeing the entire picture in VBA either. VBA is basically built on top of COM, but it tries really hard to abstract away most of it. A VBA Module can for all intents and purposes be treated as a static class. For example, you can give them properties, public members, etc. At the end of the day, it really doesn't matter whether the VBA Object Browser sees something as a class, a module, or whatever - what matters is that you can use managed code from an unmanaged context. Just work around the differences in the 2 infrastructures to get something usable and call it a day:
Imports System.Runtime.InteropServices
<ComVisible(True)>
Public Enum Constants
GlobalOne = 0
GlobalTwo = 1
GlobalEtc = 2
End Enum
<ComVisible(True)>
Public Class FunctionProvider
<ComVisible(True)>
Public Function Foo() As String
'Note that the class doesn't need to keep track of "state" to be usable.
Return "Foo"
End Function
End Class

Related

What is the use of Module in VB.Net programming?

I am creating a .dll for a CAD tool I use.
After creating a new project (class library) , I got a "public class-end class" block.
I wrote my main method inside that block. When I run the dll in my CAD tool, its giving me main entry point missing error.
So i come back and change the "public class-end class" ---> "Public Module-end Module".
Now its able to find the main method.
Why is this so? Ive read on these forums that we should not be using modules as much.
If I were to not use modules so much, how I am supposed to make it work without the module block?
VB.Net doesn't have static classes like C# has. Static classes allow you to create static methods such that
class foo
{
void bar() {}
}
Accessed in VB.Net like this
foo.bar()
If you want to create the same functionality with VB.Net you have two options, which are either using a Module
Module foo1
Sub bar()
End Sub
End Module
Class foo2
Shared Sub bar()
End Sub
End Class
However the difference is how Module and Class are scoped. Modules are globally scoped so you can call the module method outside of the originating namespace without qualifying it with the Module name, but to call the static method you need to qualify it with the class name, and even import the namespace in the case of a class with static method
bar()
foo2.bar()
But what Modules are certainly good for is Extension Methods, and the global scope actually helps here if you want to extend a class across your project
Module Extensions
<Extension>
Sub bar(value As String)
End Sub
End Module
Dim s = "my string"
s.bar()
In your case, it's expecting a C# style static class with method, and since VB.Net doesn't have static classes, a Module or class with static method both satisfy that.
Modules come from legacy versions of Visual Basic and sort of act as the "OG" of Visual Basic code files. When Visual Basic 6 and earlier was around, Visual Basic was sort of quasi object oriented and while classes existed, it made more sense to use modules in lieu of static classes. Then when Visual Basic .NET was introduced, Microsoft made a real effort to make the programming language a true object oriented programming language.
People likely tell you to use classes over modules because modules are holdovers from the days prior to Visual Basic being a pure object oriented language. Starting from 2005 on forward, anything you could do in a module could basically be done as a static (aka shared) only class with the exception of extension methods. So by using classes over modules, you're taking a more .NET oriented approach rather than a Visual Basic first approach.
With regards to your specific problem, the issue is that your CAD tool is looking for an entry method, but cannot find one. The simple solution is to add a shared method:
Public Class MyClass
Public Shared Sub Main()
' ...
End Sub
End Class

VB.NET code re-ordering to interface

Is there a simple way, without using external tools, to get the physical layout of my classes to match my interface?
If I have an interface, IFoo:
Interface IFoo
Function DoFoo() As Integer
Function DoBar() As Integer
End Function
I will end up with a class:
Class MyFoo
Public Function DoFoo() As Integer Implements IFoo.DoFoo
'...
End Function
Public Function DoBar() As Integer Implements IFoo.DoBar
'...
End Function
End Function
I then decide to add GetFoo() to my interface. I put it with DoFoo(), since it is a related thing and I want to group like terms. Visual Studio then tells me that the class no longer implements IFoo properly. The autogenerated code goes at the end of MyFoo.
What I would like to do is either get the item to be inserted in the correct place (under the previous item in the interface), or moved there post-event.
Not had anything very relevant come up using Google.
There is not such functionality in Visual Studio. For this, I am using CodeMaid extension, which can arrange VB.NET code alphabetically and by type and scope. It really makes your code beautiful. There is also very good commercial extension called CodeRush for Roslyn which have much more features and it does organizing document little better.

VB.NET how to allow people implement a class without letting people see or changing the code?

How do you allow people to implement a class without letting them see or change the code?
Implementation implies Interface. You can just define an Interface in a .NET assembly (.dll) which you provide to said people. There is no implementation in an interface. No code to change other than the property and method names and types.
Public Interface IFoo
Sub Bar()
End Interface
If this code is given to people in a code file, they can change the definition of the Interface.
There is also an abstract class, in VB it is called MustInherit. Other classes can inherit or derive from it (not implement, as your question said, but you might mean this).
Public MustInherit Class Foo
Sub Bar()
Console.WriteLine("Bar")
End Sub
MustOverride Sub Barr()
End Class
If you give this to people in a code file, they can change what happens in Bar() or really do anything they want to it. Again, putting this in a .NET assembly and giving the .dll to people would prevent this from happening. In most cases this is enough to keep people from changing your code.
You can do this by making the output type of your project a class library. This will compile into a .dll file.
Build the project. In the output directory will be a .dll file (if compilation was a success).
You, or other people, could then add a reference to the .dll file created by your class library, in a new project. You could declare a class which implements the public interface defined in it
Public Class MyIFoo
Implements WindowsApplication1.IFoo
Public Sub Bar() Implements WindowsApplication1.IFoo.Bar
End Sub
End Class
And/or make a class which inherits the abstract class you made in the .dll
Public Class MyFoo
Inherits WindowsApplication1.Foo
Public Overrides Sub Barr()
End Sub
End Class
In your new project, you cannot see or change the code of the Interface IFoo or abstract class Foo.
You should use the private keyword to prevent a client of the class from accessing or modifying fields directly. You expose the methods and properties you want the client to access via the public or protected keyword (for inheritance).

Where is VB.NET's MsgBox function defined?

This is really more of an academic question, but where is this function defined? Within .NET, I'm used to working in an object oriented manner. However, if I define a VB.NET class as follows:
Public Class foo
Public Sub showmessagebox()
Dim i As Integer
i = MsgBox("Message")
End Sub
End Class
Is MsgBox defined in a class? I am not required to reference a static class or inherit from another class. I'm not even required to import a namespace. I did find this link from msdn. But my question remains, where is this defined and how does the CLR just load up a function?
If you enter MsgBox into Visual Studio and hit F12, you will see that it is in the Microsoft.VisualBasic namespace, in the Interaction module.
In fact, this information is also available (although a bit hidden) at the bottom of the MSDN page you referenced:
Namespace: Microsoft.VisualBasic
Module: Interaction
Assembly: Visual Basic Runtime Library (in Microsoft.VisualBasic.dll)
If you look into the References section of your project properties, you'll see that the Microsoft.VisualBasic namespace is automatically imported. Since Interaction is a module, you can use its methods without having to qualify the module name (as opposed to static/Shared methods of a class).
As a side note: If you add a reference to Microsoft.VisualBasic.dll to a C# project, you can use Microsoft.VisualBasic.Interaction.MsgBox("Hello World"); there as well (although most C# users will prefer using the MessageBox class).

How to use reflection to unit-test an internal (Friend in VB) class within an assembly, when the InternalsVisibleToAttribute is not an option?

I have a solution with two projects within:
Company.Project.vbproj
Company.Project.Tests.vbproj
Within the Company.Project.vbproj assembly, I have a class FriendClass.vb which scope is Friend (internal in C#).
Now I wish to test this FriendClass.vb from within the Company.Project.Tests.vbproj assembly. I know about the InternalsVisibleToAttribute, but that is not an option in Visual Basic .NET 2.0, as it is only available with C#, in .NET 2.0 (see here).
I would like to create myself a proxy class using this internal FriendClass from within my testing assembly, so that I could instantiate it and do the testings accordingly.
Any idea or known practices to do so?
Thanks in advance! =)
The only workaround that I have found is one used back in .NET Framework 1.1.
As the InternalsVisibleToAttribute is not useable in .NET 2.0 Visual Basic, the only workaround that I have found is to include my tests within the same project as my library itself. Besides, some further work needs to be accomplished.
Create yourself a new compilation CONFIG called "Tests" (that is where you may select "Release"/"Debug");
Create a new folder named "Tests" within your project;
Add a new class, the one to test your Friend (internal in C#) members;
First line of code within this class should be: #if CONFIG = "Tests" then ... #end if;
Place your code between this compiler IF directive.
For example, if I have the following Friend class:
Friend Class MyFactory
Friend Property Property1 As Object
Get
Return _field1
End Get
Set (ByVal value As Object)
_field1 = value
End Set
End Property
Friend Sub SomeSub(ByVal param1 As Object)
' Processing here...
End Sub
End Class
Then, if you want to test this class in .NET 2.0 Visual Basic, you will need to create a test class within the same project where the MyFactory class sits. This class should look like so:
#If CONFIG = "Tests" Then
Imports NUnit.Framework
<TestFixture()> _
Public Class MyFactoryTests
<Test()> _
Public Sub SettingProperty1Test
' Doing test here...
End Sub
End Class
#End If
Since you have a compiler directive telling the compiler to compile and to include this class only when the "Tests" CONFIG is selected, you won't get this class on "Debug" or on "Release" mode. This class won't even be part of the library, this for not polluting your library unnecessarily, and this allows you to test your Friend class anyway.
That is the smartest way I have found to work around this issue in Visual Basic .NET 2.0.