Search This Blog

Friday, May 25, 2012

C# Emumeration with custom ToString method



When using Enum in any language you want to have a method to pretty print them. A very easy way to accomplish this in C# is with extension methods.
Here is a quick example

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace RickysGuitars
{
    public enum GuitarType
    {
        ACOUSTIC,
        ELECTRIC
    }

    public static class GuitarTypeExtentions
    {
        public static string ToPrettyString(this GuitarType source)
        {
            switch (source)
            {
                case GuitarType.ACOUSTIC:
                    return "Acoustic Guitar";
                case GuitarType.ELECTRIC:
                    return "Electric Guitar";
            }
            return null;
        }

    }

    
}


An example of the method call would be

GuitarType a = GuitarType.ELECTRIC;
            Debug.WriteLine(a.ToPrettyString());

which prints: Electric Guitar



Monday, May 7, 2012

Example of a COM Dll developed in VB.NET with the COM template

You can find here the code. It is a VS2008 solution file. Just use the .vb classes if you don't have VS2008 to open up the solution

I have already shown in my previous post how to create COM interop assembly in VB.NET and in C#.

You can have a look here and here. For More more detailed info please look also here
where you will find plenty of details on the how COM dll development and deployment works.

I will try to summarize some important point relevant to the VB.NET developer using the COM template here

1) The COM template automatically ticks for you
    Compile / Register for COM interop
    Application / Assembly Infomation... / Make assembly COM Visible
   
The second option is actually a bed idea, becuase it will register for COM all the types you declare in the assembly. If you have some assembly without the GUID it will create them for you generating a registry bloat.
So each time you add a COM Template, go and untick Make assembly COM-Visible.
Once you have done that you need to add as class attributes. (see the code)

2) If you define a Default Property, this will become a default property for your COM object as well. You can also have indexed properties. The ComClassAttribute will associate to it a DispId(0)

3) if you define a GeEnumerator() function that return a IEnumerator than you will enable the For Each ... Next
    loop in VBA. ComClassAttribute will associate to it a DispId(-4)

Public Function GetEnumerator() As System.Collections.IEnumerator Implements              System.Collections.IEnumerable.GetEnumerator
End Function

4) Also public events are exposed.


The code will show you how to create a Collection with a default property and the For Each ... Next loop enabled and how to expose and event.

Employee Class

Imports System.Runtime.InteropServices

<ComClass(Employee.ClassId, Employee.InterfaceId, Employee.EventsId), _
 ComVisible(True)> _
Public Class Employee

#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 = "dd3ef2f6-261f-477d-af54-10abc39a07d9"
    Public Const InterfaceId As String = "a0680708-b5ca-4679-8e8e-1b012479b8ee"
    Public Const EventsId As String = "d7361527-7a80-4e47-9aff-4e603a26812b"
#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

    Private _Name As String
    Public Property Name() As String
        Get
            Return _Name
        End Get
        Set(ByVal value As String)
            _Name = value
        End Set
    End Property
End Class 
 


 
Employer Class

Imports System.Runtime.InteropServices

<ComClass(Employer.ClassId, Employer.InterfaceId, Employer.EventsId), _
ComVisible(True)> _
Public Class Employer

#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 = "a0513ce8-fac4-4187-8190-0584f59cda1e"
    Public Const InterfaceId As String = "2c55f846-2dc9-4f0f-9b82-5e16dfefee52"
    Public Const EventsId As String = "2f99d8e4-afe8-46ef-a4e0-d62b4db18a4d"
#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 OnNameChange(ByRef newName As String)

    Private _Name As String
    Public Property Name() As String
        Get
            Return _Name
        End Get
        Set(ByVal value As String)
            RaiseEvent OnNameChange(value)
            _Name = value

        End Set
    End Property

End Class

Collection Class


Imports System.Runtime.InteropServices


<ComClass(MyCol.ClassId, MyCol.InterfaceId, MyCol.EventsId), _
ComVisible(True)> _
Public Class MyCol
    Implements IEnumerable

