OutOfProcess In C#

2. October 2008 11:49 by Mrojas in General  //  Tags: , , ,   //   Comments (0)

In VB6 you could create an OutOfProcess instance to execute some actions. But there is not a direct equivalent for that. However you can run a class in an another application domain to produce a similar effect that can be helpful in a couple of scenarios.

This example consists of two projects. One is a console application, and the other is a Class Library that holds a Class that we want to run like an "OutOfProcess" instance. In this scenario. The console application does not necessary know the type of the object before hand. This technique can be used for example for a Plugin or Addin implementation.

Code for Console Application

using System;
using System.Text;
using System.IO;
using System.Reflection;

namespace OutOfProcess
{
    /// <summary>
    /// This example shows how to create an object in an
    /// OutOfProcess similar way.
    /// In VB6 you were able to create an ActiveX-EXE, so you could create
    /// objects that execute in their own process space.
    /// In some scenarios this can be achieved in .NET by creating 
    /// instances that run in their own
    /// 'ApplicationDomain'.
    /// This simple class shows how to do that.
    /// Disclaimer: This is a quick and dirty implementation.
    /// The idea is get some comments about it.
    /// </summary>
    class Program
    {
        delegate void ReportFunction(String message);

        class RemoteTextWriter : TextWriter
        {
            ReportFunction report;
            public RemoteTextWriter(ReportFunction report)
            {
                this.report = report;
            }
            public override Encoding Encoding
            {
                get
                {
                    return new UnicodeEncoding(false, false);
                }
            }

            public override void Flush()
            {
                //Nothing to do here
            }


            public override void Write(char value)
            {
                //ignore
            }

            public override void Write(string value)
            {

                report(value);
            }

            public override void WriteLine(string value)
            {

                report(value);
            }

            //This is very important. Specially if you have a long running process
            // Remoting has a concept called Lifetime Management.
            //This method makes your remoting objects Inmmortals
            public override object InitializeLifetimeService()
            {
                return null;
            }

        }

        static void ReportOut(String message)
        {
            Console.WriteLine("[stdout] " + message);
        }

        static void ReportError(String message)
        {
            ConsoleColor oldColor = Console.ForegroundColor;
            Console.ForegroundColor = ConsoleColor.Red;
            Console.WriteLine("[stderr] " + message);
            Console.ForegroundColor = oldColor;
        }

        static void ExecuteAsOutOfProcess(String assemblyFilePath,String typeName)
        {
            RemoteTextWriter outWriter = new RemoteTextWriter(ReportOut);
            RemoteTextWriter errorWriter = new RemoteTextWriter(ReportError);

            //<-- This is my path, change it for your app


            //Type superProcessType = AspUpgradeAssembly.GetType("OutOfProcessClass.SuperProcess");
            AppDomain outofProcessDomain = 
                AppDomain.CreateDomain("outofprocess_test1", 
                AppDomain.CurrentDomain.Evidence, 
                AppDomain.CurrentDomain.BaseDirectory, 
                AppDomain.CurrentDomain.RelativeSearchPath, 
                AppDomain.CurrentDomain.ShadowCopyFiles);
            //When the invoke member is called this event must return the assembly

            AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(outofProcessDomain_AssemblyResolve);
            Object outofProcessObject = 
                outofProcessDomain.CreateInstanceFromAndUnwrap(
                assemblyFilePath, typeName);
            assemblyPath = assemblyFilePath;
            outofProcessObject.
                GetType().InvokeMember("SetOut", 
                BindingFlags.Public | BindingFlags.Instance | BindingFlags.InvokeMethod, 
                null, outofProcessObject, new object[] { outWriter });
            outofProcessObject.
                GetType().InvokeMember("SetError", 
                BindingFlags.Public | BindingFlags.Instance | BindingFlags.InvokeMethod, 
                null, outofProcessObject, new object[] { errorWriter });
            outofProcessObject.
                GetType().InvokeMember("Execute", 
                BindingFlags.Public | BindingFlags.Instance | BindingFlags.InvokeMethod, 
                null, outofProcessObject, null);
            Console.ReadLine();

        }

        static void Main(string[] args)
        {
            string testAssemblyPath = 
                @"B:\OutOfProcess\OutOfProcess\OutOfProcessClasss\bin\Debug\OutOfProcessClasss.dll";
            ExecuteAsOutOfProcess(testAssemblyPath, "OutOfProcessClass.SuperProcess");

        }

        static String assemblyPath = "";
        static Assembly outofProcessDomain_AssemblyResolve(object sender, ResolveEventArgs args)
        {

            try
            {
                //We must load it to have the metadata and do reflection
                return Assembly.LoadFrom(assemblyPath);
            }
            catch
            {
                return null;
            }
        }

    }


}

 

Code for OutOfProcess Class

 

 

using System;
using System.Collections.Generic;
using System.Text;

namespace OutOfProcessClass
{
    public class SuperProcess : MarshalByRefObject
    {
        public void SetOut(System.IO.TextWriter newOut)
        {
            Console.SetOut(newOut);
        }
        public void SetError(System.IO.TextWriter newError)
        {
            Console.SetError(newError);
        }

        public void Execute()
        {
            for (int i = 1; i < 5000; i++)
            {
                Console.WriteLine("running running running ");
                if (i%100 == 0) Console.Error.Write("an error happened");
                
            }

        }
    }
}

Visual Studio 2005. Missing Attach To Process Option

30. September 2008 11:53 by Mrojas in General  //  Tags: , ,   //   Comments (0)

We found some machines that do not show the "Attach To Process" option.

This is very important for us, specially if you are testing the migration of an VB6 ActiveX EXE or ActiveX DLL to C#.

There is a bug reported by Microsoft http://support.microsoft.com/kb/929664

Just follow the Tool/Import Settings wizard to the end. Close and restart VS and the options will reapper.

 Also you might find that the Configuration Manager is not available to switch between Release and Build for example.

To fix this problem just go to Tools -> Options -> Projects and Solutions -> General... make sure the option "Show advanced build configurations" is checked.

TLBIMP SourceCode

30. September 2008 09:50 by Mrojas in General  //  Tags: , , , , , ,   //   Comments (0)

 

Have you ever wished to modify the way Visual Studio imported a COM Class. Well finally you can.

