在.NET中转换VB6 PropertyBag

问题描述 投票:0回答:4

对于某些文件操作,我们使用 VB6 的 PropertyBag 对象通过 Content 属性将各种项目转换为字节数组。然后我们将字节数组保存为二进制文件的一部分。稍后,当我们加载文件时,我们读回文件,读入字节数组并使用 propertybag 的 readproperty 重建项目。我们经常使用它来存储客户徽标等图像。

.NET 框架对使用 PropertyBags 提供哪些支持?除了编写 COM 帮助程序 DLL 之外。属性包是 VB6 独有的构造还是通用 OLE/COM 自动化框架的一部分?

我希望避免编写 VB6 辅助 DLL 并直接访问(通过声明)所需的 COM 函数。我不想复制功能,而是将存储在 bytearray 中的对象转换为 .NET 等效项。

注意: 我对在 .NET 中实现属性包的功能不感兴趣。仅能够读取已保存的 VB6 属性包的字节。

.net vb.net vb6 propertybag
4个回答
3
投票

.NET 中对 PropertyBags 的支持为 0。对象的序列化取代了 PropertyBag 构造。

所以在我看来你有两个选择。

  1. 简要说明并编写 10 或 20 行 Interop dll。
  2. 分析序列化属性包的内容,看看是否可以对其进行解码并找出图像和流等的偏移量。

我知道我要去哪一个。


1
投票

您可以在他的旧VB6示例页面上查看Edanmo的将图片加载并保存到字节数组示例。它非常简单,可用于“序列化”任何实现 IPersistStream 接口的对象,例如 ADODB.Recordset。 VB6 的 PropertyBag 对对象使用 IPersistStream,并且可能实现“自定义”VT_Xxx 变体类型序列化。

顺便说一句,Edanmo 的代码片段允许您读取通过复制/粘贴到访问网格的图像列存储的图像。


1
投票

值得指出的是,您可以通过在项目中引用 VB6 运行时 DLL 来直接从 .NET 访问 VB6 PropertyBag。

添加对以下内容的引用:

C:\WINDOWS\SysWow64\MSVBVM60.DLL
(或者可能在 System32 文件夹中)

然后您将在对象浏览器中看到PropertyBag:

请注意,这个 VB6 类不具有读取/写入磁盘文件、字节数组等的功能。它仅管理内存中的数据(AFAIK)。因此,仅此并不能解决如何以 PropertyBag 存储的任何格式保存/加载数据的问题。


0
投票

嗯,我尝试添加对建议的 C:\WINDOWS\SysWow64\MSVBVM60.DLL 的引用,但在对象浏览器中没有得到任何PropertyBag。也许我做错了什么,但我想解析属性包二进制数据。

我已经分析了属性包的结构,并且有了部分实现。有一些东西我不知道它们代表什么(我想这可能是某种校验和,但看起来不像)。

这是我读取 VB6 属性包的实现:

public class PropertyBag
{
    private Dictionary<string, object> _values;

