Method 'set_Description' in type 'myAssembly.NetProduct' from assembly 'myAssembly' does not have an implementation - vb.net

I have a DLL file created in VB6. It contains a class named Product and that contains the following simple code:
Option Explicit
Private sDescription As String
Public Property Get Description() As String
Description = sDescription
End Property
Public Property Let Description(Value As String)
sDescription = Value
End Property
I want to use this DLL in VB.NET, which is nothing more than registering the DLL on my system and including the DLL file in the references. Visual Studio automatically generates an interop DLL to consume the COM DLL. This interop DLL generates interfaces for all classes. In VB.NET I want to create a new class that implements the Product interface from the interop DLL. So I code:
Imports myAssembly
Public Class NetProduct
Implements myAssembly.Product
Public Property Description As String Implements _Product.Description
Get
Throw New NotImplementedException()
End Get
Set(value As String)
Throw New NotImplementedException()
End Set
End Property
End Class
The property is auto-generated because I implemented the Product interface. But here comes the problem because when I start using the NetProduct class I get an error telling me this:
Method 'set_Description' in type 'myProject.NetProduct' from
assembly 'myProject, Version=1.0.0.0, Culture=neutral,
PublicKeyToken=null' does not have an implementation.
The problem is that there is no method set_Description in the interface. When I view the definition of the Product interface it shows me the following:
Imports System.Runtime.InteropServices
Namespace myAssembly
<CoClass(GetType(ProductClass))> <Guid("49CE2F98-931C-441B-B322-9F39B6D6F212")>
Public Interface Product
Implements _Product
End Interface
End Namespace
The definition of the _Product interface is:
Imports System.Runtime.InteropServices
Namespace myAssembly
<Guid("49CE2F98-931C-441B-B322-9F39B6D6F212")> <TypeLibTypeAttribute(4304)>
Public Interface _Product <DispId(1745027072)>
Property Description As String
End Interface
End Namespace
When I use the interface myAssembly.Product directly to create a new object then everything works as you would expect. The property does not pose a problem there. But when I implement the interface in a .NET class the problem arises.
How do I solve this?
[update 1] After creating a method Set_Description I see the following error appear:
property 'Description' implicitly defines 'set_Description', which
conflicts with a member of the same name in class 'NetProduct'.
This must have something to do with my problem, although I don't know what it is. I already tried completing the property to make sure the Throw New NotImplementedException() wouldn't be in the way but that didn't make the error go away. My code builds just fine by the way. The error I gave earlier is a runtime error. Not a build error.
Private myDescription As String
Public Property Description As String Implements Product.Description
Get
Return myDescription
End Get
Set(value As String)
myDescription = value
End Set
End Property
[update 2] I have used JetBrains DotPeek to disassemble the interop.dll that Visual Studio generates. Disassembly is coded in C#. It contains 2 interfaces and 1 class for the single Product class from VB6. Here are all details.
I'll start with the Product class itself.
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace myAssembly
{
[ClassInterface(0)]
[Guid("C54B96A8-1499-4B76-8508-0B732E551326")]
[TypeLibType(2)]
[ComImport]
public class ProductClass : _Product, Product
{
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
public extern ProductClass();
[DispId(1745027072)]
public virtual extern string Description { [DispId(1745027072), MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] [return: MarshalAs(UnmanagedType.BStr)] get; [DispId(1745027072), MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] [param: MarshalAs(UnmanagedType.BStr), In, Out] set; }
}
}
The ProductClass uses 2 interfaces. I don't understand why because one of those is just an implementation of the other. This is the Product interface.
using System.Runtime.InteropServices;
namespace myAssembly
{
[CoClass(typeof (ProductClass))]
[Guid("49CE2F98-931C-441B-B322-9F39B6D6F212")]
[ComImport]
public interface Product : _Product
{
}
}
And then we have the _Product interface. They even share the same Guid. It might have something to do with backwards compatibility.
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace myAssembly
{
[Guid("49CE2F98-931C-441B-B322-9F39B6D6F212")]
[TypeLibType(4304)]
[ComImport]
public interface _Product
{
[DispId(1745027072)]
string Description { [DispId(1745027072), MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] [return: MarshalAs(UnmanagedType.BStr)] get; [DispId(1745027072), MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] [param: MarshalAs(UnmanagedType.BStr), In, Out] set; }
}
}
This is all I could find. Still no clue where the error for Set_Description comes from.
[Update 3] Example code
The code for the VB6 class is on top of this question. Nothing fancy there. The code for testing implementation in .NET is like this:
Imports myAssembly
Public Class NetProduct
Implements myAssembly.Product
Private myDescription As String
Public Property Description As String Implements Product.Description
Get
Return myDescription
End Get
Set(value As String)
myDescription = value
End Set
End Property
End Class
To test the NetProduct class I dropped a Button on a Form and create an instance of the class when the button is being clicked.
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click '<- Error happens here, so on loading the datatype!
Dim Product As New NetProduct 'Error does NOT happen here.
End Sub
The whole project compiles without errors. The project even runs without errors UNTIL you click the button. Probably because the NetProduct type is first loaded on that point.

