The serializable attribute will marshal classes across app domains by value, i.e. a copy will be made. To pass an object byref, it
must inherit from MarshalByRefObject to let the marshaler know that you want a proxy made to marshal the return values back to the
calling instance.
See if that works :)
--
Dave Sexton
dave@www..jwaonline..com
-----------------------------------------------------------------------
"MatthewRoberts" <mroberts_hm@hotmail.com> wrote in message news:1111622687.919382.302660@o13g2000cwo.googlegr oups.com...[color=blue]
> Howdy All,
>
> I am having difficulty with two-way communication across AppDomains in
> an attempt to dynamically script applications. Everything works as
> expected, except when using ByRef parameters.
>
> The below explanation is lengthy, but well worth the read. If you can
> help me, I'd gladly share this code which has greatly helped my
> development of extensible applications.
>
> I have developed an easy to use scripting engine for .NET that handles
> loading and unloading of a secondary AppDomain automatically. I call
> the object "SafeScriptingEngine", and it has properties for the source
> language, source code, and any needed references and imports. When its
> compile method is called, the code is compiled by the appropriate
> compiler, then it creates a new AppDomain, loads the compiled code into
> the AppDomain, and uses a Class Factory approach to return a well-known
> interface.
>
> This simple interface contains two functions called "MethodExists",
> which accepts a function name and a list of parameters then return a
> Boolean of whether the function exists, and "CallMethod", which again
> accepts a function name and a list of parameters then actually calls
> the function.
>
>
> Public Interface IRemoteLoader
> Function Invoke(ByVal methodName As String, ByVal parameters() As
> Object) As Object
> Function MethodExists(ByVal methodName As String, ByVal parameters()
> As Object) As Boolean
> End Interface
>
>
> So, an example of using this engine looks like:
>
>
> Dim sse As New SafeScriptingEngine()
> sse.Language = Languages.VisualBasic
> sse.SourceCode = "" & _
> "Public Function UpperString(ByVal s As String) As String" & _
> " Return s.ToUpper" & _
> "End Function"
> If sse.Compile() Then
> If sse.MethodExists("UpperString", New Object() { "" }) Then
> MsgBox(sse.CallMethod("UpperString", New Object() { "this
> string will be converted to uppercase" }))
> End If
> End If
>
>
> All of this works great. There are no memory leaks, and all compilers,
> AppDomains, and assemblies are unloaded as desired and expected. In
> fact, I designed the engine to be inheritable such that functions can
> be created that are strongly typed. As you can see, the default engine
> expects a non-strongly-typed array of Objects.
>
> To prove the usability of the engine, I created a "SimpleStringScript"
> which inherits from the "SafeScriptEngine". The "SimpleStringScript"
> accepts only the part of code that does the manipulation, such as
> "s.ToUpper" in the example above. In the background, it adds function,
> parameters, and Return statement. Also, with the "SimpleStringScript",
> there is no need for "MethodExists" that accepts parameters. Instead,
> it has a "StringMethodExists" function that accepts no parameters,
> which then calls "MethodExists" on the base "SimpleStringScript" with a
> String Object as the parameter. Likewise, there is no need to call
> "CallMethod" with a function name and Object array. Instead, it has a
> "CallStringMethod" function that take a String as a parameter, which in
> turn calls the base "CallMethod" with the appropriate function name and
> the String parameter encased in an Object array.
>
>
> Public Class SimpleStringScript
> Inherits SafeScriptingEngine
>
> Public Function CallStringScript(ByVal s As String) As String
> Public Function StringMethodExists() As Boolean
> ...
> End Class
>
>
> Again, all of this works great, just as desired and expected.
>
> The problem arises when you create a script that uses ByRef parameters.
> I would like a function that looks like:
>
>
> Public Sub MyCustomFunction(ByRef e as MyEventArgs)
> e.MyObject.MyProperty = "some value"
> End Sub
>
>
> I quickly discovered that any parameter being passed into another
> AppDomain must be Serializable, as do any objects that are referenced
> in its hierarchy. Otherwise, it will throw a SerializationException
> when you try to pass in the parameter. So, I changed all objects that
> are referenced in the EventArgs to be Serializable, and that solved the
> problem when calling. However, upon return to the calling AppDomain,
> the objects and values in "e" are still the same as before the call.
> None of the manipulations taking place in the script from the secondary
> AppDomain are carried back to the calling AppDomain.
>
> I figure this is because, as expected with the Serializable objects,
> the "e" is being serialized, sent across the AppDomain boundary, then
> deserialized. When it is deserialized, an entirely new object is
> created. Changes made are not sent back across the boundary simply by
> using ByRef arguments. Unfortunately, I'm not sure exactly how to solve
> this problem.
>
> I think I should be sending an Interface as my argument, rather than an
> object of MyEventArgs. As long as the Interface is well-known in both
> AppDomains, I think the scripting AppDomain should know what to call,
> and when it does, the call should be marshaled back to the primary
> AppDomain via the Interface. Basically, the Interface is just a pointer
> back to the actual object. Can anyone confirm this to be True or False?
>
> If False, please HELP!
>
> If True, then is it possible to simply wrap the MyEventArgs object into
> an Interface, such as "IMyEventArgs", then change the scripted function
> to accept the Interface rather than the object, then pass an Interface
> rather than an object into the method? Would that suffice, so long as
> both AppDomains are aware of the Interface and anything it references?
> Obviously, if the Interface referenced a type that is not loaded in the
> secondary AppDomain, then there would be problems because the script
> would not know how to use it.
>
> Or, do I have to wrap the EventArgs into an Interface, as well as wrap
> every object that it references into an Interface. Meaning, again using
> the example above, would I also need to create an "IMyObject"
> Interface?
>
> My workaround for the time being is to return the object being
> manipulated as the return value, that way, the new object (created from
> deserialization) is again serialized upon return. However, this
> requires the scripter to know that they need to return it. Using C#,
> that is no big deal because the compiler will tell you when there is
> nothing returned. However, in Visual Basic, if you fail to supply a
> return value, you simply get Nothing. But, since I will be writing the
> scripts, I'll deal with the workaround for now. But, I sure would like
> ByRef to work across AppDomains.
>
> Any suggestions?
>
> Thanks in advance,
> Matthew
>
> ___________________________________
> Matthew Roberts
> Framework Architect
> Business Process Solutions
> SOURCECORP
> Dallas, TX 75204
> matthewroberts (at) srcp (dot) com
>[/color]