sander.rijken.info http://sander.rijken.info Most recent posts at sander.rijken.info posterous.com Thu, 07 Apr 2011 16:03:19 -0700 How to use Windbg to debug a dump of a 32bit .NET app running on a x64 machine http://sander.rijken.info/b/joaol/archive/2008/09/03/how-to-use-windbg-to-debug-a-dump-of-a-32bit-net-app-running-on-a-x64-machine.aspx http://sander.rijken.info/b/joaol/archive/2008/09/03/how-to-use-windbg-to-debug-a-dump-of-a-32bit-net-app-running-on-a-x64-machine.aspx http://blogs.msdn.com/b/joaol/archive/2008/09/03/how-to-use-windbg-to-debug-a...

Great article

Permalink | Leave a comment  »

]]>
http://posterous.com/images/profile/missing-user-75.png http://posterous.com/users/hesAXxyWQMrWi srijken srijken srijken
Tue, 01 Feb 2011 19:00:00 -0800 Reading 64 bit registry from 32bit app http://sander.rijken.info/post/3050970793/reading-64-bit-registry-from-32bit-app http://sander.rijken.info/post/3050970793/reading-64-bit-registry-from-32bit-app

By default all registry keys you access from a 32bit application are redirect to the Wow6432Node, and also the other way around, when accessing it from a 64bit program, you only see the 64bit section of the registry.

The normal way of opening a registry key is:

RegistryKey rk = Registry.LocalMachine.OpenSubKey(
    "SOFTWARE\\Microsoft");

EDIT: In .NET 4 you don’t have to do the P/Invoke, just use this instead:

RegistryKey regkey =
    RegistryKey.OpenBaseKey(
        RegistryHive.LocalMachine,
        RegistryView.Registry32)
    .OpenSubKey("SOFTWARE")

and

RegistryKey regkey =
    RegistryKey.OpenBaseKey(
        RegistryHive.LocalMachine,
        RegistryView.Registry64)
    .OpenSubKey("SOFTWARE")

Note that this returns a different section depending on wether the application that’s executing the code has 32 or 64 bit. To use this, you have to use an unmanaged api that can be P/Invoked. We add the following 2 extension methods:

public static RegistryKey Open32BitSubKey(
    this RegistryKey parentKey,
    string subKeyName,
    bool writeable)
public static RegistryKey Open64BitSubKey(
    this RegistryKey parentKey,
    string subKeyName,
    bool writeable)

Native methods required:

