Link to Class diagrma Layering
Link to Class Diagram Mail System.
NClass 2.0 or higher is needed to read the file. NClass Website
In this article we are going to discuss how to separate the Data Access Layer (DAL) form the Business Logic Layer (BLL) using
1) The Abstract Factory Pattern
2) The Model Provider Pattern
3) Reflections
On the Class Diagram Mail System you will find many notes on how to separate the BLL from the DAL
I will first comment on the Class diagram you will find on the link. After I will develop an applicaton. This is a working in progress. As usual if you have any suggestion or improvement, just add a comment.
Sunday, May 23, 2010
Parsing an XML file using C#
Link to the Code (VS 2008)
I will show you here two pieces of code in C# that will let you parse the Products.XML into a List object.
The first one makes use of the System.XML namespace, while the second use System.XML.Linq one.
The Linq code is pretty amazing, it is so simple I will not even comment it. I managed also to include a "where" clause to filter the output.
An additional note has to be done for the XMLReader methods class for the first example
xmlIn.ReadToDescendant("Product") == true //This code move the cursor to the first "Product"
product.Code = xmlIn["Code"]; //Since we are already on the Product Element we can read its attribute with the indexer
xmlIn.ReadStartElement("Product"); // Checks that we are on Product Element and moves one raw ahead
product.Description = xmlIn.ReadElementContentAsString(); //Read the Content of the Element Description, and moves one raw ahead
product.Price = xmlIn.ReadElementContentAsDouble(); //Read the Content ofthe Element Price, and moves one raw ahead
xmlIn.ReadToNextSibling("Product") // Move the Cursor to the next product and return true if found
//Read The First Root Node
if (xmlIn.ReadToDescendant("Product") == true)
{
do
{
Product product = new Product();
product.Code = xmlIn["Code"];
xmlIn.ReadStartElement("Product");
product.Description = xmlIn.ReadElementContentAsString();
product.Price = xmlIn.ReadElementContentAsDouble();
products.Add(product);
} while (xmlIn.ReadToNextSibling("Product"));
}
"Products.XML"
--------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------
"Product.cs"
--------------------------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace WindowsFormsApplication1
{
class Product
{
public string Code { get; set; }
public string Description { get; set; }
public double Price { get; set; }
}
}
--------------------------------------------------------------------------------------------------
Form1.cs
--------------------------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Xml;
using System.Xml.Linq;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
//Create the list
List products = new List();
//Create the path
string path = @"..\..\Products.xml";
//XmlReader Settings
XmlReaderSettings settings = new XmlReaderSettings();
settings.IgnoreComments = true;
settings.IgnoreWhitespace = true;
//XmlReader Objcet
XmlReader xmlIn = XmlReader.Create(path, settings);
//Read The First Root Node
if (xmlIn.ReadToDescendant("Product") == true)
{
do
{
Product product = new Product();
product.Code = xmlIn["Code"];
xmlIn.ReadStartElement("Product");
product.Description = xmlIn.ReadElementContentAsString();
product.Price = xmlIn.ReadElementContentAsDouble();
products.Add(product);
} while (xmlIn.ReadToNextSibling("Product"));
}
xmlIn.Close();
}
private void button2_Click(object sender, EventArgs e)
{
//Create the path
string path = @"..\..\Products.xml";
//Create the xmlDoc object
XDocument xmlDoc = XDocument.Load(path);
List products = (
from product in xmlDoc.Descendants("Product")
where product.Attribute("Code").Value == "A4CS"
select new Product
{
Code = product.Attribute("Code").Value,
Description = product.Element("Description").Value,
Price = Convert.ToDouble(product.Element("Price").Value),
}
).ToList();
foreach (Product product in products)
{
Console.WriteLine(product.Code);
Console.WriteLine(product.Description);
Console.WriteLine(product.Price);
}
}
}
}
--------------------------------------------------------------------------------------------------
I will show you here two pieces of code in C# that will let you parse the Products.XML into a List
The first one makes use of the System.XML namespace, while the second use System.XML.Linq one.
The Linq code is pretty amazing, it is so simple I will not even comment it. I managed also to include a "where" clause to filter the output.
An additional note has to be done for the XMLReader methods class for the first example
xmlIn.ReadToDescendant("Product") == true //This code move the cursor to the first "Product"
product.Code = xmlIn["Code"]; //Since we are already on the Product Element we can read its attribute with the indexer
xmlIn.ReadStartElement("Product"); // Checks that we are on Product Element and moves one raw ahead
product.Description = xmlIn.ReadElementContentAsString(); //Read the Content of the Element Description, and moves one raw ahead
product.Price = xmlIn.ReadElementContentAsDouble(); //Read the Content ofthe Element Price, and moves one raw ahead
xmlIn.ReadToNextSibling("Product") // Move the Cursor to the next product and return true if found
//Read The First Root Node
if (xmlIn.ReadToDescendant("Product") == true)
{
do
{
Product product = new Product();
product.Code = xmlIn["Code"];
xmlIn.ReadStartElement("Product");
product.Description = xmlIn.ReadElementContentAsString();
product.Price = xmlIn.ReadElementContentAsDouble();
products.Add(product);
} while (xmlIn.ReadToNextSibling("Product"));
}
"Products.XML"
--------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------
"Product.cs"
--------------------------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace WindowsFormsApplication1
{
class Product
{
public string Code { get; set; }
public string Description { get; set; }
public double Price { get; set; }
}
}
--------------------------------------------------------------------------------------------------
Form1.cs
--------------------------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Xml;
using System.Xml.Linq;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
//Create the list
List
//Create the path
string path = @"..\..\Products.xml";
//XmlReader Settings
XmlReaderSettings settings = new XmlReaderSettings();
settings.IgnoreComments = true;
settings.IgnoreWhitespace = true;
//XmlReader Objcet
XmlReader xmlIn = XmlReader.Create(path, settings);
//Read The First Root Node
if (xmlIn.ReadToDescendant("Product") == true)
{
do
{
Product product = new Product();
product.Code = xmlIn["Code"];
xmlIn.ReadStartElement("Product");
product.Description = xmlIn.ReadElementContentAsString();
product.Price = xmlIn.ReadElementContentAsDouble();
products.Add(product);
} while (xmlIn.ReadToNextSibling("Product"));
}
xmlIn.Close();
}
private void button2_Click(object sender, EventArgs e)
{
//Create the path
string path = @"..\..\Products.xml";
//Create the xmlDoc object
XDocument xmlDoc = XDocument.Load(path);
List
from product in xmlDoc.Descendants("Product")
where product.Attribute("Code").Value == "A4CS"
select new Product
{
Code = product.Attribute("Code").Value,
Description = product.Element("Description").Value,
Price = Convert.ToDouble(product.Element("Price").Value),
}
).ToList
foreach (Product product in products)
{
Console.WriteLine(product.Code);
Console.WriteLine(product.Description);
Console.WriteLine(product.Price);
}
}
}
}
--------------------------------------------------------------------------------------------------
Wednesday, May 19, 2010
Developing COM exposed classes in C#
Press here to download the template. CSharp_Com_Class.zip will be downloaded.
For some code example see this post
Here you can find a series of notes I took as reminders to develpod a COM Class in C#.
I will put it here just as a reference, I hope I will have more time in the future to show you a full example.
You can also find here a C# template you can use to start develop C# COM exposed class. You can delete all the comments out of it. I just add them there for my reference.
To install the template in your VS2008 you need first to find out where they are stored.
To do this, go to File, Export Template. After a few click you should find out where your exported templates
are stored
On my PC for example, they are store here
C:\Users\PP\Documents\Visual Studio 2008\My Exported Templates\WFA01.zip
Once you know this path, just copy the CSharp_Com_Class.zip in the following directory.
You must copy the .zip file. Do not unzip them.
C:\Users\PP\Documents\Visual Studio 2008\Templates\ItemTemplates\Visual C#.
If things go well (and it took me sometime to figure out how to do it) you should have a new template in your
Add New Item, Visual C# Item
C# COM Rules:
To expose properties and methods to COM, you must declare them on the class
interface and mark them with a DispId attribute, and implement them in the class.
The order in which the members are declared in the interface is the
order used for the COM vtable.
ex:
[DispId(1)] void Init(string userid , string password);
[DispId(2)] bool ExecuteSelectCommand(string selCommand);
1)The Class must be public
2) Properties, methods and events that need to be exposed:
a) must be public
b) Properties and methods must be declared in the class interface. The class must implement this interface
c) Event must be declared on the Event Interface. The class should not implement this interface
3) Other Class member that are not declared in the class interface, are not visible to COM but are
visible to .NET classes
4) The class must have a default parametereless constructor. Always write is down even if is empty.
The class can have its constructors and methods overloaded.
5) COM does not support inheritance, only interface implementation
So you can't do Class Employees : List.
This will not be exposed to COM
6) C# can pass to COM zero based array using "ref" in the method signature.
Without ref, the method will not work!
ex: double[] Compute(ref double[] a, double b)
in VB 6.0 this will be like Dim a() as double
7) Variant can be passed as type "object"
8) Enum can be exposed. Remeber you need to generate a unique Guid using
C:\Programmi\Microsoft Visual Studio 9.0\Common7\Tools\guidgen.exe Registry format
[Guid("DE23AB62-269E-4418-BCBD-193BE024E21C"),
ComVisible(true)]
public enum MyEnum{
[DispId(1)] A = 0,
[DispId(1)] B = 1,
[DispId(1)] C = 2
}
Please not that "public enum MyEnum : long" will compile but will cause method that have MyEnum
in their signature not to work at all
9) Collections.
Collections can be implemented using encapsulation and delegation.
We can encapsulate a SortedList and delegate to it the implementation of
Count, Item, Remove, Add methods defined in the COM Interface.
Ex:
-----------------------------------------------------------------------------------------
[Guid("d345c3dc-825e-4be7-b129-2b3d00a7a2a7"),
ComVisible(true)]
public interface INetSortedList : IEnumerable
{
[DispId(-4)] new IEnumerator GetEnumerator(); //Iterator
[DispId(1)] void Add(object key, object value);
[DispId(2)] int Count{ get; }
[DispId(3)] void Remove(object key);
[DispId(0)] object this[object key] {get ; set; } //Default Property
}
//Events Interface
[Guid("4b1f6f84-c971-410a-8667-f5611f632b33"),
InterfaceType(ComInterfaceType.InterfaceIsIDispatch),
ComVisible(true)]
public interface INetSortedListEvents
{
}
//Class Implement the Class Interface
[Guid("1ca3e210-b5e3-458a-9175-002b7ff3274c"),
ClassInterface(ClassInterfaceType.None),
ComSourceInterfaces(typeof(INetSortedListEvents)),
ComVisible(true)]
public class NetSortedList : INetSortedList
{
private SortedList _sortedList;
public NetSortedList() { _sortedList = new SortedList(); }
public IEnumerator GetEnumerator() { return _sortedList.GetEnumerator(); }
public void Add(object key, object value) { _sortedList.Add(key, value);}
public int Count { get { return _sortedList.Count; } }
public void Remove(object key) { _sortedList.Remove(key); }
public object this[object key] {
get { return _sortedList[key]; }
set { _sortedList[key] = value; }
}
}
----------------------------------------------------------------------------------------------------------------
Please note that
a) the indexer "Employee this[int index] { get; set; }" will be seen in VB6.0 as
a default Item property so that you can do: list(1) or list.Item(1).
Its DispId must be set to = [DispId(0)] to work as default Property
b) System.Collections.IEnumerator GetEnumerator(); allows for the "for each" loop in VB 6.0. It
must have a [DispId(-4)]
You cannot use in the interface a Generic enumerator like "List.Enumerator" which is of type
System.Collections.Generic.List.Enumerator. You must use the non generic type
System.Collections.IEnumerator
The IEnumerator GetEnumerator() return in COM a IEnumVariant so it can be passed to NewEnum in
VB 6.0
Public Function NewEnun As IUnkonwn
NewEnum = obj.GetEnumerator()
End Function
In addition the "Current" propety of the Enumerator must return a type COM compatible,
otherwise the For Each Loop will work but you will not be able to access the Item of the collection. For
example for the code above the Current property will return a DictionaryEntry type that is not supported by
COM. A work around to this is to do the following
ICollection keys = _sortedList.Keys;
return (IEnumerator)keys.GetEnumerator();
This way the "Current" property will return the Keys in the collection and not the DictionaryEntry Object.
Another way around is to write your own Enumerator.
c) The collection is 0 based.
d) You could have the NetSortedList to implement System.Collections.Generic.ICollection
10) To make C# create and register the typelibrary go to
Project/Properties/Build tick Register for COM interop. (Reccomended Choice)
Otherwise you need to use REGASM. (asembly registration tool). The Assembly Registration tool reads
the metadata within an assembly and adds the necessary entries to the registry, which allows COM clients to create .NET Framework classes transparently. This utility is necessary when you need to expose to COM e .exe (winform application) .net assembly. You can create a type libray in this way. MyAssembly.dll can be also MyAssembly.exe
REGASM /codebase MyAssembly.dll /tlb:MyAssembly.tlb
To unregister just do
REGASM /u MyAssembly.dll /tlb:MyAssembly.tlb
As a note. When you add a COM dll to a project, C# calls REGASM for you, create a tlb file out of the
COM dll and put it in your project folder
For more details see the session DEPLOYMENT
11) It is important that you do not check the Project/Properties/Application/Assembly Information...
Make assembly COM-Visible. This option will set
[assembly: ConVisible(true)]
in the AssemblyInfo.cs file
Which will make all the Class in the Project COM visible. This in practice will make C# to generate
new Guids for each class on which we did not specify a Guid attribute each time we recompile, causing a registry bloat.
It is much better to set
[assembly: ConVisible(false)], and use ComVisible(true) at class level to specify which class should be
visible for COM interop
12) The assembly should be given a strong name.
Go to Properties/Signing. Tick Sign the assembly check box.
Then go to Choose a stroing name key file
Choose a name ex "MyLibrary_COM_Key"
This will create a MyLibray_COM_Key.snk file in the project folder.
The assembly is now signed with a strong name.
-----------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------
DEPLOYMENT
To deploy the Dll we need to register the .net assembly of a computer for COM interop.
To register it manually we have two ways
01) The dll assembly will reside on a specific folder decided at the moment of the assembly registration
You need to type the following commands:
regasm /u "FullPath\MyLibray.dll" /tlb
regasm /codebase "FullPath\MyLibray.dll" /tlb
ragasm is located in c:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\regasm.exe
The first line unregister MyLibrary.dll and its type library
The second line register MyLibrary.dll create the type libary and register them in the COM registry
the /codebase option saves in the regestry the path of the dll
This is exactly what the option "Register for COM interop" Does
If we do not put the /codebase option, VB6.0 will complain that it cannot find the dll file
02) The dll assembly will reside in the GAC c:\WINDOWS\assembly. The assembly MUST be strongly named if you want to install it in the GAC.
You need to type the following commands
regasm "FullPath\MyLibray.dll" /tlb
gacutil /if "FullPath\MyLibray.dll"
The first line register the assembly for COM interop. Please note that we must leave out /codebase, because
we will put the dll in the GAC.
The second line, registers MyLibray in the GAC.
This mean that all the application will look at the GAC when we run the assembly
To unregister we do
regasm /u "FullPath\MyLibrary.dll" /tlb
gacutil /uf MyLibrary
To Remove from the GAC a specif version
gacutil /uf MyLibrary , Version=1.1.0.0
Please note the we do not use neither the FullPath nor the .dll/.exe to remove an assembly from the GAC
in addition each .NET Framework has its own gacutil.exe. The one for .NET 3.5 can be found here
"C:\Program Files\Microsoft SDKs\Windows\v6.0A\bin\gacutil.exe"
Installing the assembly in the GAC ensures that multiple versions of the component can exist side-by-side.
An additional hard requirement for COM is that you change the GUIDs of the public interfaces and classes
when you change their definitions. Failing to do so will wipe out the registry info for the old component
and will make old client programs that have not been recompiled with the new component crash and burn.
A problem better known as DLL Hëll.
if We need to change the interace of our COM component we need to
1) Change the version number of the assembly. (Do not change the strong key name of file)
2) Change the GUIDs for both the Class and interface of the class that has been changed
Changing them all could be better to avoid trouble when they depend on each other.
For some code example see this post
Here you can find a series of notes I took as reminders to develpod a COM Class in C#.
I will put it here just as a reference, I hope I will have more time in the future to show you a full example.
You can also find here a C# template you can use to start develop C# COM exposed class. You can delete all the comments out of it. I just add them there for my reference.
To install the template in your VS2008 you need first to find out where they are stored.
To do this, go to File, Export Template. After a few click you should find out where your exported templates
are stored
On my PC for example, they are store here
C:\Users\PP\Documents\Visual Studio 2008\My Exported Templates\WFA01.zip
Once you know this path, just copy the CSharp_Com_Class.zip in the following directory.
You must copy the .zip file. Do not unzip them.
C:\Users\PP\Documents\Visual Studio 2008\Templates\ItemTemplates\Visual C#.
If things go well (and it took me sometime to figure out how to do it) you should have a new template in your
Add New Item, Visual C# Item
C# COM Rules:
To expose properties and methods to COM, you must declare them on the class
interface and mark them with a DispId attribute, and implement them in the class.
The order in which the members are declared in the interface is the
order used for the COM vtable.
ex:
[DispId(1)] void Init(string userid , string password);
[DispId(2)] bool ExecuteSelectCommand(string selCommand);
1)The Class must be public
2) Properties, methods and events that need to be exposed:
a) must be public
b) Properties and methods must be declared in the class interface. The class must implement this interface
c) Event must be declared on the Event Interface. The class should not implement this interface
3) Other Class member that are not declared in the class interface, are not visible to COM but are
visible to .NET classes
4) The class must have a default parametereless constructor. Always write is down even if is empty.
The class can have its constructors and methods overloaded.
5) COM does not support inheritance, only interface implementation
So you can't do Class Employees : List
This will not be exposed to COM
6) C# can pass to COM zero based array using "ref" in the method signature.
Without ref, the method will not work!
ex: double[] Compute(ref double[] a, double b)
in VB 6.0 this will be like Dim a() as double
7) Variant can be passed as type "object"
8) Enum can be exposed. Remeber you need to generate a unique Guid using
C:\Programmi\Microsoft Visual Studio 9.0\Common7\Tools\guidgen.exe Registry format
[Guid("DE23AB62-269E-4418-BCBD-193BE024E21C"),
ComVisible(true)]
public enum MyEnum{
[DispId(1)] A = 0,
[DispId(1)] B = 1,
[DispId(1)] C = 2
Please not that "public enum MyEnum : long" will compile but will cause method that have MyEnum
in their signature not to work at all
9) Collections.
Collections can be implemented using encapsulation and delegation.
We can encapsulate a SortedList and delegate to it the implementation of
Count, Item, Remove, Add methods defined in the COM Interface.
Ex:
-----------------------------------------------------------------------------------------
[Guid("d345c3dc-825e-4be7-b129-2b3d00a7a2a7"),
ComVisible(true)]
public interface INetSortedList : IEnumerable
{
[DispId(-4)] new IEnumerator GetEnumerator(); //Iterator
[DispId(1)] void Add(object key, object value);
[DispId(2)] int Count{ get; }
[DispId(3)] void Remove(object key);
[DispId(0)] object this[object key] {get ; set; } //Default Property
//Events Interface
[Guid("4b1f6f84-c971-410a-8667-f5611f632b33"),
InterfaceType(ComInterfaceType.InterfaceIsIDispatch),
ComVisible(true)]
public interface INetSortedListEvents
{
}
//Class Implement the Class Interface
[Guid("1ca3e210-b5e3-458a-9175-002b7ff3274c"),
ClassInterface(ClassInterfaceType.None),
ComSourceInterfaces(typeof(INetSortedListEvents)),
ComVisible(true)]
public class NetSortedList : INetSortedList
{
private SortedList _sortedList;
public NetSortedList() { _sortedList = new SortedList(); }
public IEnumerator GetEnumerator() { return _sortedList.GetEnumerator(); }
public void Add(object key, object value) { _sortedList.Add(key, value);}
public int Count { get { return _sortedList.Count; } }
public void Remove(object key) { _sortedList.Remove(key); }
public object this[object key] {
get { return _sortedList[key]; }
set { _sortedList[key] = value; }
}
}
----------------------------------------------------------------------------------------------------------------
Please note that
a default Item property so that you can do: list(1) or list.Item(1).
Its DispId must be set to = [DispId(0)] to work as default Property
b) System.Collections.IEnumerator GetEnumerator(); allows for the "for each" loop in VB 6.0. It
You cannot use in the interface a Generic enumerator like "List
The IEnumerator GetEnumerator() return in COM a IEnumVariant so it can be passed to NewEnum in
NewEnum = obj.GetEnumerator()
End Function
otherwise the For Each Loop will work but you will not be able to access the Item of the collection. For
ICollection keys = _sortedList.Keys;
return (IEnumerator)keys.GetEnumerator();
This way the "Current" property will return the Keys in the collection and not the DictionaryEntry Object.
Another way around is to write your own Enumerator.
c) The collection is 0 based.
d) You could have the NetSortedList to implement System.Collections.Generic.ICollection
Project/Properties/Build tick Register for COM interop. (Reccomended Choice)
Otherwise you need to use REGASM. (asembly registration tool). The Assembly Registration tool reads
the metadata within an assembly and adds the necessary entries to the registry, which allows COM clients to create .NET Framework classes transparently. This utility is necessary when you need to expose to COM e .exe (winform application) .net assembly. You can create a type libray in this way. MyAssembly.dll can be also MyAssembly.exe
REGASM
To unregister just do
REGASM /u MyAssembly.dll /tlb:MyAssembly.tlb
As a note. When you add a COM dll to a project, C# calls REGASM for you, create a tlb file out of the
COM dll and put it in your project folder
For more details see the session DEPLOYMENT
Make assembly COM-Visible. This option will set
[assembly: ConVisible(true)]
in the AssemblyInfo.cs file
Which will make all the Class in the Project COM visible. This in practice will make C# to generate
new Guids for each class on which we did not specify a Guid attribute each time we recompile, causing a registry bloat.
It is much better to set
[assembly: ConVisible(false)], and use ComVisible(true) at class level to specify which class should be
visible for COM interop
12) The assembly should be given a strong name.
Go to Properties/Signing. Tick Sign the assembly check box.
Then go to Choose a stroing name key file
Choose a name ex "MyLibrary_COM_Key"
This will create a MyLibray_COM_Key.snk file in the project folder.
The assembly is now signed with a strong name.
-----------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------
DEPLOYMENT
To deploy the Dll we need to register the .net assembly of a computer for COM interop.
To register it manually we have two ways
01) The dll assembly will reside on a specific folder decided at the moment of the assembly registration
You need to type the following commands:
regasm /u "FullPath\MyLibray.dll" /tlb
regasm /codebase "FullPath\MyLibray.dll" /tlb
ragasm is located in c:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\regasm.exe
The first line unregister MyLibrary.dll and its type library
The second line register MyLibrary.dll create the type libary and register them in the COM registry
the /codebase option saves in the regestry the path of the dll
This is exactly what the option "Register for COM interop" Does
If we do not put the /codebase option, VB6.0 will complain that it cannot find the dll file
02) The dll assembly will reside in the GAC c:\WINDOWS\assembly. The assembly MUST be strongly named if you want to install it in the GAC.
You need to type the following commands
regasm "FullPath\MyLibray.dll" /tlb
gacutil /if "FullPath\MyLibray.dll"
The first line register the assembly for COM interop. Please note that we must leave out /codebase, because
we will put the dll in the GAC.
The second line, registers MyLibray in the GAC.
This mean that all the application will look at the GAC when we run the assembly
To unregister we do
regasm /u "FullPath\MyLibrary.dll" /tlb
gacutil /uf MyLibrary
To Remove from the GAC a specif version
gacutil /uf MyLibrary , Version=1.1.0.0
Please note the we do not use neither the FullPath nor the .dll/.exe to remove an assembly from the GAC
in addition each .NET Framework has its own gacutil.exe. The one for .NET 3.5 can be found here
"C:\Program Files\Microsoft SDKs\Windows\v6.0A\bin\gacutil.exe"
Installing the assembly in the GAC ensures that multiple versions of the component can exist side-by-side.
An additional hard requirement for COM is that you change the GUIDs of the public interfaces and classes
when you change their definitions. Failing to do so will wipe out the registry info for the old component
and will make old client programs that have not been recompiled with the new component crash and burn.
A problem better known as DLL Hëll.
if We need to change the interace of our COM component we need to
1) Change the version number of the assembly. (Do not change the strong key name of file)
2) Change the GUIDs for both the Class and interface of the class that has been changed
Changing them all could be better to avoid trouble when they depend on each other.
UDFs for Excel in VSTO
VSTO does not support yet the introduction of UDFs, which is a kind of crazy!
However there are few work around.
UDFs as Automation Add-in
This technique is good if you want to make the function available for every workbook in the Excel Application. It is based on the development of an "Excel Automation Add-in".
This consist of developing a kind of C# COM exposed class.
It was a good idea that the author included in the example the GUID identifiers to avoid registry bloating, however I don't know why he did not defined an Internface and let the class implement it, which is the standard way to develop COM exposed class in .NET
http://blogs.msdn.com/eric_carter/archive/2004/12/01/273127.aspx
UDFs in Code behind files
This technique is good when you want to define workbook level UDFs. It is based on a COM exposed class and some VBA wrapper that you need to write at workbook level. The only problem I had was to the the Exel.Application.Run command work fine. My mistake was due to the fact that I was adding the VBA code the the Excel file that shows up after building and running the program. I did not realized that each time I run, the original .xls file used by the solution was overwriting the one I was editing.
http://blogs.msdn.com/pstubbs/archive/2004/12/31/344964.aspx
Consideration
Given the fact that VSTO does not suggest to save aa .XLA, a .XLS files based on VSTO solution, it is still quite hard to deploy an add-in that can use and excel file as data storage.
For example I developed some years ago an Excel Add-in .XLA to bootstrap the Interest Rate Swap Curve in US and Euro area, and I was using a spreadsheet in the .xls file to store data. The add-in makes available to every workbook a set of functions to compute some fixed income analytics : PV, duration, factor analysis...
Of course if this is the idea, the code-behind pattern VSTO solution is ruled out (it cannot make available function to everywork book that runs in an Excel application).
We are left with the Automation add-in solution, which is basically a COM Exposed Class. However, the automation add-in does not have any worksheet to store the data. To be honest one could also develop a COM based add-in (which I do not cover here), but I think it would not sort the problem out.
If anybody has any idea on how to develop .xla kind of add-in, ie a VSTO solution that allows to
1) make UDFs available at application level
2) use an Excel file to store some data
as an .xla solution does, please let me know.
However there are few work around.
UDFs as Automation Add-in
This technique is good if you want to make the function available for every workbook in the Excel Application. It is based on the development of an "Excel Automation Add-in".
This consist of developing a kind of C# COM exposed class.
It was a good idea that the author included in the example the GUID identifiers to avoid registry bloating, however I don't know why he did not defined an Internface and let the class implement it, which is the standard way to develop COM exposed class in .NET
http://blogs.msdn.com/eric_carter/archive/2004/12/01/273127.aspx
UDFs in Code behind files
This technique is good when you want to define workbook level UDFs. It is based on a COM exposed class and some VBA wrapper that you need to write at workbook level. The only problem I had was to the the Exel.Application.Run command work fine. My mistake was due to the fact that I was adding the VBA code the the Excel file that shows up after building and running the program. I did not realized that each time I run, the original .xls file used by the solution was overwriting the one I was editing.
http://blogs.msdn.com/pstubbs/archive/2004/12/31/344964.aspx
Consideration
Given the fact that VSTO does not suggest to save aa .XLA, a .XLS files based on VSTO solution, it is still quite hard to deploy an add-in that can use and excel file as data storage.
For example I developed some years ago an Excel Add-in .XLA to bootstrap the Interest Rate Swap Curve in US and Euro area, and I was using a spreadsheet in the .xls file to store data. The add-in makes available to every workbook a set of functions to compute some fixed income analytics : PV, duration, factor analysis...
Of course if this is the idea, the code-behind pattern VSTO solution is ruled out (it cannot make available function to everywork book that runs in an Excel application).
We are left with the Automation add-in solution, which is basically a COM Exposed Class. However, the automation add-in does not have any worksheet to store the data. To be honest one could also develop a COM based add-in (which I do not cover here), but I think it would not sort the problem out.
If anybody has any idea on how to develop .xla kind of add-in, ie a VSTO solution that allows to
1) make UDFs available at application level
2) use an Excel file to store some data
as an .xla solution does, please let me know.
Windows 7 run command...
VSTO "Excel Disigner Could Not Be Activated" error
After Installing VSTO on my PC, and trying our my first "Code Behind" project, I could not have access to the Excel designer. No controls were displayed in the Control toolbox and I could not any button or any type of control on the excel worksheet I was working on
The error message was a pretty scary one
"Excel Designer Could Not Be Activated"
After a bit of diggin on google I found this Post on the msdn forum, which helped me out sort the problem.
Among the different suggestions the one that worked for me was to reinstall the VSTO run time.
If you are developing solution for Excel 2003 and VS2008 this is the one you need
Microsoft Visual Studio 2005 Tools for Office Second Edition Runtime (VSTO 2005 SE) (x86)
or the latest version (up to date of publishing)
Microsoft Visual Studio 2005 Tools for Office Second Edition Runtime (VSTO 2005 SE) (x86)(build 8.0.50727.940)
If you are developing solution for Excel 2007 and VS2008 this is the other link
Microsoft Visual Studio Tools for the Microsoft Office system (version 3.0 Runtime) SP1 (x86)
The error message was a pretty scary one
"Excel Designer Could Not Be Activated"
After a bit of diggin on google I found this Post on the msdn forum, which helped me out sort the problem.
Among the different suggestions the one that worked for me was to reinstall the VSTO run time.
If you are developing solution for Excel 2003 and VS2008 this is the one you need
Microsoft Visual Studio 2005 Tools for Office Second Edition Runtime (VSTO 2005 SE) (x86)
or the latest version (up to date of publishing)
Microsoft Visual Studio 2005 Tools for Office Second Edition Runtime (VSTO 2005 SE) (x86)(build 8.0.50727.940)
If you are developing solution for Excel 2007 and VS2008 this is the other link
Microsoft Visual Studio Tools for the Microsoft Office system (version 3.0 Runtime) SP1 (x86)
Office 2003, PIA Installation guidelines for .NET platform
In case you have problem getting the Primary Interop Assembly (PIA) working fine with your .NET Visual Studio platform, just check this link.
Installing the PIA for Office 2003
For example I kept having a "tlbimp.exe" generated assembly (not the PIA) when adding a COM Reference to Microsoft.Office.Interop.Excel, from the "Add Reference", COM tab window.
This means the the .dll path of this reference was pointing to a local dll (local to the project), rather than the one registered in the GAC.
I sorted the problem just changing the "Copy Local" property to false, removing the reference and adding it again.
However, you could also have had some problem with the registration of the PIA in the GAC (Global Assembly Cache). The above link help you sort this kind of problems.
Installing the PIA for Office 2003
For example I kept having a "tlbimp.exe" generated assembly (not the PIA) when adding a COM Reference to Microsoft.Office.Interop.Excel, from the "Add Reference", COM tab window.
This means the the .dll path of this reference was pointing to a local dll (local to the project), rather than the one registered in the GAC.
I sorted the problem just changing the "Copy Local" property to false, removing the reference and adding it again.
However, you could also have had some problem with the registration of the PIA in the GAC (Global Assembly Cache). The above link help you sort this kind of problems.
Subscribe to:
Posts (Atom)