I used a console app to do my test. Other than that, my VB.NET code is basically identical to yours in update 3. The VB.NET properties were auto-generated by VS with the stub Throw New NotImplementedException() after using the Implements statement :
Imports OurCOMDll
Class TestClass
Implements OurCOMDll.ClassInCOMDll
Dim msStringProperty As String = String.Empty
Public Property StringProperty As String Implements _ClassInCOMDll.StringProperty
Get
StringProperty= msStringProperty
End Get
Set(value As String)
msStringProperty = value
End Set
End Property
End Class
Module Module1
Sub Main()
Dim o As New OurCOMDll.ClassInCOMDll
o.StringProperty = "Hello World!"
Console.WriteLine(o.StringProperty) ' Outputs 'Hello World!' as expected
Console.ReadLine()
End Sub
End Module
Same is true for the VB6 code. The string property is implemented like yours.
Distinguishing factors so far:
VS 2019 vs. VS 2017
(Consuming) GUI vs. Console application
Different property names

Related

C# to VB6 COM events (“object or class does not support the set of events”), but different

I am aware of a 10 year old question with the same title as this one but I have double checked and I am not mistakenly using the delegate name. This is a different issue.
Here at work we have an old VB6 application I need to teach new(er) tricks. The first thing I had to do was have it call methods from a .Net COM-visible DLL written in C#. I have that working. Now I need to have it handle incoming progress notification events from that same DLL. I asked a similar question yesterday wherein the VB6 IDE was not even seeing that the DLL had events to offer. That issue was solved by decorating the C# interfaces and classes correctly.
First, the C# codez:
namespace NewTricksDLL
{
[ComVisible(true)]
[Guid("16fb3de9-3ffd-4efa-ab9b-0f4117259c75")]
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
public interface ITransfer
{
[DispId(2)]
string SendAnEvent();
}
[ComVisible(true)]
[Guid("16fb3de9-3ffd-4efa-ab9b-0f4117259c74")]
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
public interface IManagedEventsToCOM
{
[DispId(2)]
void NotificationEvent();
}
[ComVisible(true)]
[Guid("dcf177ab-24a7-4145-b7cf-fa06e892ef21")]
[ComSourceInterfaces(typeof(IManagedEventsToCOM))]
[ProgId("ADUTransferCS.NewTricks")]
public class NewTricks : ITransfer
{
public delegate void NotificationEventHandler();
public event NotificationEventHandler NotifificationEvent;
public string SendAnEvent()
{
if (NotifificationEvent != null)
NotifificationEvent();
}
}
}
An now my attempt to use it in VB6. Please note that the _tricky_NotificationEvent event handler was generated by the IDE by picking _tricky from the left-hand dropdown and NotificationEvent from the right-hand dropdown so I know this event is visible to the VB6 IDE.
Option Explicit
Public WithEvents _tricky As NewTricksDLL.NewTricks
Private Sub Command1_Click()
' The next line fails with 'Object or class does not support the set of events'
Set _tricky = CreateObject("NewTricksDLL.NewTricks")
' Execution never makes to the next line
_tricky.SendAnEvent()
End Sub
Private Sub _tricky_NotificationEvent()
' This handler was auto generated by the IDE
End Sub
Hopefully this can help - here's a minimal bare bones VB.net implementation of your requirements: COM interface consumable by VB6, one event, one method.
Option Explicit On
Option Strict On
<ComClass(newTricks.ClassId, newTricks.InterfaceId, newTricks.EventsId)>
Public Class newTricks
#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 = "386d540d-f8b8-46e1-939d-7b69dd5eff0a"
Public Const InterfaceId As String = "78b4036e-86a0-4671-997d-da5a33bf026f"
Public Const EventsId As String = "7b0fa5b5-b45e-4db2-9282-c06e09852161"
#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 Event NotificationEvent()
Public Sub SendAnEvent()
RaiseEvent NotificationEvent()
End Sub
End Class
This was created by starting a new project (Windows class library), delete from the project the Class1.vb that's automatically created, add a COM Class item renaming it to NewTricks. Then added the event declaration and the sub declaration and code; also added the Option statements. Built the project.
This was successfully used from this VB6 code. Clicking the button resulted in "Event fired" being written to the immediate window. This was successful with using both New and CreateObject methods of creating the reference to newTricks.
Option Explicit
Private WithEvents oTricks As NewTricksDll.newTricks
Private Sub Command1_Click()
Set oTricks = New NewTricksDll.newTricks
'Set oTricks = CreateObject("NewTricksDll.newTricks")
oTricks.SendAnEvent
End Sub
Private Sub oTricks_NotificationEvent()
Debug.Print "Event fired"
End Sub
Here is the corresponding C# code for the VB.net code, as converted straight-up by https://converter.telerik.com/
using Microsoft.VisualBasic;
[ComClass(newTricks.ClassId, newTricks.InterfaceId, newTricks.EventsId)]
public class newTricks
{
// 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 string ClassId = "386d540d-f8b8-46e1-939d-7b69dd5eff0a";
public const string InterfaceId = "78b4036e-86a0-4671-997d-da5a33bf026f";
public const string EventsId = "7b0fa5b5-b45e-4db2-9282-c06e09852161";
// 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 newTricks() : base()
{
}
public event NotificationEventEventHandler NotificationEvent;
public delegate void NotificationEventEventHandler();
public void SendAnEvent()
{
NotificationEvent?.Invoke();
}
}
I have not tried this C# code.
If you use CreateObject, it creates an object of type Object I believe. You should create the object simply by using New:
Set _tricky = New NewTricksDLL.NewTricks
Since you declared the variable as WithEvents, you can't just declare it using As New NewTricksDLL.NewTricks which I imagine you probably tried.

