I developed a Web Service that checks user authorizations by querying a central database. It should be consumed by any application whenever a user starts it, in order to check if the user is allowed to run the application. Since in the company there are many old applications written in Visual Basic 6 by people that do not have good programming skills, I'm trying to create a DLL that they can easily use in their VB6 applications to check user authorizations through a simple function call instead of dealing with details such as Web Service calls and XML messages.
I created a Class Library in Visual Studio 2010 using C#, which simply wraps the Web Service functions. I successfully created the DLL file and registered it (through RegAsm) into another machine used to develop Visual Basic 6 applications.
In VB6, the intellisense shows the functions exposed by the DLL (that can be called) but it is not working for the complex types returned by the functions.
For example, the DLL defines the following function, where the returned type (User
) is defined into the Web Service code:
[ClassInterface(ClassInterfaceType.AutoDual)]
[ProgId("AuthorLibrary.Author")]
[Guid("D2E23F5E-C209-464A-AF32-CC0251078076")]
//[ComDefaultInterface(typeof(IAuthor))]
public class Author //: AuthorLibrary.IAuthor
{
//[...]
public User getUserData(string username)
{
ResponseMessageOfUser user;
using (AccessControlSoapClient ws = new AccessControlSoapClient())
{
user = ws.getUserData(username);
}
if (user.response != ResponseType.Ok)
throw new Exception(user.response.ToString());
return user.result;
}
//[...]
}
In VB6, the following code runs correctly:
Dim ws As AuthorLibrary.Author
Set ws = New AuthorLibrary.Author
Dim u As AuthorLibrary.User
Set u = ws.getUserData("john.smith")
Text1.Text = u.email
The problem is that when the developer writes u.
nothing appears and so he does not know the available properties and/or methods for the object (even if, in the example above, u.email
works).
Another method returns an instance of Department
(let's call it dept
). I can successfully print the values of simple string
properties like dept.Description
or dept.Code
, but more complex properties such as dept.childrenDepartments
- which should return a list of Department
- give an error: "Missing object".
I think the problem is that the DLL file does not show the classes/types/structures defined into the Web Service. How can I do that?
I have also tried to define the following interface (IAuthor
, as you can see from the first code), but it doesn't seem to work:
[InterfaceType(ComInterfaceType.InterfaceIsDual)]
[Guid("DB3BA840-B997-4D5E-86F0-0FC0A786C0D3")]
interface IAuthor
{
//[...]
}
The [InterfaceType] attribute is indeed the key. The default is
ComInterfaceType.InterfaceIsIDispatch
if the attribute is not applied explicitly. Which permits only late-binding, the equivalent of calling a method on an object reference of type dynamic in C#. Note how you don't get IntelliSense for that either, the VB6 programmer has the same problem.The IAuthor you tried probably doesn't work because you forgot to make it public.
So you need to get that attribute on the
User
class. You'll have a problem because you didn't create that class. So for one, you probably can't easily declare an IUser interface either. The fallback for that is applying[ClassInterface(ClassInterfaceType.AutoDual)]
on the class. It isn't exactly the prettiest solution, that also exposes the members of System.Object and gives the VB6 programmer a dependency on the .NET Framework type library but that's not the end of the world. You however still have to edit the class declaration to apply that attribute.If it was generated from a web service then you'll have a maintenance headache. Every time it is re-generated you'll have to make the edit again. Then again, that is liable to break the VB6 code as well, it has to be recompiled. So you might want to consider isolating that declaration so this isn't strictly necessary. Declare your own User class and write the code to copy the fields/properties of the web service's User object to yours. And of course you won't have any trouble writing an interface and applying the attribute.