How do I set the constructor string of a COM + component from .NET?
I am trying to programmatically set the stor of a COM + component constructor from a C # application. I found the following sample code online, but it throws an exception:
COMAdminCatalogCollection Components;
COMAdminCatalogClass Catalog = new COMAdminCatalogClass();
string strConstr;
string ApplicationName = "ApplicationName"; // case sensitive
string CompName = "MyComponent.ProgID";
COMAdminCatalogCollectionClass Applications = (COMAdminCatalogCollectionClass)Catalog.GetCollection("Applications");
Applications.Populate();
// find the correct application
foreach (COMAdminCatalogObjectClass AppObject in Applications)
{
if (AppObject.Name == ApplicationName)
{
// find matching component
Components = (COMAdminCatalogCollectionClass)(Applications.GetCollection("Components", AppObject.Key));
Components.Populate();
foreach (COMAdminCatalogObjectClass CompObject in Components)
{
if (CompObject.Name.ToString() == CompName)
{
CompObject.get_Value("ConstructorString").ToString();
CompObject.get_Value("ConstructionEnabled").ToString();
}
}
}
}
When I run this code, I get the following exception on line 6:
Unable to pass COM object of type 'System .__ ComObject' to class type 'COMAdmin.COMAdminCatalogCollectionClass'. COM components that inject the CLR and do not support IProvideClassInfo or that do not have a registered merged registration will be wrapped in the __ComObject type. Instances of this type cannot be assigned to any other class; however, they can be passed to interfaces if the underlying COM component supports calls to QueryInterface on the IID interface.
Any idea where I am going wrong? Or is there an easier way to do this?
source to share
I found a way to avoid the exception. Instead of doing it in C #, I can take advantage of the optional VB.NET weak type to remove all casts and multiple types of variable declarations. The resulting code looks like this:
Dim Components As COMAdminCatalogCollection
Dim Catalog As New COMAdminCatalogClass()
Dim ApplicationName As String = "ApplicationName"
Dim CompName As String = "MyComponent.ProgID"
Dim Applications = Catalog.GetCollection("Applications")
Applications.Populate()
For Each AppObject In Applications
If (AppObject.Name = ApplicationName) Then
Components = (Applications.GetCollection("Components", AppObject.Key))
Components.Populate()
For Each CompObject In Components
If (CompObject.Name.ToString() = CompName) Then
CompObject.Value("ConstructorString") = "Some new value"
Components.SaveChanges()
End If
Next
End If
Next
This is one of the situations where VB and C # are very different from each other, and knowing these things really helps you choose the right tool for the job.
source to share
I'm sure you've moved on well, but I'm working on a project currently that needs similar functionality and I was able to come up with a solution using .NET and PowerShell to do this. I first created a custom cmdlet in C # like this:
using COMAdmin;
using System;
using System.Runtime.InteropServices;
using System.Management.Automation;
namespace COMAdminModule
{
// Name the cmdlet
[Cmdlet("Set", "COMConstructorString")]
public class SetCOMConstructorSting : PSCmdlet
{
// App name Parameter
private string comAppName;
[Parameter(
Mandatory = true,
ValueFromPipelineByPropertyName = true,
ValueFromPipeline = true,
Position = 0,
HelpMessage = "Name of COM+ Application"
)]
[Alias("App Name")]
public string COMApp
{
get { return comAppName; }
set { comAppName = value; }
}
// App Component name
private string componentName;
[Parameter(
Mandatory = true,
ValueFromPipelineByPropertyName = true,
ValueFromPipeline = true,
Position = 1,
HelpMessage = "The name of the Component that will receive a new Constructor string"
)]
[Alias("Component Name")]
public string ComponentName
{
get { return componentName; }
set { componentName = value; }
}
// Constructor String
private string constructorString;
[Parameter(
Mandatory = true,
ValueFromPipelineByPropertyName = true,
ValueFromPipeline = true,
Position = 2,
HelpMessage = "The new Constructor string"
)]
[Alias("Constructor String")]
public string ConstructorString
{
get { return constructorString; }
set { constructorString = value; }
}
// Provides a one-time, preprocessing functionality for the cmdlet
protected override void BeginProcessing()
{
base.BeginProcessing();
}
// Provides a record-by-record processing functionality for the cmdlet
protected override void ProcessRecord()
{
string working = "Setting the constructor string " + constructorString;
working = " to the Component " + componentName;
working += " for the COM App " + comAppName;
WriteObject(working);
setConstructorString(comAppName, componentName, constructorString);
}
// Provides a one-time, post-processing functionality for the cmdlet
protected override void EndProcessing()
{
base.EndProcessing();
}
//Add component method
private void setConstructorString(string comAppName, string componentName, string constructorString)
{
ICOMAdminCatalog2 oCatalog = null;
try
{
//Create the comAdmin object
oCatalog = (ICOMAdminCatalog2)Activator.CreateInstance(Type.GetTypeFromProgID("ComAdmin.COMAdminCatalog"));
//Get the comApps
ICatalogCollection comApps = (ICatalogCollection)oCatalog.GetCollection("Applications");
comApps.Populate();
foreach (ICatalogObject app in comApps)
{
//Find the comApp
if (app.Name.ToString().Equals(comAppName))
{
//Get the Components
ICatalogCollection components = (ICatalogCollection)comApps.GetCollection("Components", app.Key);
components.Populate();
foreach (ICatalogObject component in components)
{
//Find the component
if (component.Name.ToString().Equals(componentName))
{
// Set the constructor string
component.set_Value("ConstructorString", constructorString);
components.SaveChanges();
break;
}
}
break;
}
}
}
catch (Exception e)
{
WriteObject(e.Source);
throw;
}
}
}
}
And then import this module into PowerShell Script and run it like this:
PS C:\Windows\system32> Import-Module "<dll path>"
PS C:\Windows\system32> Set-COMConstructorString <Application Name> <Component Name> <Constructor String>
source to share