When adding a web service reference, I can't get all properties of an object

I'm using VB.Net in Visual Studio 2012 for a project with a web service I can't change at all. The problem is that VB does not generate all the properties it should generate.
I added the web service as a reference service. According to the XSD, collectionRAEEDataType class should include properties as receiver, referenceNumber, sigCode. But, when I try to access them, two of them are not shown: sigCode and responsabilitySystemData.
I've contacted the support email of the web service and they tell me that yes, that in the Java service the class is generated with those fields:
public class CollectionRAEEDataType {
protected String sigCode;
protected RegisteredInfoDataType responsabilitySystemData;
...
}
But in Reference.vb I get this:
Partial Public Class collectionRAEEDataType
Inherits Object
Implements System.ComponentModel.INotifyPropertyChanged
Private itemField As Object
Private receiverField As receiverType
Private referenceNumberField As String
Private assignmentOfficeIdField As String
'''<remarks/>
<System.Xml.Serialization.XmlElementAttribute("responsabilitySystemData", GetType(registeredInfoDataType), Form:=System.Xml.Schema.XmlSchemaForm.Unqualified, Order:=0), _
System.Xml.Serialization.XmlElementAttribute("sigCode", GetType(collectionRAEEDataTypeSigCode), Form:=System.Xml.Schema.XmlSchemaForm.Unqualified, Order:=0)> _
Public Property Item() As Object
Get
Return Me.itemField
End Get
Set
Me.itemField = value
Me.RaisePropertyChanged("Item")
End Set
End Property
'''<remarks/>
<System.Xml.Serialization.XmlAttributeAttribute()> _
Public Property receiver() As receiverType
Get
Return Me.receiverField
End Get
Set
Me.receiverField = value
Me.RaisePropertyChanged("receiver")
End Set
End Property
...
As you can see, the property receiver is ok, but responsabilitySystemData and sigCode are not properties.
Do you know how could I solve this problem?
Thanks a lot.
I answer myself. I had to instantiate the Item field with the type I wanted, registeredInfoDataType or collectionRAEEDataTypeSigCode.
For example,
MyElement.Item = New registeredInfoDataType()
Thank you.

Null reference exceptions on properties of a newly instantiated class? What's going on here?

