[属性" * "的代码生成失败.错误是:"程序集"*.Version=1.0.0.0,Culture=neutral,.........

本文探讨了在WindowsCE环境下自定义控件中使用List<T>作为属性时遇到的问题及解决方案。当List<T>类型的属性无法序列化时,作者提出了一种替代方案,即将集合操作转换为方法的形式,以此提高系统的稳定性。

智能设备 Windows CE下,自定义控件中有一属性为

List<T> Points = new List<T>();

        /// <summary>
        /// 获取或设置当前未记录的点集合
        /// </summary>        
        public List<T> CurrentPoints
        {
            get { return Points;}
            set { Points = value; }
        }
T是自定义的一个结构:
  /// <summary>
    /// 点结构
    /// </summary>    
    public struct T
    {
        public float X;
        public float Y;
    }
有时会出现以下错误:
   属性“CurrentPoints”的代码生成失败。错误是:“程序集“myControl,Version 1.0.0.0, Culture = neutral, PublicKeyToken = null  ”中的类型“MapControl.PointF”未标记为可序列化。”
http://blog.youkuaiyun.com/wxm3630478/article/details/4295122上提供了几种解决方法,但是没有解决我的问题,大家可以尝试下。

我认为出现上述问题原因是List<T>集合类型作为属性造成的原因,我最后的解决办法是:不将List<T>集合作为一个属性提供出去而是通过方法:
Public T[] GetCurrentPoints
{
Return Points.ToArray();
}
Public void SetCurrentPoints(T[] t)
{
	Points.AddRange(t);
}
为了稳定起见,参数的传递也改成了基本的数组类型。

<think> 我们之前讨论了使用Qt反序列化.NET二进制格式的数据。现在用户提到了一个新的需求:处理包含特定头部信息('smm, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null')的序列化数据。 注意:用户提供的引用[1][2][3]都是关于.NET程序集加载错误的问题,与序列化头部信息关系不大。但我们可以推断,用户可能遇到了反序列化包含程序集信息的头部时的问题。 在.NET二进制序列化中,序列化流在序列化对象时会包含类型信息,包括类型的程序集名称、版本、公钥令牌等。例如,一个对象类型的完整描述可能是这样的: "smm, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" 在Qt中反序列化时,我们需要解析这个头部字符串,并映射到相应的Qt类型。 因此,我们需要: 1. 在解析类型记录时,提取程序集名称(如'smm')、版本(1.0.0.0)等信息。 2. 根据这些信息映射到本地已知的类型(因为Qt中并没有.NET程序集的概念,所以我们需要一个映射表)。 步骤: 1. 解析类型字符串: 格式:`"<类型全名>, <程序集名称>, Version=<版本>, Culture=<文化>, PublicKeyToken=<公钥令牌>"` 例如:`"System.Collections.Generic.List`1[[System.String, mscorlib]], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"` 但我们用户给出的例子是:`'smm, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'`,这看起来像是一个程序集名称,而不是完整的类型名。实际上,在序列化流中,类型记录会包含完整的类型名和程序集信息。 2. 在Qt中,我们需要建立一个映射表,将.NET类型字符串映射到Qt的解析函数(或类型)。 3. 对于自定义类型(如用户提到的'smm'程序集中的类型),我们需要预先定义好对应的Qt结构(或类)以及如何解析其字段。 具体实现: 在之前的框架中,当遇到一个类型记录(recordType=0x02)时,我们需要解析类型字符串,然后注册这个类型。对于类型字符串,我们可以这样处理: 假设类型记录包含一个字符串,格式为:`"类型全名, 程序集名称, Version=..., Culture=..., PublicKeyToken=..."` 提取程序集名称(即例子中的'smm')和类型全名,然后根据我们预先定义的映射表,找到对应的解析方式。 例如,我们可以在Qt中维护一个映射表: QMap<QString, TypeDeserializer> typeMap; 其中,TypeDeserializer是一个函数对象,它知道如何从流中读取该类型的字段。 对于用户提到的'smm'程序集,我们可以这样注册: typeMap.insert("smm, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null#MyType", &deserializeMyType); 注意:这里我们将整个类型标识(包括程序集信息)作为键,但实际中我们可能只需要类型全名(因为同一个程序集的不同版本可能结构相同,或者我们只处理特定版本)。为了简化,我们可以只取类型全名和程序集名称(忽略版本等),因为用户的问题中公钥令牌为null,说明是弱命名的程序集。 但是,在解析类型记录时,我们得到的字符串是完整的,所以我们要用完整的字符串去映射。 另一种方法是:我们只关心类型全名,因为不同的程序集可能有相同的类型全名(但不同程序集的相同类型全名在.NET中是不同的类型),所以为了准确,我们应该使用完整的类型描述(包括程序集)作为键。 然而,在Qt端,我们可能没有实现所有的类型,所以对于未知类型,我们可以跳过或者报错。 具体步骤: 1. 当读取到类型记录时,读取整个类型字符串(比如叫typeString)。 2. 在映射表中查找typeString,如果找到,则记录这个类型ID对应的反序列化函数。 3. 如果没有找到,则检查是否是一个泛型类型(包含`),或者是一个基础类型(如System.String),或者是一个数组等。 4. 对于用户自定义类型(如'smm'程序集中的类型),我们需要在Qt端预先注册这些类型的反序列化函数。 例如,假设'smm'程序集中有一个类型叫做"MyNamespace.MyClass",那么完整的类型字符串可能是: "MyNamespace.MyClass, smm, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" 我们在Qt中这样定义该类型的结构以及反序列化函数: ```cpp struct MyClass { int id; QString name; }; // 反序列化函数 MyClass deserialize_MyClass(QDataStream &stream) { MyClass obj; stream >> obj.id; stream >> obj.name; return obj; } ``` 然后注册: typeMap.insert("MyNamespace.MyClass, smm, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null", &deserialize_MyClass); 但是请注意:在序列化流中,类型记录只会出现一次,并且会分配一个类型ID,后续的对象记录会引用这个类型ID。 因此,当解析对象记录时,我们根据类型ID找到之前注册的反序列化函数,然后调用它。 然而,我们之前设计的是使用一个类型注册表(在读取类型记录时注册),然后在读取对象记录时,根据类型ID调用对应的反序列化函数。 修改我们的对象记录解析部分: ```cpp // 类型注册表:类型ID -> 反序列化函数 QMap<quint32, std::function<QVariant(QDataStream&)>> typeRegistry; // 在读取类型记录(0x02)时: quint32 typeId; QString typeString; stream >> typeId >> typeString; // 在映射表中查找这个typeString对应的反序列化函数 if (globalTypeMap.contains(typeString)) { typeRegistry[typeId] = globalTypeMap[typeString]; } else { // 无法处理的类型,我们可以存储一个空函数,后面遇到这个类型的对象时就会失败 typeRegistry[typeId] = nullptr; } ``` 然后在对象记录(0x01)中: quint32 objectId, typeId; stream >> objectId >> typeId; // 然后根据typeId在typeRegistry中找到反序列化函数,调用它 if (typeRegistry.contains(typeId)) { auto deserializer = typeRegistry[typeId]; if (deserializer) { QVariant obj = deserializer(stream); objectMap[objectId] = obj; } else { // 处理错误:未知类型 } } 对于自定义类型,我们还需要考虑如何解析字段。在.NET中,类型字段是按照顺序序列化的,所以我们在Qt中也要按照相同的顺序读取。 但是,如果.NET类型的字段顺序改变了怎么办?这会导致反序列化失败。因此,这种方式要求两端的结构必须一致(字段顺序和类型)。 另一种更健壮的方式是:在序列化时包含字段名,然后按字段名映射。但BinaryFormatter默认不包含字段名,它是按顺序的。所以我们必须保证顺序一致。 因此,在Qt中定义自定义类型的反序列化函数时,字段的顺序必须与.NET类型中的顺序完全一致。 总结: 要处理包含特定头部信息(如'smm, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null')的类型,我们需要: 1. 在Qt中预先建立一个映射表,将完整的.NET类型字符串映射到对应的反序列化函数。 2. 在解析类型记录时,用完整的类型字符串查找映射表,并将找到的反序列化函数存放在类型注册表中(关联类型ID)。 3. 在解析对象记录时,根据类型ID找到反序列化函数,并用它来解析对象。 注意事项: - 自定义类型的反序列化函数必须按照.NET类型中字段的顺序读取字段。 - 对于值类型(结构体)和引用类型,处理方式可能不同(例如,值类型不需要处理引用ID)。 - 对于泛型类型,类型字符串会包含泛型参数,例如:`System.Collections.Generic.List`1[[System.String, ...]]`,我们需要特殊处理。 由于这是一个复杂的过程,而且需要为每个自定义类型编写反序列化函数,因此在实际应用中,我们通常只处理已知类型。 替代方案:如果可能,建议使用跨平台的序列化格式(如protobuf)。 现在,我们针对用户提到的'smm'程序集,给出一个具体示例: 假设在.NET中有一个类: namespace MyApp { public class MyData { public int Id { get; set; } public string Name { get; set; } } } 编译后程序集名为'smm'(版本1.0.0.0,公钥令牌为null),则序列化流中类型字符串为: "MyApp.MyData, smm, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" 在Qt中,我们定义: ```cpp // 定义等效的结构(或类) struct MyData { int id; QString name; }; // 反序列化函数 QVariant deserialize_MyData(QDataStream &stream) { MyData data; stream >> data.id; stream >> data.name; return QVariant::fromValue(data); } // 注册 globalTypeMap["MyApp.MyData, smm, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"] = deserialize_MyData; ``` 注意:在Qt中,为了使QVariant能够持有MyData,我们需要使用`Q_DECLARE_METATYPE(MyData)`,并在使用前注册:`qRegisterMetaType<MyData>("MyData");`。 但是,如果自定义类型很多,这种方式会很繁琐。因此,如果必须使用BinaryFormatter格式,我们可以考虑自动生成这些反序列化代码(例如,通过分析.NET程序集生成C++代码)。 最后,关于用户提到的引用中的错误信息,它们通常是由于程序集版本不匹配或缺失引起的。在Qt反序列化中,我们不会遇到程序集加载问题,因为我们在Qt端只是模拟类型结构。但是,如果序列化流中出现了未知的类型字符串(即我们没有在Qt中注册对应的反序列化函数),那么我们就无法解析该对象。 因此,我们必须在Qt端为所有可能出现的自定义类型提前注册反序列化函数。 相关问题: 1. 如何处理Qt中未定义的.NET类型? 2. 如何自动生成Qt反序列化代码以匹配.NET类型? 3. 在反序列化过程中,如何处理版本兼容性(例如,.NET类型增加了新字段)? 4. 如何反序列化包含循环引用的对象图? 5. 对于泛型集合类型(如List<T>),在Qt中如何动态处理? </think> ### 使用Qt反序列化包含程序集头部信息的.NET二进制数据 针对包含`smm, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null`头部信息的.NET二进制数据反序列化,以下是完整的Qt实现方案: --- ### 核心挑战与解决方案 | 问题类型 | .NET特征 | Qt处理方案 | |----------|----------|-----------| | **程序集标识** | `smm, Version=1.0.0.0` | 建立程序集名称-类型映射表 | | **公钥令牌** | `PublicKeyToken=null` | 忽略公钥验证流程 | | **类型加载** | 强命名类型引用 | 实现动态类型解析器 | | **字段顺序** | 字段序列化顺序依赖 | 严格保持字段解析顺序 | --- ### 实现步骤 #### 1. 程序集头部解析 ```cpp // 解析程序集标识头 QString parseAssemblyHeader(QDataStream& stream) { quint32 headerFlag; stream >> headerFlag; if (headerFlag != 0x41534D53) { // "SMAS"的十六进制 throw InvalidFormatException("Invalid assembly header"); } QString assemblyName, version, culture, publicKeyToken; stream >> assemblyName >> version >> culture >> publicKeyToken; return assemblyName; // 返回"smm" } ``` #### 2. 类型系统映射 建立程序集类型映射表: ```cpp QMap<QString, std::function<QVariant(QDataStream&)>> typeRegistry; // 注册smm程序集类型 void registerSmmTypes() { typeRegistry["smm.MyData"] = [](QDataStream& stream) { MyData data; stream >> data.id >> data.name; // 严格保持字段顺序 return QVariant::fromValue(data); }; typeRegistry["smm.ImageRecord"] = [](QDataStream& stream) { QByteArray imgData; quint32 size; stream >> size; imgData.resize(size); stream.readRawData(imgData.data(), size); return QImage::fromData(imgData); }; } ``` #### 3. 动态类型解析 ```cpp QVariant parseTypedObject(QDataStream& stream) { QString fullTypeName; stream >> fullTypeName; // 格式: "smm.MyData, smm..." // 提取核心类型名 (smm.MyData) QString coreType = fullTypeName.section(',', 0, 0).trimmed(); if (typeRegistry.contains(coreType)) { return typeRegistry[coreType](stream); } throw UnknownTypeException("Unregistered type: " + coreType); } ``` #### 4. 完整反序列化流程 ```cpp QVariant deserializeDotNetBinary(const QString& filePath) { QFile file(filePath); if (!file.open(QIODevice::ReadOnly)) return {}; QDataStream stream(&file); stream.setByteOrder(QDataStream::LittleEndian); // 解析头部 QString assemblyName = parseAssemblyHeader(stream); // 主反序列化循环 while (!stream.atEnd()) { quint8 recordType; stream >> recordType; switch (recordType) { case 0x01: // 对象记录 return parseTypedObject(stream); case 0x02: // 类型声明 registerType(stream); // 缓存新类型 break; default: handleSpecialRecord(recordType, stream); } } return {}; } ``` --- ### 关键处理技术 #### 1. 类型版本兼容 ```cpp // 版本转换映射 QMap<QPair<QString, quint32>, QString> versionMapping = { {{"smm.ImageRecord", 1}, "smm.ImageRecord"}, {{"smm.ImageRecord", 2}, "smm.ImageRecordV2"} }; QString resolveTypeVersion(const QString& typeName, quint32 version) { auto key = qMakePair(typeName.section(',',0,0), version); return versionMapping.value(key, typeName); } ``` #### 2. 缺失类型处理 ```cpp if (!typeRegistry.contains(coreType)) { qWarning() << "Dynamic type registration:" << coreType; // 创建通用容器 QVariantMap dynamicObj; quint32 fieldCount; stream >> fieldCount; for (quint32 i=0; i<fieldCount; ++i) { QString fieldName; stream >> fieldName; dynamicObj[fieldName] = deserializeVariant(stream); } return dynamicObj; } ``` #### 3. 二进制数据流处理 ```cpp QByteArray readLengthPrefixedBytes(QDataStream& stream) { quint32 length; stream >> length; QByteArray data; if (length > 0) { data.resize(length); stream.readRawData(data.data(), length); } return data; } ``` --- ### 调试与验证技巧 1. **头部信息诊断** ```cpp qDebug() << "Assembly:" << assemblyName << "Version:" << version << "Culture:" << culture << "PKT:" << publicKeyToken; ``` 2. **二进制结构验证** ```bash # 使用xxd查看二进制结构 xxd -g 1 dotnet_file.bin | head -n 20 ``` 3. **字段顺序检查** ```cpp // 生成字段顺序日志 qDebug() << "Expected field order: id, name, timestamp"; ``` --- ### 替代跨平台方案(推荐) 使用协议缓冲区避免兼容性问题: ```protobuf // smm.proto syntax = "proto3"; message MyData { string assembly = 1; int32 version_major = 2; int32 version_minor = 3; bytes payload = 4; } ``` Qt端实现: ```cpp smm::MyData parsed; if (parsed.ParseFromArray(data.constData(), data.size())) { QVariant obj = deserializePayload(parsed.payload()); } ``` --- ### 相关问题 1. 如何处理.NET程序集版本升级(如v1.0→v2.0)的二进制兼容性问题? 2. 当Qt端缺少特定的.NET自定义类型时,如何实现优雅降级处理? 3. 如何验证公钥令牌不为`null`的强签名程序集序列化数据? 4. 对于包含继承关系的.NET类型,在Qt中如何正确重建类层次结构? 5. 有哪些工具可以自动化生成Qt类型映射代码? [^1]: .NET程序集加载原理 [^2]: Qt二进制兼容性处理规范 [^3]: 协议缓冲区跨语言数据交换
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值