static class NativeMethods
{
    [DllImport("advapi32.dll",
        CharSet = CharSet.Unicode,
        EntryPoint = "RegOpenKeyEx")]
    static extern int RegOpenKeyEx(
        IntPtr hKey,
        string subKey,
        uint options,
        int sam,
        out IntPtr phkResult);
    [Flags]
    public enum eRegWow64Options
    {
        None = 0x0000,
        KEY_WOW64_64KEY = 0x0100,
        KEY_WOW64_32KEY = 0x0200,
    }
    [Flags]
    public enum eRegistryRights
    {
        ReadKey = 131097,
        WriteKey = 131078,
    }
    public static RegistryKey Open32BitSubKey(
        this RegistryKey parentKey,
        string subKeyName,
        bool writeable)
    {
        return OpenSubKey(
            parentKey,
            subKeyName,
            writeable,
            eRegWow64Options.KEY_WOW64_32KEY);
    }
    public static RegistryKey Open64BitSubKey(
        this RegistryKey parentKey,
        string subKeyName,
        bool writeable)
    {
        return OpenSubKey(
            parentKey,
            subKeyName,
            writeable,
            eRegWow64Options.KEY_WOW64_64KEY);
    }
    static RegistryKey OpenSubKey(
        RegistryKey parentKey,
        string subKeyName,
        bool writeable,
        eRegWow64Options options)
    {
        if (parentKey == null)
            throw new ArgumentNullException("parentKey");
        IntPtr parentKeyHandle = GetRegistryKeyHandle(parentKey);
        if (parentKeyHandle == IntPtr.Zero)
            throw new ArgumentException("Parent key is not open", "parentKey");
        eRegistryRights rights = eRegistryRights.ReadKey;
        if (writeable)
            rights = eRegistryRights.WriteKey;
        IntPtr subKeyHandle;
        int result = RegOpenKeyEx(
            parentKeyHandle,
            subKeyName,
            0,
            (int)rights | (int)options,
            out subKeyHandle);
        if (result != 0)
            Marshal.ThrowExceptionForHR(result);
        return PointerToRegistryKey(subKeyHandle, writeable, false);
    }
    private static IntPtr GetRegistryKeyHandle(
        RegistryKey pRegisteryKey)
    {
         Type type = Type.GetType("Microsoft.Win32.RegistryKey");
         FieldInfo info = type.GetField("hkey", BindingFlags.NonPublic | BindingFlags.Instance);
         SafeHandle handle = (SafeHandle)info.GetValue(pRegisteryKey);
         return handle.DangerousGetHandle();
    }
    private static RegistryKey PointerToRegistryKey(IntPtr hKey, bool pWritable,
        bool pOwnsHandle)
    {
        if (hKey == IntPtr.Zero)
            return null;
        // Create a SafeHandles.SafeRegistryHandle from this pointer - this is a private class
        BindingFlags privateConstructors = BindingFlags.Instance | BindingFlags.NonPublic;
        Type safeRegistryHandleType = typeof(
            SafeHandleZeroOrMinusOneIsInvalid).Assembly.GetType(
                "Microsoft.Win32.SafeHandles.SafeRegistryHandle");
        Type[] safeRegistryHandleConstructorTypes = new[] { typeof(IntPtr), typeof(Boolean) };
        ConstructorInfo safeRegistryHandleConstructor =
            safeRegistryHandleType.GetConstructor(privateConstructors,
                                                  null, safeRegistryHandleConstructorTypes, null);
        Object safeHandle = safeRegistryHandleConstructor.Invoke(new Object[]
                                                                    {
                                                                        hKey,
                                                                        pOwnsHandle
                                                                    });
        // Create a new Registry key using the private constructor using the
        // safeHandle - this should then behave like
        // a .NET natively opened handle and disposed of correctly
        Type registryKeyType = typeof(RegistryKey);
        Type[] registryKeyConstructorTypes = new[] { safeRegistryHandleType, typeof(Boolean) };
        ConstructorInfo registryKeyConstructor =
            registryKeyType.GetConstructor(privateConstructors, null,
                                           registryKeyConstructorTypes, null);
        RegistryKey result = (RegistryKey)registryKeyConstructor.Invoke(new[] { safeHandle, pWritable });
        return result;
    }
}

To open the 64 bit version of the same section from a 32bit application you use:

Registry.LocalMachine.Open64BitSubKey(
    subKey,
    false);

And to open the 32bit version from a 64bit application:

Registry.LocalMachine.Open32BitSubKey(
    subKey,
    false);

Permalink | Leave a comment  »

]]>
http://posterous.com/images/profile/missing-user-75.png http://posterous.com/users/hesAXxyWQMrWi srijken srijken srijken
Sat, 29 Jan 2011 10:22:00 -0800 The story about AnkhSVN and RocketSvn http://sander.rijken.info/post/2991017173/the-story-about-ankhsvn-and-rocketsvn http://sander.rijken.info/post/2991017173/the-story-about-ankhsvn-and-rocketsvn

Update April 28, 2011 (source):

 

We have decided to make RocketSVN Server open source and and RocketSVN for VS free for all users, but we are no longer actively developing or supporting RocketSVN.  I have not tested with 2010 and do not know if there are any issues with the new update.

 

Following the announcement: by Axosoft that both RocketSvn and RocketSvn for Visual Studio are now free, I felt I had to clarify what was announced, and also share some history of both AnkhSVN and RocketSvn for Visual Studio. I don’t want to bash RocketSvn, I just want to be open about both products.

AnkhSVN started as an open source project way back in January 2003. At that time the Visual Studio Industry Partner Program (VSIP) licensing was too strict for an open source project. As a result of this, AnkhSVN used the addin interfaces to integrate into Visual Studio. Today AnkhSVN 1.0.6 still uses these interfaces to support Visual Studio .NET and Visual Studio .NET 2003.

In February of 2008 AnkhSVN moved from tigris.org to openCollabNet, and at the same time a redesign was done to enable better integration in Visual Studio 2005 and 2008 (at the time). The reason that this could be done was that Microsoft opened up the licensing terms on the Visual Studio SDK enough that it would be possible for an open source project to use it.