The Managed, Native, and COM Interop Team (wow what a name). It looks like the name of that goverment office in the Ironman movie.

Well this fine group of men, have release the source code of the TLBIMP tool. I'm more that happy for this.

I can know finally get why are some things imported the way they are.

http://www.codeplex.com/clrinterop

You can dowload also the P/Invoke assistant. This assistant has a library of signatures so you can invoke any Windows API.

 

Extended WebBrowser Control Series:NewWindow2 Events in the C# WebBrowserControl

The WebBrowser control for .NET is just a wrapper for the IE ActiveX control. However this wrapper does not expose all the events that the IE ActiveX control exposes.

For example the ActiveX control has a NewWindow2 that you can use to intercept when a new window is gonna be created and you can even use the ppDisp variable to give a pointer to an IE ActiveX instance where you want the new window to be displayed.

So, our solution was to extend the WebBrowser control to make some of those events public.

In general the solution is the following:

  1. Create a new Class for your Event that extend any of the basic EventArgs classes.
  2. Add constructors and property accessor to the class
  3. Look at the IE Activex info and add the  DWebBrowserEvents2 and IWebBrowser2 COM interfaces. We need them to make our hooks.
  4. Create a WebBrowserExtendedEvents extending System.Runtime.InteropServices.StandardOleMarshalObject and DWebBrowserEvents2. We need this class to intercept the ActiveX events. Add methos for all the events that you want to intercept.
  5. Extend the WebBrowser control overriding the CreateSink and DetachSink methods, here is where the WebBrowserExtendedEvents class is used to make the conneciton.
  6. Add EventHandler for all the events.

And thats all.Here is the code. Just add it to a file like ExtendedWebBrowser.cs

using System;
using System.Windows.Forms;
using System.ComponentModel;
using System.Runtime.InteropServices;

    //First define a new EventArgs class to contain the newly exposed data
    public class NewWindow2EventArgs : CancelEventArgs
    {

        object ppDisp;

        public object PPDisp
        {
            get { return ppDisp; }
            set { ppDisp = value; }
        }


        public NewWindow2EventArgs(ref object ppDisp, ref bool cancel)
            : base()
        {
            this.ppDisp = ppDisp;
            this.Cancel = cancel;
        }
    }

    public class DocumentCompleteEventArgs : EventArgs
    {
        private object ppDisp;
        private object url;

        public object PPDisp
        {
            get { return ppDisp; }
            set { ppDisp = value; }
        }

        public object Url
        {
            get { return url; }
            set { url = value; }
        }

       public DocumentCompleteEventArgs(object ppDisp,object url)
        {
           this.ppDisp = ppDisp;
           this.url = url;

        }
    }

    public class CommandStateChangeEventArgs : EventArgs
    {
        private long command;
        private bool enable;
        public CommandStateChangeEventArgs(long command, ref bool enable)
        {
            this.command = command;
            this.enable = enable;
        }

        public long Command
        {
            get { return command; }
            set { command = value; }
        }

        public bool Enable
        {
            get { return enable; }
            set { enable = value; }
        }
    }


    //Extend the WebBrowser control
    public class ExtendedWebBrowser : WebBrowser
    {
        AxHost.ConnectionPointCookie cookie;
        WebBrowserExtendedEvents events;


        //This method will be called to give you a chance to create your own event sink
        protected override void CreateSink()
        {
            //MAKE SURE TO CALL THE BASE or the normal events won't fire
            base.CreateSink();
            events = new WebBrowserExtendedEvents(this);
            cookie = new AxHost.ConnectionPointCookie(this.ActiveXInstance, events, typeof(DWebBrowserEvents2));

        }

        public object Application
        {
            get
            {
                IWebBrowser2 axWebBrowser = this.ActiveXInstance as IWebBrowser2;
                if (axWebBrowser != null)
                {
                    return axWebBrowser.Application;
                }
                else
                    return null;
            }
        }

        protected override void DetachSink()
        {
            if (null != cookie)
            {
                cookie.Disconnect();
                cookie = null;
            }
            base.DetachSink();
        }

        //This new event will fire for the NewWindow2
        public event EventHandler<NewWindow2EventArgs> NewWindow2;

        protected void OnNewWindow2(ref object ppDisp, ref bool cancel)
        {
            EventHandler<NewWindow2EventArgs> h = NewWindow2;
            NewWindow2EventArgs args = new NewWindow2EventArgs(ref ppDisp, ref cancel);
            if (null != h)
            {
                h(this, args);
            }
            //Pass the cancellation chosen back out to the events
            //Pass the ppDisp chosen back out to the events
            cancel = args.Cancel;
            ppDisp = args.PPDisp;
        }


        //This new event will fire for the DocumentComplete
        public event EventHandler<DocumentCompleteEventArgs> DocumentComplete;

        protected void OnDocumentComplete(object ppDisp, object url)
        {
            EventHandler<DocumentCompleteEventArgs> h = DocumentComplete;
            DocumentCompleteEventArgs args = new DocumentCompleteEventArgs( ppDisp, url);
            if (null != h)
            {
                h(this, args);
            }
            //Pass the ppDisp chosen back out to the events
            ppDisp = args.PPDisp;
            //I think url is readonly
        }

        //This new event will fire for the DocumentComplete
        public event EventHandler<CommandStateChangeEventArgs> CommandStateChange;

        protected void OnCommandStateChange(long command, ref bool enable)
        {
            EventHandler<CommandStateChangeEventArgs> h = CommandStateChange;
            CommandStateChangeEventArgs args = new CommandStateChangeEventArgs(command, ref enable);
            if (null != h)
            {
                h(this, args);
            }
        }


        //This class will capture events from the WebBrowser
        public class WebBrowserExtendedEvents : System.Runtime.InteropServices.StandardOleMarshalObject, DWebBrowserEvents2
        {
            ExtendedWebBrowser _Browser;
            public WebBrowserExtendedEvents(ExtendedWebBrowser browser)
            { _Browser = browser; }

            //Implement whichever events you wish
            public void NewWindow2(ref object pDisp, ref bool cancel)
            {
                _Browser.OnNewWindow2(ref pDisp, ref cancel);
            }

            //Implement whichever events you wish
            public void DocumentComplete(object pDisp,ref object url)
            {
                _Browser.OnDocumentComplete( pDisp, url);
            }

            //Implement whichever events you wish
            public void CommandStateChange(long command, bool enable)
            {
                _Browser.OnCommandStateChange( command,  ref enable);
            }


        }
        [ComImport, Guid("34A715A0-6587-11D0-924A-0020AFC7AC4D"), InterfaceType(ComInterfaceType.InterfaceIsIDispatch), TypeLibType(TypeLibTypeFlags.FHidden)]
        public interface DWebBrowserEvents2
        {
            [DispId(0x69)]
            void CommandStateChange([In] long command, [In] bool enable);
            [DispId(0x103)]
            void DocumentComplete([In, MarshalAs(UnmanagedType.IDispatch)] object pDisp, [In] ref object URL);
            [DispId(0xfb)]
            void NewWindow2([In, Out, MarshalAs(UnmanagedType.IDispatch)] ref object pDisp, [In, Out] ref bool cancel);
        }

        [ComImport, Guid("D30C1661-CDAF-11d0-8A3E-00C04FC9E26E"), TypeLibType(TypeLibTypeFlags.FOleAutomation | TypeLibTypeFlags.FDual | TypeLibTypeFlags.FHidden)]
        public interface IWebBrowser2
        {
            [DispId(100)]
            void GoBack();
            [DispId(0x65)]
            void GoForward();
            [DispId(0x66)]
            void GoHome();
            [DispId(0x67)]
            void GoSearch();
            [DispId(0x68)]
            void Navigate([In] string Url, [In] ref object flags, [In] ref object targetFrameName, [In] ref object postData, [In] ref object headers);
            [DispId(-550)]
            void Refresh();
            [DispId(0x69)]
            void Refresh2([In] ref object level);
            [DispId(0x6a)]
            void Stop();
            [DispId(200)]
            object Application { [return: MarshalAs(UnmanagedType.IDispatch)] get; }
            [DispId(0xc9)]
            object Parent { [return: MarshalAs(UnmanagedType.IDispatch)] get; }
            [DispId(0xca)]
            object Container { [return: MarshalAs(UnmanagedType.IDispatch)] get; }
            [DispId(0xcb)]
            object Document { [return: MarshalAs(UnmanagedType.IDispatch)] get; }
            [DispId(0xcc)]
            bool TopLevelContainer { get; }
            [DispId(0xcd)]
            string Type { get; }
            [DispId(0xce)]
            int Left { get; set; }
            [DispId(0xcf)]
            int Top { get; set; }
            [DispId(0xd0)]
            int Width { get; set; }
            [DispId(0xd1)]
            int Height { get; set; }
            [DispId(210)]
            string LocationName { get; }
            [DispId(0xd3)]
            string LocationURL { get; }
            [DispId(0xd4)]
            bool Busy { get; }
            [DispId(300)]
            void Quit();
            [DispId(0x12d)]
            void ClientToWindow(out int pcx, out int pcy);
            [DispId(0x12e)]
            void PutProperty([In] string property, [In] object vtValue);
            [DispId(0x12f)]
            object GetProperty([In] string property);
            [DispId(0)]
            string Name { get; }
            [DispId(-515)]
            int HWND { get; }
            [DispId(400)]
            string FullName { get; }
            [DispId(0x191)]
            string Path { get; }
            [DispId(0x192)]
            bool Visible { get; set; }
            [DispId(0x193)]
            bool StatusBar { get; set; }
            [DispId(0x194)]
            string StatusText { get; set; }
            [DispId(0x195)]
            int ToolBar { get; set; }
            [DispId(0x196)]
            bool MenuBar { get; set; }
            [DispId(0x197)]
            bool FullScreen { get; set; }
            [DispId(500)]
            void Navigate2([In] ref object URL, [In] ref object flags, [In] ref object targetFrameName, [In] ref object postData, [In] ref object headers);
            [DispId(0x1f7)]
            void ShowBrowserBar([In] ref object pvaClsid, [In] ref object pvarShow, [In] ref object pvarSize);
            [DispId(-525)]
            WebBrowserReadyState ReadyState { get; }
            [DispId(550)]
            bool Offline { get; set; }
            [DispId(0x227)]
            bool Silent { get; set; }
            [DispId(0x228)]
            bool RegisterAsBrowser { get; set; }
            [DispId(0x229)]
            bool RegisterAsDropTarget { get; set; }
            [DispId(0x22a)]
            bool TheaterMode { get; set; }
            [DispId(0x22b)]
            bool AddressBar { get; set; }
            [DispId(0x22c)]
            bool Resizable { get; set; }
        }
   }

