I have some .Net functionality I am trying to use in VB6. I have followed several tutorials. I wrote a test program with success using the formula here: http://www.codeproject.com/Articles/3511/Exposing-NET-Components-to-COM
However, when I try to do it with my actual project, my ProgId doesn't show in the registry like my test file. I made sure property ComVisible == true
using System.Runtime.InteropServices;
namespace Controls.Graph.Web
{
[Guid("5F9F6C6F-016A-4CFF-BD7A-3463761807E1")]
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
public interface _GraphScript
{
[DispId(1)]
string getGraphXml();
}
[Guid("35901BC6-EFF1-490C-84FA-786E8462C553")]
[ClassInterface(ClassInterfaceType.None)]
[ProgId(ProgIds.GraphScript)]
public class GraphScript : _GraphScript
{
protected GraphScript()
{
}
/// <summary>
///
/// </summary>
/// <returns>The graphs xml and javascript</returns>
public string getGraphXml()
{
DisplayDefaults tempDefaults;
tempDefaults = new DisplayDefaults();
GraphConstructor graph = new GraphConstructor();
graph.constructGraph();
GraphModel completedGraph = graph.Graph;
return GraphControl.RenderGraph(completedGraph, tempDefaults, 1) + GraphControl.RenderGraphScript();
}
}
}
and my progid...
using System;
namespace Controls.Graph.Web
{
/// <summary>
/// ProgID Constant
/// </summary>
public static class ProgIds
{
public const string GraphScript = "GraphData";
}
}
I'm not sure which piece of the puzzle I'm missing here
EDIT: actually the Guid shows up in the registry however the Progid still is not. Any ideas/suggestions?
also made sure to do this:
I have figured out what was wrong. I needed to change some access modifiers to PUBLIC -- including my GraphScript() constructor.
Related
I want to acess a COM interop functionality written in c# from Python. I have the following code written in c# which defines the COM interop functionality and is compiled as a .dll and registered using regasm.exe. This seems to work fine.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace ComInteropDemo
{
/// <summary>
/// implementation of the method com interface
/// </summary>
[Guid("1C9E938A-9BF5-4A42-B795-A4AC2B1E7AC0")]
[ProgId("Test.Test.ComInteropClass")]
[ClassInterface(ClassInterfaceType.None)]
[ComDefaultInterface(typeof(IComInteropClass))]
[ComVisible(true)]
public class ComInteropClass : IComInteropClass
{
/// <summary>
/// Reads a text
/// </summary>
/// <param name="text">The text (as out parameter)</param>
/// <returns>true if successful</returns>
public bool ReadText(out string text)
{
text = "Hello from Com Interop";
return true;
}
/// <summary>
/// Writes a text to a file
/// </summary>
/// <param name="text"></param>
/// <returns>true if successful</returns>
public bool WriteText(string text)
{
string fileName = Path.Combine(Path.GetTempPath(), "ComInteropDemoOutput.txt");
try
{
File.WriteAllText(fileName, text);
}
catch (IOException)
{
return false;
}
return true;
}
}
}
The problem is, that I want to acess the COM interop functionality from python (client side), I try to use the comtypes library for that and I try to load the COM interop demo using the following code:
from comtypes.client import CreateObject
comobject = CreateObject("Test.Test.ComInteropClass")
Problem is, it throws an errors saying:
"AttributeError: module 'comtypes.gen.ComInteropDemo' has no attribute 'IComInteropClass'"
It seems I have to define the attribute IComInteropClass in the c# definition of the COM functionality, but I do not know how. Any help on this would be very much appreciated!
I have a project that follows WPF/MVVM pattern. I have tried to derive my ViewModel class from PropertyChangedBase so that I can Notify of data changes to the view. As soon as I derive the class from PropertyChangeBase and compile, I get error saying
Error 1 Cannot register assembly "D:\Source\ArcOnline\bin\Debug\ArcOnline.dll". Could not load file or assembly 'Major, Version=3.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified.
here is how my class is defined
using ArcOnline.WPF;
using System;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
namespace ArcOnline.ViewModels
{
/// <summary>
/// The class handles the template for a single
/// </summary>
public class TemplatesViewModel : PropertyChangedBase
{
/// <summary>
///
/// </summary>
public ArcOnlineViewModel ArcManager { get; set; }
private string _layerID;
/// <summary>
/// Layer ID
/// </summary>
public string LayerID
{
get { return _layerID; }
set { _layerID = value; NotifyOfPropertyChange(() => LayerID); }
}
private string _title;
/// <summary>
/// Title of the layer
/// </summary>
public string Title
{
get { return _title; }
set { _title = value; NotifyOfPropertyChange(() => Title); }
}
............. More properties as above
}
and here is the how my PropertyChangedBase is defined
using Component.Linq.Expressions;
using System;
using System.Collections;
using System.ComponentModel;
using System.Linq.Expressions;
using System.Runtime.Serialization;
using System.Xml.Serialization;
#endregion
namespace Component.ComponentModel
{
/// <summary>
/// A base class from which ViewModels can inherit to improve the quality of
/// patterns such as INotifyPropertyChanged.
/// </summary>
[DataContract]
[Serializable]
public class PropertyChangedBase : INotifyPropertyChanged, INotifyDataErrorInfo, IDataErrorInfo
{
/// <summary>
/// Initializes a new instance of the <see cref="PropertyChangedBase"/> class.
/// </summary>
public PropertyChangedBase()
{
ErrorsChanged += Notify_ErrorChanged;
}
void Notify_ErrorChanged(object sender, DataErrorsChangedEventArgs e)
{
NotifyOfPropertyChange(() => Error);
}
...... More class definition continues
}
This PropertyChangedBase is actually defined in a different Project so that is why it has all the header files included. If I copy the code of this PropertyChangedBase class and create a new class in my own Project where TemplatesViewModel is defined and then use it, it works perfectly fine.
I just want to know what is the reason for it not working in the first case?? This wasted my whole day!!
Thanks in advance
While running Code Analysis on an existing project I came across the messages Do not expose generic lists and Collection properties should be read only.
However, this class is used to read/write from/to an xml configuration file.
Is it possible to make this class comply to CA1002 and CA2227 or do I have to suppress these rules for XML-related classes (there are a lot of them in the project)?
EDIT
Changing List<string> to Collection<string> solved CA1002.
Still no clue on how to solve CA2227 and still be able to (de)serialize the whole thing.
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Xml.Serialization;
/// <summary>
/// Class containing the Configuration Storage
/// </summary>
[XmlRoot("Configuration")]
public class ConfigurationStorage
{
/// <summary>
/// Gets or sets the list of executers.
/// </summary>
[XmlArray("Executers")]
[XmlArrayItem("Executer")]
public Collection<string> Executers { get; set; }
/// <summary>
/// Gets or sets the list of IPG prefixes.
/// </summary>
[XmlArray("IpgPrefixes")]
[XmlArrayItem("IpgPrefix")]
public Collection<string> IpgPrefixes { get; set; }
}
Reading the xml-file:
public static ConfigurationStorage LoadConfiguration()
{
if (File.Exists(ConfigFile))
{
try
{
using (TextReader r = new StreamReader(ConfigFile))
{
var s = new XmlSerializer(typeof(ConfigurationStorage));
var config = (ConfigurationStorage)s.Deserialize(r);
return config;
}
}
catch (InvalidOperationException invalidOperationException)
{
throw new StorageException(
"An error occurred while deserializing the configuration XML file.", invalidOperationException);
}
}
}
How about:
/// <summary>
/// Class containing the Configuration Storage
/// </summary>
[XmlRoot("Configuration")]
public class ConfigurationStorage {
/// <summary>
/// Gets or sets the list of executers.
/// </summary>
[XmlArray("Executers")]
[XmlArrayItem("Executer")]
public Collection<string> Executers { get; private set; }
/// <summary>
/// Gets or sets the list of IPG prefixes.
/// </summary>
[XmlArray("IpgPrefixes")]
[XmlArrayItem("IpgPrefix")]
public Collection<string> IpgPrefixes { get; private set; }
public ConfigurationStorage() {
Executers = new Collection<string>();
IpgPrefixes = new Collection<string>();
}
}
This will still work for xml serialization/deserialization.
If you read the documentation on MSDN, you see a note:
The XmlSerializer gives special treatment to classes that implement
IEnumerable or ICollection. A class that implements IEnumerable must
implement a public Add method that takes a single parameter. The Add
method's parameter must be of the same type as is returned from the
Current property on the value returned from GetEnumerator, or one of
that type's bases. A class that implements ICollection (such as
CollectionBase) in addition to IEnumerable must have a public Item
indexed property (indexer in C#) that takes an integer, and it must
have a public Count property of type integer. The parameter to the Add
method must be the same type as is returned from the Item property, or
one of that type's bases. For classes that implement ICollection,
values to be serialized are retrieved from the indexed Item property,
not by calling GetEnumerator.
So, I think, if you fall in line with this special treatment, you'll have better code that works with XmlSerializer, doesn't use a legacy namespace, and satisfies Code Analysis warnings in the right way, rather than exploiting an oversight in the rule.
using System;
using System.Collections.Generic;
using System.Xml.Serialization;
/// <summary>
/// Class containing the Configuration Storage
/// </summary>
[XmlRoot("Configuration")]
public class ConfigurationStorage
{
// The executers.
private readonly ICollection<string> executers = new List<string>();
// The IPG prefixes.
private readonly ICollection<string> ipgPrefixes = new List<string>();
/// <summary>
/// Gets the list of executers.
/// </summary>
[XmlArray("Executers")]
[XmlArrayItem("Executer")]
public ICollection<string> Executers
{
get
{
return this.executers;
}
}
/// <summary>
/// Gets the list of IPG prefixes.
/// </summary>
[XmlArray("IpgPrefixes")]
[XmlArrayItem("IpgPrefix")]
public ICollection<string> IpgPrefixes
{
get
{
return this.ipgPrefixes;
}
}
}
I'm in the process of creating a framework in which I provide the base class and the implementers of the framework will inherit from the base class and provide additional properties and methods. In the base class, I would like to have a way of observing when a property value is changed. The property can be from the base class or in any of the subclasses. I know that through reflection, I can determine the list of properties from any instance, but is there a way I can track the property changing value?
Here is a very simplistic example of what I am saying:
public class BaseClass
{
public string BaseClassProperty { get; set; }
public void DoSomethingWhenEitherPropertyGetsChanged()
{
}
}
public class SubClass : BaseClass
{
public string SubClassProperty { get; set; }
}
What can I do to have DoSomethingWhenEitherPropertyGetsChanged get executed when either of the properties has it's value changed.
You can use notifypropertyweaver for this purpose. It does exactly what you want. Here's a link:
notifypropertyweaver
From the open source home page:
Uses IL weaving (via http://www.mono-project.com/Cecil) to inject INotifyPropertyChanged code into properties.
No attributes required
No references required
No base class required
Supports .net 3.5, .net 4, Silverlight 3, Silverlight 4, Silverlight 5 and Windows Phone 7
Supports client profile mode
I would probably use Postsharp and create an inherited attribute injecting interception code into all public properties. Marking the attribute as inherited should also attach it to all subclasses automatically.
I wrote my own idea of your requirements, but I am not sure if it suits your needs. INotifyProperty changed is something you could also look into, but I don't really like it because it is like wiring up speghetti. Maybe this will give you some creative ideas, though.
What this does, is allow you to use ObservableObject as for all of your properties types. By doing this, each property will have an ObjectChanged event you can wire-up to. The con(s) are that you must initialize all of your properties in the constructor to prevent a NullReferenceException somewhere in your code.
This example uses three classes.
ObservableObject.cs
Employee.cs
Program.cs
ObservableObject.cs
//-----------------------------------------------------------------------------
// <copyright file="ObservableObject.cs" company="DCOM Productions">
// Copyright (c) DCOM Productions. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------------
namespace PropertyChangedEventExample {
using System;
public class ObservableObject : Object {
/// <summary>
/// Expose the default constructor
/// </summary>
public ObservableObject() {
// No default implementation
}
private object m_Object = null;
/// <summary>
/// Base object
/// </summary>
public object Object {
get {
return m_Object;
}
set {
if (m_Object != value) {
m_Object = value;
OnObjectChanged(this, EventArgs.Empty);
}
}
}
/// <summary>
/// Triggered when the value of this object has changed.
/// </summary>
public event System.EventHandler<EventArgs> ObjectChanged;
/// <summary>
/// EventHandler wire-up
/// </summary>
protected virtual void OnObjectChanged(object sender, System.EventArgs e) {
if (ObjectChanged != null) {
ObjectChanged(sender, e);
}
}
/// <summary>
/// Gets the value
/// </summary>
public object Get() {
return this.Object;
}
/// <summary>
/// Sets the value
/// </summary>
public void Set(object value) {
this.Object = value;
}
}
}
Employee.cs
//-----------------------------------------------------------------------------
// <copyright file="Employee.cs" company="DCOM Productions">
// Copyright (c) DCOM Productions. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------------
namespace PropertyChangedEventExample {
using System;
public class Employee {
/// <summary>
/// Expose default constructor
/// </summary>
public Employee() {
Name = new ObservableObject();
}
/// <summary>
/// Gets or sets the name
/// </summary>
public ObservableObject Name {
get;
set;
}
}
}
Program.cs
//-----------------------------------------------------------------------------
// <copyright file="Program.cs" company="DCOM Productions">
// Copyright (c) DCOM Productions. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------------
namespace PropertyChangedEventExample {
using System;
class Program {
static void Main(string[] args) {
Employee employee = new Employee();
employee.Name.Set("David");
employee.Name.ObjectChanged += new EventHandler<EventArgs>(Name_ObjectChanged);
employee.Name.Set("Dave");
Console.ReadKey(true);
}
static void Name_ObjectChanged(object sender, EventArgs e) {
ObservableObject employee = sender as ObservableObject;
Console.WriteLine("Name changed to {0}", employee.Get());
}
}
}
Your best bet would be what CrisWue recommended and use postsharp or some other post-processor to inject the behavior in your properties. Other than that I think you would need to call DoSomethingWhenEitherPropertyGetsChanged() manually within your properties.
If you are creating a library that is consumed by people other than you or your organization, a post-processor may not be the right way to go as it adds the 3rd party tool as another requirement to their build process.
I trying to make automatic variables available to Excel VBA (like ActiveSheet or ActiveCell) also available to PowerShell as 'automatic variables'. PowerShell engine is hosted in an Excel VSTO add-in and Excel.Application is available to it as Globals.ThisAddin.Application. I found this thread here on StackOverflow and started created PSVariable derived classes like:
public class ActiveCell : PSVariable
{
public ActiveCell(string name) : base(name) { }
public override object Value
{
get
{
return Globals.ThisAddIn.Application.ActiveCell;
}
}
}
public class ActiveSheet : PSVariable
{
public ActiveSheet(string name) : base(name) { }
public override object Value
{
get
{
return Globals.ThisAddIn.Application.ActiveSheet;
}
}
}
and adding their instances to the current POwerShell session:
runspace.SessionStateProxy.PSVariable.Set(new ActiveCell("ActiveCell"));
runspace.SessionStateProxy.PSVariable.Set(new ActiveSheet("ActiveSheet"));
This works and I am able to use those variables from PowerShell as $ActiveCell and $ActiveSheet (their value change as Excel active sheet or cell change). Then I read PSVariable documentation here and saw this:
"There is no established scenario for deriving from this class. To programmatically create a shell variable, create an instance of this class and set it by using the PSVariableIntrinsics class."
As I was deriving from PSVariable, I tried to use what was suggested:
PSVariable activeCell = new PSVariable("ActiveCell");
activeCell.Value = Globals.ThisAddIn.Application.ActiveCell;
runspace.SessionStateProxy.PSVariable.Set(activeCell);
Using this, $ActiveCell appears in my PowerShell session, but its value doesn't change as I change the active cell in Excel.
Is the above comment from PSVariable documentation something I should worry about, or I can continue creating PSVariable derived classes? Is there another way of making Excel globals available to PowerShell?
Our documentation is wrong - it is a supported scenario.
Here's a bit more about the technique:
http://poshcode.org/2198
http://www.leeholmes.com/blog/2009/03/26/more-tied-variables-in-powershell/
http://www.pavleck.net/powershell-cookbook/ch03.html
Lee Holmes [MSFT]
Windows PowerShell Development
Obviously in your second example, where you are not deriving from PSVariable, you couldn't expect the $ActiveCell variable to change with the value of the ActiveCell property since you're capturing its value just once.
I don't believe deriving from PSVariable is a supported scenario, but it does work and I've done it to add variables such as $Now and $Today.
It might be a better idea to just expose an $Application variable to PowerShell script instead of the various properties of the Application object. The upside to this is that you wouldn't need to create a bunch of automatic variables and PowerShell scripts could access anything the Application object has to offer by using $Application.ActiveCell. The other benefit is that it doesn't need to be an automatic variable at all because the Application object reference will never change.
Having said all that, I've included a subclass of PSVariable that I use from time to time which takes a ScriptBlock for the getter and setter. This lets me define automatic variables from PowerShell without needing a separate derived class for each one.
using System;
using System.Management.Automation;
namespace Einstein.PowerShell
{
public sealed class DynamicVariable : PSVariable
{
#region Constructors
/// <summary>
/// </summary>
public DynamicVariable(string name, ScriptBlock onGet)
: this(name, onGet, null)
{
}
/// <summary>
/// </summary>
public DynamicVariable(string name, ScriptBlock onGet, ScriptBlock onSet)
: base(name, null, ScopedItemOptions.AllScope)
{
OnGet = onGet;
OnSet = onSet;
}
#endregion
#region Properties
/// <summary>
/// The ScriptBlock that runs to get the value of the variable.
/// </summary>
private ScriptBlock OnGet
{
get;
set;
}
/// <summary>
/// The ScriptBlock that runs to get the value of the variable.
/// </summary>
private ScriptBlock OnSet
{
get;
set;
}
/// <summary>
/// Gets or sets the underlying value of the variable.
/// </summary>
public override object Value
{
get
{
if (OnGet == null) {
return null;
}
return OnGet.Invoke();
}
set
{
if (OnSet == null) {
throw new InvalidOperationException("The variable is read-only.");
}
OnSet.Invoke(value);
}
}
#endregion
}
}