Castle Windsor InternalsVisibleTo Silverlight
Asked Answered
F

3

7

I'm using Castle Windsor for SL v2.5.1.0. I have it proxy internal classes (the interfaces are public of course, but the implementation is internal, so that the consumer is only aware of the interface).

I'm using the following attributes in my assembly with the internal classes

[assembly: InternalsVisibleTo("Castle.Core, PublicKey=002400000480000094000000060200000024000052534131000400000100010077F5E87030DADCCCE6902C6ADAB7A987BD69CB5819991531F560785EACFC89B6FCDDF6BB2A00743A7194E454C0273447FC6EEC36474BA8E5A3823147D214298E4F9A631B1AFEE1A51FFEAE4672D498F14B000E3D321453CDD8AC064DE7E1CF4D222B7E81F54D4FD46725370D702A05B48738CC29D09228F1AA722AE1A9CA02FB")]
[assembly: InternalsVisibleTo("Castle.Windsor, PublicKey=002400000480000094000000060200000024000052534131000400000100010077F5E87030DADCCCE6902C6ADAB7A987BD69CB5819991531F560785EACFC89B6FCDDF6BB2A00743A7194E454C0273447FC6EEC36474BA8E5A3823147D214298E4F9A631B1AFEE1A51FFEAE4672D498F14B000E3D321453CDD8AC064DE7E1CF4D222B7E81F54D4FD46725370D702A05B48738CC29D09228F1AA722AE1A9CA02FB")]
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]

In full .NET 4.0 mode, with the .NET 4.0 Castle assemblies, this works fine and my types are proxied OK. In Silverlight, with the Silverlight Castle assemblies, I get:

Type ConsoleApplication4.MyTypeToBeProxied is not public. Can not create proxy for types that are not accessible.

Also, just in troubleshooting the problem, adding the following seems to make no difference...:

[assembly: InternalsVisibleTo("System.Core, PublicKey=00000000000000000400000000000000")]
[assembly: InternalsVisibleTo("System.Core, PublicKey=" +
"00240000048000009400000006020000002400005253413100040000010001008d56c76f9e8649" +
"383049f383c44be0ec204181822a6c31cf5eb7ef486944d032188ea1d3920763712ccb12d75fb7" +
"7e9811149e6148e5d32fbaab37611c1878ddc19e20ef135d0cb2cff2bfec3d115810c3d9069638" +
"fe4be215dbf795861920e5ab6f7db2e2ceef136ac23d5dd2bf031700aec232f6c6b1c785b4305c" +
"123b37ab")]

and I've also verified at runtime that the name of the dynamically hosted assembly in SL is still in fact DynamicProxyGenAssembly2.

Any ideas? Thanks.

EDIT:

I found the problem I think:

Castle for .NET 4.0 has:

private bool IsAccessible(Type target)
{
  //      ....
  return ((target.IsPublic || target.IsNestedPublic) || internalAndVisibleToDynProxy);

}

in the DefaultProxyBuilder...and SL 4 has

private bool IsAccessible(Type target)
{
    target.IsNested();
    return (target.IsPublic || target.IsNestedPublic);
}

Is this something that can be fixed in the Castle source? Or do I need to/should I sub-class the DefaultProxyFactory?

Fleurdelis answered 11/11, 2010 at 16:11 Comment(0)
F
2

I had some luck with this. To be honest, I'm not sure why, but I couldn't reproduce the problem described by Krzysztof. I suspect...maybe...it has something to do with the fact that my assemblies are SN'd...which required me to make an additional change...but once I did, I was able to get proxies resolved for internal classes (with public interfaces) in a SL test application.

The only change I had to make to the Castle.Core source was to make the fields ModuleScope.moduleBuilder and ModuleScope.moduleBuilderWithStrongName protected instead of private. But again, that was only necessary so that I could define a SN'd dynamic assembly in SL, which is disabled for SL by the ModuleScope in Castle.Core. So, now I have a custom ModuleScope as follows:

    private class StrongNameModuleScope : ModuleScope
    {
        public StrongNameModuleScope()
        {
            var assemblyName = new AssemblyName("DynamicProxyGenAssembly2");
            // copied from another one of my SN assemblies (plus GetName() on assembly is security critical so I can't pull it off the executing assembly)
            byte[] publicKey = Convert.FromBase64String(@"ACQAAASAAACUAAAABgIAAAAkAABSU0ExAAQAAAEAAQBvwWquPXQG9zfemS8uDsFdGDScOCSjZ9aFsQDtrrAqKzvlxEGMz3t9Q9M3X9NKqy1ouLZi+sX8yVDafX+UnygFWWfOBosw9nGwG61MTKEhEjdKH0rECahGIXY+ETdNY64HduuH/BIbEs/RDhrrH2hiqGrOGb6AghD1sZ6g0A1qkg==");
            assemblyName.SetPublicKey(publicKey);
            AssemblyBuilder assembly = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
            ModuleBuilder module = assembly.DefineDynamicModule("DynamicProxyGenAssembly2");
            moduleBuilder = module;
            moduleBuilderWithStrongName = module;
        }
    }