#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 = "994ba5ce-1301-455b-9334-409e28aea0c3"
    Public Const InterfaceId As String = "87280e58-8be8-40f8-8987-d3fac317c6c3"
    Public Const EventsId As String = "11d12ead-4562-4ab1-a04b-5ef7fa9fba4c"
#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.
    Dim _SortedList As SortedList
    Public Sub New()
        MyBase.New()
        _SortedList = New SortedList
    End Sub

    Default Public Property Item(ByVal key As Object)
        Get
            Return _SortedList(key)
        End Get
        Set(ByVal value)
            _SortedList(key) = value

        End Set
    End Property

    Public ReadOnly Property Count()
        Get
            Return _SortedList.Count
        End Get
    End Property


    Public Sub Remove(ByVal key As Object)
        _SortedList.Remove(key)
    End Sub


    Public Sub Add(ByVal key As Object, ByVal value As Object)
        _SortedList.Add(key, value)
    End Sub


    Public Function GetEnumerator() As System.Collections.IEnumerator Implements System.Collections.IEnumerable.GetEnumerator
        'Return _SortedList.GetEnumerator()
        Dim keys As ICollection = _SortedList.Keys
        Return CType(keys.GetEnumerator, IEnumerator)
    End Function
End Class



VBA Code to test the class


Option Explicit
Dim WithEvents a As TestCOMVisible01.Employer


Sub prova()
If a Is Nothing Then
   Set a = New TestCOMVisible01.Employer
End If
a.Name = "Gino"

Debug.Print a.Name

Dim emp1 As New TestCOMVisible01.Employee
Dim emp2 As New TestCOMVisible01.Employee
Dim col As New TestCOMVisible01.MyCol

emp1.Name = "mario"
emp2.Name = "pluto"
Call col.Add("1", emp1)
Call col.Add("2", emp2)

Dim key As Variant
For Each key In col
 Debug.Print col(key).Name
 Next




End Sub

Private Sub a_OnNameChange(newName As String)
   newName = "ho cambiato il nome"
End Sub

Developing a COM Class Collection using VB.NET COM Template

You can find the code here

In this post I will show you how to develop a COM Class Collection in VB.NET using the COM Template.
It is actually very easy, much easier that doing it manually. Here you can see the manual procedure.

The VB.NET ComClassAttribute, used by the COM Class template will generate for you automatically all the interface that you need to be exposed to COM.


If you define a Default indexed property, it will make it the default property for the COM Object, i.e. it will associate a DispId(0) to the Default indexed property.

In addition if you define a function

Function GetEnumerator() as System.Collection.IEnumerator
End Function

it will mark it as DispId(-4) to make it usable for the VB6/VBA For Each ... Next loop.

The COM Add-in will also create for you all the necessary GUID.

As you create a COM Class using the template, the template automatically will tick for you
1) Register for COM interop in Project Property/Compile/Register for COM Interop
2) It will make the assembly COM Visible. It will tick Project Property/Application/Assembly Infomatin/Make Assemby COM Visible.

The second part is usually a bad idea, this is because every type you include in the libray will be exported to COM. In case you do not provide some GUID for the type, each time you build the assembly the project will create some new ones for you, thus creating a dll hell.

The best thing you can do is to Untick Make Assembly COM Visible (and do it every time you use the COM template) and add a attribute ComVisible(true) on top of the class.
See an exampe here



Imports System.Collections
Imports System.Runtime.InteropServices



<ComClass(Employees.ClassId, Employees.InterfaceId, Employees.EventsId)> _
Public Class Employees
    Implements System.Collections.IEnumerable

