RCode

a software development blog by Bojan Resnik

  • Archives

Using Visual Studio 2008 with Visual C++ 6.0 compiler

Posted by Bojan Resnik on October 28, 2009

04/11/2010 Update: If you are interested in using older compilers with Visual Studio 2010, there is an interesting solution by Owen Wengerd, described here.

The Microsoft Visual C++ 6.0 is a product that has been present for a long time. A lot of software was written with it, carefully tailored around the many bugs of the compiler. The IDE itself is not too pretty and lacks a lot of functionality that we expect modern IDEs to have. It’s a pitty that we have to use it to maintain legacy code.

Well, turns out this is not the case. There is a way to use the Visual Studio 2008 IDE with all its capabilities, and have it compile the code using Visual C++ 6.0 compiler. There is some fiddling with options to be done in order to have everything compile and link without errors, but I find that once the project is set up I can enjoy the features of Visual Studio 2008.

works-on-my-machine-starburst_3 Works On My Machine Disclaimer: This is released with exactly zero warranty or support. If it deletes files or kills your family pet, you have been warned. It might work great, and it might not.

Starting Visual Studio 2008 with Visual C++ 6.0 Environment

All Visual Studio IDEs since the ancient Visual Studio 6.0 have had a convenient switch: /useenv. The documentation for the switch states the following:

Starts Visual Studio and uses the environment variables for PATH, INCLUDE, LIBS, and LIBPATH in the VC++ Directories dialog box.

Combined with the vcvars32.bat file that Visual Studio installations traditionally create, this switch gives us the ability to run any Visual Studio IDE with any version of the compiler and support tools. That’s in theory, of course. I only tried Visual Studio 2005 and Visual Studio 2008 IDEs with Visual C++ 6.0 tools.

For convience, I have a batch file that first sets up the environment for Visual C++ 6.0 and then starts Visual Studio 2008 with the /useenv switch:

call "C:\Program Files (x86)\Microsoft Visual Studio\VC98\Bin\VCVARS32.BAT"
start "" "C:\Program Files (x86)\Microsoft Visual Studio 9.0\Common7\IDE\devenv.exe" /useenv

I use ‘start’ instead of directly running devenv.exe in order to immediately close the window that opens for the batch file. ‘start’ command will run devenv.exe asynchronously and the process started for the batch file will exit. Of course, the paths in the batch file are specific to my machine. They may be different on other systems, depending on where the products have been installed.

Setting C++ Compiler Options

The first part is done – Visual Studio 2008 has been started and it will use the correct environment settings. To verify this, go to Tools/Options/Projects and Solutions/VC++ Directories. You should see something similar to this:

image

Notice that the first four paths actually reference folders in the Visual C++ 6.0 installation. You will find similar references in “Include files” and “Library files” directories.

This all looks great, but if you create a new VC++ console application and try to build it, you will be greeted with some errors:

image

As the compilers have evolved, so have their options. The VS 2008 IDE supports more options than the old VC6 compiler. In order for the IDE to pass correct options to the VC6 compiler, we need to do some tweaking.

The first warning above comes from an apparently unknown option related to error reporting. A quick search through the properties of the project gives us this:

image

Setting this option to Default will cause it to be omitted from the options passed to the compiler.

The other two problematic messages involve a strange character. When compiling, IDE first creates a special file (called response file) containing all options for the compiler, including the names of the source files and the compiler reads this file to determine what needs to be done. In Visual Studio 6.0 that file is regular ASCII text file, while newer versions of Visual Studio use Unicode encoding by default. To instruct Visual Studio 2008 not to use Unicode, we need to set options for the compiler and for the linker:

image

image

Setting these option to No will cause the response files to be created as plain ASCII, just as the VC6 compiler needs. If you have a static library project, you would have to do this in the librarian’s options as well.

After these changes, we can build the project again:

image

Again, hunting for the /RTC1 option brings us to this property screen:

image

Setting the Basic Runtime Checks to Default will again solve the warning issue.

The linker’s /INCREMENTAL option was supported in VC6, but with a different syntax. In VC6 you can use either /INCREMENTAL:YES or /INCREMENTAL:NO, but you can’t specify /INCREMENTAL by itself. Fortunately, we don’t have to give up incremental linking, but we have to specify the correct option manually. First, we need to set the Enable Incremental Linking option to Default:

image

Then, we need to manually include this option in linker’s Command Line:

image

Compiling again will produce some new issues:

image

The actual error here is that the IDE is instructing the linker to create a manifest file, which is not supported by VC6 linker. To turn this off, we need to go to linker’s Manifest File properties:

image

And set the Generate Manifest option to No. In order to take care of the other unknown options DYNAMICBASE, NXCOMPAT and ERRORREPORT:PROMPT, we need to go to linker’s Advanced properties and set the corresponding properties to Default:

