How do I use AppDomains and add search paths to it?

E

Eric Renken

I have an application that I am adding support for plug-ins and I am looking
for some help on the best way to handle this. My question has to do with
AppDomains and the best way to use them.

I have a directory structure like this:

MyApp
\AddIns
\AddIn1
\AddIn2

Part of my problem is that AddIn1 uses some of the assemblies that are
installed with MyApp and I don't want to duplicate those assemblies in the
AddIn1 or 2 directory. I don't want to put them in the GAC, so please don't
suggest that.

To support that now I am currently using this method:

AppDomain.CurrentDomain.AppendPrivatePath( <AddInPath> );

The problem with that is that this method is considered obsolete and I
really want to do this right. I have read that I should put the add-in a
separate AppDomain, but I am wondering if each AddIn should be in its own
AppDomain and if so what is the memory overhead of the AppDomain. These are
mostly very small add-ins and I don't want to kill the computer this is
running on.

I am also wondering if this is possible with my directory structure. I
really don't know much about creating AppDomains or what would be needed. I
hoping that with the AppDomain pointing to a DLL in MyApp\AddIns\AddIn1 that
I can also add the directory MyApp to its search path so that it can use the
same assemblies as the main application.

Can a search path be added that is higher than the main application for a
dynamically loaded AppDomain and a dynamically loaded DLL? Any Help would
be greatly appreciated on this.

Thanks,

Eric Renken
 
J

Jeffrey Tan[MSFT]

Hi Eric,

Can you show me your special requirement regarding the plug-in model?
Normally, there are 2 requirements for us to create a new AppDomain for the
plug-in assemblies:
1. Needs unloading support of plug-in assemblies
2. Needs a separate sandbox security environment to host the plug-in
assemblies

If you do not have these 2 requirements, I do not think you need to create
a separate AppDomain. You may just use Assembly.Load() method to load your
plug-in assemblies dynamically in your default AppDomain.
Yes, AppDomain.AppendPrivatePath() method is obsolete in .Net2.0, however,
you may set the probing path in the app.config file through <probing>
element. Please refer to the link below for more details:
http://msdn2.microsoft.com/en-us/library/823z9h8w.aspx

The link below documents the offical search paths of Assembly.Load method:
"How the Runtime Locates Assemblies"
http://msdn2.microsoft.com/en-us/library/yx7xezcf.aspx

If you really wanted to leverage the new AppDomain model to load the
plug-in assemblies into the separate AppDomain for security and unloading
benifit, I recommend the following good article written by CLR developer
"Shawn Farkas", it provided a complete steps and source code of adopting
this model.
"Discover Techniques for Safely Hosting Untrusted Add-Ins with the .NET
Framework 2.0"
http://msdn.microsoft.com/msdnmag/issues/05/11/HostingAddIns/default.aspx

In this scenario, you can not use the private probing path to specify the
"MyApp" directory for the new AppDomain, because private probing path must
be a sub-folder of the application base directory. However, you may set the
new AppDomain's ApplicationBase to the same folder("MyApp") as the main
AppDomain, so that the new AppDomain(plug-in assemblies) can locate the
assemblies in this folder.

Finally, using multiple AppDomains is not a big overhead. Asp.net leverages
the same model: if you create multiple web applications on a Asp.net web
site, it will create a separate AppDomain for each web application to
handle the request.

Hope this helps.

Best regards,
Jeffrey Tan
Microsoft Online Community Support
==================================================
Get notification to my posts through email? Please refer to
http://msdn.microsoft.com/subscriptions/managednewsgroups/default.aspx#notif
ications.

Note: The MSDN Managed Newsgroup support offering is for non-urgent issues
where an initial response from the community or a Microsoft Support
Engineer within 1 business day is acceptable. Please note that each follow
up response may take approximately 2 business days as the support
professional working with you may need further investigation to reach the
most efficient resolution. The offering is not appropriate for situations
that require urgent, real-time or phone-based interactions or complex
project analysis and dump analysis issues. Issues of this nature are best
handled working with a dedicated Microsoft Support Engineer by contacting
Microsoft Customer Support Services (CSS) at
http://msdn.microsoft.com/subscriptions/support/default.aspx.
==================================================
This posting is provided "AS IS" with no warranties, and confers no rights.
 