Using C# or VB.NET collection in your VB6 Applications

28. August 2008 05:34 by Mrojas in General  //  Tags: , , , ,   //   Comments (0)

If you have some .NET code that you want to share with VB6, COM has always been a nice option. You just add couple of ComVisible tags and that's all.

But...

Collections can be a little tricky.

This is a simple example of how to expose your Collections To VB6.

Here I create an ArrayList descendant that you can use to expose your collections.
Just create a new C# class library project and add the code below.
Remember to check the Register for ComInterop setting.

 
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;

namespace CollectionsInterop
{
    
    [Guid("0490E147-F2D2-4909-A4B8-3533D2F264D0")]
    [ComVisible(true)]
    public interface IMyCollectionInterface 
    {

        int Add(object value);
        void Clear();
        bool Contains(object value);
        int IndexOf(object value);
        void Insert(int index, object value);
        void Remove(object value);
        void RemoveAt(int index);

        [DispId(-4)]
        System.Collections.IEnumerator GetEnumerator();
      
        [DispId(0)]
        [System.Runtime.CompilerServices.IndexerName("_Default")]
        object this[int index]
        {
            get;
        }
    }

    
    [ComVisible(true)]
    [ClassInterface(ClassInterfaceType.None)]
    [ComDefaultInterface(typeof(IMyCollectionInterface))]
    [ProgId("CollectionsInterop.VB6InteropArrayList")]
    public class VB6InteropArrayList : System.Collections.ArrayList, IMyCollectionInterface
    {

        #region IMyCollectionInterface Members


        // COM friendly strong typed GetEnumerator

        [DispId(-4)]
        public System.Collections.IEnumerator GetEnumerator()
        {
            return base.GetEnumerator();
        }



        #endregion
    }



