Pages

Saturday 30 December 2006

Mixing legacy MFC code with Windows Forms (Part 3)

Summary: The ability to intimately mix unmanaged MFC/C++ code with managed code is one of my favorite features in .NET. Migration projects that would otherwise take years can be done in a matter of months. To re-use legacy MFC/C++ code with minimal changes and call the code from a C# Winforms application, you need to:
  1. Create an MFC DLL project and activate the /CLR switch to indicate that this is a mixed assembly.
  2. Add the legacy source files to the MFC DLL project.
  3. Prepare the legacy code so that it compiles for Unicode (see Converting an MFC project to Unicode)
  4. Add a managed class to the above project. This class will be the interface between the managed Winforms application and the legacy MFC/C++ code.

Project settings:

  • Dynamically link against the MFC DLLs: in Configuration Properties > General > Use of MFC
  • Compile for Unicode (this is mandatory because .NET works only with Unicode): General > Character Set
  • Make it a mixed assembly: General > Common Language Runtime Support, set to /CLR. This way the DLL can contain both unmanaged and managed code. The managed code will be visible to any managed application using the DLL.

Can I debug the legacy MFC/C++ code loaded as part of the managed application?

Yes! In the project properties for the managed executable using the DLL, go to the Debug tab and tick Enable unmanaged code debugging.

Step by step:

  1. Create an MFC DLL using the MFC DLL Wizard. Call the project Legacy for instance. Options: Regular DLL using MFC DLL. If the legacy code is an MFC-based MDI application, the main frame will be created as soon as the DLL is loaded (through CLegacyApp::InitInstance)
  2. Add the source files containing the legacy code to this new project.
  3. Set the project settings as described above.
  4. Compile and fix all Unicode issues and other compiler annoyances...
  5. Created a LegacyWrapper C++ class.

Issues associated with compiling with /CLR:

  • Warning LNK4248: unresolved typeref token (01000013) for '_TREEITEM'; image may not run. Solution here. This is because _TREEITEM is defined in a native library, but the forward declaration is done in an MSIL module. Forcing the definition of _TREEITEM in stdafx.h eliminates the warning.

Cool things in Visual Studio 2005

Useful features that do not exist in Visual Studio 2003

  • Code definition window (separate window that shows automatically the definition of the variable currently under the cursor)
  • Find all references command
  • Class view is searchable
  • Class view shows base types and derived types (C++)
  • Class view contains a subpane showing members of current class (makes the tree cleaner)
  • Find in files dialog is modeless (crucial for refactoring) and automatically moves to display the matched string.

New in Visual Studio 2005: http://msdn2.microsoft.com/en-us/library/88fx1xy0(VS.80).aspx

Wednesday 27 December 2006

IE7 or Firefox?

IE7:
  • I like the thumbnails view (Ctrl Q)
  • I like the way it handles RSS feeds: by displaying an HTML view of the feed content first, before offering you to add it to your list.
  • I don't like the text search.

Firefox:

  • I like the text search (embedded in the status bar instead of a modal dialog)
  • I like the download manager.

Saturday 23 December 2006

Installing a wireless adapter under Vista RC2

I bought a Netgear WG111T USB2.0 adapter to connect to my Netgear DG834PN router. I thought I would have problems with Vista RC2 but I was surprised how easy it was. I followed the install intructions: installed the drivers first then plugged-in the USB adapter. The drivers installed without any problem. To make things easier, I disabled security on the router and enabled broadcasting of the SSID. I tried starting up the 'NETGEAR WG111T Smart Wizard' but that didn't work: the wizard always closes immediately after launch, so I used the software built into Vista instead. Went into Network and Sharing Center > Manage wireless networks, clicked Add. Then I selected Add a network that is in range of this computer. The wizard displayed my wireless connection with a little icon indicating that it was insecure. I selected the connection then gave a try to Internet Explorer: no problem, Internet worked like a charm. Then I enabled WEP security and disabled the SSID broadcast. Went back to Manage wireless entworks, clicked Add. This time, my wireless connection was displayed as secured this time. I selected it and Vista prompted me for a key. Out of the 4 WEP keys it asked only the first one, which I entered, then it connected successfully. Checked that IE still works OK, no problem.

Wednesday 6 December 2006

Handy Collections

  • Generic key/value pair collection that automatically sorts based on the key: SortedList<TKey,TValue>
  • Same as above but without the automatic sorting and better performance with large number of items: Dictionary<TKey,TValue>

http://msdn2.microsoft.com/en-us/library/5tbh8a42.aspx

Declare either of them it as follows:
        public SortedList<int, Antenna> antennas;
