Trying to create a library for com using VB.net - vb.net

I've been having some trouble with this topic for awhile and thought I'd ask the community for advice. I just can't seem to find anything geared towards vb.net online, and my C# knowledge is not sharp or complete enough to make sense of the C# examples.
I'd like to be able to create a class library in vb.net for use in VBA macros elsewhere. In particular, in Excel and a terminal emulator we use. I'm trying to start simple, and work my way up from there. For the moment, here is the test code I'm trying to use:
Public Class ComTest
Public Function SayHello() As String
Return "Hello"
End Function
End Class
Under properties > application > assembly information I've checked the "make assembly com visible" box.
When I try to set a reference to this dll using the VBA editor I get the error message that I can't add a reference to the specified file. If I try to declare the function as such:
Private Declare Function SayHello Lib "C:\SomePath\ComTest.dll" ()
I get a Run-time error '453': Can't find DLL entry point SayHello in C:\SomePath\ComTest.dll
I vaguely understand that declaring the function would be the approach to take with a "regular" DLL containing unmanaged code. The former would actually be creating a COM component. Given the choice, I'd take the former and just set a reference to the file. In particular, I'd like it to be registration free, but I think I understand that part if I can just get it to work as a com component for VBA.
So if anyone can provide some advice on either method, I'd be most thankful.