    /// <summary>
    /// Simple object for example 
    /// </summary>
    [ComVisible(true)]
    [ClassInterface(ClassInterfaceType.AutoDual)]
    [ProgId("CollectionsInterop.MyObject")]
    public class MyObject
    {
        String value1 = "nulo";

        public String Value1
        {
            get { return value1; }
            set { value1 = value; }
        }
        String value2 = "nulo";

        public String Value2
        {
            get { return value2; }
            set { value2 = value; }
        }
    }


}
 
To test this code you can use this VB6 code. Remember to add a reference to this class library.
Private Sub Form_Load()
    Dim simpleCollection As New CollectionsInterop.VB6InteropArrayList
    Dim value As New CollectionsInterop.MyObject
    value.Value1 = "Mi valor1"
    value.Value2 = "Autre valeur"
    simpleCollection.Add value
    
    For Each c In simpleCollection
      MsgBox value.Value1
    Next
End Sub

Natural Languages and Programming Languages

19. August 2008 09:36 by Mrojas in General  //  Tags: ,   //   Comments (0)

I am a firm believer in program understanding and in that our computer skills will allow us to develop programs that will understand programs and maybe in the future even write some of them :).

 I also belive that natural languages ans programming languages are two things with a lot in common.

These are just some ideas about this subject. 

"A language convertion translates one languate to another language, while a language-level upgrade moves an application from an older version of a language to a modern or more standardized version of that same language. In both cases, the goal is to improve portability and understanbility of an application and position that application for subsequent transformation", Legacy Systems, Transformation Strategies by  William M. Ulrich.

An natural language convertion is exactly that. Translating one language to another language.

Natural language processing and transformation have a lot in common with automated source code migration. There is a lot of grammar studies on both areas, and a lot of common algorithms.

I keep quoting:

"Comparing artificial language and natural language it is very helpful to our understanding of semantics of programming languages since programming languages are artificial. We can see much similarity between these two kinds of languages:

Both of them must explain "given'" languages.
The goal of research on semantics of programming languages is the same as that of natural language: explanation of the meanings of given language. This is unavoidable for natural language but undesirable for programming language. The latter one has often led to post-design analysis of the semantics of programming languages wherein the syntax and informal meaning of the language is treated as given( such as PL/I, Fortran and C ). Then the research on the semantics is to understand the semantics of the language in another way or to sort out anomalies, ommisions, or defects in the given semantics-which hasn't had much impact on the language design. We have another kind of programming languages that have formal definitions, such as Pascal, Ada, SML. The given semantics allow this kind of programming language to be more robust than the previous ones.

Both of them separate "syntax'" and "semantics'".
Despite these similarities, the difference between the studies of natural and artificial language is profound. First of all, natural language existed for thousands of years, nobody knows who designed the language; but artificial languages are synthesized by logicians and computer scientists to meet some specific design criteria. Thus, `` the most basic characteritic of the distinction is the fact that an artificial language can be fully circumscribed and studied in its entirety.''

We already have developed a mature system for SYNTAX. In 1950's, linguist Chomsky first proposed formal language theory for English, thus came up with Formal Language Theory, Grammar, Regular Grammar, CFG etc. The ``first'' application of this theory was to define syntax for Algol and to build parser for it. The landmarks in the development of formal language theory are: Knuth's  parser, and YACC-which is a successful and ``final''application of formal language theory.
"

from Cornell university http://www.cs.cornell.edu/info/projects/nuprl/cs611/fall94notes/cn2/cn2.html
Jing Huang