Through CollabNet we also got access to other VSIP benefits, like pre-release builds of Visual Studio 2010, enabling us to have compatibility with Visual Studio 2010 the very day it was released to MSDN subscribers.

We always anticipated that it would be possible for a commercial company to take the AnkhSVN source, and use it in a commercial product, because the AnkhSVN codebase uses the Apache2 license. We were still surprised about it when Axosoft did this, mostly because there doesn’t seem to be a very good reason to do so at the moment. To provide the extension they have in place today, they could have used an existing plug in infrastructure in AnkhSVN (more on this later).

After we found out that the AnkhSVN source code was being used in a commercial product, we received an email from one of the developers at Axosoft on May 13, 2010, notifying us of the release of RocketSvn for Visual Studio. This was the first, and last time the AnkhSVN project heard from them.

On January 26, 2011 Axosoft announced that RocketSvn Server and RocketSvn for VS would both be free to use. RocketSvn Server was released as an open-source project, whilst RocketSvn for VS is still closed.

EDIT: In an email I recently received, I heard that there were also plans to open source RocketSvn for VS. I can see why sometimes closed-sources forks can be necessary, but when you don’t change the forked code, but only add to it, I can’t see a good reason to have 2 very similar open source projects side-by-side, as this is the worst form of code duplication you can have.

A quote from the announcement:

While we have been doing great additions to both open source projects (Ankh and Subversion), we decided it was important not to charge for the work we’ve done.

In reality, Axosoft did contact the AnkhSVN project once, promising to submit patches and collaborate on making AnkhSVN a better product as well. We have not seen patches ever, neither has the Subversion project (see also the contribulyzer of the Subversion project).

I would also like to note that all these “changes” they made to AnkhSVN could have been implemented as a add-in to Visual Studio and AnkhSVN. AnkhSVN has excellent integration for issue trackers like OnTime. In fact it seems that Axosoft uses this very integration to plug in OnTime, so it could just as well have been plugged in to AnkhSvn. Also the top level menu that they add, could have been implemented with a simple Visual Studio add-in that fires existing AnkhSVN commands.

Permalink | Leave a comment  »

]]>
http://posterous.com/images/profile/missing-user-75.png http://posterous.com/users/hesAXxyWQMrWi srijken srijken srijken
Fri, 07 May 2010 01:06:00 -0700 Entity Framework 4 - Inheritance http://sander.rijken.info/post/1224258446/entity-framework-4-inheritance http://sander.rijken.info/post/1224258446/entity-framework-4-inheritance

In Entity Framework 4.0 it’s possible to model objects with inheritance in several different ways. This post goes into a multi-table layout. The following diagram is assumed for examples:

A database layout for this can look like:

Person
-----
Id PK
Name
Address

Student
------
PersonID PK
StudentNumber

Teacher
------
PersonID PK
Subject

When you generate an .edmx file with Visual Studio 2010 based on this table layout, you end up with these 3 tables mapped as 3 classes. The inheritance hasn’t been detected/modeled, instead there are 1 to 0..1 associations.

When you’re trying to use .OfType() to retrieve only the students, it fails with this exception:

DbOfTypeExpression requires an expression argument with a polymorphic result type that is compatible with the type argument.

The solution is to model the inheritance, but the steps to do that need to be taken in a very specific order to avoid problems.

  1. You need to make sure the entity’s primary keys all have the same name, it doesn’t matter how they’re called in the database, but the entity name has to be equal.

  2. Add the “Inheritance” shape from the Toolbox, and drag it from Student to Person and from Teacher to Person.

  3. Now remove the primary key property on the Student and Teacher entities, otherwise the generated objects will get the property from 2 sources, which doesn’t work.

  4. Go to the mapping details of Student and Teacher (right-click -> Table mapping). The mapping for the primary key is now empty here, because you removed the primary key in the previous step. From the drop down select the primary key, this time it’s the property from the base class.

  5. Remove the navigation properties from person to student/teacher and back, they no longer serve a purpose because a student is a person.

If someone knows a faster/better way to get this done, please comment.

Permalink | Leave a comment  »

]]>
http://posterous.com/images/profile/missing-user-75.png http://posterous.com/users/hesAXxyWQMrWi srijken srijken srijken
Fri, 27 Nov 2009 22:36:00 -0800 Generic IRepository using Entity Framework and .NET 4.0 http://sander.rijken.info/post/1224286145/generic-irepository-using-entity-framework-and-net-4-0 http://sander.rijken.info/post/1224286145/generic-irepository-using-entity-framework-and-net-4-0