To iterate through it:
        for each (KeyValuePair<int, Antenna^> kvp in rigging->antennas)
        {
            Antenna^ antenna = kvp.Value;
 
            CString antennaId;
            antennaId.Format(_T("%d"), kvp.Key);
 
            (...)
        }
 
  • I'm using key/value pair collections because I want to be able to retrieve an element given its key. But it is possible to perform much more complex retrievals in a very elegant way. Anything that implements IEnumerable can be searched.

Friday 1 December 2006

C# or C++/CLI?

Advantages of C#
  • no cpp/h, just one file
  • better IDE support:
    • better intellisense
    • code auto-complete
    • refactoring (auto rename, method extraction, ...)
    • code snippets
    • compile errors displayed as you type
  • no, it's not Microsoft proprietary, there is an ECMA standard for it.
Advantages of C++/CLI
  • support for mixing managed and unmanaged code. Excellent for re-use of legacy code.
  • cool language extensions (e.g. for each, ...)
  • no, it's not Microsoft proprietary, there is an ECMA standard for it

Articles:

Monday 20 November 2006

Books I currently flip through...

Code...

Architecture...

Others...

Mixing legacy MFC code with Windows Forms (Part 2)

I know how to call unmanaged code from a managed class. How to do the opposite? This piece of code
    public class DiagramContainer
    {
 
    private:
        // Managed reference to some data
        Rigging^ m_rigging;
    };
returns the following error: Error 1 error C3265: cannot declare a managed 'm_rigging' in an unmanaged 'MFCControls::DiagramContainer' Hints: http://www.ondotnet.com/pub/a/dotnet/2003/03/03/mcppp2.html?page=last Solution: Use gcroot
    public class DiagramContainer 
    {
    private:
        // Managed reference to some data. gcroot makes it look like an unmanaged type
        gcroot<rigging^ > m_rigging;
    };
m_rigging can be used as a usual Rigging^ object. You reach the members using ->

Friday 2 June 2006

Mixing legacy MFC code with Windows Forms (Part 1)

I am looking into how to run legacy MFC code within a nice-looking Windows Forms framework. I built an MDI with Windows Forms and C# using ADO.NET. I have a large code base in C++: rather than rewriting the complete application from scratch (and therefore having to maintain and deploy 2 distinct applications, one in Windows Forms, one in MFC), I'd like to integrate the legacy code inside the new Windows Forms framework. This would allow for a smooth transition from MFC to Windows Forms.
I must solve two problems:
  • use an MFC property sheet from a C# app
  • manage ODBC and ADO.NET connections in a single exe
Backgrounders:
Getting closer:
What I have tried so far:

  • Subclass System.Windows.Forms.Control
As a starting point I used http://www.codeproject.com/managedcpp/mfcandwindowsforms.asp, which explains how to drag and drop into the Windows Forms designer a control based on legacy MFC code. This technique relies on subclassing: the Windows Forms control is created first, then its handle is passed to the MFC class which is then responsible for drawing the control. It works well as long as the MFC class derives from CWnd.
Subclass a Windows forms control with an MFC CWnd
The Winforms C# App calls a C++ managed wrapper (derived from System::Windows::Forms::Control). Upon creation of its handle, the wrapper associates the handle with a CWnd-derived MFC class. The original code from the Codeproject article used C++ managed extensions. I compiled it with VS2005 so I had to translate it from C++ managed extensions to C++/CLI and it worked just fine.
This technique works ok with a CWnd-derived class. Unfortunately our legacy MFC code is based on property sheets so this is not going to be enough, but it’s a start.
  • One single C++/CLI App containing MFC legacy source and dialog templates.
Created a C++ Windows Forms project. Added the MFC property sheets and property pages sources files to it. Also added the dialog templates associated with each page. The app consisted of a windows form that launches the MFC property sheet when you click a button. It worked.
However for some reason only the Release version of the executable worked. The Debug version kept displaying the following assertion,
Error
an instead of displaying the appropriate line of code just got my machine to thrash.
Anyway this is still not quite what I want to do: my Windows Forms MDI framework is written in C#, not in C++. The legacy code must reside a DLL compiled in mixed mode.
  • A C# Windows Forms project calls a dialog residing inside a mixed-mode exe
Attempt2
I created the MFC dialog project with the Wizard then modified its properties:
    • Changed to DLL
    • Switched on /clr
    • Disabled pre-compiled headers.
Got the following error:
Fixups
  1. Created an MFC MDI App with Visual Studio 2005.
  2. Added MFCAppAdapter.cpp (copied from Alexey Shalnov’s article).
  3. Changed MFCAppAdapter.cpp compile options to /clr (at the file level, not the project level).
  4. Set Code Generation option: set to default.
  5. Set Enable Minimal Rebuild option to No.
  6. Set Enable C++ Exceptions to Yes with SEH
  7. Set Debug Information Format to Zi
  8. Add references to System and System.Windows.Forms
TBC