#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 = "4999e186-4ea8-4ce1-8da4-12db6f8600e8"
    Public Const InterfaceId As String = "43ecbe2f-714b-4dc9-a76c-85a84320b66d"
    Public Const EventsId As String = "069d7776-4953-44c2-bd17-0ff75cb5748b"#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.
    Dim _SortedList As SortedList
    Public Sub New()
        MyBase.New()
        _SortedList = New SortedList
    End Sub

    Default Public Property Item(ByVal key As Object)
        Get
            Return _SortedList(key)
        End Get
        Set(ByVal value)
            _SortedList(key) = value

        End Set
    End Property

    Public ReadOnly Property Count()
        Get
            Return _SortedList.Count
        End Get
    End Property


    Public Sub Remove(ByVal key As Object)
        _SortedList.Remove(key)
    End Sub


    Public Sub Add(ByVal key As Object, ByVal value As Object)
        _SortedList.Add(key, value)
    End Sub


    Public Function GetEnumerator() As System.Collections.IEnumerator Implements System.Collections.IEnumerable.GetEnumerator
        'Return _SortedList.GetEnumerator()
        Dim keys As ICollection = _SortedList.Keys
        Return CType(keys.GetEnumerator, IEnumerator)
    End FunctionEnd Class

Developing a COM Class Collection in VB.NET without using the COM Template

you can find the code here

This is an example of a COM Class Collection written in VB.NET.

You need to start a new project of type library, and set the project property Build -> Register for COM Interop
Do not check: Application, Assembly Information, Make Class COM Visible.
We are using the COMVisible attribute to decide which class is visible for us
The class will have both a default property  and an iterator. The iterator is exposed defining a public function.

Function GetEnumerator() as IEnumerator
End Function


It is also a Good Idea having the class to implement IEnumerable

Function GetEnumerator() as IEnumerator Implements IEnumerable.GetEnumerator

End Function

In order to get the new GUID use can either use the VB.NET COM template of the Tools- Create GUID tool.
In addtion you can also use my C# Com template to start with, and translate the code with a C# to VB.NET tool.
Other wise, you can just use the VB.NET COM tool. It is kind of easy to use and much faster


Imports System.Runtime.InteropServices
Imports System.Collections

'Wee first define the interface of the Collection
<Guid("8beb176f-5357-4bb9-a5c1-38bdd0f7d3df"), _
InterfaceType(ComInterfaceType.InterfaceIsDual), _
ComVisible(True)> _
Public Interface INewEmployees
    Inherits System.Collections.IEnumerable


    <DispId(-4)> Shadows Function GetEnumerator() As IEnumerator 'Iterator
    <DispId(1)> Sub Add(ByVal key As Object, ByVal value As Object)
    <DispId(2)> ReadOnly Property Count()
    <DispId(3)> Sub Remove(ByVal key As Object)
    <DispId(0)> Default Property Item(ByVal key As Object)

End Interface

'We define the event interface
<Guid("e96bda2f-596f-419b-840c-4bd165930c4d"), _
InterfaceType(ComInterfaceType.InterfaceIsIDispatch), _
ComVisible(True)> _
Public Interface INewEmployeesEvents

End Interface



'<ComClass(NewEmployees.ClassId, NewEmployees.InterfaceId, NewEmployees.EventsId)> _
<Guid("67d85fea-43d6-457e-8db1-cc9601bdd9ec"), _
ClassInterface(ClassInterfaceType.None), _
ComSourceInterfaces(GetType(INewEmployeesEvents)), _
ComDefaultInterface(GetType(INewEmployees)), _
ComVisible(True)> _
Public Class NewEmployees
    Implements INewEmployees


#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 = "67d85fea-43d6-457e-8db1-cc9601bdd9ec"
    Public Const InterfaceId As String = "8beb176f-5357-4bb9-a5c1-38bdd0f7d3df"
    Public Const EventsId As String = "e96bda2f-596f-419b-840c-4bd165930c4d"
#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.
    Dim _SortedList As SortedList
    Public Sub New()
        MyBase.New()
        _SortedList = New SortedList
    End Sub



    Default Public Property Item(ByVal key As Object) Implements INewEmployees.Item
        Get
            Return _SortedList(key)
        End Get
        Set(ByVal value)
            _SortedList(key) = value

        End Set
    End Property

    Public ReadOnly Property Count() Implements INewEmployees.Count
        Get
            Return _SortedList.Count
        End Get
    End Property


    Public Sub Remove(ByVal key As Object) Implements INewEmployees.Remove
        _SortedList.Remove(key)
    End Sub


    Public Sub Add(ByVal key As Object, ByVal value As Object) Implements INewEmployees.Add
        _SortedList.Add(key, value)
    End Sub


    Public Function GetEnumerator() As System.Collections.IEnumerator Implements INewEmployees.GetEnumerator, System.Collections.IEnumerable.GetEnumerator
        'Return _SortedList.GetEnumerator()
        Dim keys As ICollection = _SortedList.Keys
        Return CType(keys.GetEnumerator, IEnumerator)
    End Function