An ideal repository interface for Linq queries looks something like:

public interface IRepository : IDisposable
{
    IQueryable GetAll() where T : class;
    void Add(T instance) where T : class;
    void Delete(T instance) where T : class;
    void Delete(Expression> filter) 
        where T : class;
    int SaveChanges();
}

This interface can be implemented by wrapping the generated Entity Framework ObjectContext class with an implementation of IRepository.

The challenge is getting the ObjectSet instance from the context in a generic way. The properties that are generated all have a different name, which makes it difficult to implement the generic IRepository.

If it were possible to get the ObjectSet in a generic way, I would probably implement the repository like so:

sealed class ClassroomRepository :IRepository 
{
    private ClassroomModel _context;

    public ClassroomRepository()
    {
        _context = new ClassroomModel();
    }

    public void Dispose()
    {
        if(_context != null)
        {
            _context.Dispose();
            _context = null;
        }
    }

    public IQueryable GetAll() 
        where T : class
    {
        return _context.GetObjectSet();
    }

    public void Add(T instance) 
        where T : class
    {
        _context.GetObjectSet().AddObject(instance);
    }

    public void Delete(T instance) 
        where T:class
    {
        _context.GetObjectSet().DeleteObject(instance);
    }

    public void Delete(IEnumerable items) 
        where T : class
    {
        foreach(T instance in items)
            objectSet.DeleteObject(instance);
    }

    public int SaveChanges()
    {
        return _context.SaveChanges();
    }
}

I Googled around for ways other ways to implement a generic IRepository, but I didn’t find a way that would allow me to use the change tracking from ObjectContext, and at the same time use the repository pattern on top of that.

I started experimenting with t4 to return ObjectContext.Persons when I passed in GetObjectSet. The code that we would like to generate on the context class to be able to get the object set based on the type would look like this:

public ObjectSet GetObjectSet() 
    where T : class
{
    if(typeof(T) == typeof(Customer))
    {
        return (ObjectSet)(object)Customers;
    }
    if(typeof(T) == typeof(Order))
    {
        return (ObjectSet)(object)Orders;
    }
    return null;
}

Note that we need to cast to object first, and then to ObjectSet, because the compile cannot guarantee there’s a conversion from ObjectSet to ObjectSet. The code does guarantee this however, so the double cast is safe.

The section of T4 that was needed to generated this is below:

region.Begin("GenericObjectSets");

    #>
    public ObjectSet GetObjectSet()
        where T : class
    {
<#
        foreach (EntitySet set in container.BaseEntitySets.OfType())
        {#>
        if (typeof(T) == typeof(<#=MultiSchemaEscape(set.ElementType, code)#>))
        {
            return (ObjectSet)(object)<#=code.Escape(set)#>;
        }
<#
    }#>
        return null;
    }
    <#
region.End();

Permalink | Leave a comment  »

]]>
http://posterous.com/images/profile/missing-user-75.png http://posterous.com/users/hesAXxyWQMrWi srijken srijken srijken
Thu, 05 Feb 2009 00:00:00 -0800 Workaround for Silverlight ScrollViewer bug http://sander.rijken.info/post/2961494673/workaround-for-silverlight-scrollviewer-bug http://sander.rijken.info/post/2961494673/workaround-for-silverlight-scrollviewer-bug

There’s a bug in the way the ScrollViewer works that can be triggered by dragging the “thumb” part of the scrollbar and then letting go outside of the Silverlight control. The thumb remains “active” visually; it has the visual aspects of a thumb with the mouse button down.

The effect of this is that when you move the mouse back into the Silverlight control, the scrollbar starts reacting to mouse movement, even though the mouse buttons are up. It didn’t see the OnMouseUp event, because the mouse cursor was outside the frame.

There’s a simple fix for this problem, inspired by a forum post. I didn’t like that solution because you loose the drag state when you drag away from the thumb (but are still within the silverlight control)

I changed the solution provided by the forum post into something that works almost as desired. The only problem that remains is that the Thumb seems to be active when you let go of the mouse outside the Silverlight control. This is fixed as soon as you move the mouse over the Silverlight control.

