Problem using ActiveX controls with VS2010

11. May 2012 11:56 by Mrojas in ActiveX, COM Interop, VB6 Migration, Visual Studio, WinForms  //  Tags: , , , , , , , ,   //   Comments (0)

 

Sometimes during migrations from VB6 to VS2010 we have found issues when you tried to add an ActiveX control with the VS2010 winforms designer. The issue is only present in VS2010 not on previous versions.

You usually will see an error in the added Interop references, and messages like a missing VBA or StdLib library.

The error has been reported several times so please vote on Connect to make sure MS will consider fixing it.

 

https://connect.microsoft.com/VisualStudio/feedback/details/557722/errors-utilizing-activex-controls-in-vs-2010

http://connect.microsoft.com/VisualStudio/feedback/details/568769/aximp-error-with-vb6-activex-control

 

And possible workarounds are running the Aximp manually from the command line and the add the references. You will then need to add the control by hand in your forms. Do not use the designer to add the component, this will try to regenerate the references and reproduice the issue.

Control Property Serialization in .NET

While solving a bug with a custom class that extended the System.Data.DataSet class, I found a situation where the class implemented, the ISerializable interface, but for some reason, during the call to the base.GetObjectData in my serialization code it was trying to get the value of some properties that caused an exception.

The reason was that those properties were not “ready” because my serialization code had not finish initializing the object. But why was the Dataset.GetObjectData trying to get or set those values.

It seems that there is some code in the dataset that used reflection to get the object properties and try to serialize them. I did not want that.
How could I stop the framework from doing that?


I thought of the NonSerializable attribute but that works only on fields and what I have is a property.

I thought of the XmlIgnore attribute but it had no effect.

Why!!!!

Well I finally found that you can add a couple of (not attributes) methods to your component.

They should be named Reset<Property>() and ShouldSerialize<Property>() and returning a boolean value
from these functions will control if the properties are serialized or not.

For more info see MSDN page for ShouldSerialize

VB6 Migration of Property Pages

9. June 2009 08:36 by Mrojas in General  //  Tags: , , , , , , ,   //   Comments (0)

How can I migrate property pages? Well that is a common question when migrating VB6 Activex controls.

Property Pages where commonly used in VB6 to provide a mechanism for your user controls to edit values.

.NET provides even more mechanisms for editing your control properties.  You can provide an editor for each one of your component properties or you can provide a ComponentEditor for all the component, this is very similar to the VB6 concept.

In .NET the ComponentEditor can be actived in the designer selecting Properties from the context menu when you right click over the control.

This is from the MSDN documentation:

“A component editor is used to edit a component as a whole and can be used to implement a
user interface similar to that of the property pages. You associate a component editor with a
component by using the
EditorAttribute attribute.” From: ComponentEditor Class

The VBUC does not process out of the box, your PropertyPages, but I developed a tool that can be
used so the VBUC can help you migrate those property pages. This tool will modify your VB6 project,
and VB6 PropertyPages source code to make those VB6 PropertyPages look like VB6 UserControls.
This will allow  the VBUC migration tool to recover some of the VB6 PropertyPages code and appearance
and with some manual changes you can get your property pages to work again.

Use the following link to downlaod the tool: DOWNLOAD TOOL

So these are the steps to migrate a VB6 Project that has Property Pages with the VB6.

1) Make a backup copy of your source code.

2) Run the TOOL with your project file. For example if your project file is Project1.vbp then run the tool like this:

FixPropertyPages Project1.vbp

This will generate a new VB6 Project file called ModifiedProject1.vbp

3) Open the VBUC, and migrate the new project file ModifiedProject1.vbp

4) Open the migrated solution in Visual Studio.

5) All your property pages will be migrated to .NET UserControls. You might need to go thru some changes to make them completely functional. Remeber to add the [ToolboxItem(false)] to these property pages because they do not need to be visible in your toolbox.

6) Now, to associate those property pages with your UserControl do this:

6.1) Add a new code file to your migrated solution. We are going to create a ComponentEditor, that will hold all the pages and associate that to the migrated control. Lets say the control is named Control1 and the property pages are PropertyPage1 and PropertyPage2.
We will call the ComponentEditor ComponentEditorToAssociatePagesForMyControl.
In this ComponentEditor we will add an internal class for each PropertyPage. This class will inherit from ComponentEditorPage. We will call this internal classes Page1, and Page2. And we will associate those classes with the ComponentEditorToAssociatePagesForMyControl in the GetComponentEditorPages().
 

The resulting code will be like:

C#
using System.Windows.Forms.Design;
using WindowsFormsApplication1;
using System.Drawing;
using System.ComponentModel;
[ToolboxItem(false)] 
public class ComponentEditorToAssociatePagesForMyControl : WindowsFormsComponentEditor
{
    // Methods
    public override bool EditComponent(ITypeDescriptorContext context, object component)
    {
        return false;
    }

    class Page1 : ComponentEditorPage
    {
        // Methods
        public Page1()
        {
            PropertyPage1ForControl1 page1 = new PropertyPage1ForControl1();
            Size mysize = new Size(400, 250);
            this.Size = mysize;
            this.Text = "Page 1 for Control1";
            this.Controls.Add(page1);
        }
        protected override void LoadComponent() { }
        protected override void SaveComponent() { }
    }

    class Page2 : ComponentEditorPage
    {
        // Methods
        public Page2()
        {
            PropertyPage2ForControl1 page2 = new PropertyPage2ForControl1();
            Size mysize = new Size(400, 250);
            this.Size = mysize;
            this.Text = "Page 2 for Control1";
            this.Controls.Add(page2);
        }
        protected override void LoadComponent() { }
        protected override void SaveComponent() { }
    }