image

Compiling now finally yields an error-free output:

image

The linker warning here cannot be removed, but is completely harmless.

Incremental Building

Although everything compiles and links now, you will soon notice that the enitre project is being rebuilt every time. It seems that Visual Studio determines what needs to be built by reading the program database that is created by the compiler, and it expects the database to have a certain name. By default, Visual Studio 2008 uses vc90.pdb as the name of the program database, but Visual Studio 6.0 uses vc60.pdb. To change this, open the C/C++ properties window again:

image

After this, your project will rebuild once again and generate the vc60.pdb database. Subsequent builds should be incremental, as expected.

Wrapping It All Up

Depending on your project’s requirements and options, you might need to tweak some more options. However, the two techniques presented here (setting to Default and manually specifying in Command Line) should get you through it. In the end, it is up to you to decide whether it is all worth it. To me, having a much better debugger and not needing to close the solution in order to attach it to a process are more than worth a little fiddling with options.

04/11/2010 Update:

As Christian I reported in the comments, you might need to add compiler options /FD /GR /GX and linker options /pdbtype:sept /machine:I386.

Posted in c++ | Tagged: , | 88 Comments »

Type Aliases in C#

Posted by Bojan Resnik on August 14, 2009

I was maintaining a piece of C# code today, which made heavy use of a class with several generic parameters. The class has several static methods and nested types, so its full signature was all over the place:

private Graph<int, object, List<int>> CreateGraph()
{
    var result = new Graph<int, object, List<int>>();

    // Code to populate the graph...

    return result;
}

private void Traverse(Graph<int, object, List<int>> graph)
{
    foreach (var node in graph.Roots)
        Traverse(node);
}

private void Traverse(Graph<int,object,List<int>>.Node node)
{
    // Do something with node...

    foreach (var child in node.Children)
        Traverse(child);
}

This is just a sample of the code – almost every method in the class had Graph<int, object, List<int>> somewhere in it. The code would be much easier to maintain if it didn’t depend on this full signature so heavily. Fortunately, C# provides a convenient way to solve this with type aliases:

using DocumentGraph = Graph<int, object, List<int>>;

The offending code can now use DocumentGraph:

private DocumentGraph CreateGraph()
{
    var result = new DocumentGraph();

    // Code to populate the graph...

    return result;
}

private void Traverse(DocumentGraph graph)
{
    foreach (var node in graph.Roots)
        Traverse(node);
}

private void Traverse(DocumentGraph.Node node)
{
    // Do something with node...

    foreach (var child in node.Children)
        Traverse(child);
}

The type alias directive can be specified at file or namespace scope – it cannot be specified in a class or method.

kick it on DotNetKicks.comShout it

Posted in .NET, C# | 1 Comment »

C# lambda and foreach variable

Posted by Bojan Resnik on June 17, 2009

Can you guess what the output of the following program is?

using System;
using System.Collections.Generic;

namespace Lambda
{
    class Program
    {
        static void Main(string[] args)
        {
            var strings = new[] {"a", "b", "c"};
            var actions = CreateActions(strings);
            actions.ForEach(f => f());
        }
        
        private static List<Action> CreateActions(IEnumerable<string> strings)
        {
            var actions = new List<Action>();
            
            foreach (var s in strings)
                actions.Add( () => Console.WriteLine(s) );
            
            return actions;
        }
    }
}

I expected to see all three strings (a, b and c), but instead I got this:
First output

While analyzing the IL code with Reflector I learned a thing or two about lambdas in C#.
When a lambda is encountered by the compiler, it generates a class which has a field for each local variable used by the lambda. In this case, the generated class would look like this in C#:

private sealed class <>c_DisplayClass3
{
    public string s;
    public void <Main>b_0()
    { Console.WriteLine(this.s); }
}

Of course, the above is not valid C# due to invalid characters in identifiers, but is pretty much what is produced in the IL.
So far so good – no real surprises here. However, look at this code which is my translation from IL to C# of the CreateActions method:

private static List<Action> CreateActions(IEnumerable<string> strings)
{
    List<Action> actions = new List<Action>();
    using (IEnumerator<string> enumerator = strings.GetEnumerator())
    {
        // Note 1: The lambda is created outside of the loop
        <>c_DisplayClass3 lambda = new <>c_DisplayClass3();

        string s;
        while (enumerator.MoveNext())
        {
            s = enumerator.Current;

            // Note 2: The instance field is reassigned each time
            lambda.s = s;
            actions.Add(lambda);
        }
    }
    return actions;
}