<Grid x:Name="LayoutRoot" Background="White" 
      Width="Auto" 
      Height="Auto">
    <ScrollViewer HorizontalScrollBarVisibility="Visible"
                  VerticalScrollBarVisibility="Visible"
                  Width="Auto" 
                  Height="Auto"
                  x:Name="ScrollViewer"
                  LostMouseCapture="ScrollViewer_LostMouseCapture">
        <Canvas x:Name="ScrollPanel" Width="800" Height="1500">
        </Canvas>
    </ScrollViewer>
</Grid>

The event that’s most suitable for working around the problem seems to be LostMouseCapture that gets hooked in line 9. In the event handler we search for the thumbs on both scrollbars, and call CancelDrag to get rid of the dragging state.

private void ScrollViewer_LostMouseCapture(
    object sender, MouseEventArgs e)
{
    FrameworkElement fe = VisualTreeHelper.GetChild(ScrollViewer, 0) 
        as FrameworkElement;
    if (fe == null)
        return;

    //find the vertical scrollbar
    ScrollBar sb = fe.FindName("VerticalScrollBar") as ScrollBar; 
    if (sb != null)
    {
        //Get the scrollbar's thumbnail. This is the element that
        //remains stuck in the "drag" state
        Thumb thumb = (Thumb)((FrameworkElement)VisualTreeHelper
            .GetChild(sb, 0)).FindName("VerticalThumb");
        //cancel the operation
        thumb.CancelDrag();
    }
    sb = fe.FindName("HorizontalScrollBar") as ScrollBar;
    if (sb != null)
    {
        //Get the scrollbar's thumbnail. This is the element that 
        //remains stuck in the "drag" state
        Thumb thumb = (Thumb)((FrameworkElement)VisualTreeHelper
            .GetChild(sb, 0)).FindName("HorizontalThumb");
        //cancel the operation
        thumb.CancelDrag();
    }
}

Permalink | Leave a comment  »

]]>
http://posterous.com/images/profile/missing-user-75.png http://posterous.com/users/hesAXxyWQMrWi srijken srijken srijken
Wed, 04 Feb 2009 00:00:00 -0800 Silverlight zooming with wheel mouse http://sander.rijken.info/post/2962904277/silverlight-zooming-with-wheel-mouse http://sander.rijken.info/post/2962904277/silverlight-zooming-with-wheel-mouse

You can hook mouse wheel events as described in a previous post to do other things, besides just scrolling the scroll viewer, for instance for zooming in and out the entire scene.

<Grid x:Name="LayoutRoot" Background="White" 
      Width="Auto" 
      Height="Auto">
    <ScrollViewer HorizontalScrollBarVisibility="Visible"
                  VerticalScrollBarVisibility="Visible"
                  Width="Auto" 
                  Height="Auto"
                  x:Name="ScrollViewer">
        <Canvas x:Name="ScrollPanel" Width="800" Height="1500">
            <Canvas.RenderTransform>
                <ScaleTransform x:Name="ZoomTransform" />
            </Canvas.RenderTransform>
        </Canvas>
    </ScrollViewer>
</Grid>

This “OnMouseWheelTurned” changes the zoom. In this example, we check wether the Shift key is pressed so we can use the ‘normal’ mousewheel actions for other stuff like scrolling the ScrollViewer

private void OnMouseWheelTurned(Object sender, HtmlEventArgs args)
{
    double delta = 0;
    ScriptObject e = args.EventObject;

    if (e.GetProperty("wheelDelta") != null) // IE and Opera
    {
        delta = ((double)e.GetProperty("wheelDelta"));
        if (HtmlPage.Window.GetProperty("opera") != null)
            delta = -delta;
    }
    else if (e.GetProperty("detail") != null) // Mozilla and Safari
    {
        delta = -((double)e.GetProperty("detail"));
    }

    // Enable zooming when the shift key is pressed
    if (args.ShiftKey)
    {
        if (delta > 0)
        {
            ZoomTransform.ScaleX += 0.1;
            ZoomTransform.ScaleY += 0.1;
        }
        else if (delta  0.1 &&
                ZoomTransform.ScaleY > 0.1)
            {
                ZoomTransform.ScaleX -= 0.1;
                ZoomTransform.ScaleY -= 0.1;
            }
        }

        if (delta != 0)
        {
            // Prevent the default action (scrolling the page) and
            // return false if we handled the wheel event
            args.PreventDefault();
            e.SetProperty("returnValue", false);
            return;
        }
    }

    // TODO: Different mousewheel actions, for example scrolling 
}