And a custom DefaultProxyBuilder:

    /// <summary>
    ///   A custom IProxyBuilder copies from the full .NET Castle implementation that allows for proxies of internal types where the InternalsVisibleToAttribute is applied.
    /// </summary>
    private class DefaultProxyBuilder : IProxyBuilder
    {
       ...
        // Methods
        public DefaultProxyBuilder()
            : this(new StrongNameModuleScope())
        {
        }
       ...
        private static bool IsAccessible(Type target)
        {
            bool isTargetNested = target.IsNested;
            bool isNestedAndInternal = isTargetNested && (target.IsNestedAssembly || target.IsNestedFamORAssem);
            bool internalAndVisibleToDynProxy = ((!target.IsVisible && !isTargetNested) || isNestedAndInternal) && InternalsHelper.IsInternalToDynamicProxy(target.Assembly);
            return ((target.IsPublic || target.IsNestedPublic) || internalAndVisibleToDynProxy);
        }
    }

And a custom DefaultProxyFactory:

  /// <summary>
    ///   A simple DefaultProxyFactory to wrap the modified DefaultProxyBuilder.
    /// </summary>
    private class DefaultProxyFactory : global::Castle.Windsor.Proxy.DefaultProxyFactory
    {
        public DefaultProxyFactory()
        {
            generator = new ProxyGenerator(new DefaultProxyBuilder());
        }
    }

And the container setup:

        container = new WindsorContainer();

        container.Kernel.ProxyFactory = new DefaultProxyFactory();

I'm not so fond of having had to modify the Castle.Core sources, so I'd really like to hear your thoughts Krzysztof...maybe could you just make those fields protected if this solution doesn't work for other test cases?

Fleurdelis answered 17/11, 2010 at 1:11 Comment(4)
Hmm interesting - so I suppose the critical difference here may be that you sign the generated assembly with the same key as your own assemblies, whereas normally if DP generates strongly named assembly it is signed with Castle's key. So I'm guessing that perhaps rule is that you can use internal type from assembly A in assebly B if A has B as friend and both are signed with the same key.Revolutionist
Thanks for investigating it - put this in the issue tracker and we'll look into baking it in for vNext.Revolutionist
Also, regarding your first point, it may not be that using the same key has a relevance...rather that DP does not do ANY strong signing on its own in the SL version (it's hard coded to false for ShouldStrongName or some property like that)...presumably because SL doesn't have a StrongNameKeyPair class (msdn.microsoft.com/en-us/library/w58ww7se.aspx) ...but that didn't stop me from simply calling SetPublicKey and getting a strong named dynamic assembly anyway :) ....which may just be what makes the thing workFleurdelis
I finally had a minute and was able to produce an Access Denied exception by having a different public key on my dynamic Assembly than the one for the InternalsVisibleTo assembly.Fleurdelis
V
0

I may be totally off base here, but aren't you looking for IncludeNonPublicTypes()?

From the documentation:

Registering non-public types

By default only types visible from outside of the assembly will be registered. If you want to include non-public types, you have to start with specifying assembly first, and then call IncludeNonPublicTypes

container.Register(
    AllTypes.FromThisAssembly()
        .IncludeNonPublicTypes()
        .BasedOn<NonPublicComponent>()
);
Vermicular answered 11/11, 2010 at 17:41 Comment(2)
No, I'm doing my registration manually. The type's getting registered fine, it's just on proxy generation that it blows up (because of the code cited above).Fleurdelis
Hmmmm. Then I have no idea. You really can't make your classes public :P.Vermicular
R
0

The reason for that is that Silverlight security model does not allow us to build a proxy for an internal type, even with InternalsVisibleTo.

Revolutionist answered 11/11, 2010 at 21:52 Comment(3)
How would you suggest approaching this from a design standpoint then? I really don't want all of the internals of my code exposed to the consumer...only the interfaces... Thanks.Fleurdelis
Interesting side note...I did copy the DefaultProxyBuilder from .NET 4.0 into my code, then made my Windsor container with a dummy ProxyFactory that instantiates the generator field with a ProxyGenerator referring to my new DefaultProxyBuilder...and it works in my unit tests! But I guess it won't work when I try to actually run it under the SL runtime...Fleurdelis
yeah, it fails on Silverlight in Silverlight security sandbox, at least it did when I ran the tests. Feel free to try it out, if you find a solution I'm happy to bake it in.Revolutionist

© 2022 - 2024 — McMap. All rights reserved.