    public PropertyBag(byte[] data)
    {
        _values = new Dictionary<string, object>();

        if (data != null && data.Length > 12
            && data[0] == 147 && data[1] == 178)
        {
            int offset = 4;
            int totalSize = BitConverter.ToInt32(data, offset);

            if (data.Length != totalSize)
                throw new IndexOutOfRangeException("The PropertyBag total size does not match the actual data size");

            offset += 4;

            while (data.Length > offset + 4)
            {
                PropertyBagValueType valueType = (PropertyBagValueType)BitConverter.ToUInt16(data, offset);
                offset += 2;

                var valueNameLen = BitConverter.ToUInt16(data, offset);
                offset += 2;
                string valueName;

                offset += 8; // Some data I don't understand

                valueName = UnicodeEncoding.Unicode.GetString(data, offset, valueNameLen * 2);
                offset += valueNameLen * 2; // Unicode take 2 bytes per char
                int valueLen = 0;

                switch (valueType)
                {
                    case PropertyBagValueType.Bool:
                        valueLen = 2;
                        if (data[offset] == 0)
                            _values.Add(valueName, false);
                        else
                        {
                            Debug.Assert(data[offset] == 255);
                            _values.Add(valueName, true);
                        }
                        break;
                    case PropertyBagValueType.Byte:
                        valueLen = 2;
                        _values.Add(valueName, data[offset]);
                        break;
                    case PropertyBagValueType.Int16:
                        valueLen = 2;
                        _values.Add(valueName, BitConverter.ToInt16(data, offset));
                        break;
                    case PropertyBagValueType.Int32:
                        valueLen = 4;
                        _values.Add(valueName, BitConverter.ToInt32(data, offset));
                        break;
                    case PropertyBagValueType.Single:
                        valueLen = 4;
                        _values.Add(valueName, BitConverter.ToSingle(data, offset));
                        break;
                    case PropertyBagValueType.Double:
                        valueLen = 8;
                        _values.Add(valueName, BitConverter.ToDouble(data, offset));
                        break;
                    case PropertyBagValueType.Currency:
                        valueLen = 8;
                        var i64Value = BitConverter.ToInt64(data, offset);
                        _values.Add(valueName, i64Value / 10000.0);
                        break;
                    case PropertyBagValueType.String:
                        valueLen = BitConverter.ToInt32(data, offset) * 2; // Unicode
                        offset += 4;
                        _values.Add(valueName, UnicodeEncoding.Unicode.GetString(data, offset, valueLen));
                        break;
                    case PropertyBagValueType.ByteArray:
                        offset += 2; // The value in the current offset seem to be 1. It might be the size per item in the array. Other array types are not seem to be supported
                        valueLen = BitConverter.ToInt32(data, offset);
                        offset += 4;
                        offset += 4; // I have no idea why extra 4 bytes padding. Maybe it's for supporting arrays with size larger than 4GB, but VB is 32 bit, so it can't support more than 4GB in RAM
                        _values.Add(valueName, data.Skip(offset).Take(valueLen).ToArray());
                        break;
                    case PropertyBagValueType.Date:
                        valueLen  = 8;
                        /* A Date is stored as an IEEE 64-bit (8-byte) floating point number, just like a Double
                        Digits to the left of the decimal point (when converted to decimal) are interperated as a date between 1 January 100 and 31 December 9999. Values to the right of the decimal point indicate time between 0:00:00 and 23:59:59.
                        The date section of the Date data type (before the decimal point), is a count of the number of days that have passed since 1 Jan 100, offset by 657434. That is, 1 Jan 100 is denoted by a value of -657434; 2 Jan 100 is denoted -657433; 31 Dec 1899 is 1; 1 Jan 2000 is 36526, and so on.
                        The time section of the date (after the decimal point) is the fraction of a day, expressed as a time. For example, 1.5 indicates the date 31 Dec 1899 (as above) and half a day, i.e. 12:00:00. So, an hour is denoted by an additional 4.16666666666667E-02, a minute by 6.94444444444444E-04, and a second by 1.15740740740741E-05.

                        Taken from: https://www.codeguru.com/visual-basic/how-visual-basic-6-stores-data/
                        */
                        var num = BitConverter.ToDouble(data, offset);
                        DateTime dateTime = new DateTime(1899, 12, 30);
                        var days = Math.Floor(num);
                        var seconds = (num - days) * (3600 * 24);

                        dateTime = dateTime.AddDays(days);
                        dateTime = dateTime.AddSeconds(seconds);
                        _values.Add(valueName, dateTime);
                        break;
                    default:
                        throw new NotSupportedException("Unsupported value type");
                }

                offset += valueLen;

                if (offset % 4 != 0)
                    offset += 4 - offset % 4;
            }

        }
    }

    public IEnumerable<string> Keys
    {
        get
        {
            return _values.Keys.ToList();
        }
    }

    public object ReadValue(string key, object defaultValue = null)
    {
        if (string.IsNullOrEmpty(key)) throw new ArgumentNullException("key");

        var keyToUse = key.ToLowerInvariant();
        if (_values.ContainsKey(keyToUse))
            return _values[keyToUse];

        return defaultValue;
    }
}

internal enum PropertyBagValueType
{
    Bool = 11,
    Byte = 17, // 0x11
    Int16 = 2,
    Int32 = 3,
    Single = 4,
    Double = 5,
    Currency = 6,
    String = 8,
    Date = 7,
    ByteArray = 8209, //0x2011
}

如您所见,我能够读取大多数类型的值,包括日期和时间。 我不确定值类型1、9、10、12-16是否被占用。 我原本假设 1 将用于位/布尔值,但事实并非如此,并且 Byte 的枚举高于 Int16 和 Int32

由于 PropertyBag 不支持内部属性包、类和集合,所以我不确定我还没有涵盖哪些其他类型。

还有什么要补充的吗? (就像我错过的类型?)

© www.soinside.com 2019 - 2024. All rights reserved.