I want to serialize blockreference handle in an xml file and (its properties). So I store this value at the initialization :
blockReference.ObjectId.Handle.Value; // decimal value = 10658
But when I select the blockReference in AutoCAD the handle has changed.
private void database_ObjectModified(object sender, ObjectEventArgs e)
{
long currentId = e.DBObject.ObjectId.Handle.Value; // Now it's 10659 !!!!
...
}
Do I use handle no correctly ?
Use theEntityObj.Handle.Value
If you get if from the ObjectId, it will change when you close/open a drawing.
Note a Handle can change in some cases, for instance, if you have a LINE inside a block, if you BEDIT the block, the HANDLES will change.
When you use the Handle in the XML file you need to get the ObjectId by its .Handle and then use ObjectId that is valid only in the current session to read/write the object. See http://through-the-interface.typepad.com/through_the_interface/2007/02/getting_access_.html
Look for the Database.GetObjectId() method in the SDK docs (the managed interface CHM file). This is it in a nutshell...
public ObjectId GetObjectId(
[MarshalAs(UnmanagedType.U1)] bool createIfNotFound,
Handle objHandle,
int identifier
);
[MarshalAs(UnmanagedType.U1)] bool createIfNotFound Input Boolean indicating to create a objectId stub if input handle is not found
Handle objHandle Input Handle object containing the handle being passed in
int identifier Reserved for future use
Related
I am using System.IO.BACnet library for my C# BACNet client.
I am trying to send to server "ReadPropertyMultiple" request, however I am unable to read non-array BACNet properties, since the System.IO.BACnet.BacnetClient.ReadPropertyMultipleRequest requires list of System.IO.BACnet.BacnetPropertyReference(s) ...
public bool ReadPropertyMultipleRequest(BacnetAddress address, BacnetObjectId objectId, IList<BacnetPropertyReference> propertyIdAndArrayIndex, out IList<BacnetReadAccessResult> values, byte invokeId = 0);
.. and the System.IO.BACnet.BacnetPropertyReference requires propertyArrayIndex ..
public struct BacnetPropertyReference
{
public uint propertyIdentifier;
public uint propertyArrayIndex;
public BacnetPropertyReference(uint id, uint arrayIndex);
public BacnetPropertyIds GetPropertyId();
public override string ToString();
}
.. which, when not set, defaults to 0. This causes, that after request with this list is sent, all properties are requested with propertyArrayIndex: 0, which fail for non-array object properties.
Example:
Request:
Response:
What is the right way to not add the propertyArrayIndex into request and thus be able to read non-array properties with ReadPropertyMultiple request?
If you request array-index "0" you should (in theory) receive the number of (following) elements, and not the full list of values (/remaining elements containing values).
Can't you use (the) 'Read-Property' (service) for non-array items (?).
The solution is to set propertyArrayIndex = uint.MaxValue.
For example:
BacnetPropertyReference reff = new BacnetPropertyReference((uint) BacnetPropertyIds.PROP_PRESENT_VALUE, uint.MaxValue);
So, I want to save object and then load it and take data from it. I made an class called SaveData, in there I have field isVibrationOn.
Working code below:
public class SaveData
{
public bool isVibratonOn;
}
Here is the code for serialization:
public void SaveGame()
{
SaveData saveData = new SaveData();
BinaryFormatter bf = new BinaryFormatter();
FileStream file = File.Create(Path.Combine(Application.persistentDataPath, FILE_NAME));
SaveData(saveData);
bf.Serialize(file, saveData);
file.Close();
}
private void SaveData(SaveData saveData)
{
saveData.isVibrationOn = VibrationController.controller.isVibrationOn;
}
And here is code for load data:
public void LoadGame()
{
if (File.Exists(Path.Combine(Application.persistentDataPath, FILE_NAME)))
{
FileStream file = File.Open(Path.Combine(Application.persistentDataPath, FILE_NAME), FileMode.Open);
if (file.Length > 0)
{
SaveData saveData = new SaveData();
BinaryFormatter bf = new BinaryFormatter();
saveData = (SaveData)bf.Deserialize(file);
LoadData(saveData);
file.Close();
}
}
}
public LoadData(SaveData saveData)
{
VibrationController.controller.isVibrationOn = saveData.isVibrationOn;
}
My question here is, when I decide to add or remove some fields (lists etc) to SaveData object, my LoadData would look different, but object that is saved on device whould have different fields as well. Simple expample
public LoadData(SaveData saveData)
{
VibrationController.controller.isVibrationOn = saveData.isVibrationOn;
//old save data doesn't have isMusicOn field
//LoadData method is different because I added new field on SaveData object after I saved file.
//This is simple example, but also it could be any changes like list of objects with an object that has different fields added/changed.
MusicController.controller.isMusicOn = saveData.isMusicOn;
}
How would I check if old instance has that field?
As a general recommendation, do not use binaryFormatter. It is slow, inefficient, unsafe and has poor backwardscompatibility.
So if you change the class I would not expect it to be possible to de serialize older data at all, let alone tell you what fields where missing. Switching .net versions can also be an issue with binaryformatter.
There are much better serialization libraries out there. Json.net is the standard for text-based serialization, and I have used protobuf.net for binary serialization. But there are many other libraries that can be used.
To handle missing or optional fields you would typically have some default value, like null, that you can check. It should also be possible to initialize the fields to some other default value if desired.
I would recommend separating your serialization objects from your domain objects, since serialization frameworks may require parameter less constructors or public setters. And separate serialization objects provide a chance to manage differences in object structures between versions.
If your goal is to make sure your code doesn't break because of "missing" field:
If you already have the "old" version rolled out, that's tough - you will have to implement some kind of "migration" from old data to new, probably by keeping the old class as-is, implementing your changes in a new class (possibly derived from old, to keep code duplication to a minimum), and then checking if the data you are deserializing is old (I'll refer to it as "MyClass_Old") or new ("MyClass_New"). If you are able to determine that from some metadata, file attributes or such - great. If not, you could just deserialize it as MyClass_New on purpose and wrap in in try-catch. If you caught a SerializationException, then it's probably MyClass_Old, and then you deserialize it as MyClass_Old and then use it to construct a new MyClass_New instance.
However, if you didn't yet roll out those changes, you can make use of version-tolerant serialization. You can use attributes like [OptionalFieldAttribute] to mark fields that might be missing in different version, [OnDeserializingAttribute] (that goes on method that will be called before deserializing - to maybe set some values in those missing fields), and [OnDeserializedAttribute] to "fix" or validate your deserialized object.
So for your example, knowing that you added the isMusicOn field, you'd mark it as optional (because it might be missing in deserialized data):
[OptionalField(VersionAdded = 2)]
bool isMusicOn;
and then set it to some kind of default value if it's missing. Let's say you want it to be on by default:
[OnDeserializing]
internal void OnDeserializingMethod(StreamingContext context)
{
isMusicOn = true;
}
Note that here you use the "deserializing" attribute, because you do want to keep the value if it is not missing. Since OnDeserializingMethod is called before deserializing, it will be overwritten by deserialized value if it is present. This specific case (you have a field that might be missing, and you want it to have some specific value) is also covered here.
If your goal is to check if this specific field was missing in deserialized object:
With some preparation this same principle (version-tolerant serialization) can also be used to specifically determine if the field was present in deserialized object. You can set it to some predetermined value (one that would not be allowed when serializing) before deserializing (using [OnDeserializing]). Then after deserialization check if that value is still there or it was replaced by something reasonable.
So in your case let's say that allowed values for isMusicOn are true and false, then you can make your isMusicOn a nullable bool:
[OptionalField(VersionAdded = 2)]
bool? isMusicOn;
Then you'll make sure that serialized object cannot have isMusicOn set to null, by using [OnSerializing]:
[OnSerializing()]
internal void OnSerializingMethod(StreamingContext context)
{
if (isMusicOn == null)
isMusicOn = false;
}
Then assign null in a method marked with [OnDeserializing]:
[OnDeserializing]
internal void OnDeserializingMethod(StreamingContext context)
{
isMusicOn = null;
}
and then in [OnDeserialized] check if it is still null (if it was deserialized, it will have changed to proper value like true or false, if not it'll stay null):
[OnDeserialized]
internal void OnDeserializedMethod(StreamingContext context)
{
Console.WriteLine($"isMusicOn {isMusicOn == null ? "wasn't" : "was" } present in deserialized object!");
if (isMusicOn == null)
isMusicOn = false;//set it to some "proper" default value;
}
In this example, actually, nullable value wouldn't even require the setup (it'll be null by default even without the OnDeserializing part), but i'll leave it in as an example.
All this last part is probably more trouble then it's worth. Besides all the hassle with attributes, you have to change your field type to allow for that "super special value", and that might require you to change a lot of other code that depends on it. I suspect the real question is not "how to check if the field was there", but rather "how to make sure my app doesn't break because it wasn't there", and for that you don't need to know if your field was deserialized - just to make sure that it has some reasonable value in it after deserialization.
I am trying to write a MongoDb serializer in c# that will allow me to decorate properties via a [Encrypt()] attribute and then at runtime it would allow me to generate an additional property called PropertyName_Encrypted which would contain the encrypted value.
On deserialization, the encrypted property value would be set in the parent property so that the default GET for the property always returns the encrypted value. Users will then call an optional Decrypt() method on the object to get decrypted values.
In doing so, I'm running into some interesting challenges:
How do I add Additional properties to the document when I am serializing current Element? How do I get the current element's name?
Is there a way I can read a specific property from the document/object? For e.g. say I want to pass a symmetric encryption key and read that to encrypt the data while serializing the current element? Is there any way I can do that?
Here are things I have done so far:
I've built an Encrypt Attribute as follows:
[AttributeUsage(AttributeTargets.Property)]
public class EncryptAttribute : Attribute
{
private readonly EncryptedFieldType _fieldType;
private readonly bool _tokenizeDisplay;
private readonly string _encryptedFieldName;
/// <summary>
///
/// </summary>
/// <param name="fieldType">The field type to encrypt. Useful if display needs to show some formatting. If no formatting is necessary, simply set to "Other".</param>
/// <param name="tokenizeDisplay">If set to true, will persist the tokenized value in the original field for display purposes.</param>
/// <param name="encryptedFieldName">Optional. If set, will save the encrypted value in the field name specified. By default all encrypted field values are stored in the corresponding _Encrypted field name. So EmailAddress field if encrypted, would have value under EmailAddress_Encrypted.</param>
public EncryptAttribute(EncryptedFieldType fieldType, bool tokenizeDisplay, string encryptedFieldName = "")
{
_fieldType = fieldType;
_tokenizeDisplay = tokenizeDisplay;
_encryptedFieldName = encryptedFieldName;
}
}
I read this Attribute on Startup and add an Encryption Serializer to the properties that are decorated using this attribute. The code that does that is like so:
var assemblies = AppDomain.CurrentDomain.GetAssemblies()
.Where(x => x.FullName.StartsWith("MongoCustomSerializer"))
.ToList();
var mapper = new Mapper();
foreach (var assembly in assemblies)
{
mapper.Map(assembly);
}
The mapper simply checks which properties in the document have the Encrypt attribute to add the serializer:
public sealed class Mapper
{
public void Map(Assembly assembly)
{
var encryptableTypes = assembly.GetTypes().Where(p =>
typeof(IEncryptable).IsAssignableFrom(p) && p.IsClass && !p.IsInterface && !p.IsValueType &&
!p.IsAbstract).ToList();
if (encryptableTypes.Any())
{
foreach (var encryptableType in encryptableTypes)
{
Map(encryptableType);
}
}
}
private void Map(Type documentType)
{
var properties =
documentType.GetProperties(BindingFlags.Public | BindingFlags.Instance);
if (properties.Length <= 0)
{
return;
}
foreach (var property in properties)
{
RegisterEncrpytionSerializer(property, typeof(EncryptAttribute), documentType);
}
}
private void RegisterEncrpytionSerializer(PropertyInfo property, Type encryptAttributeType, Type documentType)
{
var encryptAttributes = property.GetCustomAttributes(encryptAttributeType, false).ToList();
if (!encryptAttributes.Any()) return;
var memberMap = BsonClassMap.LookupClassMap(documentType).GetMemberMap(property.Name);
memberMap?.SetSerializer(new EncryptionSerializer());
}
}
In my unit tests, I'm getting an error stating that the Bson Class Map is already frozen. Even if I were to figure out a way to bypass that, how would this EncryptionSerializer class work to where I could write an additional property?
Would love to see if someone can assist!
UPDATE 1 - I was able to get the FREEZE error taken care of. It would appear that the LookupClassMap freezes the Member and Class Map info.
This change from the link allows me to take care of that issue:
private void RegisterEncrpytionSerializer(PropertyInfo property, Type encryptAttributeType, Type documentType)
{
var encryptAttributes = property.GetCustomAttributes(encryptAttributeType, false).ToList();
if (!encryptAttributes.Any()) return;
var classMapDefinition = typeof(BsonClassMap<>);
var classMapType = classMapDefinition.MakeGenericType(documentType);
var classMap = (BsonClassMap)Activator.CreateInstance(classMapType);
classMap.AutoMap();
var memberMap = classMap.GetMemberMap(property.Name);
memberMap?.SetSerializer(new KeyVaultEncryptionSerializer(memberMap.ElementName));
}
Are you using a service for saving/retrieving your items that actually call the DB?
I believe you should move the responsibility for writing/reading encrypted values to the calling service (i.e a repository implementation) instead of the BsonSerializer.
It would make sense to me that encryption/decryption is part of the persistence layer and something not handled in the application when needed.
Your implementation targets only the specified property you want to serialize. It doesn't make sense that it creates another property.
A second thought is that your suggested approach with properties that change value based on Decrypt() probably isn't a good idea since it makes your code unpredictable and hard to read. Make your properties dead simple.
What extra security in your code does it really give you if you can decrypt properties by just calling a method anyway?
If you still need to have a Decrypt() would suggest that you create methods for decrypting that return the decrypted value like GetUnencryptedCode() etc, it could just as well be an extension method but still not a readable property.
You should also be looking into using SecureString depending on your use case.
All, I am localising my C# application and I have come to my settings control. As I am using a PropertyGrid I am using properties to provide the options. I have a CategoryAttribute for "Server Access" I need to show in different languages - I had:
[CategoryAttribute("ServerAccess"),
ReadOnlyAttribute(false),
XmlElement,
DescriptionAttribute("Message about the Server Access Option.")]
public bool ShowSystemDatabases
{
get { return this.showSystemDatabases; }
set { this.showSystemDatabases = value; }
}
and I attempted to change this to:
[CategoryAttribute(ObjectStrings.SettingsServerAccess),
ReadOnlyAttribute(false),
XmlElement,
DescriptionAttribute(MessageStrings.SettingsShowSystemDatabaseInfo)]
public bool ShowSystemDatabases
{
get { return this.showSystemDatabases; }
set { this.showSystemDatabases = value; }
}
Where ObjectStrings is my resource file ('ObjectStrings.resx' et al.). This is throwing a compile-time error
An attribute argument must be a constant expression, typeof expression or array
creation expression of an attribute parameter type...
clearly I can't use a string when the constructor is expecting a const string. I have tried casting from string to const string using various round-the-houses method but all have failed. it is a simple enough issue, but...
What is the easiest way around this problem?
Thanks for your time.
I don't know how you could handle this especially in the case of control's attributes, but a general way to achieve this is to make Resources resolved at runtime, using parameters like these on your attribute :
MessageResourceType = typeof (MyResource), MessageResourceName = "MyResourceKey")
instead of directy passing the pointer to resource key.
I don't know if there is an equivalent way for CategoryAttribute, DescriptionAttribute and others Controls' properties attributes, or if there is a way to overload them.
Lets say we have a native library that works with data like this:
double *pointer = &globalValue;
SetAddress(pointer);
//Then we can change value and write it to disk
globalValue = 5.0;
FlushValues(); // this function writes all values
// registered by SetAddress(..) functions
... //Then we change our value (or values)
FlushValues(); //and do write any time we need
Now I have to interop it to C#. I would like to avoid using unsafe... but... I dont know =)
So on C# side we will have some class wich fields we will write. And we can do writing like:
public class Data
{
[Serializable] <-- somehow we mark what we are going to write
double myValue;
...
[Serializable]
double myValueN;
}
public class LibraryInterop
{
IntPtr[] pointers; //Pointers that will go to SetAddressMethod
...
public void RegisterObject(Data data)
{
... //With reflection we look for [Serializable] values
//create a pointer for each and some kind of BindingInfo
//that knows connection of pointers[i] to data.myValueN
//Then add this pointers to C++ library
foreach(IntPtr pointer in pointers) { SetAddress(pointer);}
}
public void Flush()
{
//Loop through all added pointers
for(int i=0; i<pointers.Length; i++)
{
double value = ... //With reflections and BindingInfo we find data.myValueN
// that corresponds to pointers[i]
// then we write this value to memory of IntPtr
// we have to brake value to bytes and write it one by one to memory
// we could use BitConverter.DoubleToInt64Bits() but double - is just
// an example, so in general case we will use GetBytes
int offset = 0;
foreach(byte b in BitConverter.GetBytes(value))
{
Marshal.WriteByte(pointer[i],offset,byte);
offset++;
}
}
//Save values of IntPtr to disk
FlushValues();
}
}
Then the user code looks like this then
var library = new LibraryInterop();
var data1 = new Data();
var data2 = new AnotherData();
library.RegisterObject(data1);
library.RegisterObject(data2);
...//change data
library.Flush();
...//change data
library.Flush();
...//change data
library.Flush();
So in C++ we have a very neat structure. We have pointers, we fill data behind this pointers and on FlushValues all this values are writed.
C# part cannot have SetAddress(ref double value). Since address may change, we have to pin pointers - use unsafe+fixed or IntPtr, and have SO many headache.
On the other hand, we can have "managed pointers" by boxing|unboxing data.myValue to Object. So if it would be possible to somehow bind this ValueType data.myValue to IntPtr without this coping and reflection - it would be much much neater.
Is it possible to do this interop and have less ugly and slow C# part then the one I listed here?
(There are some major downsides to this...)
You can use GCHandle.Alloc(data1, GCHandleType.Pinned) to "pin" an object, and then get an IntPtr from GCHandle.AddrOfPinnedObject. If you do this, you'll be able to pass this IntPtr to your native code, which should work as expected.
However, this is going to cause a lot of issues in terms of undermining the efficiency of the garbage collector. If you decide to do something like this, I'd recommend allocating all of the objects very early on in your program (potentially immediately after calling a GC.Collect(), which is something I really don't normally recommend), and leaving them alone for the lifetime of your program. This would (slightly) mitigate the GC issues, as it'd allocate them into the "best" possible spot early on, and leave them where they'll only be touched in Gen2 collections.