The reason for the strange output is clear now – the compiler did not create a lambda object in each iteration but rather reused the existing one each time. After the loop, all items in the actions list are actually the same instance and their s field is the last element of the collection. Apparently, C# compiler creates a lambda instance immediately before the local variable it uses. So, in order to force it to create a new lambda for each iteration, we need to introduce a new local variable in the loop:

private static List<Action> CreateActions(IEnumerable<string> strings)
{
    var actions = new List<Action>();
    
    foreach (var s in strings)
    {
        // This variable will be used in the lambda
        var lambda_param = s;
        actions.Add( () => Console.WriteLine(lambda_param) );
    }
    
    return actions;
}

With this modification the program now produces this output:
Second output

kick it on DotNetKicks.comShout it

Posted in .NET, C# | Tagged: , , | 41 Comments »

Chaos and Order on Disk

Posted by Bojan Resnik on June 2, 2009

Yesterday I was searching for a program on my external disk, yet again. I’ve been sick and tired of searching for stuff on it for a long time, and have been contemplating to reorganize it in order to make things more logical. A lot of stuff, both useful and not, has accumulated over the years, and, needless to say, many of them are in various, completely illogical places. There are tutorials and source codes dating back over a decade ago – mostly public domain, but there are some of my own as well. There are also utility programs that I just cannot be bothered to download each time I install a new machine, such as Winamp, Avira Antivirus, Opera, Python, and a multitude of others. Some of the stuff are backups (usually in forms of disk images) of software CDs and DVDs. And finally, there are numerous silly texts, pictures and the like.

At some point, up to about 5 years ago, I had a system, and the stuff was organized according to it.But, alas, some times I was in a hurry and just wanted to copy something over, and at other times I just couldn’t come up with a logical place for an item. And so, file by file, folder by folder, everything turned to chaos. Well, to be completely honest, it’s not that chaotic. It still has some leftover organization from my old system and I can (mostly) remember where things are. But, there are many things I would like to see categorized differently, in different folders, some packed with WinRAR, some unpacked.

It would be nice if I didn’t have to do all these by hand, so I started thinking about a program that could help me do most of these. Some of the requirements I can think of include:

  • Has to run off disk – no installation necessary
  • Minimal requirements (.NET framework is ok)
  • It must work with physical files and folders – no database or some such required.
    Basically, I’d like to run it and have it automatically collect everything on disk, allowing me to categorize the stuff without moving it around
  • Metadata must be kept with physical files/folders.
    If I move a folder manually, I want it to retain all the categories it belonged to
  • Easy searching by name, description, category

Apart from these basic requirements, I’d like it to also have some other things I find useful:

  • Automatically update installations
    For example, it should connect to Winamp site and download the latest installation if necessary
  • Automatically install programs
  • Launch viewers for images, PDF, HTML, etc.
  • Reorganize files and folders
  • Pack/unpack entire applications

An application that satisfies the basic requirements would be extremely useful to me, but I would ultimately want one that satisfies these additional requirements as well. I am seriously thinking of starting this application as an open source project, but I am currently overwhelmed with other issues. Still, I think I might start it in the near future.

Posted in Uncategorized | Tagged: , | Leave a Comment »

Installing the same managed addin in 32bit and 64bit Autodesk Inventor

Posted by Bojan Resnik on May 21, 2009

I have an addin for AutoDesk Inventor 2009, written in C# and a setup project to go with it. In order to register the addin with Inventor, I needed to register the assembly with “/codebase” switch, so I made a custom installer action:

[RunInstaller(true)]
public partial class AddinInstaller : Installer
{
    public override void Install(System.Collections.IDictionary stateSaver)
    {
        base.Install(stateSaver);
        RegistrationServices regsrv = new RegistrationServices();
        regsrv.RegisterAssembly(GetType().Assembly, AssemblyRegistrationFlags.SetCodeBase);
    }

    public override void Uninstall(System.Collections.IDictionary savedState)
    {
        base.Uninstall(savedState);

        RegistrationServices regsrv = new RegistrationServices();
        regsrv.UnregisterAssembly(GetType().Assembly);
    }
}

The actual registration code run by the registration service modifies some registry keys under HKEY_CLASSES_ROOT. This worked fine on Windows XP, but when run on my Vista 64, the setup completed successfully, but failed to register the addin with Inventor. The reason became apparent when I realized that my Inventor installation is also 64 bit – which means it looks for the keys using the 64bit view of the registry, while the installer action runs with the 32bit view.

The code itself is all C#, compiled as “Any CPU”, so it should have no problems running unmodified with both 64 and 32 bit Inventors. The only problem is how to run the installer as 64 bit. I also wanted to allow the same MSI to be used on all platforms, so compiling multiple versions was not an option. And searching the net, I got the impression that it would not solve the problem anyway – at least not without some other tools.

