Okay, after racking our brains against the wall here, we finally figured it
out.  It's an issue that can rear its head if you want to create a COM
object that dynamically loads assemblies (not strongly named), and/or uses
remoting and transparent proxies to cross an AppDomain.  

Here's the situation:  The assembly that's registered for COM had the three
types listed below.  The interface was used as the explicit COM interface
definition.  Those files were located in some random directory
(user-selected upon install or during development in the bin\debug dir).  I
did not, and do not, want to put any files (including a .config file) in the
application directory of the application calling the COM object.  

Here's why the problem occurs.  The AppBase of the DefaultDomain created by
the interop is set to the directory of the calling application.  Since my
assemblies were not in that directory (or a subdir), any call that needed to
read the assembly, for unwrapping a transparent proxy, or to load another
assembly dynamically, the system couldn't find the types.  In the case of
the transparent proxy, it searches the DefaultDomain's
AppBase/PrivateAppBase for the Assembly--even though the type is currently
present in the loaded assembly.  In the case of dynamically loading, that's
more straight-forward; it would be a security breach for an assembly to be
dynamically loaded outside of the AppDomain's paths.  That's one reason why
I wanted to create another domain to begin with, so that I could specify my
own AppBase and .config file.

The solution was as follows.  I put the COM object in the GAC, and that
solved the problem.  Since it was inside of the COM object that it creates
the proxy to my AppDomain, if it's in the GAC it'll find the type to unwrap
the proxy.  Granted, I didn't want to put it in the GAC, but that was the
only solution that worked.

What that left though was no dynamic way to determine where the rest of my
assemblies are to set the AppBase.  Before, I was using
GetExecutingAssembly().Location, but since it's now running in the GAC, that
doesn't help.  My temporary solution was to just set a registry key pointing
to the installed location of my files and have the COM assembly read that to
set the AppBase.  If anyone has any better ideas, I'm all ears.

This scenario would seem to apply not to just COM objects though.  If anyone
wanted to write an Add-In for a 3rd party application (one that supports
add-in's), I would think that best practices would be to put your add-in in
your own directory and not the application directory.  In that case, your
add-in would need to create its own AppDomain and call it via a proxy.  

Hope this post helps someone else in the future :)
--Oren


> -----Original Message-----
> From: Moderated discussion of advanced .NET topics. [mailto:ADVANCED-
> [EMAIL PROTECTED]] On Behalf Of Oren Novotny
> Sent: Tuesday, January 21, 2003 12:00 PM
> To: [EMAIL PROTECTED]
> Subject: InvalidCast with AppDomain.CreateInstanceAndUnwrap
> 
> I'm getting a strange InvalidCastException when I try to convert the =
> object
> I get from an AppDomain.CreateInstanceAndUnwrap call.  What I'm trying =
> to do
> is create a proxy object in my own AppDomain so that I can define my own
> AppBase and config file. =20
> 
> Now, the assembly is loaded, and I can tell because of the constructor
> outputs in the debug console, and I can get an "object" out of the
> CreateAndUnwrap.  But, if I try to cast it either to the implemented
> interface or even just the object itself, I get an InvalidCastException.
> 
> Any ideas?
> 
> Thanks,
> --Oren
> 
> 
> class TraceabilityAddIn : MarshalByRefObject, ITraceabilityAddIn
> {
> ...
> }
> 
> interface ITraceabilityAddIn { ... }
> 
> 
> class TraceabilityAddInProxy : ITraceabilityAddIn=20
> {
> ITraceabilityAddIn _AddIn;
> void Init() {
> // Get the path we're executing in
> string path =3D new
> System.IO.FileInfo(Assembly.GetExecutingAssembly().Location).DirectoryNam=
> e;
> 
> AppDomainSetup setup =3D new AppDomainSetup();
> setup.ApplicationBase =3D path;
> setup.ApplicationName =3D "Traceability AddIn";
> setup.ConfigurationFile =3D "Traceability.config";
> 
> AppDomain ad =3D AppDomain.CreateDomain("TraceabilityDomain" , null, =
> setup);
> 
> try
> {
>         _AddIn =3D
> (ITraceabilityAddIn)ad.CreateInstanceAndUnwrap(Assembly.GetExecutingAssem=
> bly
> ().FullName, typeof(TraceabilityAddIn).FullName);               =09
> =09
> // It doesn't matter whether I hard-code the assembly and type name or =
> not
> //_AddIn =3D
> (ITraceabilityAddIn)ad.CreateInstanceAndUnwrap("Traceability.AddIn",
> "Traceability.AddIn.TraceabilityAddIn");                =09
> 
> }
> catch(Exception e)
> {
>         // I get an exception--I shouldn't
>         Debug.WriteLine(e.Message);
>         Debug.WriteLine(e.StackTrace);
> }
> 
> }
> }
> 
> You can read messages from the Advanced DOTNET archive, unsubscribe from
> Advanced DOTNET, or
> subscribe to other DevelopMentor lists at http://discuss.develop.com.

You can read messages from the Advanced DOTNET archive, unsubscribe from Advanced 
DOTNET, or
subscribe to other DevelopMentor lists at http://discuss.develop.com.

Reply via email to