You shouldn't declare a function when you use COM. COM was made so that you wouldn't need to do that.
Your application should have an assembly name in the property pages (in the same dialog where you've set the "make assembly com visible" check box). That name is used to instantiate your object with:
Dim obj, msg
Set obj = CreateObject("YourAssemblyName.ComTest")
msg = obj.SayHello()
If you need to early bind your library, just add it to your project references (it should appear listed in the components list). Then you can do:
Dim obj As YourAssemblyName.ComTest
Dim msg As String
Set obj = New YourAssemblyName.ComTest
msg = obj.SayHello()

Related

References to startup object fail when converting WinForms app to Class Library

I'm trying to convert WinForms app into a class library - long story short the production environment I'm working in will allow our users to make changes to DLLs but not EXEs, so I'm trying to shove an entire existing app into a DLL and then just create and show an instance of the startup object/form from a second WinForms app with the goal of creating some kind of auto-update system.
I've changed the output type of the project to Class Library, added the launcher app, etc, but attempting to build the old app as a class library throws hundreds of errors, almost all of which are Reference to a non-shared member requires an object reference.
Upon inspection, these errors are appearing everywhere in the code that the startup object/form or any of its properties or methods are referenced. Since a great many things in a WinForms application naturally reference the main form... this is problematic.
Stuff like:
If DbConn = n.DbConn_.Prod Then
miParent = mainform.MiProdReq
throws the aforementioned error upon the attempt to access mainform.MiProdReq
Am I missing some simple/obvious step here?
You are referring to the default instance of the mainform type in that code. Default instances are provided by something automagically generated when building Windows Forms Application projects. Class Library projects have no such thing as default instances, so any code that tries to use them will appear to be trying to access instance members as though they were Shared.
You need to put an instance somewhere and change your code to refer to that instead. If you use a global variable, which is not ideal itself but the simplest option for where you're at, then you can just do a Find & Replace In Files to find the references you need to change.
Note that default instances are something that most experienced developers would suggest avoiding anyway. They don't exist in C# and I've never heard complaints about that, so it's hardly onerous. They were added to VB as a convenience for beginners and migrating VB6 developers who weren't used to proper OOP.
EDIT:
I haven't tested it but you may be able to use Application.OpenForms(0) to get a reference to the startup form anywhere in your library. You could, perhaps, add a module like this:
Module Module1
Private _mainform As Form1
Public ReadOnly Property mainform As mainform
Get
If _mainform Is Nothing Then
_mainform = DirectCast(Application.OpenForms(0), mainform)
End If
Return _mainform
End Get
End Property
End Module
and then your code may even just work as it is.

Set IntPtr value of COM DLL in VB.net project

my task is to write a VB.NET library to interact with a piece of VB6 application. This VB6 application provides some unmanaged COM DLLs.
I created interop types (by using tlbimp) and added them as a reference in my VB.NET project.
Well, there is also an old VB6 project (library) existing, which is also referencing the (native) COM DLLs, let's call them
COM1.dll, COM2.dll and COM3.dll.
This old project is having a "Class Module" called claMod where it is doing the following:
Option Explicit
Public Property Set GlobalProp(oObj As claClass1)
Set COM1.MyPointer = oObj
Set COM2.MyPointer = oObj
End Property
where claClass1 is a class in COM3 and COM1.MyPointer and COM2.MyPointer are of type System.IntPtr.
My question is: How is possible to set these two pointers without declaring the corresponding objects (instances of COM1 and COM2) ?? And secondly, how can I do the same in my new VB.NET library?
To allow referencing of COM1 and COM2 without specifically instantiating them, set each class's Instancing property to GlobalMultiUse. Then the code that you have in your post will work without having to create a reference to it.
There are many such objects already running in the VB6 environment; the Screen object is a familiar example.
This is basically the same as declaring a Shared class in VB.Net or a static class in C#.

Using CreateObject ActiveX component can't create object w/ Registered Class Library

I have written some classes in visual basic.net and want to use them in a visual basic 6 application.
Now I have registered the tlb/dll files on my computer and I am able to create an instance of the class in vb6 with
Dim c As New Advantage_Dealer_Email_CoreClassLibrary.CoreClass
However I want to use CreateObject(Class) but when I try I get the ActiveX error, here is the code I am using for that
Dim c As Object
Set c = CreateObject("Advantage.Dealer.Email.CoreClassLibrary.CoreClass")
Is this possible what am I doing wrong?
Thanks
Update:
After searching through the registry I am only able to find a CLSID which references Advantage.Dealer.Email.CoreClassLibrary.
{CFB8F7A1-BC6F-4771-839F-1343785ED9D6} > 1.0 > (Default) REG_SZ Advantage.Dealer.Email.CoreClassLibrary
Solution
I had another look in the registry and found a Guid called
CoreClassInterface
which referenced the library, when I used the code
CreateObject("CoreClassInterface")
the vb6 program worked.
For anyone that comes across this post in the future it was because I had set the ProgID in the vb.net class when setting up the class with an interface for the Com Interop.
<Guid("7EB55A33-34E7-4FC4-A87B-41635EEAF32D"), ClassInterface(ClassInterfaceType.None), ProgId("CommClassInterface")> Public Class CoreClass : Implements _CoreClassInterface
After removing the ProgID for the class and rebuilding/registering the library on my computer I found
Advantage.Dealer.Email.CoreClassLibrary.CoreClass
In the Registry, and my vb6 app worked.
Thanks for your help tcarvin
I find it best, when creating .NET libraries for consumption by VB6, to keep the namespace of COM-visible classes to only one-layer deep to avoid the underscores. Anyway, have you attempted to use:
CreateObject("Advantage_Dealer_Email_CoreClassLibrary.CoreClass")
When all else fails, the answer is in your registry. Use regedit and see what the component is listed under.

Error while Obfuscation

We have developed project using vb.net for our internal purposes and then we obfuscated it. It is throwing error as mentioned below.
“Public member ‘Var1’ on type ‘e’ not found.”
Code:
Public Sub get_constants_from_DbList(ByRef frm As Object, ByRef sDbname As String)
For Each Row As DataRow In CommonObj.DSCommonProc.Tables("dblist").Rows
If StrComp(Row("DbName").ToString, sDbname, CompareMethod.Text) = 0 Then
prg_id = Row("PrgId").ToString
frm.Var1= Row("ChangesDbName").ToString
frm.Var2 = Row("LoadTableName").ToString
frm.Var3 = Row("ServerName").ToString
Exit Sub
End If
Next
End Sub
A form (named FrmMain) is passed to the parameter ‘frm’ from the calling procedure to this sub-routine. Var1, etc are public variables in that form.
Obfuscation tools we tried are –
SmartAssembly 6
PreEmptive Dotfuscator and Analytics CE (which has come with Visual studio 2012)
Without obfuscation exe is working fine.
Error is thrown while assigning variable ‘Var1’ in the sub-routine. If the code line is modified as below then obfuscated exe will work fine.
FrmMain.Var1= Row("ChangesDbName").ToString
We thought obfuscation is missing late binding & tried similar type of code in a small sample project. But that didn’t have any error. We have attached this small code. But due to its magnitude we can’t upload original project.
How can we trace the error?
You can find the source code of my sample application here
Don't use obfuscation with reflection/late binding/dynamic. It will only get you into troubles like this.
The obfuscator will obfuscate all private & internal identifiers but it can't know that you are binding to them by name at run-time.
Turn on Option Strict and resolve the errors (i.e. change the type of the argument frm to its real type)
Obfuscators rely on static analysis to determine what is "safe" to obfuscate. When you introduce late binding and reflection into the mix, it becomes very difficult to detect that something isn't safe to rename. Most obfuscators provide the ability to exclude certain elements of your application from obfuscation so that you can work around this problem.
I actually don't know VB.Net very well, but with the way that you're doing late binding appears to be something that an obfuscator can't detect. So, this means you need to exclude that property from being renamed. In Dotfuscator at least, this is should be easy. You should also be able to turn on "Library Mode" which will automatically exclude all public members of every class.

Will VB.NET automatically generate ComClass attribute and guids?

I've run across some VB.NET code that explicitly creates three GUID constants and uses them in a class's ComClass attribute. I've written COM-aware classes in the past just by checking the "Make COM-Visible" and "Register for COM interop" options in the project options. Is this explicit code simply unnecessary, or is it doing something above-and-beyond what those two options do? Here's a snippet:
<ComClass(MenuHandler.ClassId, MenuHandler.InterfaceId, MenuHandler.EventsId)> _
Public Class MenuHandler
Public Const ClassId As String = "A2204623-A902-44d4-B524-FDFFCD176E53"
Public Const InterfaceId As String = "3449CA8B-16DF-4a61-8BAB-DFF27AE70F5E"
Public Const EventsId As String = "06C156DD-0ABA-437e-9EE0-C1CE2CA34033"
End Class
The ComClass attribute is specific to Visual Basic and is intended to simplify the creation of COM objects in .Net code. When the compiler sees this attribute it do some of the tedious work under the hood that must be manually done in C#. In summary, it's a shortcut for Visual Basic Code for creating COM objects.
Here's a great article on what this provides over doing the same in C#:
http://codebetter.com/blogs/peter.van.ooijen/archive/2005/08/02/130157.aspx
By explicitly putting attributes on the classes and enumerations that you want exposed you can prevent things like helper classes, internal tools, or internal classes that should not be exposed from being exposed. This is the recommended way from Microsoft to do it...and when they recommend something, it usually means that if you don't do it, your program may break in the future. :)
Checking "make COM visible" and "register for COM interop" is the simple method for making COM visible objects, but is not recommended. This will add the overhead for every class visible - even ones that you may not want.
Will VB.NET automatically generate ComClass attribute and guids?
Yes, if you use VB.NET COMClass template to create a class, COMClassAttribute and those GUIDs will be automatically generated.
Detail in MSDN
Section "To create a COM object by using the COM class template"