E

Eric Renken

I was reading online and most people recommended running them in a separate
AppDomain. I don't mind running them with just the AssemblyLoad, but the
problem I have with that is that I don't know the exact path of the AddIn
until my application is running, so I can't put this information in the
app.config file.

The real problem is that the AddIn might have its own dependent DLLs that
are installed with it in its directory and when it tries to load its
dependent DLLs the AddIn can't search its own directory.

I will probably look at least creating one AppDomain for the AddIns and I
was thinking along the lines of what you said and setting the
ApplicationBase to MyApp and then I believe there is a way to set search
paths to a new AppDomain. I didn't see that in "Shawn Farkas" document. Do
you have a good reference on how to do that?

Eric Renken

"Jeffrey Tan[MSFT]" said:
Hi Eric,

Can you show me your special requirement regarding the plug-in model?
Normally, there are 2 requirements for us to create a new AppDomain for
the
plug-in assemblies:
1. Needs unloading support of plug-in assemblies
2. Needs a separate sandbox security environment to host the plug-in
assemblies

If you do not have these 2 requirements, I do not think you need to create
a separate AppDomain. You may just use Assembly.Load() method to load your
plug-in assemblies dynamically in your default AppDomain.
Yes, AppDomain.AppendPrivatePath() method is obsolete in .Net2.0, however,
you may set the probing path in the app.config file through <probing>
element. Please refer to the link below for more details:
http://msdn2.microsoft.com/en-us/library/823z9h8w.aspx

The link below documents the offical search paths of Assembly.Load method:
"How the Runtime Locates Assemblies"
http://msdn2.microsoft.com/en-us/library/yx7xezcf.aspx

If you really wanted to leverage the new AppDomain model to load the
plug-in assemblies into the separate AppDomain for security and unloading
benifit, I recommend the following good article written by CLR developer
"Shawn Farkas", it provided a complete steps and source code of adopting
this model.
"Discover Techniques for Safely Hosting Untrusted Add-Ins with the .NET
Framework 2.0"
http://msdn.microsoft.com/msdnmag/issues/05/11/HostingAddIns/default.aspx

In this scenario, you can not use the private probing path to specify the
"MyApp" directory for the new AppDomain, because private probing path must
be a sub-folder of the application base directory. However, you may set
the
new AppDomain's ApplicationBase to the same folder("MyApp") as the main
AppDomain, so that the new AppDomain(plug-in assemblies) can locate the
assemblies in this folder.

Finally, using multiple AppDomains is not a big overhead. Asp.net
leverages
the same model: if you create multiple web applications on a Asp.net web
site, it will create a separate AppDomain for each web application to
handle the request.

Hope this helps.

Best regards,
Jeffrey Tan
Microsoft Online Community Support
==================================================
Get notification to my posts through email? Please refer to
http://msdn.microsoft.com/subscriptions/managednewsgroups/default.aspx#notif
ications.

Note: The MSDN Managed Newsgroup support offering is for non-urgent issues
where an initial response from the community or a Microsoft Support
Engineer within 1 business day is acceptable. Please note that each follow
up response may take approximately 2 business days as the support
professional working with you may need further investigation to reach the
most efficient resolution. The offering is not appropriate for situations
that require urgent, real-time or phone-based interactions or complex
project analysis and dump analysis issues. Issues of this nature are best
handled working with a dedicated Microsoft Support Engineer by contacting
Microsoft Customer Support Services (CSS) at
http://msdn.microsoft.com/subscriptions/support/default.aspx.
==================================================
This posting is provided "AS IS" with no warranties, and confers no
rights.
 
J

Jeffrey Tan[MSFT]

Hi Eric,

Thanks for your feedback!

#1, >> the problem I have with that is that I don't know the exact path of
the AddIn until my application is running
Yes, I see your concern. However, Assembly.Load() method does not need the
absolute path of the Addin, you need to set the relative path to the Addin
through <probing> in the app.config file(I assume you know of the relative
directory structures of Addin). Only Assembly.LoadFrom() method needs the
absolute path of the Addin. Please refer to the link below for more
information:
http://blogs.msdn.com/suzcook/archive/2003/05/29/57143.aspx