Permalink | Leave a comment  »

]]>
http://posterous.com/images/profile/missing-user-75.png http://posterous.com/users/hesAXxyWQMrWi srijken srijken srijken
Tue, 03 Feb 2009 00:00:00 -0800 Adding mouse wheel support in Silverlight 2.0 http://sander.rijken.info/post/2962855844/adding-mouse-wheel-support-in-silverlight-2-0 http://sander.rijken.info/post/2962855844/adding-mouse-wheel-support-in-silverlight-2-0

By default the ScrollViewer control in Silverlight 2.0 doesn’t scroll when using the mouse wheel. The only way to scroll using the mouse is by clicking/dragging the scrollbars.

The best way I’ve found to solve this, is by handling the javascript “onmousewheel” event, on the silverlight control. I have my control hosted in ASP.NET, and pass it an InitParam that contains the ASP.NET ClientID of the Silverlight control itself. I’m sure there must be a better way the get at the control from inside Silverlight, but I haven’t found it (yet).

The code below finds the Silverlight html element, and attaches the onmousewheel event.

HtmlElement slElement = 
    HtmlPage.Document.GetElementById(_silverlightHost);
slElement.AttachEvent("onmousewheel", OnMouseWheelTurned);

In the handler, ScrollViewer is the viewer itself; ScrollPanel is the panel contained within the ScrollViewer.

private void OnMouseWheelTurned(Object sender, HtmlEventArgs args)
{
    double delta = 0;
    ScriptObject e = args.EventObject;

    if (e.GetProperty("wheelDelta") != null) // IE and Opera
    {
        delta = ((double)e.GetProperty("wheelDelta"));
        if (HtmlPage.Window.GetProperty("opera") != null)
            delta = -delta;
    }
    else if (e.GetProperty("detail") != null) // Mozilla and Safari
    {
        delta = -((double)e.GetProperty("detail"));
    }
    
    if (
        // Override to horizontal scrolling when Ctrl is pressed.
        !args.CtrlKey &&
        // Use vertical scrolling if there's something to scroll
        ScrollPanel.ActualHeight > ScrollViewer.ActualHeight &&
        ScrollViewer.ComputedVerticalScrollBarVisibility == 
            Visibility.Visible)
    {
        ScrollViewer.ScrollToVerticalOffset(
            ScrollViewer.VerticalOffset - delta);
    }
    else if (ScrollViewer.ComputedHorizontalScrollBarVisibility == 
        Visibility.Visible)
    {
        ScrollViewer.ScrollToHorizontalOffset(
            ScrollViewer.HorizontalOffset - delta);
    }

    if (delta != 0)
    {
        // Prevent default, and return false, to make sure the page 
        // itself doesn't respond to the mousewheel
        args.PreventDefault();
        e.SetProperty("returnValue", false);
    }
}

Permalink | Leave a comment  »

]]>
http://posterous.com/images/profile/missing-user-75.png http://posterous.com/users/hesAXxyWQMrWi srijken srijken srijken
Fri, 23 Jan 2009 00:00:00 -0800 .NET Interop - Targeting x86 vs x64 vs AnyCpu http://sander.rijken.info/post/2962785666/net-interop-targeting-x86-vs-x64-vs-anycpu http://sander.rijken.info/post/2962785666/net-interop-targeting-x86-vs-x64-vs-anycpu

AnkhSvn uses a .NET library called SharpSvn as a wrapper on the Subversion C library. SharpSvn.dll is a mixed assembly, meaning it contains both IL-code (platform independent) and native code (platform dependent). We keep getting questions on how to deal with this when writing applications for 32bit and 64bit operating systems. Scott Hanselman also talks about this confusion between 32bit and 64bit when using .NET in detail.

By default when you create a new executable in Visual Studio, the executable is compiled for “AnyCpu”. This means that the .NET runtime executes the assembly on the ‘best match’ available. This means it’ll run as a 32bit application on Win32 and a 64bit application on Win64. See this MSDN article how this is done

