Re: GAC hell
- From: "Willy Denoyette [MVP]" <willy.denoyette@xxxxxxxxxx>
- Date: Wed, 27 Feb 2008 17:53:07 +0100
The only way to get at the GAC is by calling into the unmanaged fusion API's.
Following is a complete sample that illustrates how you can perform a name lookup.
Beware that the GAC uses fully qualified assembly names to store assembly references, because you don't know whether an assembly resides in the GAC, you also don't have a FQAN, this may result in false positives!
Willy.
// Note that this requires V2 of the framework.
using System;
using System.Runtime.InteropServices;
using System.Text;
namespace GacStuff
{
internal class GacApi
{
[DllImport("fusion.dll")]
internal static extern int CreateAssemblyCache(
out IAssemblyCache ppAsmCache,
int reserved);
}
// GAC Interfaces - IAssemblyCache. Non used vtable entries declared as
dummy.
[ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown),
Guid("e707dcde-d1cd-11d2-bab9-00c04f8eceae")]
internal interface IAssemblyCache
{
int Dummy1();
[PreserveSig()]
int QueryAssemblyInfo(
int flags,
[MarshalAs(UnmanagedType.LPWStr)]
String assemblyName,
ref ASSEMBLY_INFO assemblyInfo);
int Dummy2();
int Dummy3();
int Dummy4();
}
[StructLayout(LayoutKind.Sequential)]
internal struct ASSEMBLY_INFO
{
public int cbAssemblyInfo;
public int assemblyFlags;
public long assemblySizeInKB;
[MarshalAs(UnmanagedType.LPWStr)]
public String currentAssemblyPath;
public int cchBuf;
}
class Program
{
static void Main()
{
Console.WriteLine(QueryAssemblyInfo("System"));
}
// If assemblyName is not fully specified, a random matching will be
returned!!!!
public static String QueryAssemblyInfo(String assemblyName)
{
ASSEMBLY_INFO assembyInfo = new ASSEMBLY_INFO ();
assembyInfo.cchBuf = 512;
assembyInfo.currentAssemblyPath = new String('\0',
assembyInfo.cchBuf) ;
IAssemblyCache assemblyCache = null;
// Get IAssemblyCache pointer
int hr = GacApi.CreateAssemblyCache(out assemblyCache, 0);
if (hr >= 0)
hr = assemblyCache.QueryAssemblyInfo(1, assemblyName, ref
assembyInfo);
else
Marshal.ThrowExceptionForHR(hr);
return assembyInfo.currentAssemblyPath;
}
}
}
//-------------------------------
"Fredo" <fredo@xxxxxxxxxxx> wrote in message news:EqednSqmQO1y-1janZ2dnUVZ_uuqnZ2d@xxxxxxxxxxxxxxx
Willy,
This is the engine I'm using. I'm aware it's deprecated. Be that as it may, it's the one I'm choosing to use because it meets my needs on several levels.
But we've gotten so far off topic here, we're nowhere near my original question anymore, which had to do with determining which referenced assemblies are in the GAC and which are not.
"Willy Denoyette [MVP]" <willy.denoyette@xxxxxxxxxx> wrote in message news:usJ2CYSeIHA.1824@xxxxxxxxxxxxxxxxxxxxxxxDon't know what version of VS and the Framework you are using, but the article you are referring to is old hat (the article was written back in 2002 and was based on v1.0 of the framework), the namespace the author is talking about was deprecated a long time ago (since VS2005 and V2 of the framework).
VSA was deprecated back in 2004 in favor of VSTA, something the author was not aware of (see the articles FAQ).
The V2 way to script your application, that is generate/compile/run scripts, is by using the CodeDom providers (included with the framework for C#, managed C++, JS.NET and VB.NET). Other languages are supported through the VSTA SDK, but this implies licensing the SDK and the Runtime.
The sample project included with the article from the same author here http://www.123aspx.com/redir.aspx?res=32808 , compiles with a bunch of warnings and doesn't run after the build.
Also, the author talks about a non existing language VBScript.NET, which he dislikes (<quote ..I tended to dislike ..VBScript /quote>).
Take a look at the demo sample included with the article, the author includes two (what he calls) script files (Scripts-DebugWrite.js and Scripts-DebugWrite.vb), but both are *managed* language code , the .js code being JScript.NET and the .vb code being VB.NET. Both have nothing in common with VBScript or JScript, apart some of the syntaxes.
So, please if you have a small but complete sample that uses the (obsolete) Microsoft.Vsa namespace to generate/compile/Run "VBScript" and "JScript" scripting code (that is interpreted scripting code), feel free to post it here.
Willy.
"Fredo" <fredo@xxxxxxxxxxx> wrote in message news:8-ednUMxU_Qo0VnanZ2dnUVZ_r6rnZ2d@xxxxxxxxxxxxxxxWilly,
What you're referring to are the CodeDom providers. What I'm talking about is the scripting engines. These are different things as well.
Here's an article that discuesses VSA scripting:
http://www.developerfusion.co.uk/show/4675/
More specifically, adding references which is discussed on this page:
http://www.developerfusion.co.uk/show/4675/3/
There's a difference in the way VBScript and JScript handle assembly references. JScript will allow you to pass a path to the assembly. VBScript requires that all referenced assemblies that aren't in the GAC, be located in a single directory and you then have to call IVsaEngine.SetOption("AssemblyBase", referencesDirectory) to set the directory.
So, my question is: I need to figure out which references are GAC references and which ones aren't, so I can copy the ones that aren't GAC references into a temporary directory for the script to access them.
"Willy Denoyette [MVP]" <willy.denoyette@xxxxxxxxxx> wrote in message news:%23QNzoiJeIHA.2000@xxxxxxxxxxxxxxxxxxxxxxxHmm... So, you are talking about the CodeDom providers, right? In that case I don't see where "VBScript and WSH" comes from, there is no language provider for unmanaged languages in the framework. All there is, are the language providers for CSharp, VisualBasic, J#, C++ and JScript, but all are producing pure managed CSharp, VB.NET, J#, C++ and JScript.NET code.
Also all CodeDom providers share a common interface, so I don't know exactly what problem you have with "adding references".
Mind to give an example that illustrates your issue?
Willy.
"Fredo" <fredo@xxxxxxxxxxx> wrote in message news:Q9-dnXAW0J5K_l7anZ2dnUVZ_qygnZ2d@xxxxxxxxxxxxxxxWilly,
Sorry, I think you're misunderstanding. I'm talking about the scripting engines: Microsoft.JScript, Microsoft.Vsa, and Microsoft.VisualBasic.Vsa managed engines, not the Windows scripting host or cscript.exe.
These engines work with managed objects, much in the way WSH works with COM objects, but they're different engines, at least the JScript engine is. The entire (or a vast majority of the) JScript interpreter is managed code. The VBScript seems to be doing a lot of stuff in unmanaged code, so for all I know, they're somehow using WSH underneath, but I doubt it since I don't see how it would deal with managed objects.
So my question has to do with the resolution of referenced managed assemblies, not COM components.
"Willy Denoyette [MVP]" <willy.denoyette@xxxxxxxxxx> wrote in message news:%23FxHq6AeIHA.2000@xxxxxxxxxxxxxxxxxxxxxxx"Fredo" <fredo@xxxxxxxxxxx> wrote in message news:geadnVY7J5Qc0l7anZ2dnUVZ_vyinZ2d@xxxxxxxxxxxxxxxI'm writing a wrapper for the VBScript and JScript engines in .NET. I'm trying to add a little uniformity where Microsoft has some divergence.
With the JScript engine, you can add references and provide a path to where each DLL is. With the VBScript engine, you can't do that. For DLLs that aren't in the GAC, you have to provide a directory where all the referenced .DLLs are.
To keep the user of the wrapper from having to deal with this, for the VBScript I want to add some code to copy all the references that aren't in the GAC, to a temporary directory. The problem is, how would one programmatically determine which ones are in the GAC and which ones aren't? I know I can go snooping around in \Windows\assembly\GAC, but I wanted ot know if there was a better way of doing this that doesn't require me digging through directories, checking DLL versions and such. I don't know precisely how .NET normally does this and unfortunately, I can't see how the VBScript engine deals with it since that part of the framework code appears to be unmanaged.
Thanks.
The scripting engine is a COM client and COM uses the registry to locate the COM server DLL(s) that contains the components as referred to by the scripts.
Now, for .NET components you want to expose to COM clients, you have a number of options when registering your assembly:
1. you can run regasm without the /codebase command line switch, and you have to install the assembly in the GAC, or
2. you can run regasm with the /codebase switch, and you don't have to install the assembly in the GAC, or
3. you can run regasm without the /codebase switch and install the assembly in the same directory as the client.
Note that the COM client in your case is the scripting engine (cscript.exe) which is found in %windir%\system32 (or %windir%\syswow64), so installing the assemblies with the client is excluded, which leaves you with option 1 and 2. Installing in the GAC is
only a viable option if you have a small number of assemblies used by different clients and client types. Option 2 is preferable when you have a number of assemblies used by a single client (or a limited number of clients). If your component is only used by scripting clients (well.. there is only a single one) then you should definitely go for 2.
Willy.
.
- Follow-Ups:
- Re: GAC hell
- From: Fredo
- Re: GAC hell
- References:
- GAC hell
- From: Fredo
- Re: GAC hell
- From: Willy Denoyette [MVP]
- Re: GAC hell
- From: Fredo
- Re: GAC hell
- From: Willy Denoyette [MVP]
- Re: GAC hell
- From: Fredo
- Re: GAC hell
- From: Willy Denoyette [MVP]
- Re: GAC hell
- From: Fredo
- GAC hell
- Prev by Date: Re: Why are these NOT equal?
- Next by Date: Adding multiple User Controls to a single placeholder
- Previous by thread: Re: GAC hell
- Next by thread: Re: GAC hell
- Index(es):
Relevant Pages
|