C# PInvoke out or ref??

17. August 2009 09:40 by Mrojas in General  //  Tags: , , , ,   //   Comments (0)

If I have a PInvoke call like the following:

[DllImport("Advapi32.dll", CharSet=CharSet.Auto)]
static extern Boolean FileEncryptionStatus(String filename, 
   out UInt32 status);

What is the difference between

 

[DllImport("Advapi32.dll", CharSet=CharSet.Auto)] static extern Boolean FileEncryptionStatus(String filename, out UInt32 status);

and

[DllImport("Advapi32.dll", CharSet=CharSet.Auto)] static extern Boolean FileEncryptionStatus(String filename, ref UInt32 status);

Well, as long as I have tested it, they exactly the same. From the MSDN you can even read

“I could have selected the ref keyword here as well, and in fact both result in the same machine code at run time. The out keyword is simply a specialization of a by-ref parameter that indicates to the C# compiler that the data being passed is only being passed out of the called function. In contrast, with the ref keyword the compiler assumes that data may flow both in and out of the called function.”

“When marshaling pointers through P/Invoke, ref and out are only used with value types in managed code. You can tell a parameter is a value type when its CLR type is defined using the struct keyword. Out and ref are used to marshal pointers to these data types”

So what should you use? Well using the out keyword for PInvoke will just add some information or documentation to your method, but because these functions are implemented in C or C++ they might treat an out parameter as an IN parameter so I really prefere to use ref when I’m calling functions with PInvoke.

Migration from .NET Framework 1.1

13. August 2009 18:07 by Mrojas in General  //  Tags: , , , , , ,   //   Comments (0)

.NET has been around for quite a while. According Wikipedia it has been around since on 3 April 2003
So now there exist applications developed for .NET Framework 1.0 or 1.1 and people
need to migrate them to Framework 2.0 or Framework 3.5.

It is the general impression that there is not a direct path to 3.5.
As Zain Naboulsi explains in his blog you can go from 1.1 to 2.0 then from 2.0 to 3.5.
And From 2.0 to 3.5 the migration is a no-brainer because, both, 3.0 and 3.5 are based on 2.0.

A good reference also is the post of Peter Laudati on migration from 1.1 to 2.0.
Note: Peter’s post seem to have a broken link to the microsoft document about breaking changes in 2.0.
The correct link is this.

A more recent post by The Moth provides more links to breaking changes documents:

- Design time Breaking Changes in .NET Framework 2.0
- Runtime Breaking Changes in .NET Framework 2.0
- Microsoft .NET Framework 1.1 and 2.0 Compatibility
- Compatibility Testing Scenarios

Going from 1.1 to 2.0 or 3.5 can be just as simple as opening the solution in VS and compile
or it can take a lot of effort. Web Projects then to be more difficult due to several changes in ASP.NET.

So good luck.

Tools?

Well there a lot of static analyisis tools we have used
(some internal, some from Third Parties. I particulary like Understand and NDepend)

No more System.Data.OracleClient

7. August 2009 06:15 by Mrojas in General  //  Tags: , , , , ,   //   Comments (0)

 Microsoft announced that they won’t support the System.Data.OracleClient anymore :(

 

The message says that it will still be available in .NET 4.0 but “deprecated” and that it wont impact existing applicatoions.

 

So what are my options?

Well you have to go to a third party. Not now but eventually.

 

Oracle Data Provider for .NET (ODP.NET) (Oracle states that ODP.NET is Free)

DataDirect ADO.NET Provider for Oracle

dotConnect for Oracle

 

Migration to ODP.NET

Installation

Microsoft OracleClient to ODP.NET Application Migration: Code Migration

Oracle provide general instructions. In summary you need to:

  1. Add the references to Oracle.DataAccess
  2. Change namespaces: System.Data.OracleClient by  Oracle.DataAccess.Client; and maybe add “using Oracle.DataAccess.Types”
  3. Update the connection String (most attributes are the same). Change Integrated Security by  "User Id=/", change “Server” attribute to “Data Source” and remove Unicode
  4. The following is anoying but you have to add after all OracleCommands something like OracleCommand1.BindByName = true;

Deployment

  • Oracle Universal Installer (either in normal installation or Silent Install)
  • XCopy (I like this :) ) just remember to download the ODAC xcopy version