#2, >> The real problem is that the AddIn might have its own dependent DLLs
thatSorry, do you mean this when using new AppDomain or using single AppDomain?
I assume you are talking under the context of single AppDomain. In this
scenario, once you have set the <probing> to the "AddIns\AddIn1" relative
path, the entire AppDomain CLR code will probe the "AddIns\AddIn1" folder
for future using. So this is not a problem. To provide this, I have created
3 sample projects:
1. ClassLibrary1 (Class Library) with code below:
namespace ClassLibrary1
{
public class Class1
{
public static int Add(int a, int b)
{
return a + b + ClassLibrary2.Class1.Multiply(a, b);
}
}
}

2. ClassLibrary2 (Class Library) with code below
namespace ClassLibrary2
{
public class Class1
{
public static int Multiply(int a, int b)
{
return a * b;
}
}
}

3. ProbingDllTest (Winform App)
private void button1_Click(object sender, EventArgs e)
{
MessageBox.Show(ClassLibrary1.Class1.Add(5, 6).ToString());
}

Now, I added an App.Config to the ProbingDllTest winform project with
setting the relative probing path:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<probing privatePath="AddIns\AddIn1"/>
</assemblyBinding>
</runtime>
</configuration>

Finally, after building these 3 projects, I go into
"ProbingDllTest\bin\Debug" and I moved the ClassLibrary1.dll and
ClassLibrary2.dll into the "ProbingDllTest\bin\Debug\AddIns\AddIn1"(yes, I
created the similar directory structure as you). When I click
"ProbingDllTest.exe", all can work well. This means ProbingDllTest.exe can
find "ClassLibrary1.dll" without any problem, and "ClassLibrary1.dll" can
find "ClassLibrary2.dll" without any problem either. If I have
misunderstood your point, please feel free to tell me, thanks.

#3, >>setting theI assume you mean you have created a new AppDomain and set the
AppDomainSetup.ApplicationBase to "MyApp" base directory, you want to know
how to tell the new AppDomain to search the Addin sub-directory. If I have
misunderstood you, please feel free to tell me.

Since you have set AppDomainSetup.ApplicationBase to "MyApp" base
directory, CLR will always probing "MyApp" base directory assemblies
without any problem. To set to another sub-directory(Addin), you may use
AppDomainSetup.PrivateBinPath property. This property has the same effect
as <probing> element in app.config

If you still have anything unclear, please feel free to tell me, thanks.

Best regards,
Jeffrey Tan
Microsoft Online Community Support
==================================================
Get notification to my posts through email? Please refer to
http://msdn.microsoft.com/subscriptions/managednewsgroups/default.aspx#notif
ications.

Note: The MSDN Managed Newsgroup support offering is for non-urgent issues
where an initial response from the community or a Microsoft Support
Engineer within 1 business day is acceptable. Please note that each follow
up response may take approximately 2 business days as the support
professional working with you may need further investigation to reach the
most efficient resolution. The offering is not appropriate for situations
that require urgent, real-time or phone-based interactions or complex
project analysis and dump analysis issues. Issues of this nature are best
handled working with a dedicated Microsoft Support Engineer by contacting
Microsoft Customer Support Services (CSS) at
http://msdn.microsoft.com/subscriptions/support/default.aspx.
==================================================
This posting is provided "AS IS" with no warranties, and confers no rights.
 
J

Jeffrey Tan[MSFT]

Hi Eric,

Have you reviewed my last reply to you? Does my reply make sense to you? If
you still need any help or have any concern, please feel free to tell me,
thanks.

Best regards,
Jeffrey Tan
Microsoft Online Community Support
==================================================
Get notification to my posts through email? Please refer to
http://msdn.microsoft.com/subscriptions/managednewsgroups/default.aspx#notif
ications.

Note: The MSDN Managed Newsgroup support offering is for non-urgent issues
where an initial response from the community or a Microsoft Support
Engineer within 1 business day is acceptable. Please note that each follow
up response may take approximately 2 business days as the support
professional working with you may need further investigation to reach the
most efficient resolution. The offering is not appropriate for situations
that require urgent, real-time or phone-based interactions or complex
project analysis and dump analysis issues. Issues of this nature are best
handled working with a dedicated Microsoft Support Engineer by contacting
Microsoft Customer Support Services (CSS) at
http://msdn.microsoft.com/subscriptions/support/default.aspx.
==================================================
This posting is provided "AS IS" with no warranties, and confers no rights.
 
