A
Asaf
Hi,
We have a VB6 application that needs to use a new control written in
..NET v1.1 C#, imported as a COM control.
We have tried adding the C# control to the VB6 control both dynamically
(using Controls.Add) and using the VB6 designer. In both approaches
the behavior was identical: the control was functional and accessible,
but the application crashed on exit with the message <app name> "has
encountered a problem and needs to close. We are sorry for the
inconvenience." etc.
I have searched the web, with no luck. Any ideas will be greatly
appreciated. The C# code is embedded below.
Thanks
Asaf.
using System;
using System.Collections;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Text;
using System.Reflection;
using Microsoft.Win32;
[assembly:ClassInterface(ClassInterfaceType.AutoDual)]
namespace MyCtrlLib
{
///
/// Summary description for UserControl1.
///
[ GuidAttribute("F321BAC9-5019-4c6f-BADF-8D104A499870") ]
[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsDual)]
public interface ICSharpCOMInterface
{
void MethodA();
void MethodB(int a);
}
[ComVisible(false)]
public delegate void CSharpEventHandlerA ();
[ComVisible(false)]
public delegate void CSharpEventHandlerB (int a);
[GuidAttribute("03018F99-263B-417e-94F3-F367471F6679"),
InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIDispatch)]
public interface ICSharpCOMEvents
{
[DispId(1)]void EventA();
[DispId(2)]void EventB(int a);
}
[GuidAttribute("E022271B-63C8-4878-8C9E-712E776C4785")]
[ProgIdAttribute("MyCtrlLib.CSharpUserControl")]
//[ComSourceInterfacesAttribute(typeof (ICSharpCOMEvents))]
[ComSourceInterfacesAttribute(typeof(MyCtrlLib.ICSharpCOMEvents))]
[ClassInterfaceAttribute(ClassInterfaceType.None)]
public class CSharpUserControl : System.Windows.Forms.UserControl,
ICSharpCOMInterface
{
private System.Windows.Forms.TextBox textBox1;
private System.Windows.Forms.Button button1;
///
/// Required designer variable.
///
private System.ComponentModel.Container components = null;
[Category("Action")]
public event CSharpEventHandlerA EventA;
[Category("Action")]
public event CSharpEventHandlerB EventB;
public CSharpUserControl()
{
// This call is required by the Windows.Forms Form Designer.
InitializeComponent();
// TODO: Add any initialization after the InitForm call
}
///
/// Clean up any resources being used.
///
protected override void Dispose( bool disposing )
{
MessageBox.Show("Dispose");
if( disposing )
{
if( components != null )
components.Dispose();
}
base.Dispose( disposing );
}
public void MethodA()
{
this.textBox1.Text = "MethodA";
OnMyEventA();
}
public void MethodB(int a)
{
OnMyEventB(a);
}
protected void OnMyEventA()
{
if (EventA != null)
{
this.textBox1.Text = "Event fired!";
EventA();
}
}
protected void OnMyEventB(int a)
{
if (EventB != null)
EventB(a);
}
[ComRegisterFunction()]
public static void RegisterClass ( Type t )
{
// Strip off HKEY_CLASSES_ROOT\ from the passed key as I don't need
it
string sb = @"CLSID\" + t.GUID.ToString("B");
sb.Replace(@"HKEY_CLASSES_ROOT\","") ;
// Open the CLSID\{guid} key for write access
RegistryKey k = Registry.ClassesRoot.OpenSubKey(sb.ToString(),true);
// And create the 'Control' key - this allows it to show up in
// the ActiveX control container
RegistryKey ctrl = k.CreateSubKey ( "Control" ) ;
ctrl.Close ( ) ;
using( RegistryKey subkey = k.CreateSubKey("MiscStatus") )
{
subkey.SetValue("", "131473");
}
using( RegistryKey subkey = k.CreateSubKey("MiscStatus\\1") )
{
subkey.SetValue("", "197009");
}
// Next create the CodeBase entry - needed if not string named and
GACced
RegistryKey inprocServer32 = k.OpenSubKey ( "InprocServer32" , true
);
inprocServer32.SetValue ( "CodeBase" ,
Assembly.GetExecutingAssembly().CodeBase ) ;
inprocServer32.Close ( ) ;
// Now create the Version entry
RegistryKey vers = k.CreateSubKey("Version");
vers.SetValue("", "1.0");
vers.Close();
RegistryKey inst = k.CreateSubKey ( "Insertable" ) ;
inst.Close ( ) ;
// And the Type Lib Entry
using( RegistryKey subkey = k.CreateSubKey("TypeLib") )
{
Guid libid = Marshal.GetTypeLibGuidForAssembly(t.Assembly);
subkey.SetValue("", libid.ToString("B"));
}
// Finally close the main key
k.Close ( ) ;
}
[ComUnregisterFunction()]
public static void UnregisterClass ( string key )
{
StringBuilder sb = new StringBuilder ( key ) ;
sb.Replace(@"HKEY_CLASSES_ROOT\","") ;
// Open HKCR\CLSID\{guid} for write access
RegistryKey k = Registry.ClassesRoot.OpenSubKey(sb.ToString(),true);
// Delete the 'Control' key, but don't throw an exception if it does
not exist
k.DeleteSubKey ( "Control" , false ) ;
// Next open up InprocServer32
RegistryKey inprocServer32 = k.OpenSubKey ( "InprocServer32" , true
);
// And delete the CodeBase key, again not throwing if missing
k.DeleteSubKey ( "CodeBase" , false ) ;
k.DeleteSubKey ( "TypeLib" , false ) ;
k.DeleteSubKey ( "Version" , false ) ;
// Finally close the main key
k.Close ( ) ;
}
#region Component Designer generated code
///
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
///
private void InitializeComponent()
{
this.textBox1 = new System.Windows.Forms.TextBox();
this.button1 = new System.Windows.Forms.Button();
this.SuspendLayout();
//
// textBox1
//
this.textBox1.Location = new System.Drawing.Point(8, 8);
this.textBox1.Name = "textBox1";
this.textBox1.Size = new System.Drawing.Size(192, 20);
this.textBox1.TabIndex = 0;
this.textBox1.Text = "textBox1";
//
// button1
//
this.button1.Location = new System.Drawing.Point(8, 32);
this.button1.Name = "button1";
this.button1.Size = new System.Drawing.Size(192, 24);
this.button1.TabIndex = 1;
this.button1.Text = "button1";
this.button1.Click += new System.EventHandler(this.button1_Click);
//
// CSharpUserControl
//
this.Controls.Add(this.button1);
this.Controls.Add(this.textBox1);
this.Name = "CSharpUserControl";
this.Size = new System.Drawing.Size(208, 64);
this.ResumeLayout(false);
}
#endregion
private void button1_Click(object sender, System.EventArgs e)
{
OnMyEventA();
}
}
}
We have a VB6 application that needs to use a new control written in
..NET v1.1 C#, imported as a COM control.
We have tried adding the C# control to the VB6 control both dynamically
(using Controls.Add) and using the VB6 designer. In both approaches
the behavior was identical: the control was functional and accessible,
but the application crashed on exit with the message <app name> "has
encountered a problem and needs to close. We are sorry for the
inconvenience." etc.
I have searched the web, with no luck. Any ideas will be greatly
appreciated. The C# code is embedded below.
Thanks
Asaf.
using System;
using System.Collections;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Text;
using System.Reflection;
using Microsoft.Win32;
[assembly:ClassInterface(ClassInterfaceType.AutoDual)]
namespace MyCtrlLib
{
///
/// Summary description for UserControl1.
///
[ GuidAttribute("F321BAC9-5019-4c6f-BADF-8D104A499870") ]
[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsDual)]
public interface ICSharpCOMInterface
{
void MethodA();
void MethodB(int a);
}
[ComVisible(false)]
public delegate void CSharpEventHandlerA ();
[ComVisible(false)]
public delegate void CSharpEventHandlerB (int a);
[GuidAttribute("03018F99-263B-417e-94F3-F367471F6679"),
InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIDispatch)]
public interface ICSharpCOMEvents
{
[DispId(1)]void EventA();
[DispId(2)]void EventB(int a);
}
[GuidAttribute("E022271B-63C8-4878-8C9E-712E776C4785")]
[ProgIdAttribute("MyCtrlLib.CSharpUserControl")]
//[ComSourceInterfacesAttribute(typeof (ICSharpCOMEvents))]
[ComSourceInterfacesAttribute(typeof(MyCtrlLib.ICSharpCOMEvents))]
[ClassInterfaceAttribute(ClassInterfaceType.None)]
public class CSharpUserControl : System.Windows.Forms.UserControl,
ICSharpCOMInterface
{
private System.Windows.Forms.TextBox textBox1;
private System.Windows.Forms.Button button1;
///
/// Required designer variable.
///
private System.ComponentModel.Container components = null;
[Category("Action")]
public event CSharpEventHandlerA EventA;
[Category("Action")]
public event CSharpEventHandlerB EventB;
public CSharpUserControl()
{
// This call is required by the Windows.Forms Form Designer.
InitializeComponent();
// TODO: Add any initialization after the InitForm call
}
///
/// Clean up any resources being used.
///
protected override void Dispose( bool disposing )
{
MessageBox.Show("Dispose");
if( disposing )
{
if( components != null )
components.Dispose();
}
base.Dispose( disposing );
}
public void MethodA()
{
this.textBox1.Text = "MethodA";
OnMyEventA();
}
public void MethodB(int a)
{
OnMyEventB(a);
}
protected void OnMyEventA()
{
if (EventA != null)
{
this.textBox1.Text = "Event fired!";
EventA();
}
}
protected void OnMyEventB(int a)
{
if (EventB != null)
EventB(a);
}
[ComRegisterFunction()]
public static void RegisterClass ( Type t )
{
// Strip off HKEY_CLASSES_ROOT\ from the passed key as I don't need
it
string sb = @"CLSID\" + t.GUID.ToString("B");
sb.Replace(@"HKEY_CLASSES_ROOT\","") ;
// Open the CLSID\{guid} key for write access
RegistryKey k = Registry.ClassesRoot.OpenSubKey(sb.ToString(),true);
// And create the 'Control' key - this allows it to show up in
// the ActiveX control container
RegistryKey ctrl = k.CreateSubKey ( "Control" ) ;
ctrl.Close ( ) ;
using( RegistryKey subkey = k.CreateSubKey("MiscStatus") )
{
subkey.SetValue("", "131473");
}
using( RegistryKey subkey = k.CreateSubKey("MiscStatus\\1") )
{
subkey.SetValue("", "197009");
}
// Next create the CodeBase entry - needed if not string named and
GACced
RegistryKey inprocServer32 = k.OpenSubKey ( "InprocServer32" , true
);
inprocServer32.SetValue ( "CodeBase" ,
Assembly.GetExecutingAssembly().CodeBase ) ;
inprocServer32.Close ( ) ;
// Now create the Version entry
RegistryKey vers = k.CreateSubKey("Version");
vers.SetValue("", "1.0");
vers.Close();
RegistryKey inst = k.CreateSubKey ( "Insertable" ) ;
inst.Close ( ) ;
// And the Type Lib Entry
using( RegistryKey subkey = k.CreateSubKey("TypeLib") )
{
Guid libid = Marshal.GetTypeLibGuidForAssembly(t.Assembly);
subkey.SetValue("", libid.ToString("B"));
}
// Finally close the main key
k.Close ( ) ;
}
[ComUnregisterFunction()]
public static void UnregisterClass ( string key )
{
StringBuilder sb = new StringBuilder ( key ) ;
sb.Replace(@"HKEY_CLASSES_ROOT\","") ;
// Open HKCR\CLSID\{guid} for write access
RegistryKey k = Registry.ClassesRoot.OpenSubKey(sb.ToString(),true);
// Delete the 'Control' key, but don't throw an exception if it does
not exist
k.DeleteSubKey ( "Control" , false ) ;
// Next open up InprocServer32
RegistryKey inprocServer32 = k.OpenSubKey ( "InprocServer32" , true
);
// And delete the CodeBase key, again not throwing if missing
k.DeleteSubKey ( "CodeBase" , false ) ;
k.DeleteSubKey ( "TypeLib" , false ) ;
k.DeleteSubKey ( "Version" , false ) ;
// Finally close the main key
k.Close ( ) ;
}
#region Component Designer generated code
///
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
///
private void InitializeComponent()
{
this.textBox1 = new System.Windows.Forms.TextBox();
this.button1 = new System.Windows.Forms.Button();
this.SuspendLayout();
//
// textBox1
//
this.textBox1.Location = new System.Drawing.Point(8, 8);
this.textBox1.Name = "textBox1";
this.textBox1.Size = new System.Drawing.Size(192, 20);
this.textBox1.TabIndex = 0;
this.textBox1.Text = "textBox1";
//
// button1
//
this.button1.Location = new System.Drawing.Point(8, 32);
this.button1.Name = "button1";
this.button1.Size = new System.Drawing.Size(192, 24);
this.button1.TabIndex = 1;
this.button1.Text = "button1";
this.button1.Click += new System.EventHandler(this.button1_Click);
//
// CSharpUserControl
//
this.Controls.Add(this.button1);
this.Controls.Add(this.textBox1);
this.Name = "CSharpUserControl";
this.Size = new System.Drawing.Size(208, 64);
this.ResumeLayout(false);
}
#endregion
private void button1_Click(object sender, System.EventArgs e)
{
OnMyEventA();
}
}
}