Extended WebBrowser Control Series: NewWindow3

Recently an user of the ExtendedBrowser v2 commented that he needed access to the NewWindow3 event.

The NewWindow3 event is raised when a new window is to be created. It extends NewWindow2 with additional information about the new window. 

Syntax

Private Sub object_NewWindow3( _
	ByRef ppDisp As Object, _
	ByRef Cancel As Boolean, _
	ByVal dwFlags As Long, _
	ByVal bstrUrlContext As String, _
	ByVal bstrUrl As String)

Parameters

object Object expression that resolves to the objects in the Applies To list. ppDisp Object expression that, optionally, receives a new, hidden WebBrowser or InternetExplorer object with no URL loaded. Cancel A Boolean value that determines whether the current navigation should be canceled. true Cancel the navigation. false Do not cancel the navigation. dwFlags The flags from the NWMFenumeration that pertain to the new window.

typedef enum NWMF 
{     
    NWMF_UNLOADING = 0x00000001,
     NWMF_USERINITED = 0x00000002,
     NWMF_FIRST = 0x00000004,
     NWMF_OVERRIDEKEY = 0x00000008,
     NWMF_SHOWHELP = 0x00000010,
     NWMF_HTMLDIALOG = 0x00000020,
    NWMF_FROMDIALOGCHILD = 0x00000040,
     NWMF_USERREQUESTED = 0x00000080,
     NWMF_USERALLOWED = 0x00000100,
     NWMF_FORCEWINDOW = 0x00010000,
     NWMF_FORCETAB = 0x00020000,
     NWMF_SUGGESTWINDOW = 0x00040000,
     NWMF_SUGGESTTAB = 0x00080000,
     NWMF_INACTIVETAB = 0x00100000
} NWMF;

bstrUrlContext The URL of the page that is opening the new window. bstrUrlThe URL that is opened in the new window.

Please notice:

Note   The NewWindow3 event is only fired when a new instance of Internet Explorer is about to be created. Calling showModalDialog or showModelessDialog does not trigger an event because they are not new instances of Internet Explorer. They are implemented as MSHTML host windows, which allows them to render and display HTML content but not hyperlinks between documents.

You can download from here

DOWNLOAD CODE HERE v3_1

ExtendedBrowserExampleVBNET.zip (92.56 kb)

CapsLock, NumLock in C# and VB.NET

10. July 2009 12:49 by Mrojas in General  //  Tags: , , , , ,   //   Comments (0)

I was looking for a “.net” way of detecting the CapsLock state, but almost all the references pointed to pinvoke code like:

<DllImport("user32.dll")> _
Public Shared Function GetKeyState(VirtKey As Integer) As Integer
End Sub

And I finally found two ways:

1) You can call methods from the System.Console class:

You can use the System.Console.CapsLock property and if you want the NumLock state use: System.Console.NumberLock

or