The solution I came up with is simpler: instead of using RegistrationServices object, I can manually execute the RegAsm.exe tool to register the assembly. On my Vista 64, there are two versions of this tool:

  • C:\Windows\Microsoft.NET\Framework\v2.0.50727\RegAsm.exe
  • C:\Windows\Microsoft.NET\Framework64\v2.0.50727\RegAsm.exe

The directories might not be the same on other machines, of course. Fortunately, .NET provides the RuntimeEnvironment.GetRuntimeDirectory() static method which returns the folder of the common language runtime. On my machine, it returns C:\Windows\Microsoft.NET\Framework\v2.0.50727\ inside the installer action. We can use this, combined with RuntimeEnvironment.GetSystemVersion() to construct the paths to 32bit and 64bit versions of the tool:

[RunInstaller(true)]
public partial class AddInInstaller : Installer 
{
    public AddInInstaller() 
    { InitializeComponent(); }

    public override void Install(IDictionary stateSaver) 
    {
        base.Install(stateSaver);
        RegAsm("/codebase");
    }

    public override void Uninstall(IDictionary savedState) 
    {
        RegAsm("/u");
        base.Uninstall(savedState);
    }

    private static void RegAsm(string parameters)
    {
        // RuntimeEnvironment.GetRuntimeDirectory() returns something like
        //    C:\Windows\Microsoft.NET\Framework64\v2.0.50727\
        // We need to get to the
        //    C:\Windows\Microsoft.NET
        // part in order to create 32 and 64 bit paths
        var net_base = Path.GetFullPath(Path.Combine(RuntimeEnvironment.GetRuntimeDirectory(), @"..\.."));
                    
        // Create paths to 32bit and 64bit versions of regasm.exe
        var to_exec = new[]
        {
            string.Concat(net_base, "\\Framework\\", RuntimeEnvironment.GetSystemVersion(), "\\regasm.exe"), 
            string.Concat(net_base, "\\Framework64\\", RuntimeEnvironment.GetSystemVersion(), "\\regasm.exe")
        };

        var dll_path = Assembly.GetExecutingAssembly().Location;
 
        foreach (var path in to_exec)
        {
            // Skip the executables that do not exist
            // This most likely happens on a 32bit machine when processing the path to 64bit regasm
            if ( !File.Exists(path) )
                continue;
                
            var process = new Process
            {
                StartInfo =
                {
                    CreateNoWindow = true,
                    ErrorDialog = false,
                    UseShellExecute = false,
                    FileName = path,
                    Arguments = string.Format("\"{0}\" {1}", dll_path, parameters)
                }
            };
            
            using (process)
            {
                process.Start();
                process.WaitForExit();
            }
        }
    }
}

Posted in .NET, C#, Inventor | 9 Comments »

Passing C++/CLI delegate to native code

Posted by Bojan Resnik on May 18, 2009

Recently I had to interface a C++/CLI assembly with a native DLL written in C. This is mostly straightforward, but the C DLL could raise an internal event and provided a way to have the application notified of this event. In order to be informed, the application has to register a callback function that will be invoked by the DLL when the event is raised. The registration function is declared like this:

typedef void (__stdcall* EventCallback)();
void RegisterCallback(EventCallback callback);

Using an ordinary function for the callback would be easy, but I wanted to use a .NET delegate so that I could convert the native event into a .NET event. This scenario also turns out to be supported by .NET. All you need to take care of is to prevent the delegate from being moved or collected by the garbage collector.

public delegate void EventDelegate();

ref class NativeInterface
{
public:
    NativeInterface()
    {
        // Create the delegate from a member function
        nativeCallback_ = gcnew EventDelegate(this, &NativeInterface::Callback);

        // As long as this handle is alive, the GC will not move or collect the delegate
        // This is important, because moving or collecting invalidate the pointer
        // that is passed to the native function below
        delegateHandle_ = GCHandle::Alloc(nativeCallback_);

        // This line will actually get the pointer that can be passed to
        // native code
        IntPtr ptr = Marshal::GetFunctionPointerForDelegate(nativeCallback_);

        // Convert the pointer to the type required by the native code
        RegisterCallback( static_cast<EventCallback>(ptr.ToPointer()) );
    }

    !NativeInterface()
    {
        // Free the handle to the delegate, allowing GC to collect
        // the delegate
        if (delegateHandle_.IsAllocated)
            delegateHandle_.Free();
    }

private:
    GCHandle delegateHandle_;
    EventDelegate^ nativeCallback_;

    void Callback()
    {
        Console::WriteLine("Native event raised");
    }
};

kick it on DotNetKicks.comShout it

Posted in .NET, C++/CLI | Tagged: , , | 9 Comments »