E

Eric Renken

Thanks for the information. I think your last example using
AppDomainSetup.PrivateBinPath will be the way I will need to go, because I
don't know the path of the Add-In. I do know it is in MyApp\AddIns, but I
don't know what directory after AddIns. It could be in MyApp\AddIns\AddIn1
or in MyApp\AddIns\AddIn2....

The only way to add that information to the Probing key in the app.config
would be by having the setup for the add-in modify that file, and I just
don't feel like that is a good idea.

I will do some research on how to implement the AppDomaiSetup.PrivateBinPath
unless you know of a link that would show this.

Thanks,

Eric


"Jeffrey Tan[MSFT]" said:
Hi Eric,

Thanks for your feedback!

#1, >> the problem I have with that is that I don't know the exact path of
the AddIn until my application is running
Yes, I see your concern. However, Assembly.Load() method does not need the
absolute path of the Addin, you need to set the relative path to the Addin
through <probing> in the app.config file(I assume you know of the relative
directory structures of Addin). Only Assembly.LoadFrom() method needs the
absolute path of the Addin. Please refer to the link below for more
information:
http://blogs.msdn.com/suzcook/archive/2003/05/29/57143.aspx

#2, >> The real problem is that the AddIn might have its own dependent
DLLs
thatSorry, do you mean this when using new AppDomain or using single
AppDomain?
I assume you are talking under the context of single AppDomain. In this
scenario, once you have set the <probing> to the "AddIns\AddIn1" relative
path, the entire AppDomain CLR code will probe the "AddIns\AddIn1" folder
for future using. So this is not a problem. To provide this, I have
created
3 sample projects:
1. ClassLibrary1 (Class Library) with code below:
namespace ClassLibrary1
{
public class Class1
{
public static int Add(int a, int b)
{
return a + b + ClassLibrary2.Class1.Multiply(a, b);
}
}
}

2. ClassLibrary2 (Class Library) with code below
namespace ClassLibrary2
{
public class Class1
{
public static int Multiply(int a, int b)
{
return a * b;
}
}
}

3. ProbingDllTest (Winform App)
private void button1_Click(object sender, EventArgs e)
{
MessageBox.Show(ClassLibrary1.Class1.Add(5, 6).ToString());
}

Now, I added an App.Config to the ProbingDllTest winform project with
setting the relative probing path:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<probing privatePath="AddIns\AddIn1"/>
</assemblyBinding>
</runtime>
</configuration>

Finally, after building these 3 projects, I go into
"ProbingDllTest\bin\Debug" and I moved the ClassLibrary1.dll and
ClassLibrary2.dll into the "ProbingDllTest\bin\Debug\AddIns\AddIn1"(yes, I
created the similar directory structure as you). When I click
"ProbingDllTest.exe", all can work well. This means ProbingDllTest.exe can
find "ClassLibrary1.dll" without any problem, and "ClassLibrary1.dll" can
find "ClassLibrary2.dll" without any problem either. If I have
misunderstood your point, please feel free to tell me, thanks.

#3, >>setting theI assume you mean you have created a new AppDomain and set the
AppDomainSetup.ApplicationBase to "MyApp" base directory, you want to know
how to tell the new AppDomain to search the Addin sub-directory. If I have
misunderstood you, please feel free to tell me.

Since you have set AppDomainSetup.ApplicationBase to "MyApp" base
directory, CLR will always probing "MyApp" base directory assemblies
without any problem. To set to another sub-directory(Addin), you may use
AppDomainSetup.PrivateBinPath property. This property has the same effect
as <probing> element in app.config

If you still have anything unclear, please feel free to tell me, thanks.

Best regards,
Jeffrey Tan
Microsoft Online Community Support
==================================================
Get notification to my posts through email? Please refer to
http://msdn.microsoft.com/subscriptions/managednewsgroups/default.aspx#notif
ications.