2) You can call make an instance of Microsoft.VisualBasic.Devices.Keyboard. (For this if you are in C# you need to add a reference to Microsoft.VisualBasic.dll)

For example:

Microsoft.VisualBasic.Devices.Keyboard key = new Microsoft.VisualBasic.Devices.Keyboard();

and use properties like:

key.CapsLock

key.NumLock

key.ScrollLock

key.ShiftKeyDown

key.CtrlKeyDown

key.AltKeyDown

Printing RichTextBox contents in C#

10. July 2009 05:54 by Mrojas in General  //  Tags: , , , ,   //   Comments (0)

This post discusses and provides the implementation of a helper class to add support
for printing the formatted contents of a richtextbox control.

The print model in .NET is a little different than one used in VB6.

For example see: http://support.microsoft.com/kb/146022

Public Sub PrintRTF(RTF As RichTextBox, LeftMarginWidth As Long, _
   TopMarginHeight, RightMarginWidth, BottomMarginHeight)
   Dim LeftOffset As Long, TopOffset As Long
   Dim LeftMargin As Long, TopMargin As Long
   Dim RightMargin As Long, BottomMargin As Long
   Dim fr As FormatRange
   Dim rcDrawTo As Rect
   Dim rcPage As Rect
   Dim TextLength As Long
   Dim NextCharPosition As Long
   Dim r As Long

   ' Start a print job to get a valid Printer.hDC
   Printer.Print Space(1)
   Printer.ScaleMode = vbTwips

   ' Get the offsett to the printable area on the page in twips
   LeftOffset = Printer.ScaleX(GetDeviceCaps(Printer.hdc, _
      PHYSICALOFFSETX), vbPixels, vbTwips)
   TopOffset = Printer.ScaleY(GetDeviceCaps(Printer.hdc, _
      PHYSICALOFFSETY), vbPixels, vbTwips)

   ' Calculate the Left, Top, Right, and Bottom margins
   LeftMargin = LeftMarginWidth - LeftOffset
   TopMargin = TopMarginHeight - TopOffset
   RightMargin = (Printer.Width - RightMarginWidth) - LeftOffset
   BottomMargin = (Printer.Height - BottomMarginHeight) - TopOffset

   ' Set printable area rect
   rcPage.Left = 0
   rcPage.Top = 0
   rcPage.Right = Printer.ScaleWidth
   rcPage.Bottom = Printer.ScaleHeight

   ' Set rect in which to print (relative to printable area)
   rcDrawTo.Left = LeftMargin
   rcDrawTo.Top = TopMargin
   rcDrawTo.Right = RightMargin
   rcDrawTo.Bottom = BottomMargin

   ' Set up the print instructions
   fr.hdc = Printer.hdc   ' Use the same DC for measuring and rendering
   fr.hdcTarget = Printer.hdc  ' Point at printer hDC
   fr.rc = rcDrawTo            ' Indicate the area on page to draw to
   fr.rcPage = rcPage          ' Indicate entire size of page
   fr.chrg.cpMin = 0           ' Indicate start of text through
   fr.chrg.cpMax = -1          ' end of the text

   ' Get length of text in RTF
   TextLength = Len(RTF.Text)

   ' Loop printing each page until done
   Do
      ' Print the page by sending EM_FORMATRANGE message
      NextCharPosition = SendMessage(RTF.hWnd, EM_FORMATRANGE, True, fr)
      If NextCharPosition >= TextLength Then Exit Do  'If done then exit
      fr.chrg.cpMin = NextCharPosition ' Starting position for next page
      Printer.NewPage                  ' Move on to next page
      Printer.Print Space(1) ' Re-initialize hDC
      fr.hdc = Printer.hdc
      fr.hdcTarget = Printer.hdc
   Loop

   ' Commit the print job
   Printer.EndDoc

   ' Allow the RTF to free up memory
   r = SendMessage(RTF.hWnd, EM_FORMATRANGE, False, ByVal CLng(0))
End Sub

The VBCompanion provides excellent helpers that provide a lot fo the VB6 Printer object functionality, so you dont have to change any of your actual code, but in some cases, you might just want to remove that code, specially for very specific things like printing a RichTextBox.

So here I’m providing a .NET simplified helper that allows you to print the contents of a RichTextBox control. This helper is just based on the code published by Martin Muller in http://msdn.microsoft.com/en-us/library/ms996492.aspx. It provides an extension method for VS 2008 user so all you have to do is call RichTextBox.Print.

The implementation is simple. The RichTextBoxPrintHelper creates or receives an instance of a PrintDocument object, and event handlers are added to it for the BeginPrint, PrintPage and EndPrint events.

    private int m_nFirstCharOnPage;

    private void printDocument_BeginPrint(object sender, System.Drawing.Printing.PrintEventArgs e)
    {
        // Start at the beginning of the text
        m_nFirstCharOnPage = 0;
    }

    private void printDocument_PrintPage(object sender, System.Drawing.Printing.PrintPageEventArgs e)
    {
        // To print the boundaries of the current page margins
        // uncomment the next line:
        //e.Graphics.DrawRectangle(System.Drawing.Pens.Blue, e.MarginBounds);

        // make the RichTextBoxEx calculate and render as much text as will
        // fit on the page and remember the last character printed for the
        // beginning of the next page
        m_nFirstCharOnPage = FormatRange(false,
            e,
            m_nFirstCharOnPage,
            control.TextLength);

        // check if there are more pages to print
        if (m_nFirstCharOnPage < control.TextLength)
            e.HasMorePages = true;
        else
            e.HasMorePages = false;
    }

    private void printDocument_EndPrint(object sender, System.Drawing.Printing.PrintEventArgs e)
    {
        // Clean up cached information
        FormatRangeDone();
    }
The FormatRange method is called. This method will use the fill out some structures 
with page information and use the RichTextBox handle to send messages that will render
the control contents to the Printer’s HDC.
 
 /// <summary>
    /// Calculate or render the contents of our RichTextBox for printing
    /// </summary>
    /// <param name="measureOnly">If true, only the calculation is performed,
    /// otherwise the text is rendered as well</param>
    /// <param name="e">The PrintPageEventArgs object from the
    /// PrintPage event</param>
    /// <param name="charFrom">Index of first character to be printed</param>
    /// <param name="charTo">Index of last character to be printed</param>
    /// <returns>(Index of last character that fitted on the
    /// page) + 1</returns>
    public int FormatRange(bool measureOnly, PrintPageEventArgs e,
        int charFrom, int charTo)
    {
        // Specify which characters to print
        STRUCT_CHARRANGE cr;
        cr.cpMin = charFrom;
        cr.cpMax = charTo;

        // Specify the area inside page margins
        STRUCT_RECT rc;
        rc.top = HundredthInchToTwips(e.MarginBounds.Top);
        rc.bottom = HundredthInchToTwips(e.MarginBounds.Bottom);
        rc.left = HundredthInchToTwips(e.MarginBounds.Left);
        rc.right = HundredthInchToTwips(e.MarginBounds.Right);

        // Specify the page area
        STRUCT_RECT rcPage;
        rcPage.top = HundredthInchToTwips(e.PageBounds.Top);
        rcPage.bottom = HundredthInchToTwips(e.PageBounds.Bottom);
        rcPage.left = HundredthInchToTwips(e.PageBounds.Left);
        rcPage.right = HundredthInchToTwips(e.PageBounds.Right);

        // Get device context of output device
        IntPtr hdc = e.Graphics.GetHdc();

        // Fill in the FORMATRANGE struct
        STRUCT_FORMATRANGE fr;
        fr.chrg = cr;
        fr.hdc = hdc;
        fr.hdcTarget = hdc;
        fr.rc = rc;
        fr.rcPage = rcPage;

        // Non-Zero wParam means render, Zero means measure
        Int32 wParam = (measureOnly ? 0 : 1);

        // Allocate memory for the FORMATRANGE struct and
        // copy the contents of our struct to this memory
        IntPtr lParam = Marshal.AllocCoTaskMem(Marshal.SizeOf(fr));
        Marshal.StructureToPtr(fr, lParam, false);

        // Send the actual Win32 message
        int res = SendMessage(control.Handle, EM_FORMATRANGE, wParam, lParam);

        // Free allocated memory
        Marshal.FreeCoTaskMem(lParam);

        // and release the device context
        e.Graphics.ReleaseHdc(hdc);

        return res;
    }
 

Using the RichTextBox is even more simple. You add a richtextbox to a form and call the Print method:

image

        private void printToolStripMenuItem_Click(object sender, EventArgs e)
        {
            richTextBox1.Print();
        }

I’m attaching the source code for the helper an this sample application so you can use this.

DOWNLOAD SOURCE CODE

ColdFusion 8 and JRockIt

15. June 2009 18:41 by Mrojas in General  //  Tags: , , , , , , ,   //   Comments (0)

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

10. June 2009 04:51 by Mrojas in General  //  Tags: , , , , , ,   //   Comments (0)

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

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.

VB6 TabIndex and C#

5. June 2009 05:30 by Mrojas in General  //  Tags: , , , , ,   //   Comments (0)

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!

3. June 2009 04:21 by Mrojas in General  //  Tags: , , ,   //   Comments (0)

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.

Extended WebBrowser Control Series: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

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

List Jobs in Oracle

21. May 2009 06:41 by Mrojas in General  //  Tags: ,   //   Comments (0)

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

Extended WebBrowser Control Series: 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

30. April 2009 13:17 by Mrojas in General  //  Tags:   //   Comments (0)

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#

30. April 2009 10:56 by Mrojas in General  //  Tags: , , ,   //   Comments (0)

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

31. March 2009 09:14 by Mrojas in General  //  Tags:   //   Comments (0)

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.

Categories