This works fine for applications that only contain platform independent IL code. However if you try to execute an AnyCpu application that links to a 32bit mixed mode assembly, the program starts in 64bit mode on 64bit system. When it tries to load the 32bit assembly, you get a BadImageFormatException, because the 64bit program cannot execute the 32bit native code.

For most applications the solution to this is quite simple. If they run well on a 32bit system, it means the 32bit address space is large enough for the memory needs of the application. In this case the ‘fix’ is to change the project properties of the executable to target 32bit. This changes the header of the resulting executable, telling the .NET runtime to load the application as a 32bit application on both Win32 and Win64. This way you’ll be able to use SharpSvn (or other mixed mode assemblies just fine).

For applications that require a 64bit address space, the solution is just as simple; they should target the 64bit platform. They cannot run well (or not at all) on a 32bit OS in the first place. Microsoft Exchange 2007 for instance, is targeted at 64bit only. SharpSvn provides a 64bit build for applications that need to be able to address that much memory.

The third option would be to create 2 versions of your program; one that targets 32bit, and one that targets 64 bit. The reason could be that the 64bit program can handle larger sets of data, or use a faster algorithm. When using this approach the installer has to figure out on what system the program is being deployed, and then pick appropriate assembly. This choice leads to much more testing work, because the program has to be able to run in 32bit address space, and the extra feature or smarter algorithm in the 64bit version has to be tested as well.

The SharpSvn mailing list contains a post that describes other ways to create a mixed 32bit/64bit application than switching 32bit and 64bit dlls in the installer.

Edit: Rico Mariani has written a post on why Visual Studio shouldn’t be a 64-bit process. If this is true for Visual Studio, it’s most likely also the case for other applications using SharpSvn. Ofcourse applications other than Source Control have different needs and characteristics

Permalink | Leave a comment  »

]]>
http://posterous.com/images/profile/missing-user-75.png http://posterous.com/users/hesAXxyWQMrWi srijken srijken srijken
Wed, 27 Feb 2008 00:00:00 -0800 PowerShell Cmdlets - adding/overriding members http://sander.rijken.info/post/2962080706/powershell-cmdlets-adding-overriding-members http://sander.rijken.info/post/2962080706/powershell-cmdlets-adding-overriding-members

Scott Hanselman described how to “spot-weld” new properties on existing types using the types.ps1xml file. When writing your own Cmdlets this might not be the best way to modify the objects you return. In this post I’m going into how to change existing types in code using ETS Properties based on a SharpSvn/Subversion cmdlet. A common use-case would be to fix a function that returns a string instead of a FileInfo or Uri instance (that can be passed along the pipeline).

The main trick is not to return the plain object, but a PSObject wrapping the plain object. The PSObject allows you to add and remove custom properties and methods from C# code.

This is the code before overriding propeties and extending the pipeline object

protected override void ProcessRecord()
{
    Client.Info(
        GetTarget<SvnTarget>(),
        SvnArguments,
        delegate(object sender, SvnInfoEventArgs e)
        {
            e.Detach();

            WriteObject(e);
        });
}

And its output.

Get-SvnInfo | % { $_.Path.GetType() }

IsPublic IsSerial Name        BaseType
-------- -------- ----        --------
True     True     String      System.Object

SvnInfoEventArgs has a Path property of type String. For a Cmdlet it’s more usual to write FileInfo objects to the pipeline, so we need to remove the original Path property, and provide a new one.

The properties/methods that seem to make most sense here are: * CodeProperty/CodeMethod Allows you to write C# code behind a new property or method, this is the most flexible, and can only be added from C# code. In PowerShell you would write a ScriptProperty or ScriptMethod instead, implementing it in PowerShell. * AliasProperty Exposes an existing property with another name, nice for creating a consistent PowerShell API on top of a non-existing API * NoteProperty This is just a readonly Key/Value

Adding a new property with the same name overrides the existing one

protected override void ProcessRecord()
{
    Client.Info(
      GetTarget<SvnTarget>(),
      SvnArguments,
      delegate(object sender, SvnInfoEventArgs e)
      {
          e.Detach();

          PSObject obj = new PSObject(e);
          obj.Properties.Add(new PSCodeProperty(
              "Path",
              GetType().GetMethod("GetPath")));

          WriteObject(obj);
      });
}

public static FileInfo GetPath(PSObject obj)
{
    SvnInfoEventArgs e = obj.BaseObject as SvnInfoEventArgs;
    if (e == null)
        return null;

    return new FileInfo(e.Path);
}

