/* * Method: FindExistingWrapper * * Checks if there's a preexisting wrapper for this reference. */ internal override bool FindExistingWrapper(out ComReferenceWrapperInfo wrapperInfo, DateTime componentTimestamp) { if (!HasTemporaryWrapper) { return(base.FindExistingWrapper(out wrapperInfo, componentTimestamp)); } // if this reference has a temporary wrapper, it can't possibly have a preexisting one wrapperInfo = null; return(false); }
/// <summary> /// Compare the strong name signing state of the existing wrapper to the signing /// state we are requesting in this run of the task. Return true if they match (e.g. /// from a signing perspective, the wrapper is up-to-date) or false otherwise. /// </summary> private bool SigningRequirementsMatchExistingWrapper(ComReferenceWrapperInfo wrapperInfo) { StrongNameLevel desiredStrongNameLevel = StrongNameLevel.None; if (!string.IsNullOrEmpty(KeyFile) || !string.IsNullOrEmpty(KeyContainer)) { desiredStrongNameLevel = DelaySign ? StrongNameLevel.DelaySigned : StrongNameLevel.FullySigned; } // ...and see what we have already StrongNameLevel currentStrongNameLevel = StrongNameUtils.GetAssemblyStrongNameLevel(wrapperInfo.path); // if not matching, need to regenerate wrapper if (desiredStrongNameLevel != currentStrongNameLevel) { return(false); } // if the wrapper needs a strong name, see if the public keys match if (desiredStrongNameLevel == StrongNameLevel.DelaySigned || desiredStrongNameLevel == StrongNameLevel.FullySigned) { // get desired public key StrongNameUtils.GetStrongNameKey(Log, KeyFile, KeyContainer, out _, out byte[] desiredPublicKey); // get current public key AssemblyName assemblyName = AssemblyName.GetAssemblyName(wrapperInfo.path); if (assemblyName == null) { return(false); } byte[] currentPublicKey = assemblyName.GetPublicKey(); if (currentPublicKey.Length != desiredPublicKey.Length) { return(false); } // compare public keys byte by byte for (int i = 0; i < currentPublicKey.Length; i++) { if (currentPublicKey[i] != desiredPublicKey[i]) { return(false); } } } return(true); }
/// <summary> /// Construct a new ComReferenceInfo copying all state from the given ComReferenceInfo instance /// </summary> internal ComReferenceInfo(ComReferenceInfo copyFrom) { this.attr = copyFrom.attr; this.typeLibName = copyFrom.typeLibName; this.strippedTypeLibPath = copyFrom.strippedTypeLibPath; this.fullTypeLibPath = copyFrom.fullTypeLibPath; this.typeLibPointer = copyFrom.typeLibPointer; this.primaryOfAxImpRef = copyFrom.primaryOfAxImpRef; this.resolvedWrapper = copyFrom.resolvedWrapper; this.taskItem = new TaskItem(copyFrom.taskItem); this.dependentWrapperPaths = copyFrom.dependentWrapperPaths; this.referencePathItem = copyFrom.referencePathItem; }
/// <summary> /// Generates a wrapper for this reference. /// </summary> internal bool GenerateWrapper(out ComReferenceWrapperInfo wrapperInfo) { wrapperInfo = null; // The tool gets the public key for itself, but we get it here anyway to // give nice messages in errors cases. GetAndValidateStrongNameKey(out _, out _); // Generate wrapper out-of-proc using aximp.exe from the target framework. MUST // HAVE SET SDKTOOLSPATH TO THE TARGET SDK TO WORK var axImp = new ResolveComReference.AxImp(); if (ReferenceInfo != null) { axImp.ActiveXControlName = ReferenceInfo.strippedTypeLibPath; } axImp.BuildEngine = BuildEngine; axImp.ToolPath = ToolPath; axImp.EnvironmentVariables = EnvironmentVariables; axImp.DelaySign = DelaySign; axImp.GenerateSource = false; axImp.KeyContainer = KeyContainer; axImp.KeyFile = KeyFile; axImp.Silent = Silent; if (ReferenceInfo?.primaryOfAxImpRef?.resolvedWrapper?.path != null) { // This path should hit unless there was a prior resolution error or bug in the resolution code. // The reason is that everything (tlbs and pias) gets resolved before AxImp references. axImp.RuntimeCallableWrapperAssembly = ReferenceInfo.primaryOfAxImpRef.resolvedWrapper.path; } axImp.OutputAssembly = Path.Combine(OutputDirectory, GetWrapperFileName()); bool generateWrapperSucceeded = axImp.Execute(); string wrapperPath = GetWrapperPath(); // store the wrapper info... wrapperInfo = new ComReferenceWrapperInfo { path = wrapperPath }; wrapperInfo.assembly = Assembly.UnsafeLoadFrom(wrapperInfo.path); // ...and we're done! return(generateWrapperSucceeded); }
/// <summary> /// Checks if there's a preexisting wrapper for this reference. /// </summary> internal override bool FindExistingWrapper(out ComReferenceWrapperInfo wrapperInfo, DateTime componentTimestamp) { wrapperInfo = null; string wrapperPath = GetWrapperPath(); // now see if the wrapper assembly actually exists if (!FileSystems.Default.FileExists(wrapperPath)) { return(false); } wrapperInfo = new ComReferenceWrapperInfo { path = wrapperPath }; return(IsWrapperUpToDate(wrapperInfo, componentTimestamp)); }
/// <summary> /// Checks if the existing wrapper is up to date. /// </summary> protected virtual bool IsWrapperUpToDate(ComReferenceWrapperInfo wrapperInfo, DateTime componentTimestamp) { Debug.Assert(!string.IsNullOrEmpty(ReferenceInfo.strippedTypeLibPath), "ReferenceInfo.path should be valid if we got here"); if (string.IsNullOrEmpty(ReferenceInfo.strippedTypeLibPath)) { throw new ComReferenceResolutionException(); } // if wrapper doesn't exist, wrapper is obviously not up to date if (!FileSystems.Default.FileExists(wrapperInfo.path)) { return(false); } // if typelib file has a DIFFERENT last write time, wrapper is not up to date // the reason we're comparing write times in an unusual way is that type libraries are unusual // "source files" for wrappers. If you upgrade/downgrade a system component, its write // time may be earlier than before but we should still regenerate the wrapper. if (DateTime.Compare(File.GetLastWriteTime(ReferenceInfo.strippedTypeLibPath), componentTimestamp) != 0) { return(false); } // Compare our the existing wrapper's strong name state to the one we are requesting. if (!SigningRequirementsMatchExistingWrapper(wrapperInfo)) { return(false); } // ok, everything's looking fine, now just verify the assembly file is valid try { wrapperInfo.assembly = Assembly.UnsafeLoadFrom(wrapperInfo.path); } catch (BadImageFormatException) { // ouch, this assembly is malformed... need to regenerate the wrapper. wrapperInfo.assembly = null; } return(wrapperInfo.assembly != null); }
/// <summary> /// Finds an existing wrapper for the specified component /// </summary> internal abstract bool FindExistingWrapper(out ComReferenceWrapperInfo wrapperInfo, DateTime componentTimestamp);
/* * Method: GenerateWrapper * * Generates a wrapper for this reference. */ internal bool GenerateWrapper(out ComReferenceWrapperInfo wrapperInfo) { wrapperInfo = null; string rootNamespace = ReferenceInfo.typeLibName; string wrapperPath = GetWrapperPath(); bool generateWrapperSucceeded = true; if (ExecuteAsTool) { // delegate generation of the assembly to an instance of the TlbImp ToolTask. MUST // HAVE SET SDKTOOLSPATH TO THE TARGET SDK TO WORK var tlbImp = new ResolveComReference.TlbImp { BuildEngine = BuildEngine, EnvironmentVariables = EnvironmentVariables, DelaySign = DelaySign, KeyContainer = KeyContainer, KeyFile = KeyFile, OutputAssembly = wrapperPath, ToolPath = ToolPath, TypeLibName = ReferenceInfo.fullTypeLibPath, AssemblyNamespace = rootNamespace, AssemblyVersion = null, PreventClassMembers = _noClassMembers, SafeArrayAsSystemArray = true, Silent = Silent, Transform = ResolveComReference.TlbImpTransformFlags.TransformDispRetVals }; if (_referenceFiles != null) { // Issue is that there may be reference dependencies that need to be passed in. It is possible // that the set of references will also contain the file that is meant to be written here (when reference resolution // found the file in the output folder). We need to filter out this case. var fullPathToOutput = Path.GetFullPath(wrapperPath); // Current directory is the directory of the project file. tlbImp.ReferenceFiles = _referenceFiles.Where(rf => String.Compare(fullPathToOutput, rf, StringComparison.OrdinalIgnoreCase) != 0).ToArray(); } switch (_targetProcessorArchitecture) { case UtilitiesProcessorArchitecture.MSIL: tlbImp.Machine = "Agnostic"; break; case UtilitiesProcessorArchitecture.AMD64: tlbImp.Machine = "X64"; break; case UtilitiesProcessorArchitecture.IA64: tlbImp.Machine = "Itanium"; break; case UtilitiesProcessorArchitecture.X86: tlbImp.Machine = "X86"; break; case UtilitiesProcessorArchitecture.ARM: tlbImp.Machine = "ARM"; break; case null: break; default: // Transmit the flag directly from the .targets files and rely on tlbimp.exe to produce a good error message. tlbImp.Machine = _targetProcessorArchitecture; break; } generateWrapperSucceeded = tlbImp.Execute(); // store the wrapper info... wrapperInfo = new ComReferenceWrapperInfo { path = (HasTemporaryWrapper) ? null : wrapperPath }; // Changed to ReflectionOnlyLoadFrom, related to bug: // RCR: Bad COM-interop assemblies being generated when using 64-bit MSBuild to build a project that is targeting 32-bit platform // The original call to UnsafeLoadFrom loads the assembly in preparation for execution. If the assembly is x86 and this is x64 msbuild.exe then we // have problems (UnsafeLoadFrom will fail). We only use this assembly for reference resolution so we don't need to be ready to execute the code. // // Its actually not clear to me that we even need to load the assembly at all. Reference resoluton is only used in the !ExecuteAsTool which is not // where we are right now. // // If we really do need to load it then: // // wrapperInfo.assembly = Assembly.ReflectionOnlyLoadFrom(wrapperPath); } else { // use framework classes in-proc to generate the assembly var converter = new TypeLibConverter(); AssemblyBuilder assemblyBuilder; GetAndValidateStrongNameKey(out StrongNameKeyPair keyPair, out byte[] publicKey); try { TypeLibImporterFlags flags = TypeLibImporterFlags.SafeArrayAsSystemArray | TypeLibImporterFlags.TransformDispRetVals; if (_noClassMembers) { flags |= TypeLibImporterFlags.PreventClassMembers; } switch (_targetProcessorArchitecture) { case UtilitiesProcessorArchitecture.MSIL: flags |= TypeLibImporterFlags.ImportAsAgnostic; break; case UtilitiesProcessorArchitecture.AMD64: flags |= TypeLibImporterFlags.ImportAsX64; break; case UtilitiesProcessorArchitecture.IA64: flags |= TypeLibImporterFlags.ImportAsItanium; break; case UtilitiesProcessorArchitecture.X86: flags |= TypeLibImporterFlags.ImportAsX86; break; #if !MONO case UtilitiesProcessorArchitecture.ARM: flags |= TypeLibImporterFlags.ImportAsArm; break; #endif default: // Let the type importer decide. break; } // Start the conversion process. We'll get callbacks on ITypeLibImporterNotifySink to resolve dependent refs. assemblyBuilder = converter.ConvertTypeLibToAssembly(ReferenceInfo.typeLibPointer, wrapperPath, flags, this, publicKey, keyPair, rootNamespace, null); } catch (COMException ex) { if (!Silent) { Log.LogWarningWithCodeFromResources("ResolveComReference.ErrorCreatingWrapperAssembly", ItemName, ex.Message); } throw new ComReferenceResolutionException(ex); } // if we're done, and this is not a temporary wrapper, write it out to disk if (!HasTemporaryWrapper) { WriteWrapperToDisk(assemblyBuilder, wrapperPath); } // store the wrapper info... wrapperInfo = new ComReferenceWrapperInfo { path = (HasTemporaryWrapper) ? null : wrapperPath, assembly = assemblyBuilder }; } // ...and we're done! return(generateWrapperSucceeded); }
/// <summary> /// Gets the resolved assembly path for the typelib wrapper. /// </summary> internal override bool FindExistingWrapper(out ComReferenceWrapperInfo wrapperInfo, DateTime componentTimestamp) { wrapperInfo = null; // Let NDP do the dirty work... TypeLibConverter converter = new TypeLibConverter(); if (!converter.GetPrimaryInteropAssembly(ReferenceInfo.attr.guid, ReferenceInfo.attr.wMajorVerNum, ReferenceInfo.attr.wMinorVerNum, ReferenceInfo.attr.lcid, out string asmName, out string asmCodeBase)) { return(false); } // let's try to load the assembly to determine its path and if it's there try { if (!string.IsNullOrEmpty(asmCodeBase)) { var uri = new Uri(asmCodeBase); // make sure the PIA can be loaded Assembly assembly = Assembly.UnsafeLoadFrom(uri.LocalPath); // got here? then assembly must have been loaded successfully. wrapperInfo = new ComReferenceWrapperInfo { path = uri.LocalPath, assembly = assembly, // We need to remember the original assembly name of this PIA in case it gets redirected to a newer // version and other COM components use that name to reference the PIA. assembly.FullName wouldn't // work here since we'd get the redirected assembly name. originalPiaName = new AssemblyNameExtension(AssemblyName.GetAssemblyName(uri.LocalPath)) }; } else { Assembly assembly = Assembly.Load(asmName); // got here? then assembly must have been loaded successfully. wrapperInfo = new ComReferenceWrapperInfo { path = assembly.Location, assembly = assembly, // We need to remember the original assembly name of this PIA in case it gets redirected to a newer // version and other COM components use that name to reference the PIA. originalPiaName = new AssemblyNameExtension(asmName, true) }; } } catch (FileNotFoundException) { // This means that assembly file cannot be found. // We don't need to do anything here; wrapperInfo is not set // and we'll assume that the assembly doesn't exist. } catch (BadImageFormatException) { // Similar case as above, except we should additionally warn the user that the assembly file // is not really a valid assembly file. if (!Silent) { Log.LogWarningWithCodeFromResources("ResolveComReference.BadAssemblyImage", asmName); } } // have we found the wrapper? if (wrapperInfo != null) { return(true); } return(false); }