Mono RegistryHive

Jul 23, 2011 at 10:39 PM

Has anyone else been having issues with reading/creating Registry Hives using MonoDevelop or Mono Tools for Visual Studio?  I did find that the binary search in Mono reverses the order and therefore instead of using the x parameter, you have to use the y; however, the indexes being returned are not aligned.  In windows, the returned indices start off like 0, 0, 0, 0, 0, 3, 1 but in Mono, they are -7, -7, -7, -7, -7, -1, -7.  I haven't figured out the offset and if this is what would be returned for every single test case.

Just hoping someone has figured out this issue and is willing to share.

Jul 24, 2011 at 12:01 AM
Edited Jul 24, 2011 at 12:03 AM

Okay, got this issues fixed.  It is weird though but maybe someone with more experience using BinarySearch can provide a better answer.  I replaced the IComparer within the RegistryHive to look like this.  Again, mono not only passes the parameters incorrect but was also returning incorrect indices.  So, as you can see, the program is running in Mono, then x will be null and y should be used.  Also, after I got this fixed, I ran into another issue that I am actively working.  I'll keep posting here with what I find so hopefully this library can be 100% cross-platform capable.

            int IComparer<BinHeader>.Compare(BinHeader x, BinHeader y)
            {
                if (x == null)
                {
                    if (y.FileOffset + y.BinSize < _index)
                    {
                        return 1;
                    }
                    else if (y.FileOffset > _index)
                    {
                        return -1;
                    }
                    else
                    {
                        return 0;
                    }
                }
                else
                {
                    if (x.FileOffset + x.BinSize < _index)
                    {
                        return -1;
                    }
                    else if (x.FileOffset > _index)
                    {
                        return 1;
                    }
                    else
                    {
                        return 0;
                    }
                }
            }
Coordinator
Jul 24, 2011 at 7:13 AM

Jason,

Thanks for digging in to figure this out.  I'm not sure why Mono's swapping the inputs.  I haven't traced this to be 100% sure, but from looking at the code, it looks like List<T>.BinarySearch ultimately is implemented by [corlib]::System.Array.DoBinarySearch:

0b945399»vargaz
2007-08-242007-08-24 Zoltan Varga <vargaz@gmail.com>
872 // Be careful with overflow
873 // http://googleresearch.blogspot.com/2006/06/extra-extra-read-all-about-it-nearly.html
874 int iMid = iMin + ((iMax - iMin) / 2);
eac9e572»illupus
2002-04-10Thu Apr 11 12:28:13 CEST 2002 Paolo Molaro <lupus@...
875 object elt = array.GetValueImpl (iMid);
b81bdae1»Piers Haken
2002-04-05implement BinarySearch, cleanup
876  
328bb56d»Ben Maurer
2005-05-07In System:
877 iCmp = comparer.Compare (elt, value);

'value' should be the null value passed in, in DiscUtils - so y should always be null in the comparison performed in DiscUtils.  It looks like this code has been stable for a few years.

Cheers,

Ken

Jul 24, 2011 at 8:14 AM

Ken,

I agree, there should be no reason for it switching like that.  I did find another problem in SubKeyHashedListCell with Mono as well.  It was a pretty easy fix and again had to do with Mono incorrectly setting indexes.  I added another method to simply use a for loop to compare the names insted of using KeyFinder.  Once I did this, it actually broke the portion I have been working with (BootConfig) on windows as well because there are objects and elements that have been added since the last release.  Again, no huge problem since I can easily add those in.  Here is the code that I changed in SubKeyHashedListCell.cs:

        internal override int FindKey(string name, out int cellIndex)
        {
            // Check first and last, to early abort if the name is outside the range of this list
            int result = FindKeyAt(name, 0, out cellIndex);
            if (result <= 0)
            {
                return result;
            }
            result = FindKeyAt(name, _subKeyIndexes.Count - 1, out cellIndex);
            if (result >= 0)
            {
                return result;
            }

            //KeyFinder finder = new KeyFinder(_hive, name);
            int idx = FindKey(_hive, name);// _subKeyIndexes.BinarySearch(-1, finder);
            cellIndex = idx;
            return (idx < 0) ? -1 : 0;
        }

        private int FindKey(RegistryHive hive, string name)
        {
            for (int i = 0; i < _subKeyIndexes.Count; i++)
            {
                KeyNodeCell cell = hive.GetCell<KeyNodeCell>(_subKeyIndexes[i]);

                if (cell.Name == name)
                    return _subKeyIndexes[i];
            }

            return -1;
        }

Coordinator
Jul 24, 2011 at 8:31 AM

I'm a bit wary about changing DiscUtils to work around a Mono bug, especially until the actual problem can be pinpointed inside Mono.

Can you provide a small sample program that uses DiscUtils that shows the bug (works on Windows, fails on Mono)?

Cheers,

Ken