注意 无法在运行 Microsoft NET Framework 1.1 Service Pack 1 系统上应用此修复程序。 如果安装了 NET Framework 1.1 Service Pack 1, 是遇到症状, 本文中描述请求修复程序总成包 890673 以获取解决 Microsoft 知识库文章中描述。 有关详细信息, 请单击下列文章编号以查看 Microsoft 知识库中相应:
890673?
(http://support.microsoft.com/kb/890673/
)
NET Framework 1.1 Service Pack 1 XML Web 服务和 XML 消息修复累积程序包 8 的可用性
namespace PreGenNS
{
using System;
using System.Collections;
using System.IO;
using System.Reflection;
using System.Xml.Serialization;
using System.Text;
using System.Globalization;
using System.Web.Services.Protocols;
using System.Threading;
using System.CodeDom.Compiler;
using System.Diagnostics;
using System.Text.RegularExpressions;
public class Pregen
{
private static bool _verbose = true;
// We use this as the standard suffix on all the serializer DLLs.
// It must match the short name of the proxy assembly class with
const string SerializerSuffix = ".Serializer.dll";
/// Key in the app config file with path of AssemblyInfo file.
private const string AssemblyInfoAppKey = "AssemblyInfoFile";
//private const string CSCCmd = "csc.exe";
// Obtain the full path for the compiler, just in case the path is not set correctly
private readonly string CSCCmd = System.Runtime.InteropServices.RuntimeEnvironment.RuntimeDirectory() + "csc.exe";
public static int Main(string[] args) {
// Are we in a recursive call?
//TODO: could really use a single value -- use filesToDelete...
if (AppDomain.CurrentDomain.GetData(CallingAppDomainKey) == null) {
return RunSlave(args);
}
string dllName = "";
bool invalidUsage = false;
if(args.Length == 1)
{
dllName = args[0];
}
else if(args.Length == 2)
{
if(!(args[0] == "/Q" || args[0] == "/q"))
{
invalidUsage = true;
}
else
{
dllName = args[1];
_verbose = false;
}
}
else
{
invalidUsage = true;
}
if (invalidUsage)
{
Console.WriteLine("usage: ");
Console.WriteLine(" pregen [/Q] assembly");
return 1;
}
// Update Private path setting in current application domain
if (updatePrivatePath() != 0)
{
return 1;
}
Pregen pregen = new Pregen();
int rc = pregen.Run(dllName);
//Console.Read();
return rc;
}
// Reads private path settings from config file and updates appdomain. This permits configurable probing that is needed for preserialization.
static int updatePrivatePath()
{
string defaultPrivatePath = System.Configuration.ConfigurationSettings.AppSettings["PregenDefaultPrivatePath"];
string dynamicPrivatePath = System.Configuration.ConfigurationSettings.AppSettings["PregenDynamicPrivatePath"];
string env_PREGEN_VALUES = Environment.GetEnvironmentVariable("PREGEN_VALUES");
string [] replacementBlocks, temp;
if (_verbose)
Console.WriteLine("Read PREGEN_VALUES Env Variable, Value = " + env_PREGEN_VALUES);
//process the dynamic path if the environment variable PREGEN_VALUES is present
if (env_PREGEN_VALUES == null || env_PREGEN_VALUES == "")
{
if (defaultPrivatePath != null && defaultPrivatePath != "")
{
AppDomain.CurrentDomain.AppendPrivatePath(defaultPrivatePath);
if (_verbose)
Console.WriteLine("Appended private path with: " + defaultPrivatePath);
}
}
else
{
if (dynamicPrivatePath != null && dynamicPrivatePath != "")
{
//do substitutions in dynamic path
replacementBlocks = env_PREGEN_VALUES.ToUpper().Split(";".ToCharArray());
dynamicPrivatePath = dynamicPrivatePath.ToUpper();
for(int i = 0; i < replacementBlocks.Length; i++)
{
temp = replacementBlocks[i].Split("=".ToCharArray());
if(temp.Length != 2)
{
Console.Error.WriteLine("Invalid Environment Variable format - PREGEN_VALUES");
return 1;
}
dynamicPrivatePath = dynamicPrivatePath.Replace(temp[0],temp[1]);
}
AppDomain.CurrentDomain.AppendPrivatePath(dynamicPrivatePath);
if (_verbose )
Console.WriteLine("Appended private path with: " + dynamicPrivatePath);
}
}
return 0;
}
int Run(string assemblyName)
{
try
{
GenerateAssembly(assemblyName);
}
catch (Exception e)
{
if (e is ThreadAbortException || e is StackOverflowException || e is OutOfMemoryException)
{
throw;
}
Error(e, "Error processing " + assemblyName + ": ");
return 1;
}
return 0;
}
// Generates the serializer assembly for the proxy assembly specified
void GenerateAssembly(string assemblyName)
{
Assembly assembly = LoadAssembly(assemblyName, true);
Type[] types = assembly.GetTypes();
ArrayList mappings = new ArrayList();
ArrayList importedTypes = new ArrayList();
XmlReflectionImporter importer = new XmlReflectionImporter();
//Obtain the imported serializable types
for (int i = 0; i < types.Length; i++)
{
Type type = types[i];
if (HttpWebClientProtocol.GenerateXmlMappings(type, mappings))
{
importedTypes.Add(type);
}
}
if (importedTypes.Count <= 0) {
Console.Out.WriteLine("Assembly '{0}' does not contain any serializable types.", assembly.Location);
return;
}
{
Type[] serializableTypes = (Type[])importedTypes.ToArray(typeof(Type));
XmlMapping[] allMappings = (XmlMapping[])mappings.ToArray(typeof(XmlMapping));
bool wasError = false;
bool gac = assembly.GlobalAssemblyCache;
string codePath = gac ? Environment.CurrentDirectory : Path.GetDirectoryName(assembly.Location);
//adjust compiler params
CompilerParameters parameters = new CompilerParameters();
parameters.GenerateInMemory = false;
parameters.IncludeDebugInformation = false;
parameters.TempFiles = new TempFileCollection(codePath, true);
//generate the serializer
Assembly serializer = XmlSerializer.GenerateSerializer(serializableTypes, allMappings, parameters);
if (serializer == null) {
Console.Out.WriteLine("Failed pregenerate serializer for '{0}'", assembly.Location);
wasError = true;
}
else
{
serializer = null;
}
// Determine whether there is an assemblyInfoFile in the config file.
string assemblyInfoFile = System.Configuration.ConfigurationSettings.AppSettings[AssemblyInfoAppKey];
if (assemblyInfoFile != null) {
if (! File.Exists(assemblyInfoFile)) {
Console.WriteLine("ERROR: AssemblyInfo file: {0} does not exist.", assemblyInfoFile);
wasError = true;
}
}
if (!wasError) {
// Recompile the Serializer, same options, except to include the assemblyInfo file and
// adjust the output name.
// We have to find
// 1. a .cs file (the serializer source)
// 2. a .cmdline file (the compiler options used)
// among the temp files from the first compile.
string csFile = null;
string cmdlineFile = null;
foreach (string curFile in parameters.TempFiles ) {
string fileNameLC = curFile.ToLower();
if (fileNameLC.EndsWith(".cs") ) {
csFile = curFile;
}
else if (fileNameLC.EndsWith(".cmdline")) {
cmdlineFile = curFile;
}
//Do not care about the other files...
}
if (csFile == null || cmdlineFile == null) {
Console.WriteLine("Error: needed to rebuild, but cannot find either .cs or .cmdline file\n");
DeleteTempFiles(parameters);
return;
}
// So now we have found the file and the cmdline args. We only need run the compiled application after
// adjusting the parameters to include the AssemblyInfo file and to change the output.
// Typical calling options to csc for this sequence are:
// csc /noconfig @xxx.cmdline
// we'll change this to expand the contents of the cmdline file
// build the right name for the target serializer.
//TODO: we should be able to read the attribute from the proxy DLL and match our output
// to that name.
string serializerName = Path.GetDirectoryName(assembly.Location) + @"\"
+ assembly.GetName().Name + SerializerSuffix;
string cmdLine = AdjustCmdLine(cmdlineFile, assemblyInfoFile, serializerName);
ProcessStartInfo ps = new ProcessStartInfo(CSCCmd, cmdLine);
ps.WindowStyle = ProcessWindowStyle.Hidden;
Process p = Process.Start(ps);
p.WaitForExit();
int rc = p.ExitCode;
if (rc > 0) {
//TODO: put useful handling here...
Console.WriteLine("ERROR: Compiler problem, rc = {0}", rc);
wasError = true;
}
//TODO: Cannot ditch temp assembly because the assembly is now loaded.
DeleteTempFiles(parameters);
if (!wasError) {
Console.Out.WriteLine("Generated Serialization Assembly Name: {0}", serializerName);
}
Console.Out.WriteLine("Done");
}
}
}
// Delete temporary files from a CompilerParameters list.
private void DeleteTempFiles(CompilerParameters cp) {
ArrayList unDeletedFiles = new ArrayList(10);
foreach(string fileName in cp.TempFiles) {
try {
File.Delete(fileName);
}
catch(Exception) {
unDeletedFiles.Add(fileName);
//Console.WriteLine("Warning: Unable to delete temp file: {0}, exception={1}(\"{2}\")",
// Path.GetFileName(fileName), e.GetType().FullName, e.Message);
}
}
if (unDeletedFiles.Count > 0) {
// put the list into the calling appDomain's environment for later deletion
string[] files = new string[unDeletedFiles.Count];
unDeletedFiles.CopyTo(files);
//TODO: should really be concatenating to any existing value -- maybe leave as an ArrayList?
AppDomain.CurrentDomain.SetData(FilesToDeleteKey, files);
}
}
/// Rebuild a commandline for csc, adding assemblyInfoFile to the end of the line and adjusting the
/// output file name as specified.
private string AdjustCmdLine(string cmdlineFile, string assemblyInfoFile, string outputFile) {
// Obtain the text from the @ response file that is used by the Framework Serialization builder
StreamReader file = File.OpenText(cmdlineFile);
string cmdLine = file.ReadToEnd();
file.Close();
// add the assemblyInfo file at the end of the command if it was specified
if (assemblyInfoFile != null)
cmdLine = String.Format(@"/noconfig {0} ""{1}""", cmdLine, assemblyInfoFile);
// replace the /OUT option with our value.
Regex re = new Regex(@"/OUT:""[^""]+.", RegexOptions.IgnoreCase);
cmdLine = re.Replace(cmdLine, @"/OUT:""" + outputFile + @"""");
return cmdLine;
}
static Assembly LoadAssembly(string assemblyName, bool throwOnFail)
{
Assembly assembly = null;
string path = Path.GetFullPath(assemblyName).ToLower(CultureInfo.InvariantCulture);
if (File.Exists(path))
{
assembly = Assembly.LoadFrom(path);
}
else
{
try
{
assembly = Assembly.Load(assemblyName);
}
catch (Exception e)
{
if (e is ThreadAbortException || e is StackOverflowException || e is OutOfMemoryException)
{
throw;
}
Error(e, "Error: ");
}
if (assembly == null)
{
string justName = Path.GetFileNameWithoutExtension(assemblyName);
try
{
assembly = Assembly.Load(justName);
}
catch (Exception e)
{
if (e is ThreadAbortException || e is StackOverflowException || e is OutOfMemoryException)
{
throw;
}
Error(e, "Error: ");
}
}
}
if (assembly == null)
{
if (throwOnFail)
throw new InvalidOperationException("Cannot load assembly " + assemblyName);
return null;
}
return assembly;
}
static void Error(Exception e, string prefix)
{
Console.Error.WriteLine(prefix + e.Message);
if (e.InnerException != null)
{
Error(e.InnerException, " - ");
}
}
static void Warning(Exception e)
{
Console.Out.WriteLine(" - " + e.Message);
if (e.InnerException != null)
{
Warning(e.InnerException);
}
}
private static AppDomain DuplicateAppDomain(AppDomain template, string newName) {
AppDomain res = AppDomain.CreateDomain(newName, template.Evidence, template.SetupInformation);
return res;
}
// keys in AppDomain properties
private const string CallingAppDomainKey = "__Calling_AppDomain__";
private const string FilesToDeleteKey = "__Files_To_Delete__";
// Called from Main to set up and run the second copy of the program.
// args: command-line arguments for second execution
private static int RunSlave(string[] args) {
// Start a copy of this program in another application domain
AppDomain ad = DuplicateAppDomain(AppDomain.CurrentDomain, "serializerAD");
Assembly ca = Assembly.GetExecutingAssembly();
// set a marker so target domain knows that it is the subordinate.
ad.SetData(CallingAppDomainKey, AppDomain.CurrentDomain.FriendlyName);
ad.SetData(FilesToDeleteKey, new string[0]);
int rc = ad.ExecuteAssembly(ca.Location, ca.Evidence, args);
// Now delete any files
string[] fileList = (string[])ad.GetData(FilesToDeleteKey);
AppDomain.Unload(ad);
if (fileList != null) {
foreach(string fileName in fileList) {
try {
File.Delete(fileName);
}
catch(Exception e) {
Console.WriteLine("Warning: Unable to delete temp file: {0}, exception={1}(\"{2}\")",
Path.GetFileName(fileName), e.GetType().FullName, e.Message);
}
}
}
return rc;
}
}
}