The method represented by MethodInfo has to be public, static, non-void and take one parameter of type PSObject to work.

This overrides the Path property:

Get-SvnInfo | % { $_.Path.GetType() }

IsPublic IsSerial Name          BaseType
-------- -------- ----          --------
True     True     FileInfo      System.IO.FileSys...

If you have “spot-welded” on extra properties on FileInfo, those will also be present on $_.Path ofcourse.

Permalink | Leave a comment  »

]]>
http://posterous.com/images/profile/missing-user-75.png http://posterous.com/users/hesAXxyWQMrWi srijken srijken srijken
Fri, 22 Feb 2008 00:00:00 -0800 SourceServer indexing and Subversion http://sander.rijken.info/post/2962017715/sourceserver-indexing-and-subversion http://sander.rijken.info/post/2962017715/sourceserver-indexing-and-subversion

This is a walkthrough about using source server indexed PDBs using Visual Studio 2005 or 2008, as described in a MSDN Magazine article. The next post will talk about producing source server indexed PDB files.

Visual Studio 2008 can also be configured to use a Symbol Server that provides source server indexed PDBs for debugging through the .NET Framework source code. SourceServerSharp enables you to produce PDBs for your own assemblies with similar possibilities.

  1. Install Subversion Command-line binaries and make sure svn.exe is in the path. This happens automatically when you use the installer.

  2. Configure source server inside Visual Studio 2005 or 2008:

    • Turn off ‘Enable Just My Code (Managed only)’
    • Turn on ‘Enable source server support’

Media_httpmediatumblr_khcdr

  1. You are now set-up, but when you are debugging and step into code outside your solution that is source server indexed, or hit an exception in that code, the following dialog comes up:

Media_httpmediatumblr_gghyz

To prevent this create a file called srcsrv.ini in %ProgramFiles%\Microsoft Visual Studio 8\Common7\IDE\ containing:

[trusted commands]
svn.exe export

Permalink | Leave a comment  »

]]>
http://posterous.com/images/profile/missing-user-75.png http://posterous.com/users/hesAXxyWQMrWi srijken srijken srijken
Mon, 04 Feb 2008 00:00:00 -0800 Updating file inside MSI for major upgrade http://sander.rijken.info/post/2961774537/updating-file-inside-msi-for-major-upgrade http://sander.rijken.info/post/2961774537/updating-file-inside-msi-for-major-upgrade

This post describes how to create a new msi that can be installed as a major upgrade based on an existing one (that support major upgrades), whilst patching/updating some files. This is useful when it’s easier to patch an existing msi than it is to create a new one.

Cabarc to create new cabinet containing the patched files:

CabArc.exe n mypatch.cab c:\patch\*.dll

MsiFiler to correct version information:

MsiFiler.exe -d myinstaller.msi -v -s c:\patch\

MsiDb to insert new cabinet in msi (optional):

MsiDb.exe -d myinstaller.msi -a mypatch.cab

Open myinstaller.msi in Orca and:

  • Insert a new Guid into PackageCode because we are changing the msi.

    To do this in Orca, go to ‘View -> Summary Information’ and click the ‘New GUID’ button next to the Package Code. It’s also possible to configure Orca to always change the PackageCode when saving, under ‘Options -> Database’

  • Insert a new Guid into ProductCode to allow major upgrade
  • Change the ProductVersion to a higher version number, and change the Upgrade table accordingly.
  • Change sequence in the File table for the patched file(s)
  • Add reference to mypatch.cab in Media table:

    • DiskId should be a new unique number
    • Cabinet should be ‘#mypatch.cab’ because the cabinet was inserted in the msi.

      It’s also possible to keep the cabinet file external, in that case call it ‘mypatch.cab’

    • LastSequence should correspond to the highest sequence number of the file in the File table.

      For example, if the highest number before patching is 72, and we added 3 files, LastSequence should be 75, and 3 entries in the File table should use Sequence 73, 74 and 75. LastSequence and Sequence is the only link between files and cabinets.

Orca, MsiDb, MsiFiler and CabArc are all part of the Windows Platform SDK

Permalink | Leave a comment  »

]]>
http://posterous.com/images/profile/missing-user-75.png http://posterous.com/users/hesAXxyWQMrWi srijken srijken srijken