I also will like to add a reference from an interesting work related to pattern recognition a technique used both in natural language processing (see for example http://prhlt.iti.es/) and reverse engineering.
This work is from Francesca Arcelli and Claudia Raibulet from Italy and they are working with the NASA Automated Software EngineeringResearch Center
http://smallwiki.unibe.ch/woor2006/woor2006paper3/?action=MimeView

 

Scripting your applications in .NET

14. August 2008 16:48 by Mrojas in General  //  Tags: , , , , ,   //   Comments (0)

In VB6 it was very simple to add scripting capabilities to your application.
Just by using the Microsoft Script Control Library
You can still use this library in .NET just as Roy Osherove' Bloc show in
http://weblogs.asp.net/rosherove/articles/dotnetscripting.aspx

However there are some minor details that must be taken care of:

* Objects must be exposed thru COM (Add the [ComVisible(true)] attribute to the class
* Add the ComVisible(true) attribute to the AssemblyInfo file
* Make these objects public
* Recommended (put your calls to Eval or ExecuteStatement inside try-catch blocks).

And here's an example:

using System;
using System.Windows.Forms;
 
namespace ScriptingDotNetTest
{
    [System.Runtime.InteropServices.ComVisible(true)]
    public partial class frmTestVBScript  : Form
    {
        public int MyBackColor
        {
            get { return System.Drawing.ColorTranslator.ToOle(this.BackColor); }
            set { this.BackColor = System.Drawing.ColorTranslator.FromOle(value); }
        }
 
        MSScriptControl.ScriptControl sc = new MSScriptControl.ScriptControl();
        private void RunScript(Object eventSender, EventArgs eventArgs)
        {
            try
            {
                sc.Language = "VbScript";
                sc.Reset();
                sc.AddObject("myform", this, true);
                sc.ExecuteStatement("myform.MyBackColor = vbRed");
            }
            catch 
            {
                MSScriptControl.IScriptControl iscriptControl = sc as MSScriptControl.IScriptControl;
                lblError.Text = "ERROR" + iscriptControl.Error.Description + " | Line of error: " + iscriptControl.Error.Line + " | Code error: " + iscriptControl.Error.Text;
            }
        }
 
        [STAThread]
        static void Main()
        {
            Application.Run(new frmTestVBScript());
        }
    }
}


TIP: If you don find the reference in the COM tab, just browse to c:\windows\system32\msscript.ocx

 

Vb Migration (not for the weak of mind) Post 2

14. August 2008 05:50 by Mrojas in General  //  Tags: , , , ,   //   Comments (0)

When people decide to migrate their VB6 applications they eventually end up questioning where they should go. Is VB.NET or C# a good choice?

I have my personal preference, but my emphasis is in developing the technology to take where YOU want to go.

VB.NET is a VB dialect very similar to VB6. It supports several constructs and it makes the changes easier.
C# has several differences from VB6, but it has it a growing language with lots of enthusiasts in its community.
Obviously migrating VB6 to VB dialect is a task far more easier than migrating to a different language.
However we are a research company with years of work in this area and challenges is just what we love.

Let's use a methaphor here.

My beautiful wife, was born in Moscow, Russia. Like her, I really enjoy reading a good book. Some of my favorite authors are
russian authors like Dostoievsky, Tolstoi and Chejov. However I still do not speak russian. I have tried, and I will keep trying but
I still don't know russian. I have read only translations of their books, and I really enjoy them.
As a native speaker my wife always tells me, that it is not the same to read those books in another language besides russian.
And they are phrases (specially in Chejov books that I might not completely understand) but I really got the author
message and enjoyed it.
Translating a book from russian to a more similar language like Ucranian is easier than translating it to English or Spanish.
But I think everybody agrees that is a task than can be done.

You can use terrible works case scenarios, but these scenarios must be analized.
Let see (I took these example from the link in that Francesco put in my previous post http://blogs.artinsoft.net/mrojas/archive/2008/08/07/vb-migration-not-for-the-weak-of-mind.aspx)

If you have code like this:

Sub CopyFiles(ByVal throwIfError As Boolean)
    If Not throwIfError Then On Error Resume Next
    Dim fso As New FileSystemObject
    fso.CopyFile "sourcefile1", "destfile1"
    fso.CopyFile "sourcefile2", "destfile2"
    fso.CopyFile "sourcefile3", "destfile3"
    ' seven more CopyFile method calls …
End Sub


and you translate it to:

void CopyFiles(bool throwIfError)
{
    Scripting.FileSystemObject fso = new Scripting.FileSystemObjectClass();
    try
    {
        fso.CopyFile("sourcefile1", "destfile1", true);
    }
    catch
    {
        if (throwIfError)
        {
            throw;
        }
    }
    try
    {
        fso.CopyFile("sourcefile1", "destfile1", true);
    }
    catch
    {
        if (throwIfError)
        {
            throw;
        }
    }
    try
    {
        fso.CopyFile("sourcefile1", "destfile1", true);
    }
    catch
    {
        if (throwIfError)
        {
            throw;
        }
    }
    // seven more try-catch blocks
}

I think that the russian is really keep in this translation.

First of all. When you do a translation, you should try to make it as native as possible. So why will you keep using a COM function when there is an
equivalent in .NET. So why not use System.IO.File.CopyFile("sourcefile1", "destfile1", true); instead?

Second of all. The On Error Resume Next, I agree is a not a natural statement in C#. I really think that using it could provide results that are less predictable.
Why? Becuase after executing it, are you sure that all the CopyFile occurred successfully? I would prefer wrapping the whole code inside a try-catch instead of trying
to provide an implementation that is not natural in C#, will Aspect Oriented programming provide a clean solution for this cases. Maybe?

RPG and COBOL to Object Oriented Programming, PowerBuilder to C#, Hierarquical Databases to Relational Databases are just the kind of challenges we have faced in our research project.
Not everything is easy, and we might not be able to automate all the tasks (commonly due to the cost of implementing the automation not becuase of feasability).

But at the end Could you understand the whole novel?, even if you didn't understand the joke in the one of the paragraphs in the page?

My years of reading make be belive that you can.


 

VB Migration (not for the weak of mind)

7. August 2008 05:14 by Mrojas in General  //  Tags: , , , ,   //   Comments (0)

Motivation: 

I hate to be disappointed. Specially if it is by a person you had respect for. And that's exactly what Francisco Balena from VB Migration Partner, has done. I have respected him for his books and all his VB6 experience. In terms of legacy VB6 code he is a monster. He is the man.

But in terms of code migration...

I'm not saying this because I work on code migration or maybe I'm biased a little by the fact that I work almost 10 years with a company that has done source code migration research on a big number of legacy languages such as COBOL, RPG, PL\1, Algol, Progress, PowerBuilder and also VB6.

I can tell the difference between a "compiler" and a system that rewrites a expression to the closest equivalent in the target language. We are aware of limitations. We are aware of paradigm differences and functional equivalence, but we talk from experience. We talk about our results. And we have proven those things we talk about.

 Let's says you create a Cobol Library with a "MOVE" function, and a COBOLPicture Type and add minus, divide, and add operators. And I write something like:

CobolPicture x = new CobolPicture("XXXX");

x.move(10);

x.add(10);

We have things like that, and it works. It's feasible and maybe there are cases where that is a solution. But we are also proud of have researchers that have been able to detect pattern to rewrite something like that like:

int x = 0;

x = 10;

x+=10;

And saying, that something is not possible just because you can't or you dont like it, just seem uneducated to me.

 All of this has motivated me to start a series of chapters I for a small blog book I will call VB Migration (not for the weak of mind).

For those of you, who really are tecnology savvy and are in the process of a VB Migration, this is YOUR book.

Migrating RDS CreateRecordSet in .NET

19. June 2008 06:24 by Mrojas in General  //  Tags: , ,   //   Comments (0)

This is way a discused with a friend for migrating a VB6 RDS CreateRecordset

 

Private Function Foo(rs As ADOR.Recordset) As Boolean
    On Error GoTo Failed
    Dim ColumnInfo(0 To 1), c0(0 To 3), c1(0 To 3)
    Dim auxVar As RDS.DataControl
    Set auxVar = New RDS.DataControl
 
    ColInfo(0) = Array("Value", CInt(201), CInt(1024), True)
    ColInfo(1) = Array("Name", CInt(129), CInt(255), True)
   
    Set rs = auxVar.CreateRecordSet(ColumnInfo)
    Foo = True
    Exit Function
Failed:
    Foo = False
    Exit Function
    
End Function

 

According to MSDN the CreateRecordset function takes a Varriant array with definitions for the columns. This definitions are made up of four parts

 
Attribute Description
Name Name of the column header.
Type Integer of the data type.
Size Integer of the width in characters, regardless of data type.
Nullability Boolean value.
Scale (Optional) This optional attribute defines the scale for numeric fields. If this value is not specified, numeric values will be truncated to a scale of three. Precision is not affected, but the number of digits following the decimal point will be truncated to three.
 

So if we are going to migrate to System.Data.DataColumn we will used a type translation like the following (for now I’m just putting some simple cases)

 
Length Constant Number DataColumn Type
Fixed adTinyInt 16 typeof(byte)
Fixed adSmallInt 2 typeof(short)
Fixed adInteger 3 typeof(int)
Fixed adBigInt 20  
Fixed adUnsignedTinyInt 17  
Fixed adUnsignedSmallInt 18  
Fixed adUnsignedInt 19  
Fixed adUnsignedBigInt 21  
Fixed adSingle 4  
Fixed adDouble 5  
Fixed adCurrency 6  
Fixed adDecimal 14  
Fixed adNumeric 131  
Fixed adBoolean 11  
Fixed adError 10  
Fixed adGuid 72 typeof(System.Guid)
Fixed adDate 7 Typeof(System.DateTime)
Fixed adDBDate 133  
Fixed adDBTime 134  
Fixed adDBTimestamp 135  
Variable adBSTR 8  
Variable adChar 129 typeof(string)
Variable adVarChar 200 typeof(string)
Variable adLongVarChar 201 typeof(string)
Variable adWChar 130  
Variable adVarWChar 202  
Variable adLongVarWChar 203  
Variable adBinary 128  
Variable adVarBinary 204  
Variable adLongVarBinary 205  
So the final code can be something like this: private bool Foo(DataSet rs)

{

try

{

DataColumn dtCol1 = new DataColumn("Value",typeof(string));
dtCol1.AllowDBNull = true;
dtCol1.MaxLength = 1024;

DataColumn dtCol2 = new DataColumn("Name",typeof(string));

dtCol2.AllowDBNull = true;
dtCol2.MaxLength = 255;
DataTable dt = rs.Tables.Add();

dt.Columns.Add(dtCol1);

dt.Columns.Add(dtCol2);

return true;

}

catch

{

return false;

}

}

 

NOTES:

My friend Esteban also told my that I can use C# 3 syntax and write something even cooler like:

DataColumn dtCol1 = new DataColumn()

{

ColumnName = "Value",

DataType = typeof (string),

AllowDBNull = true,

MaxLength = 1024

};

Dataset save columns as Attributes

5. June 2008 11:07 by Mrojas in General  //  Tags: , , , ,   //   Comments (0)

This code is so handy that I'm posting it just to remember. I preffer to serialize my datasets as attributes instead of elements. And its just a matter of using a setting. See: 

Dim cnPubs As New SqlConnection("Data Source=<servername>;user id=<username>;" & _
"password=<password>;Initial Catalog=Pubs;")
Dim daAuthors As New SqlDataAdapter("Select * from Authors", cnPubs)

Dim ds As New DataSet()
cnPubs.Open()
daAuthors.Fill(ds, "Authors")

Dim dc As DataColumn
For Each dc In ds.Tables("Authors").Columns
dc.ColumnMapping = MappingType.Attribute
Next

ds.WriteXml("c:\Authors.xml")

Console.WriteLine("Completed writing XML file, using a DataSet")
Console.Read()

VB Runtime supported in next Windows

Here is an excerpt from an article that Greg DeMichillie wrote on Directions on Microsoft April Edition:

"The planned follow-on release to Windows Vista, code-named Windows 7, will not include the Visual Basic 6.0 (VB 6) runtime libraries, Microsoft has begun informing customers. This sets a timeframe for the final end of support for the runtime."

As we have informed on several occasion in this Blog, Microsoft is performing all the normal steps to retire a technology from market.  Visual Basic 6 was/is a tremendously popular technology but never the less it will have to go away.

Jarvis Coffin once said: "All technologies fade away, but they don’t die."  This is most probably what is going to happen to VB6 (hey.. we still have COBOL code written more than 30 years ago that is alive and kicking!!!) but the question I have for you is: will you embrace the new technology?  Or will you fade away with it?

It is time to upgrade your skills as a developer and also to migrate your application to greener grounds. 

ArtinSoft has been hugely successful at migrating customers as Eric Nelson (Microsoft UK DPE and blogger) recently mentioned: "Artinsoft have a lot of VB6 migration experience and can help you do the migration - either by licensing their VB Upgrade Companion or by taking advantage of their migration services. Artinsoft are doing some great work with some of my UK ISVs helping them move off VB6."

If you have any questions or comments regarding your migration strategy let's cover them in this blog.

UPDATE March 11th 2009: The title of this post was: "VB Runtime NOT in next Windows".  However,  Microsoft has recently updated the support policy for Visual Basic 6 Runtime.  The new policy states that the VB runtime is now supported for the full lifecycle of Windows 7. 

PS: You can read the inflammatory comments I got over the past week below!

 

ActiveX Controls in .NET and the Enabled bug

18. April 2008 07:04 by Mrojas in General  //  Tags: , , , ,   //   Comments (0)

We found and interesting bug during a migration. The issue was that when there was an iteration through the controls in the forms, and you set the Enabled property, the property didn't get set.

After some research my friend Olman found this workaroung

 

foreach(Control c in Controls)

  ctrl.Enabled = true;
  if (ctrl is AxHost) ((AxHost)ctrl).Enabled = true;
}

Install Assembly in GAC with C#

9. April 2008 05:42 by Mrojas in General  //  Tags: , , , ,   //   Comments (0)

 

Do you want to create a program to install your assembly in the GAC using C#. Well if you had that requirement or you are just curious, here is how.

I read these three articles:

Demystifying the .NET Global Assembly Cache

GAC API Interface 

Undocumented Fusion

What I wanted just a straight answer of how to do it. Well here is how:

 

using System;
using System.Collections.Generic;
using System.Text;
using System.GAC;
//// Artinsoft
//// Author: Mauricio Rojas orellabac@gmail.com mrojas@artinsoft.com
//// This program uses the undocumented GAC API to perform a simple install of an assembly in the GAC
namespace AddAssemblyToGAC
{
    class Program
    {
        static void Main(string[] args)
        {
            // Create an FUSION_INSTALL_REFERENCE struct and fill it with data
            FUSION_INSTALL_REFERENCE[] installReference = new FUSION_INSTALL_REFERENCE[1];
            installReference[0].dwFlags = 0;
            // Using opaque scheme here
            installReference[0].guidScheme = System.GAC.AssemblyCache.FUSION_REFCOUNT_OPAQUE_STRING_GUID;
            installReference[0].szIdentifier = "My Pretty Aplication Identifier";
            installReference[0].szNonCannonicalData= "My other info";
 
            // Get an IAssemblyCache interface
 
            IAssemblyCache pCache = AssemblyCache.CreateAssemblyCache();
            String AssemblyFilePath = args[0];
 
            if (!System.IO.File.Exists(AssemblyFilePath))
            {
                Console.WriteLine("Hey! Please use a valid path to an assembly, assembly was not found!");
            }
            int result = pCache.InstallAssembly(0, AssemblyFilePath,installReference);
            //NOTE recently someone reported a problem with this code and I tried this:
            // int result = pCache.InstallAssembly(0, AssemblyFilePath,null); and it worked. I think is a marshalling issue I will probably review it later
 
            Console.WriteLine("Process returned " + result);
            Console.WriteLine("Done!");
 
        }
 
    }
}

And here's the complete source code for this application: DOWNLOAD SOURCE CODE AND BINARIES

April 8th 2008 and the end of Visual Basic 6 Support

8. April 2008 10:04 by Fzoufaly in General  //  Tags: , ,   //   Comments (0)

The date has arrived Visual Basic 6 leaves Extended support today.

Rob Helm recently wrote on "Directions on Microsoft": "Some organizations will let support lapse on the VB6 development environment, gambling that any serious problems in the VB6 environment has already been discovered"  Additionally, Rob adds: "... organizations remaining loyal to VB6 applications will have to make increasingly heroic efforts to keep those applications running as their IT environments change."

Organizations that GAMBLE with their business continuity, IT professionals that need to make HEROIC efforts to keep applications running!  Don't you believe that maintaining an IT organization supporting a business is already enough of an effort to add to the mix unsupported applications?

Do you plan to be a GAMBLING HERO or is it about time to consider ways out of Visual Basic 6?

Well this might be just the right time.  ArtinSoft is about to release a new version of the Visual Basic Upgrade companion. The effort required to migrate has been reduced even further and it now makes more sense than ever to automatically upgrade your applications to C# or VB.NET.

Have you been procrastinating the decision to move?  Act now!!

Useful MSBuild Custom Tasks

3. April 2008 08:48 by Mrojas in General  //  Tags: , , , ,   //   Comments (0)

I present here the implementation of some useful tasks
In Artinsoft we perform massive migrations of VB6 code to VB.Net
and C#.

And sometimes after migration there are customizations to be
performed on the code, to add new functionality or to set certain new
properties.

 The idea was to provide a couple of very simple and puntual MSBuildTask
 to illustrate how easy it is to create custom tasks and to provide a starting
 point to create new one.

 You can freely use this code, just keep this comments and remember this is just
 a sample code. There are not warranties. ;) And i made it a rush I know it could have
 been written better

 Artinsoft
 mrojas@artinsoft.com
 

The implemented tasks are:

RemoveCOMReference 
 Removes COMReferences from your project. COM references are for when you are using things thru Interop
FixOutputPath 
 Resets the output paths to bin\Release and bin\Debug
AddProjectReference Add a reference to another project. A nice feature is that it generates RelativePaths the way Visual Studio does
AddSimpleReference Add a reference to a very simple references like the ones you add when you click Add Reference and add System.EnterpriseServices
ChangeCurrentBuildSetting This can be used for a lot of things.

For example to turn on or off the RegisterForComInterop setting

To set conditional compilation variables

To set debug info to pdbonly

The sky is the limit jeje

The following is a sample project file

<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- make sure that the Assembly is in a place where msbuild can find it, a simple way is just to put it 
in the same directory of your .proj file -->
<UsingTask TaskName="SomeUsefulTasks.MSBuild.RemoveCOMReference"    
  AssemblyFile="SomeUsefulTasks.dll"/> 
<UsingTask TaskName="SomeUsefulTasks.MSBuild.FixOutputPath"         
  AssemblyFile="SomeUsefulTasks.dll"/> 
<UsingTask TaskName="SomeUsefulTasks.MSBuild.AddProjectReference"   
  AssemblyFile="SomeUsefulTasks.dll"/> 
<UsingTask TaskName="SomeUsefulTasks.MSBuild.AddSimpleReference"    
  AssemblyFile="SomeUsefulTasks.dll"/> 
<UsingTask TaskName="SomeUsefulTasks.MSBuild.ChangeProjectBuildSetting"    
  AssemblyFile="SomeUsefulTasks.dll"/> 
 
   <ItemGroup>
    <VSProjects Include="$(Start)\**\*.*proj" />
  </ItemGroup>
 
<!--
Run with 
MSBUILD SampleProject.proj /target:COMReference /p:Start="C:\MyCode"
-->
  <Target Name="COMReference">
    <RemoveCOMReference SourceFiles="@(VSProjects)" ComReferenceName="MSXML2" />
  </Target> 
  
  
<!-- 
Adds a project reference  
Run with 
MSBUILD SampleProject.proj /target:AddProjectReference /p:Start="C:\MyCode" /p:ProjectPath="C:\MyCode\MyNewSuperProject\Project1.csproj"
-->
   <Target Name="AddProjectReference">
      <AddProjectReference SourceFiles="@(VSProjects)"  AbsolutePathToProject="$(ProjectPath)"/>
   </Target> 
   
   
<!-- 
Adds a reference to a standard assembly 
Run with 
MSBUILD SampleProject.proj /target:AddSimpleReference /p:Start="C:\MyCode" /p:Reference="System.EnterpriseServices"   
-->
<Target Name="AddSimpleReference">
      <AddSimpleReference SourceFiles="@(VSProjects)" Reference="$(Reference)" />
</Target> 
 
  
<!-- 
Resets the OutputPaths to .\bin\Debug and .\bin\Release 
Run with 
MSBUILD SampleProject.proj /target:FixOutput /p:Start="C:\MyCode" /p:Reference="System.EnterpriseServices"   
-->
<Target Name="FixOutput">
    <FixOutputPath SourceFiles="@(VSProjects)"  />
</Target> 
  
<!-- 
Adds a reference to a standard assembly 
There are several options here for example to set the project debug info to pdb-only do this:
Run with 
MSBUILD SampleProject.proj /target:ChangeSettingToPDBOnly /p:Start="C:\MyCode" 
Or run with 
MSBUILD SampleProject.proj /target:ChangeSettingAddAConstant /p:Start="C:\MyCode" 
Or run with 
MSBUILD SampleProject.proj /target:SettingComInterop /p:Start="C:\MyCode" 
-->
 
<Target Name="ChangeSettingToPDBOnly">
      <ChangeProjectBuildSetting 
          SourceFiles="@(VSProjects)" 
          ConfigurationType="All" 
          Setting="DebugType" 
          NewValue="pdbonly" />
</Target> 
   
<Target Name="ChangeSettingAddAConstant">
      <ChangeProjectBuildSetting 
          SourceFiles="@(VSProjects)" 
          ConfigurationType="All" 
          Setting="DefineConstants" 
          NewValue="MYNEWVAL" 
          Add="True"/>
</Target> 
 
 
<Target Name="SettingComInterop">
      <ChangeProjectBuildSetting 
          SourceFiles="@(VSProjects)" 
          ConfigurationType="All" 
          Setting="RegisterForComInterop" 
         NewValue="true" />
</Target> 
 
  
</Project>

DOWNLOAD CODE AND BINARIES

ActiveX exceptions when running in .NET

5. February 2008 04:11 by Mrojas in General  //  Tags: , , , ,   //   Comments (0)

During migration to C# or .NET it is easier to keep the same ActiveX.
The VBCompanion does a great work in migrating the ActiveX control using the .NET ActiveX wrappings and fixing all method calls. 
Sadly sometimes those ActiveX do not work properly in .NET.

Well we have good news.
Recently my friend Jose David (who we keep bothering because he is now 
a Project Manager and now he only programs in MS Excel and MS Project, I added the MS by his request :P) fixed a curious bug
we had with an aplication we migrated from VB6 to C#.

The thing is that the aplication had an ActiveX control with a strange runtime behaviour.
We migrated the application keeping the ActiveX control and in most ocasions it worked ok.

But randomly it started throwing exceptions.

During the testing he discovered that if he repeated the steps slowly the bug did not reproduced.

So his idea was that it was due a garbage collection issue. And SURPRINSINLY he was right :P

He added this:

System.GC.Collect();

System.

GC.WaitForPendingFinalizers();

 And the application started to work.

It seems like some of the COM objects needed a little more time for releasing all references :)

 