Note: The MSDN Managed Newsgroup support offering is for non-urgent issues
where an initial response from the community or a Microsoft Support
Engineer within 1 business day is acceptable. Please note that each follow
up response may take approximately 2 business days as the support
professional working with you may need further investigation to reach the
most efficient resolution. The offering is not appropriate for situations
that require urgent, real-time or phone-based interactions or complex
project analysis and dump analysis issues. Issues of this nature are best
handled working with a dedicated Microsoft Support Engineer by contacting
Microsoft Customer Support Services (CSS) at
http://msdn.microsoft.com/subscriptions/support/default.aspx.
==================================================
This posting is provided "AS IS" with no warranties, and confers no
rights.
 
J

Jeffrey Tan[MSFT]

Hi Eric,

Thanks for your feedback.

Yes, for the new AppDomain, you should use AppDomainSetup.PrivateBinPath
property to set the private probing paths. If you are not sure which
sub-directories under MyApp\AddIns you wanted to search, you may just
enumerate "MyApp\AddIns" directory and add all the sub-folders to
AppDomainSetup.PrivateBinPath property. The usage of
AppDomainSetup.PrivateBinPath property is pretty straight, you may just
separate multiple sub-folder paths with semicolon. The "Figure 3" in the
article below demonstrated the usage of AppDomainSetup, so you may add an
extra AppDomainSetup.PrivateBinPath property setting:
"Discover Techniques for Safely Hosting Untrusted Add-Ins with the .NET
Framework 2.0"
http://msdn.microsoft.com/msdnmag/issues/05/11/HostingAddIns/default.aspx

If you still have anything unclear or need any help, please feel free to
tell me, thanks.

Best regards,
Jeffrey Tan
Microsoft Online Community Support
==================================================
Get notification to my posts through email? Please refer to
http://msdn.microsoft.com/subscriptions/managednewsgroups/default.aspx#notif
ications.

Note: The MSDN Managed Newsgroup support offering is for non-urgent issues
where an initial response from the community or a Microsoft Support
Engineer within 1 business day is acceptable. Please note that each follow
up response may take approximately 2 business days as the support
professional working with you may need further investigation to reach the
most efficient resolution. The offering is not appropriate for situations
that require urgent, real-time or phone-based interactions or complex
project analysis and dump analysis issues. Issues of this nature are best
handled working with a dedicated Microsoft Support Engineer by contacting
Microsoft Customer Support Services (CSS) at
http://msdn.microsoft.com/subscriptions/support/default.aspx.
==================================================
This posting is provided "AS IS" with no warranties, and confers no rights.
 
J

Jeffrey Tan[MSFT]

Ok, if you meet any further problem during the implementation, please feel
free to tell me, I am glad to be any help. Thanks.

Best regards,
Jeffrey Tan
Microsoft Online Community Support
==================================================
Get notification to my posts through email? Please refer to
http://msdn.microsoft.com/subscriptions/managednewsgroups/default.aspx#notif
ications.

Note: The MSDN Managed Newsgroup support offering is for non-urgent issues
where an initial response from the community or a Microsoft Support
Engineer within 1 business day is acceptable. Please note that each follow
up response may take approximately 2 business days as the support
professional working with you may need further investigation to reach the
most efficient resolution. The offering is not appropriate for situations
that require urgent, real-time or phone-based interactions or complex
project analysis and dump analysis issues. Issues of this nature are best
handled working with a dedicated Microsoft Support Engineer by contacting
Microsoft Customer Support Services (CSS) at
http://msdn.microsoft.com/subscriptions/support/default.aspx.
==================================================
This posting is provided "AS IS" with no warranties, and confers no rights.
 
E

Eric Renken

OK, now I am trying to implement this and it just isn't working.

Here is my basic code:

System.IO.FileInfo fi = new System.IO.FileInfo(path);

AppDomainSetup setup = new AppDomainSetup();
setup.ApplicationBase =
AppDomain.CurrentDomain.SetupInformation.ApplicationBase;
setup.PrivateBinPath = fi.DirectoryName;

Log.Information("Creating new AppDomain: {0}",
node.Attributes["name"].Value);
Log.Information("Setup Private Bin Path: {0}", setup.PrivateBinPath);

AppDomain domain =
AppDomain.CreateDomain(node.Attributes["name"].Value, myComputerEvidence,
setup);
_domains.Add(domain);

bool found = false;

TimeSummit.AddIn.IAddIn addIn =
(TimeSummit.AddIn.IAddIn)domain.CreateInstanceFrom(path,
node.SelectSingleNode("type").InnerText).Unwrap();


