RCode

a software development blog by Bojan Resnik

Posts Tagged ‘c++’

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: , | 81 Comments »

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: , , | 28 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: , , | 8 Comments »

 
Follow

Get every new post delivered to your Inbox.