IsMissing migration in VB.NET or C#

4. February 2008 11:43 by Mrojas in   //  Tags: , , , , , ,   //   Comments (0)

Recently  we added some support for migrating the IsMissing function to VB.NEt or C#

The thing is. In VB6 the IsMissing Function is TRUE only if you have something like:

Public Sub Foo(Optional str)

 

Where you dont specify the variable type, or if you have

 

Public Sub Foo(Optional str as Variant)

 

And is IsMissing is FALSE for any other case. Including Optional variables whose definition type is not Variant.

 

So let's see some examples to illustrate the idea:
 Example 1:

Public Sub Foo(str, a As Integer, b As Integer, Optional c As Integer)
    MsgBox (str & "Foo Is missing a " & IsMissing(a))
    MsgBox (str & "Foo Is missing b " & IsMissing(b))
    MsgBox (str & "Foo Is missing c " & IsMissing(c))
End Sub

 

It occurs that IsMissing is really FALSE in all cases. So it is equivalent to:

 

Public Sub Foo(str, a As Integer, b As Integer, Optional c As Integer)
    MsgBox (str & "Foo Is missing a " & false)
    MsgBox (str & "Foo Is missing b " & false)
    MsgBox (str & "Foo Is missing c " & false)
End Sub

 

 

Example 2:

 