Everything seems to execute correctly except for the last line. I am
loading this from an XML file and I know the "type" InnerText is the full
name for the type that has my Interface.

When this code executes I keep getting this error:

Type is not resolved for member
'TimeSummit.AddIn.TimeSummitSynch.TimeSummitSynch,TimeSummit.AddIn.TimeSummitSynch,
Version=3.2.0.3, Culture=neutral, PublicKeyToken=null'.

I am thinking this is because it has having problems loading some of its
referenced assemblies. Remember this is in a structure like this:

C:\MyApp
\AddIn
\MyAddIn1

I believe I have the AppDomainSetup configured correctly as its AppBase is
set to the main AppDomain's base, and the I add the path for the AddIn.
This is the full path C:\MyApp\AddIn\MyAddIn1.

Any help would be greatly appreciated.

Thanks,

Eric Renken





"Jeffrey Tan[MSFT]" said:
Ok, if you meet any further problem during the implementation, please feel
free to tell me, I am glad to be any help. Thanks.

Best regards,
Jeffrey Tan
Microsoft Online Community Support
==================================================
Get notification to my posts through email? Please refer to
http://msdn.microsoft.com/subscriptions/managednewsgroups/default.aspx#notif
ications.

Note: The MSDN Managed Newsgroup support offering is for non-urgent issues
where an initial response from the community or a Microsoft Support
Engineer within 1 business day is acceptable. Please note that each follow
up response may take approximately 2 business days as the support
professional working with you may need further investigation to reach the
most efficient resolution. The offering is not appropriate for situations
that require urgent, real-time or phone-based interactions or complex
project analysis and dump analysis issues. Issues of this nature are best
handled working with a dedicated Microsoft Support Engineer by contacting
Microsoft Customer Support Services (CSS) at
http://msdn.microsoft.com/subscriptions/support/default.aspx.
==================================================
This posting is provided "AS IS" with no warranties, and confers no
rights.
 
J

Jeffrey Tan[MSFT]

Hi Eric ,

I have added a reply to you in the post new with the same title. Please
check it there, thanks.

Best regards,
Jeffrey Tan
Microsoft Online Community Support
==================================================
Get notification to my posts through email? Please refer to
http://msdn.microsoft.com/subscriptions/managednewsgroups/default.aspx#notif
ications.

Note: The MSDN Managed Newsgroup support offering is for non-urgent issues
where an initial response from the community or a Microsoft Support
Engineer within 1 business day is acceptable. Please note that each follow
up response may take approximately 2 business days as the support
professional working with you may need further investigation to reach the
most efficient resolution. The offering is not appropriate for situations
that require urgent, real-time or phone-based interactions or complex
project analysis and dump analysis issues. Issues of this nature are best
handled working with a dedicated Microsoft Support Engineer by contacting
Microsoft Customer Support Services (CSS) at
http://msdn.microsoft.com/subscriptions/support/default.aspx.
==================================================
This posting is provided "AS IS" with no warranties, and confers no rights.
 
E

Eric Renken

Sorry, didn't mean to post it twice but I wasn't sure how far back you
looked back.

Thanks again,

Eric Renken
 
J

Jeffrey Tan[MSFT]

Hi Eric ,

No problem. I am also glad to work with you in the new thread.

Best regards,
Jeffrey Tan
Microsoft Online Community Support
==================================================
Get notification to my posts through email? Please refer to
http://msdn.microsoft.com/subscriptions/managednewsgroups/default.aspx#notif
ications.

Note: The MSDN Managed Newsgroup support offering is for non-urgent issues
where an initial response from the community or a Microsoft Support
Engineer within 1 business day is acceptable. Please note that each follow
up response may take approximately 2 business days as the support
professional working with you may need further investigation to reach the
most efficient resolution. The offering is not appropriate for situations
that require urgent, real-time or phone-based interactions or complex
project analysis and dump analysis issues. Issues of this nature are best
handled working with a dedicated Microsoft Support Engineer by contacting
Microsoft Customer Support Services (CSS) at
http://msdn.microsoft.com/subscriptions/support/default.aspx.
==================================================
This posting is provided "AS IS" with no warranties, and confers no rights.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top