    protected override System.Type[] GetComponentEditorPages()
    {
        return new System.Type[] { typeof(Page1),typeof(Page2) };
    }

    protected override int GetInitialComponentEditorPageIndex()
    {
        return 0;
    }
}

VB.NET

<ToolboxItem(False)> _
Public Class ComponentEditorToAssociatePagesForMyControl
    Inherits WindowsFormsComponentEditor
    ' Methods
    Public Overrides Function EditComponent(ByVal context As ITypeDescriptorContext, ByVal component As Object) As Boolean
        Return False
    End Function

    Protected Overrides Function GetComponentEditorPages() As Type()
        Return New Type() { GetType(Page1), GetType(Page2) }
    End Function

    Protected Overrides Function GetInitialComponentEditorPageIndex() As Integer
        Return 0
    End Function


    ' Nested Types
    Private Class Page1
        Inherits ComponentEditorPage
        ' Methods
        Public Sub New()
            Dim page1 As New PropertyPage1ForControl1
            Dim mysize As New Size(400, 250)
            MyBase.Size = mysize
            Me.Text = "Page 1 for Control1"
            MyBase.Controls.Add(page1)
        End Sub

        Protected Overrides Sub LoadComponent()
        End Sub

        Protected Overrides Sub SaveComponent()
        End Sub

    End Class

    Private Class Page2
        Inherits ComponentEditorPage
        ' Methods
        Public Sub New()
            Dim page2 As New PropertyPage2ForControl1
            Dim mysize As New Size(400, 250)
            MyBase.Size = mysize
            Me.Text = "Page 2 for Control1"
            MyBase.Controls.Add(page2)
        End Sub

        Protected Overrides Sub LoadComponent()
        End Sub

        Protected Overrides Sub SaveComponent()
        End Sub

    End Class
End Class

 

7) After creating the ComponentEditor you must associate the component Editor to your new component editors. This can be done with something like:

C# 

[Editor(typeof(ComponentEditorToAssociatePagesForMyControl), typeof(ComponentEditor))]
public class Control1 : UserControl

VB.NET

<Editor(GetType(ComponentEditorToAssociatePagesForMyControl), GetType(ComponentEditor))> _
Public Class Control1
8)  Now to use this property pages, go to the designer screen and open the context menu and select properties. And editor with your properties pages will appear :)

9) You still need to write some code for saving the property values that is something you have to add to the LoadComponent and SaveComponent methods of the internal classes in your ComponentEditor (ComponentEditorToAssociatePagesForMyControl in our previous example).

I hope this helps to get your code faster in .NET. I'm attaching a C# sample if you want to try it out.

Extended WebBrowser Control Series: WebBrowser Control and window.Close()

I had previously posted an extended version of the WebBrowser Control. This code posted in Opening Popup in a NewWindow and NewWindow2 Events in the C# WebBrowserControl, dealt with some issues when you want to have a form with a WebBrowser and in the enclosed page you have a Javascript code like:

window.open(“ <some url to a page”)

But recently another problem arised. What if you have a Javascript snippet like:

window.close()

OMG!!! Why haven’t I thought about it. Well Kelder wrote me about this problem and he also sent me some of his\her research results:

Solution (Add WebBrowser as unmanaged code):  blogs.msdn.com/jpsanders/archive/2008/04/23/window-close-freezes-net-2-0-webbrowser-control-in-windows-form-application.aspx

Solution (Add WebBrowser using WM_NOTIFYPARENT override):blogs.msdn.com/jpsanders/archive/2007/05/25/how-to-close-the-form-hosting-the-webbrowser-control-when-scripting-calls-window-close-in-the-net-framework-version-2-0.aspx

http://blogs.msdn.com/jpsanders/archive/2007/05/25/how-to-close-the-form-hosting-the-webbrowser-control-when-scripting-calls-window-close-in-the-net-framework-version-2-0.aspx

Solution (Implementation not detailed): social.msdn.microsoft.com/forums/en-US/winforms/thread/1199c004-9eb2-400d-a118-6e06bca9f1f0/

Proposes changing pop-up links to WebBrowser navigate: dotnetninja.wordpress.com/2008/02/26/prevent-opening-new-window-from-webbrowser-control/Close

problem observed (no solution):www.codeproject.com/KB/cpp/ExtendedWebBrowser.aspx

It seams to me that the better solution is to use jpsanders solution, so I created an ExtendWebBrowser_v2 (the following is the modified fragment):

//Extend the WebBrowser control
public class ExtendedWebBrowser : WebBrowser
{
    
    // Define constants from winuser.h
    private const int WM_PARENTNOTIFY = 0x210;
    private const int WM_DESTROY = 2;
    
    AxHost.ConnectionPointCookie cookie;
    WebBrowserExtendedEvents events;

    protected override void WndProc(ref Message m)
    {
        switch (m.Msg)
        {
            case WM_PARENTNOTIFY:
             if (!DesignMode) 
             {
                if (m.WParam.ToInt32() == WM_DESTROY) 
                {
                    Message newMsg = new Message();
                    newMsg.Msg = WM_DESTROY;
                    // Tell whoever cares we are closing
                    Form parent = this.Parent as Form;
                    if (parent!=null)
                        parent.Close();
                }
             }
            DefWndProc(ref m);
            break;
          default:
            base.WndProc(ref m);
            break;
        }
    }

The problem that might arise with this solution is that the parent might not be a Form but an user control, etc. For a more general aproach I think I should send a WM_DESTROY directly to the parent, but for most cases it works. I’m attaching the code and a sample page called test0.htm. I hope this helps and rembember you can always donate to programming geeks jejejejeje just kidding

HERE IS THE CODE