I'm toying around with a 3rd party library and something has me absolutely puzzled.
When I instantiate this class, immediately all of the properties of the class throw exceptions before any more code even runs. Why is this happening? It's not just this TypedSegmentPWK class, it's all of the typedsegment classes.. of which there are many.
Simple instantiation fails
Imports OopFactory.X12.Parsing
Imports OopFactory.X12.Parsing.Model
Imports OopFactory.X12.Parsing.Model.Typed
....
Dim test As New TypedSegmentPWK
test.PWK04_EntityIdentifierCode = "blah"
Assigning a value to PWK04_EntityIdentifierCode or any other property of test fails with a null reference exception.
TypedSegmentPWK:
Namespace OopFactory.X12.Parsing.Model.Typed
Public Class TypedSegmentPWK
Inherits TypedSegment
Public Sub New()
Public Property PWK01_ReportTypeCode As String
Public Property PWK02_ReportTransmissionCode As String
Public Property PWK03_ReportCopiesNeeded As Integer?
Public Property PWK04_EntityIdentiferCodeEnum As EntityIdentifierCode
Public Property PWK04_EntityIdentifierCode As String
Public Property PWK05_IdentificationCodeQualifier As String
Public Property PWK05_IdentificationCodeQualifierEnum As IdentificationCodeQualifier
Public Property PWK06_IdentificationCode As String
Public Property PWK07_Description As String
Public Property PWK08_ActionsIndicated As String
Public Property PWK09_RequestCategoryCode As String
End Class
End Namespace
TypedSegment:
Namespace OopFactory.X12.Parsing.Model
Public MustInherit Class TypedSegment
Protected Sub New(segmentId As String)
Public Event Initialized As EventHandler
Public Event Initializing As EventHandler
Protected Overridable Sub OnInitialized(e As EventArgs)
Protected Overridable Sub OnInitializing(e As EventArgs)
End Class
End Namespace
Full source here: https://github.com/KeyMarkInc/OopFactory.X12
All the properties reference _segment defined in TypedSegment, e.g.
public string PWK04_EntityIdentifierCode
{
get { return _segment.GetElement(4); }
set { _segment.SetElement(4, value); }
}
However, the _segment variable is not initialized until TypedSegment.Initialize(Container parent, X12DelimiterSet delimiters) is called...
internal void Initialize(Container parent, X12DelimiterSet delimiters)
{
OnInitializing(new EventArgs());
_segment = new Segment(parent, delimiters, _segmentId);
OnInitialized(new EventArgs());
}
This is an internal method, so presumably something in this framework is supposed to call it, and not you as the user. So, I would guess the answer is that you are using the TypedSegmentPWK class incorrectly, although I don't know what the correct way is.

Using VB.net Collection class via COM returns error: "Class doesn't support automation"

I have an existing VB.net class library which has a public property with a type of VB's Collection class. I'm exposing the class library as a COM-object to be able to use it in Progress.
When I access the Collection-property with an integer index (e.g. comObj.OutputCol.Item(1)) it works fine, but when I try to use the string indexer (e.g. comObj.OutputCol.Item("FirstCol")) I get the following error (from a VBScript I use for testing):
Error message: Class doesn't support automation
Error code: 800A01AE
Is it possible to use the string indexer in any way via COM?
Sample code, COM-object i VB.net:
<ComClass(TestClass.ClassId, TestClass.InterfaceId, TestClass.EventsId)>
Public Class TestClass
Public Const ClassId As String = "063CA388-9926-44EC-B3A6-856D5299C210"
Public Const InterfaceId As String = "094ECC57-4E84-423A-B20E-BD109AEDBC20"
Public Const EventsId As String = "038B18BD-54B4-42D3-B868-71F4C52345B0"
Private _sOutputCol As Collection = Nothing
Private Property sOutputCol() As Collection
Get
If _sOutputCol Is Nothing Then
_sOutputCol = New Collection()
End If
Return _sOutputCol
End Get
Set(ByVal Value As Collection)
_sOutputCol = Value
End Set
End Property
Public ReadOnly Property OutputCol() As Collection
Get
Return sOutputCol
End Get
End Property
Public Sub New()
sOutputCol.Add("First object", "FirstCol")
sOutputCol.Add(2, "SecondCol")
End Sub
End Class
Sample test-code in VBScript:
Set comObj = WScript.CreateObject("VbComTest.TestClass")
wscript.echo comObj.OutputCol.Item(1) ' Works
wscript.echo comObj.OutputCol.Item(CStr("FirstCol")) ' Gives the error
I have registred the dll with: >regasm "...path...\VbComTest.dll" /codebase
OK, the problem was that the indexer is overloaded and you shouldn't use that in COM-visible interfaces: https://msdn.microsoft.com/en-us/library/ms182197.aspx
Extract from the page about what happens to overloaded methods:
When overloaded methods are exposed to COM clients, only the first
method overload retains its name. Subsequent overloads are uniquely
renamed by appending to the name an underscore character '_' and an
integer that corresponds to the order of declaration of the overload.
For example, consider the following methods.
void SomeMethod(int valueOne); void SomeMethod(int valueOne, int
valueTwo, int valueThree); void SomeMethod(int valueOne, int
valueTwo);
These methods are exposed to COM clients as the following.
void SomeMethod(int valueOne); void SomeMethod_2(int valueOne,
int valueTwo, int valueThree); void SomeMethod_3(int valueOne, int
valueTwo);
Visual Basic 6 COM clients cannot implement interface methods by using
an underscore in the name.
So to use the string indexer I have to write:
wscript.echo comObj.OutputCol.Item_3("FirstCol")
(Item_2 takes an Object as parameter and will also work, if the documentation is correct).