Public Sub Goo(str, a As Integer, b As Integer, Optional c As Object, Optional d As Byte, Optional e)
    MsgBox (str & "Goo Is missing a" & IsMissing(a))
    MsgBox (str & "Goo Is missing b" & IsMissing(b))
    MsgBox (str & "Goo Is missing c" & IsMissing(c))
    MsgBox (str & "Goo Is missing d" & IsMissing(d))
    MsgBox (str & "Goo Is missing e" & IsMissing(e))
End Sub

 

All cases EXCEPT "e" are equivalent to FALSE

 

Public Sub Goo(str, a As Integer, b As Integer, Optional c As Object, Optional d As Byte, Optional e)
    MsgBox (str & "Goo Is missing a" & false)
    MsgBox (str & "Goo Is missing b" & false)
    MsgBox (str & "Goo Is missing c" &false)
    MsgBox (str & "Goo Is missing d" & false)
    MsgBox (str & "Goo Is missing e" & IsMissing(e))
End Sub

 

So if you are migrating your VB6 Code to C# put attention to these little details it can save you a lot of time.And remember that this is just one feature of VBCompanion tool ;)

 

Case Sensitive SQL Server

26. October 2007 06:29 by Mrojas in General  //  Tags: , , , ,   //   Comments (0)
Recently a friend at work had a problem querying a SQL server that indicated that the column name was wrong.

