ArtinSoft's Blogs

Software Migration Experts
Welcome to ArtinSoft's Blogs Sign in | Join | Help
in Search

Mauricio Rojas Blog

  • ColdFusion 8 and JRockIt

    JRockIt is an interesting Java Virtual Machine. I really admire the technology used in it. Well following Mike’s post about Memory Leaks in Coldfusion http://www.schierberl.com/cfblog/index.cfm/2006/10/12/ColdFusion_memoryLeak_profiler I was trying to configure the JRockit JVM to do some profiling on an important application.

    But alas. Time just flies and things have change a lot since Mike wrote that post. So this is basically an update:

    1) JRockit must be downloaded from: http://www.oracle.com/technology/software/products/jrockit/index.html You must accept the license and also have an OTN Account (this is a free process). Download the right version for your platform. I tried the x86 version on my 64 bit server and it didn;t work, so use the version that fits better to your platform. However there is no 64 bit client version of Memory Leak and all the other nice client tools. But dont worry. Download both version 64 and 32 bits versions. You will use the 64 version for your server and the 32 bit version for monitoring.

    2) Stop coldfusion server

    3) Modify the jvm.config file. In my case it was in: C:\ColdFusion8\runtime\bin\jvm.config

    comment out old java.home and add a new line like:
    java.home=C:/Program Files/Java/jrmc-3.1.0-1.6.0/jre

    In the java.args setting, remove parameter –Xbatch (dont know why, it just didnt work)

    add a parameter like:
    -Xmanagement:ssl=false,authenticate=false,autodiscovery=true

    *In my case I also had to disable security in C:\Program files\Java\jrmc-3.1.0-1.6.0\jre\management\management.properties with a line like:

    com.sun.management.jmxremote.authenticate=false

    4) Re start coldfusion

  • DDE in .NET

    Someone recently made me remind an old technology called DDE.

    “Dynamic Data Exchange (DDE) is a technology for communication between multiple applications under Microsoft Windows or OS/2

     

    “The primary function of DDE is to allow Windows applications to share data. For example, a cell in Microsoft Excel could be linked to a value in another application and when the value changed, it would be automatically updated in the Excel spreadsheet. The data communication was established by a simple, three-segment model. Each program was known to DDE by its "application" name. Each application could further organize information by groups known as "topic" and each topic could serve up individual pieces of data as an "item". For example, if a user wanted to pull a value from Microsoft Excel which was contained in a spreadsheet called "Sheet1" in the cell in the first row and first column, the application would be "Excel", the topic "Sheet1" and the item "r1c1".

    Note: In DDE, the application, topic and item are not case-sensitive.”

     

    So in VB6 you can have something like:

     

    Private Sub Form_Load()
    Text1.LinkMode = 0
    Text1.LinkTopic = "Excel|Sheet1"
    Text1.LinkItem = "R1C1"
    Text1.LinkMode = 1
    End Sub

     

     

    How can you do that in .NET. Is it possible in C#? Well I started looking around and found several forums explaining about all the API calls and I was just about to write my own solution when I found NDDE. This project hosted in CodePlex “provides a convenient and easy way to integrate .NET applications with legacy applications that use Dynamic Data Exchange (DDE)” :)

     

    So this is a  nice example of how to do the previous lines in C#:

            //This class provides the infraestructure for DDE comunication
            NDde.Client.DdeClient ddeClient_TextBox1 = null;
    
            private void Form1_Load(object sender, EventArgs e)
            {
                //I initialize the DDEClient object. Application is Excel and Topic is Sheet1. I'm using the 
                //the TextBox as the syncronization object
                ddeClient_TextBox1 = new NDde.Client.DdeClient("Excel", "Sheet1", textBox1);
                //Connect to the DDE Server
                ddeClient_TextBox1.Connect();
                //Start the Advise Loop
                ddeClient_TextBox1.StartAdvise("R1C1", 1, true, 60000);
                //Setup the Advise Method
                ddeClient_TextBox1.Advise += new EventHandler<NDde.Client.DdeAdviseEventArgs>(ddeClient_TextBox1_Advise);
                //Setup a method to Poke the Server for TextBox cahnges
                textBox1.TextChanged += new EventHandler(textBox1_TextChanged);
    
            }
            
            void textBox1_TextChanged(object sender, EventArgs e)
            {
                //Syncronous Poking the server
                ddeClient_TextBox1.Poke("R1C1", textBox1.Text + "\0", 4000);
            }
    
            const string DDE_postFix = "\r\n\0";
            void ddeClient_TextBox1_Advise(object sender, NDde.Client.DdeAdviseEventArgs e)
            {
                //Advise only if needed
                if (e.Text.Length >=DDE_postFix.Length && textBox1.Text + DDE_postFix != e.Text)
                    textBox1.Text = e.Text.Substring(0,e.Text.Length-3);
            }
    NOTE: Remember that you need to download NDDE and add a reference to this library

    This is very good library, you can also set up a lot of Async calls to even improve performance. I have even thought of making an extender as the ToolTip control to add LinkTopic, LinkMode and LinkItem properties for Winforms controls or provide extensions methods to make all the syntax easier, but that is for a future post. Good Luck.

  • VB6 Migration of Property Pages

    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 editor can be actived in the designer selecting Properties from the context menu when you right click over the control.

    “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 following tool will modify your project to make your PropertyPages look like UserControls. This will allow  the VBUC migration tool to recover some of the PropertyPages code and appearance and with some manual changes you can get your property pages to work again.

    DOWNLOAD TOOL

    For each of your migrated controls that used property pages, you will need to create a ComponentEditor. The following code shows a ComponentEditor with using one property 

    Imports System
    Imports System.ComponentModel
    Imports System.ComponentModel.Design
    Imports System.Collections
    Imports System.Drawing
    Imports System.IO
    Imports System.Runtime.Serialization
    Imports System.Runtime.Serialization.Formatters.Binary
    Imports System.Windows.Forms
    Imports System.Windows.Forms.Design
    
    ' This example demonstrates how to implement a component editor that hosts 
    ' component pages and associate it with a component. 
    Public Class YourComponentEditor
        Inherits System.Windows.Forms.Design.WindowsFormsComponentEditor
    
        ' This method override returns an type array containing the type of 
        ' each component editor page to display.
        Protected Overrides Function GetComponentEditorPages() As Type()
            Return New Type() {GetType(YourComponentEditorPages)}
        End Function
    
        ' This method override returns the index of the page to display when the 
        ' component editor is first displayed.
        Protected Overrides Function GetInitialComponentEditorPageIndex() As Integer
            Return 0
        End Function
    
        Public Overloads Overrides Function EditComponent(ByVal context As System.ComponentModel.ITypeDescriptorContext, ByVal component As Object) As Boolean
            'You should add some code HERE
        End Function
    End Class
    
    Friend Class YourComponentEditorPages
        Inherits System.Windows.Forms.Design.ComponentEditorPage
    
        Public Sub New()
            ' Initialize the page, and its controls.
    
            'For each page do somethin like this
            Dim page1 As New YourPropertyPage1
            Me.Size = New Size(400, 250)
            Me.Text = "Your Page Caption"
            Me.Controls.Add(page1)
        End Sub
    
        ' The LoadComponent method is raised when the ComponentEditorPage is displayed.
        Protected Overrides Sub LoadComponent()
        End Sub
    
        ' The SaveComponent method is raised when the WindowsFormsComponentEditor is closing 
        ' or the current ComponentEditorPage is closing.
        Protected Overrides Sub SaveComponent()
        End Sub
    
    End Class

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

    <EditorAttribute(GetType(YourComponent), GetType(ComponentEditor))> _
    Partial Public Class CenteredLabel
        Inherits System.Windows.Forms.UserControl

    I hope this helps to get your code faster in .NET

  • VB6 TabIndex and C#

    Some time ago Jose Aguilar had blogged about the Interesting Behavior of TabIndex in Migrated Applications. As he explained at the time there are functional differences between the TabIndex behaviour in VB6

     

    If you look at Figure1.

    image

    Figure 1. This image show a VB6 form, the TabIndex values and the way the form navigates when you press Tab.

    If you migrate that form with the VBUC and activate the TabOrder option in View\TabOrder you will see something like:

    image

    As you can see by the 0.1 and 0.3 and 5.4 and 5.2 values. TabOrder in .NET is hierarquical. When you press tab you will navigate to the next control in the container, and when you get to the last in that container then you will switch to the next one in the following container. This is different from the VB6 world when you would have switched from 0.1 to 5.2.

    How can we fix this without a lot of manual corrections. Well you can override the ProcessTabKey method to navigate controls following the tabIndex without taking into account the containers.

    The code you will need to add is:

            /// <summary>
    /// holds a list of controls for tab navigation
    /// </summary>
    List<Control> controls = new List<Control>();
    /// <summary>
    /// Populates the list used for tab navigation
    /// </summary>
    /// <param name="c">Control to use to populate list</param>
    protected void BuildOrder(Control c)
    {
    if (c.TabStop)
    controls.Add(c);
    if (c.Controls.Count > 0)
    {
    foreach (Control child in c.Controls)
    BuildOrder(child);
    }
    }
    /// <summary>
    /// Transversers all form controls to populate a list ordered by TabIndex
    /// that will be used to follow tabindex ignoring containers
    /// </summary>
    protected void BuildOrder()
    {
    if (controls.Count == 0)
    {

    foreach (Control c in this.Controls)
    {
    BuildOrder(c);
    }
    controls.Sort(
    delegate(Control c1, Control c2) { return c1.TabIndex.CompareTo(c2.TabIndex); });
    }
    }
    /// <summary>
    /// Overrides default tabIndex behaviour
    /// </summary>
    /// <param name="forward"></param>
    /// <returns></returns>
    protected override bool ProcessTabKey(bool forward)
    {
    BuildOrder();
    if (ActiveControl != null)
    {
    int index = controls.IndexOf(ActiveControl);
    if (index != -1)
    {
    if (forward)
    controls[(index + 1) % controls.Count].Select();
    else
    controls[index==0?controls.Count-1:index-1].Select();

    return true;
    }

    else
    return false;
    }
    else
    return base.ProcessTabKey(forward);
    }

    After adding this code just run your project and it will fix the tabIndex issues.

  • A Better Visual Studio!

    Recently I discovered in MSDN a great addition, a must to for all C# developers. CodeRush Express.

    This product was build by DevExpress and it just make it perfect your experience with Visual Studio.

     

    For example finding symbols or files, tabbing between references, and more than 20 differente refactorings!!!!

    Take at look at this new extension! It’s a absolutely a must.

  • And the WebBrowser keeps going…

    Well recently Kingsley has point me to a lot of useful links to improve the ExtendedWebBrowser. However he found another detail. When in Javascript you do something like a:

    window.open(‘url’,’window’,’width=200;height=300’);

    Those width and height settings were not being considered in the new window. I researched for I while until I found this great link:

     

    HOW TO: Get Width and Height from window.open() Inside a WebBrowser Host by Using Visual Basic .NET

    So basicly I follow the sugested code and added logic in my EventSink class:

            public void WindowSetLeft(int Left)
            {
                ///Should I calculate any diff?
                _Browser.Parent.Left = Left;
    
            }
    
            public void WindowSetTop(int Top)
            {
                _Browser.Parent.Top = Top;
    
            }
    
            public void WindowSetWidth(int Width)
            {
                int diff = 0;
                diff = _Browser.Parent.Width - _Browser.Width;
                _Browser.Parent.Width = diff + Width;
    
            }
            public void WindowSetHeight(int Height)
            {
                int diff = 0;
                diff = _Browser.Parent.Height - _Browser.Height;
                _Browser.Parent.Height = diff + Height;
    
            }
    So now when the window opens it takes the specified width, heigth, left and top.

    As always

    HERE IS THE UPDATED CODE

  • 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

  • List Jobs in Oracle

    If you have created any schedule jobs or you just need to see what jobs are available in a server you use the dba_jobs table.

    The following links provides more details about this view: http://www.praetoriate.com/data_dictionary/dd_dba_jobs.htm

  • Opening Popup in a NewWindow

    In a previous post, i had published an “Extended Version” of the WebBrowser control
    that gave you access to events like the NewWindow2.
    This event that is not public in the common WebBrowser control allows you to intercept
    the NewWindow event but gives you the posibility to setup the the ppDisp property witch sets
    a pointer to the WebBrowser where the new window will be open.

    So i setup a small example using this “ExtendedBrowser”.

    I created a simple page (well really it was my wife, I know about transport-layer, C++, bits etc, but I never remember HTML syntax):

    <html>
    <body>
    <H1> This is sample page to test opening a pop up in a new form </H1>
    <input type="button" onclick="window.open('test0.htm')"/>
    </body>
    </html>

    And created a simple form like in the following picture:

    image

    Instead of using a WebBrowser control i just used an ExtendedWebBrowser from my previous post.

    And added code like:

            private void extendedWebBrowser1_NewWindow2(object sender, NewWindow2EventArgs e)
            {
                //Intercepting this event will allow us to create a new form in which
                //we will open the new webpage, to do that we must set the ppDisp property
                //of the NewWindow2EventArgs
                FormWithExtendedBrowser form1 = new FormWithExtendedBrowser();
                form1.Show();
                e.PPDisp = form1.extendedWebBrowser1.Application;
            }

    When I run the code , it now opens the pop up in my form:

    image

    But test if for yourself! :) HERE IS THE CODE

  • VB6 Migrating MouseIcon Property

    In VB6 you can have code like:

    Private Sub Command1_Click()
        Dim x As StdPicture
        Set x = LoadPicture("C:\setup.ico")
        Me.MouseIcon = LoadPicture("C:\setup.ico")
        Me.MousePointer = ccCustom
    End Sub

     

    How can you migrate that to .NET??????

    Well maybe with a helper like the following can help:

    using System; 
    using System.Windows.Forms; 
    using VB6 = Microsoft.VisualBasic.Compatibility.VB6.Support;
    
    namespace Project2
    {
        internal partial class Form1
            : System.Windows.Forms.Form
            {
            
                private void  Command1_Click( Object eventSender,  EventArgs eventArgs)
                {
                    System.Drawing.Image x;
                    x = System.Drawing.Image.FromFile("C:\\setup.ico");
                    this.Cursor = CursorHelper.CreateCursor(x);
    
            }
                [STAThread]
                 static void  Main()
                {
                        Application.Run(new Form1());
                }
            }
    
    
    
    
      public class CursorHelper 
      {
          private struct IconInfo
          {
            public bool fIcon;
            public int xHotspot;
            public int yHotspot;
            public IntPtr hbmMask;
            public IntPtr hbmColor;
          }
      
        [System.Runtime.InteropServices.DllImport("user32.dll")]
        static extern IntPtr CreateIconIndirect(ref IconInfo icon);
    
        [System.Runtime.InteropServices.DllImport("user32.dll")]
        [return: System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.Bool)]
        static extern bool GetIconInfo(IntPtr hIcon, ref IconInfo pIconInfo);
    
        private static Cursor CreateCursor(System.Drawing.Bitmap bmp, int xHotSpot, int yHotSpot)
        {
            IconInfo tmp = new IconInfo();
            GetIconInfo(bmp.GetHicon(), ref tmp);
            tmp.xHotspot = xHotSpot;
            tmp.yHotspot = yHotSpot;
            tmp.fIcon = false;
            return new Cursor(CreateIconIndirect(ref tmp));
        }
    
    
        public static Cursor CreateCursor(object picture)
        {
          if (picture is System.Drawing.Bitmap)
              return CreateCursor(picture as System.Drawing.Bitmap, 3, 3);
          else 
          {
              System.Drawing.Image image = null;
              IntPtr iunknown = System.Runtime.InteropServices.Marshal.GetIUnknownForObject(picture);
              if (iunknown == IntPtr.Zero)
              {
                  throw new Exception("Unsupported format");
              }
              else 
              {
                  Guid guidIPicture = new Guid("7BF80980-BF32-101A-8BBB-00AA00300CAB");
                  Guid guidIPictureDisp = new Guid("7BF80981-BF32-101A-8BBB-00AA00300CAB");
                  IntPtr testIntPtr = IntPtr.Zero;
                  if (System.Runtime.InteropServices.Marshal.QueryInterface(iunknown,ref guidIPicture,out testIntPtr)==0)
                  {
                      image = Microsoft.VisualBasic.Compatibility.VB6.Support.IPictureToImage(picture);
                  }
                  else if (System.Runtime.InteropServices.Marshal.QueryInterface(iunknown,ref guidIPictureDisp,out testIntPtr)==0)
                  {
                      image = Microsoft.VisualBasic.Compatibility.VB6.Support.IPictureDispToImage(picture);
                  }
                  if (image == null)
                  {
                      throw new Exception("Unsupported format");
                  }
                  using (System.Drawing.Bitmap bitmap = new System.Drawing.Bitmap(image))
                  {
                      return CreateCursor(bitmap, 3, 3);
                  }
              };
               
              
          }
    
        }
    
      }
    
    }
  • Get the Week Number in C#

    Here is some examples of how to determine the WeekNumber of a given Date

    using System;
    using System.Collections.Generic;
    using System.Text;
    
    namespace ConsoleApplication1
    {
        class Program
        {
            static void Main(string[] args)
            {
    
            object index = DateTime.Now;
            int res = 0;
            //0    First day of year
            res = System.Globalization.CultureInfo.CurrentCulture.Calendar.GetWeekOfYear(
            Convert.ToDateTime(index), System.Globalization.CalendarWeekRule.FirstDay, System.Globalization.DateTimeFormatInfo.CurrentInfo.FirstDayOfWeek);
    
            //1    (Default) First four day week from Sunday
            res = System.Globalization.CultureInfo.CurrentCulture.Calendar.GetWeekOfYear(
            Convert.ToDateTime(index), System.Globalization.CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Sunday);
    
            //2    First four day week from StartOfWeek
            res = System.Globalization.CultureInfo.CurrentCulture.Calendar.GetWeekOfYear(
                    Convert.ToDateTime(index), System.Globalization.CalendarWeekRule.FirstFourDayWeek, System.Globalization.DateTimeFormatInfo.CurrentInfo.FirstDayOfWeek);
    
            //3    First full week from Sunday
            res = System.Globalization.CultureInfo.CurrentCulture.Calendar.GetWeekOfYear(
                    Convert.ToDateTime(index), System.Globalization.CalendarWeekRule.FirstFullWeek, DayOfWeek.Sunday);
            
            //4    First full week from StartOfWeek
            res = System.Globalization.CultureInfo.CurrentCulture.Calendar.GetWeekOfYear(
                    Convert.ToDateTime(index), System.Globalization.CalendarWeekRule.FirstFullWeek, System.Globalization.DateTimeFormatInfo.CurrentInfo.FirstDayOfWeek);
    
    
            }
        }
    }
  • VB6 AddressOf operator Migration

    I was looking into ways to migrate something like the AddressOf operator in VB6. I read in some forums that I could use a delegate, but I hadn’t seen a code sample, so I started googling until I found this great post.

    It provided a great example of how to pass a pointer to a function. I am attaching here the code so you can benefit from this too.

     

    Remember to put the Working directory pointing to the output directory of the C DLL. In my case it is

    image

    The idea in general is like this:

    /// <summary>
    /// Simple callback function.
    /// </summary>
    /// <param name="a">Some integer parameter.</param>
    public delegate void CBFUNC(int a);

    /// <summary>
    /// Demo of a simple callback.
    /// </summary>
    /// <param name="f">Function to call back to</param>
    /// <param name="a">Parameter which will be returned through the callback</param>
    [DllImport("c_test_lib.dll")]
    public static extern void DoCallback(CBFUNC f, int a);

    DOWNLOAD CODE

  • Bittable What????

    As vb6 migration experts in our company we deal everyday with a lot of issues around Interop and serialization.

    One important thing to note is the concept of “Bittable Types”. I’m not making up terms. Those terms actually exist. Just see this link in MSDN.

    In a few words, a bittable type is a type that has the same representation in managed and unmanaged code.

    Why in earth is that important at all?

    Because if you are calling that great C++ DLL implemented some years ago that just works ok, you won’t be able to pass a NON-Bittable type because that DLL will expect a binary representation different from that in the .NET virtual machine.

    This is also an issue in other scenarios like:

    • Serializing content to files
    • Sending messages through messaging mechanisms like named-pipes or sockets.

    Well, we have just introduced the problem so now let’s think on a nice solution for this problem.

    Well Bittable Types are:

    The following types from the System namespace are blittable types:

     

    So now let’s look at a couple of non-BITTABLE types

    DateTime

    To test this differences let’s make a small test in VB6 and write a Date value to a file:

     

    Private Sub SaveDateToFile()
        Open "C:\test1.bin" For Binary Access Write As #1
        Dim d1 As Date
        d1 = "1/1/2009"
        Put #1, , d1
        Close #1
    End Sub

    Now let’s make a quick program in Vb.NET

     

    Sub Main()
            Dim f As System.IO.FileStream = System.IO.File.Open("C:\test2.bin", IO.FileMode.Create, IO.FileAccess.Write)
            Dim fw As New System.IO.BinaryWriter(f)
            Dim d As Date
            d = Convert.ToDateTime("1/1/2009")
            Dim val As Long = d.ToBinary()
            fw.Write(val)
            fw.Close()
            Main2()
        End Sub

     

    If we compare these files we will have:

    image

    So the values are obviously different. This is because VB6 Date are stores with the OLE Automation DateFormat

    So let’s change the C# code for something like:

     

        Sub Main2()
            Dim f As System.IO.FileStream = System.IO.File.Open("C:\test3.bin", IO.FileMode.Create, IO.FileAccess.Write)
            Dim fw As New System.IO.BinaryWriter(f)
            Dim d As Date
            d = Convert.ToDateTime("1/1/2009")
            fw.Write(d.ToOADate())
            fw.Close()
        End Sub

    And now when we compare the files we will have:

    image

     

    So to make your Date values compatible with VB6 format you must user the DateTime method .ToOADate. Now if you are calling a DLL that expects a Date value in the same format used by VB6 then you will have to do this:

     

            Dim d As Date
            d = Convert.ToDateTime("1/1/2009")
            Dim handle As System.Runtime.InteropServices.GCHandle = System.Runtime.InteropServices.GCHandle.Alloc(d.ToOADate(), Runtime.InteropServices.GCHandleType.Pinned)
            Dim memory_address As IntPtr = handle.AddrOfPinnedObject()
            Try
                APICall(memory_address)
            Finally
                d = DateTime.FromOADate(System.Runtime.InteropServices.Marshal.ReadInt64(memory_address))
                handle.Free()
            End Try  

     

    String

    Most of the time you wont have to deal with String marshalling because adding marshaling tags to your API call solves most of the problems, but if you arent that luckyly then you might do something like:

    IntPtr ptrToStringVar = System.Runtime.InteropServices.Marshal.StringToHGlobalAnsi(strVar);
    try
    {
       APICall(ptrToStringVar);
    }
    finally
    {
    strVar = System.Runtime.InteropServices.Marshal.PtrToStringAnsi(ptrToStringVar);
    System.Runtime.InteropServices.Marshal.FreeHGlobal(ptrToStringVar);
    }

    NOTE: if you have an API that might return an string with /0 characters you must call the API with System.Runtime.InteropServices.Marshal.PtrToStringAnsi(ptrToStringVar,size), if you do that the Framework will take in consideration the size bytes at the ptrToStringVar memory address.

     

    Double and Singles

    At least between VB6 and VB.NET the double and single types follows the same format. Well, at least, that is the result of my tests.

    Try it yourself, the following shows a simple test for double variables:

    VB6

    Private Sub SaveDoubleToFile()
        Open "C:\test1.bin" For Binary Access Write As #1
        Dim d1 As Double
        d1 = 1.123
        Put #1, , d1
        Close #1
    End Sub
    
    Sub Main()
        SaveDoubleToFile
    End Sub

     

    .NET

    Module Module1
    
        Sub Main()
            Dim f As System.IO.FileStream = System.IO.File.Open("C:\test2.bin", IO.FileMode.Create, IO.FileAccess.Write)
            Dim fw As New System.IO.BinaryWriter(f)
            Dim d As Double
            d = 1.123
            fw.Write(d)
            fw.Close()
        End Sub
    
    
    End Module
     

    So you could make an api call in those cases with something like:

    Dim handle As System.Runtime.InteropServices.GCHandle = System.Runtime.InteropServices.GCHandle.Alloc(d, System.Runtime.InteropServices.GCHandleType.Pinned)
    Dim ptr As System.IntPtr = handle.AddrOfPinnedObject()
    Try
        APICall(ptr)
    Finally
        handle.Free()
    End Try
  • Change CreateObject during Migration

    One of our clients wanted to change the CreateObject function migration for a function of their own. So they wanted all cases like:

    Dim x As Object
    Set x = CreateObject("Excel.Application")

     

    To be migrated to something like:

    Excel.Application x = (Excel.Application) Utils.MyCreateObject("Excel.Application", "");

    Our migratio vb6migration tool provides a new cool feature called CustomMaps. This feature allows you to provide some simple but useful changes to the way things get migrated.

    For this case follow these steps:

    1. Open the Visual Basic Upgrade Companion.

    2. In the Tools Menu choose:

    image

    3. Create a new CustomMaps File and an an entry like the following:

     

    image

    Notice the Source name is VBA.Interaction.CreateObject. To find out this name you can look in your VB6 IDE, right click on the CreateObject and select goto Definition.
    image 
     
    image 
    and for the target name just put the implementation that you what, for example you can write a function like:
    class Utils
            {
                public static object MyCreateObject(string className,params object[] ignoreRestParams)
                {
                    return Activator.CreateInstance(Type.GetType(className));
                }
            }

    and set the SourceName to Utils.MyCreateObject (or NameSpace.Utils.MyCreateObject to use the fully qualified name). You just need to set the New Reference Name column because we will not change the definition of the function.
  • .NET Calculate week number of a date

    This post shows a way to calculate the number of weeks.
    Remember that this calculation is culture-dependant
    For example the GetWeekOfYear methods requires a criteria to determine 
    how to determine the first week and which day to consider as FirstDayOfWeek for more info see here:

    CalendarWeekRule.FirstDay

    Supported by the .NET Compact Framework.

    Indicates that the first week of the year starts on the first day of the year and ends before the following designated first day of the week. The value is 0.

    CalendarWeekRule.FirstFourDayWeek

    Indicates that the first week of the year is the first week with four or more days before the designated first day of the week. The value is 2.

    CalendarWeekRule.FirstFullWeek

    Indicates that the first week of the year begins on the first occurrence of the designated first day of the week on or after the first day of the year. The value is 1.

     

     

    Sample Code

            Dim x As Date
            Dim currentCulture As System.Globalization.CultureInfo
            currentCulture = CultureInfo.CurrentCulture
            Dim weekNum = currentCulture.Calendar.GetWeekOfYear(x, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday)
More Posts Next page »
Powered by Community Server (Non-Commercial Edition), by Telligent Systems