How to Inject a parameter to constructor using unity

Hi I am using unity in WebAPI 1.0 and registered it under Global.asax.vb file as below
Dim container As IUnityContainer = New UnityContainer()
container.RegisterType(Of Car)(New InjectionConstructor(GetType(Integer)))
Now how do i pass integer value which is passed by the client application into car type using
Public Sub New(ByVal intUserId As Int32)
objCar = DependencyResolver.Current.GetService(Of car)()
End Sub
Can't find anything to use (ParameterOverride("intUserId", intUserId) within DependencyResolver
It is very similar as registering unity container with ASP.NET MVC. Though web api has a different execution pipe line. Follow these steps.
NB: I have converted this code from C# to VB using a converter. So I hope it is syntactically correct. Though it is accurate in c#.
1) Implement IDependencyResolver (Make sure you are resolving correct namespace here. IDependencyResolver should come from System.Web.Http.Dependencies. Make note of Imported namespaces.
Imports System.Collections.Generic
Imports System.Linq
Imports System.Web
Imports Microsoft.Practices.Unity
Imports System.Web.Http.Dependencies
Namespace YourNamespace.Framework
Public Class UnityApiDependencyResolver
Implements IDependencyResolver
Private _container As IUnityContainer = Nothing
Public Sub New(container As IUnityContainer)
Me._container = container
End Sub
Public Function GetService(serviceType As Type) As Object
Try
Return _container.Resolve(serviceType)
Catch generatedExceptionName As Exception
Return Nothing
End Try
End Function
Public Function GetServices(serviceType As Type) As IEnumerable(Of Object)
Try
Return _container.ResolveAll(serviceType)
Catch generatedExceptionName As Exception
Return Nothing
End Try
End Function
Public Function BeginScope() As IDependencyScope
Return Me
End Function
Public Sub Dispose()
'
End Sub
End Class
End Namespace
'=======================================================
'Service provided by Telerik (www.telerik.com)
'Conversion powered by NRefactory.
'Twitter: #telerik
'Facebook: facebook.com/telerik
'=======================================================
2) Configure your container either thgough config or through code.
3) Register your container in Global.asax.vb file
Dim container = New UnityContainer()
Dim section As UnityConfigurationSection = TryCast(ConfigurationManager.GetSection("unity"), UnityConfigurationSection)
section.Configure(container, "UnitySection")
'api dependency resolver
GlobalConfiguration.Configuration.DependencyResolver = New UnityApiDependencyResolver(container)
'=======================================================
'Service provided by Telerik (www.telerik.com)
'Conversion powered by NRefactory.
'Twitter: #telerik
'Facebook: facebook.com/telerik
'=======================================================
Thats it.
Now you can declare your dependency in any of your API controller and it will be injected by Unity
Public Sub New(repositoryFactory As IRepositoryFactory, serviceFactory As IServiceFactory)
Me.repositoryFactory = repositoryFactory
Me.serviceFactory = serviceFactory
End Sub
'=======================================================
'Service provided by Telerik (www.telerik.com)
'Conversion powered by NRefactory.
'Twitter: #telerik
'Facebook: facebook.com/telerik
'=======================================================
Your type registration is wrong as well. you have to specify either an interface or an abstract class as dependency and its concrete implementation as its mapping.
e.g
container.RegisterType(Of IContext, Context)()
I don't understand what are trying to achieve by mapping an integer value to car. Do you want car object to be loaded based on integer value in your parameter?