The only thing wrong was the the case. For example he had COLUMN1 instead of Column1. I had never seen that problem in SQLServer.
I had seed that in Sybase but not in SQLServer. He solved that by changing the database collating sequence to something like this:

alter database database1 collate SQL_Latin1_General_CP1_CI_AI

the CI in the collating indicates Case Insensitive

For more information on SQL Server collations check: http://msdn2.microsoft.com/en-us/library/aa258233(SQL.80).aspx

And you determine your current database collation use a code like this:

USE yourdb>
GO

print 'My database [' + db_name() + '] collation is: ' + cast( DATABASEPROPERTYEX ( db_name(), N'Collation' ) as varchar(128) )

print 'My tempdb database collation is: ' + cast( DATABASEPROPERTYEX ( 'tempdb', N'Collation' ) as varchar(128) )

 

Track Changes in VSS (Visual Source Safe)

24. October 2007 06:18 by Mrojas in General  //  Tags: , , , , ,   //   Comments (0)

MS VSS (Visual SourceSafe) is not really my preferred Source Control application, but
sometimes in your company that is what is available and you need to used it to have
at least some versioning of the code.

But haven't you had a situation where last week everything worked and
now everything is broken. And now is up to you to determine what went
wrong? I have it all the time.
 

VSS have some search tools but I really do not enjoy using them.
The Code Project Site provides an excellent tool called VssReporter

Sample Image - VssReporter.jpg

Do take a look at it, it makes it more easy to track changes. :)

 

Categories