End Class


COM interop an interesting link


Here you can find an interesting link about COM interop

http://limbioliong.wordpress.com/2011/10/28/exposing-an-enumerator-from-managed-code-to-com/

This is one of his most important tips

The interface type should be either :

ComInterfaceType.InterfaceIsDual
or ComInterfaceType.InterfaceIsIDispatch
otherwise the GetEnumerator() method
will not be marked with dispid -4.
Without this dispid, this method
will not be recognized by COM as
returning an enumerator. It will not thus
not be usable in a VB6.0 For Each Next
statement.

Example of a COM Class Collection Written in C#

This is an example of a COM Class Collection written in C#.
You can use as starting point my template or use the Tools - Create Guid Tool on VS 2008.
You need to start a new project of type library, and set the project property Build -> Register for COM Interop
Do not check: Application, Assembly Information, Make Class COM Visible.
We are using the COMVisible attribute to decide which class is visible for us
The class will have both a defaul property (the indexers) and an iterator




using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.Collections;

namespace TestEmployeesCol
{
    //Wee first define the interface of the Collection
    [Guid("21C027E8-CF8C-4166-A63B-25D8E790F040"), InterfaceType(ComInterfaceType.InterfaceIsDual), ComVisible(true)]
    public interface INewEmployees : System.Collections.IEnumerable
    {


        [DispId(-4)]
        new IEnumerator GetEnumerator();
        //Iterator
        [DispId(1)]
        void Add(object key, object value);
        [DispId(2)]
        object Count { get; }
        [DispId(3)]
        void Remove(object key);
        [DispId(0)]
        object this[object key] { get; set; }
    }

    //We define the event interface
    [Guid("5C6B8153-D2D6-4e98-80EF-D13A53CC9CDD"), InterfaceType(ComInterfaceType.InterfaceIsIDispatch), ComVisible(true)]
    public interface INewEmployeesEvents
    {

    }



    //<ComClass(NewEmployees.ClassId, NewEmployees.InterfaceId, NewEmployees.EventsId)> _
    [Guid("1692DD4D-6F3E-4e77-AB50-5401F04306DC"), 
    ClassInterface(ClassInterfaceType.None),
    ComSourceInterfaces(typeof(INewEmployeesEvents)), 
    ComDefaultInterface(typeof(INewEmployees)), 
    ComVisible(true)]
    public class NewEmployees : INewEmployees
    {


        #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 string ClassId = "1692DD4D-6F3E-4e77-AB50-5401F04306DC";
        public const string InterfaceId = "21C027E8-CF8C-4166-A63B-25D8E790F040";
        #endregion
        public const string EventsId = "5C6B8153-D2D6-4e98-80EF-D13A53CC9CDD";

        // 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.
        SortedList _SortedList;
        public NewEmployees()
            : base()
        {
            _SortedList = new SortedList();
        }



        public object this[object key]
        {
            get { return _SortedList[key]; }

            set { _SortedList[key] = value; }
        }

        public object Count
        {
            get { return _SortedList.Count; }
        }


        public void Remove(object key)
        {
            _SortedList.Remove(key);
        }


        public void Add(object key, object value)
        {
            _SortedList.Add(key, value);
        }


        public System.Collections.IEnumerator GetEnumerator()
        {
            ICollection keys = _SortedList.Keys;
            return (IEnumerator)keys.GetEnumerator();
        }


    }



}

Saturday, May 5, 2012

Visual Studio 2008 Image library



The image library should be at "C:\Program Files\Microsoft Visual Studio 9.0\Common7\VS2008ImageLibrary\1033\VS2008ImageLibrary.zip" and in your installation process you must ensure that you check the option "Tools for redistributing Applications\Graphics Library" (See the attached image)