1 // Copyright (c) Microsoft. All rights reserved.
2 // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3 
4 #if FEATURE_APPDOMAIN
5 
6 using System;
7 using System.Collections;
8 using System.Diagnostics;
9 using System.Globalization;
10 using System.IO;
11 using System.Reflection;
12 using System.Runtime.InteropServices;
13 using System.Collections.Generic;
14 using System.Linq;
15 
16 // TYPELIBATTR clashes with the one in InteropServices.
17 using TYPELIBATTR = System.Runtime.InteropServices.ComTypes.TYPELIBATTR;
18 using UtilitiesProcessorArchitecture = Microsoft.Build.Utilities.ProcessorArchitecture;
19 
20 using Microsoft.Build.Framework;
21 using Microsoft.Build.Shared;
22 using Microsoft.Build.Utilities;
23 
24 namespace Microsoft.Build.Tasks
25 {
26     /// <summary>
27     /// Main class for the COM reference resolution task
28     /// </summary>
29     public sealed partial class ResolveComReference : AppDomainIsolatedTaskExtension, IComReferenceResolver
30     {
31         #region Constructors
32 
33         /// <summary>
34         /// public constructor
35         /// </summary>
ResolveComReference()36         public ResolveComReference()
37         {
38             // do nothing.
39         }
40 
41         #endregion
42 
43         #region Properties
44 
45         /// <summary>
46         /// COM references specified by guid/version/lcid
47         /// </summary>
48         public ITaskItem[] TypeLibNames
49         {
50             get
51             {
52                 return _typeLibNames;
53             }
54             set
55             {
56                 _typeLibNames = value;
57             }
58         }
59 
60         private ITaskItem[] _typeLibNames = null;
61 
62         /// <summary>
63         /// COM references specified by type library file path
64         /// </summary>
65         public ITaskItem[] TypeLibFiles
66         {
67             get
68             {
69                 return _typeLibFiles;
70             }
71             set
72             {
73                 _typeLibFiles = value;
74             }
75         }
76 
77         /// <summary>
78         /// Array of equals-separated pairs of environment
79         /// variables that should be passed to the spawned tlbimp.exe and aximp.exe,
80         /// in addition to (or selectively overriding) the regular environment block.
81         /// </summary>
82         public string[] EnvironmentVariables
83         {
84             get;
85             set;
86         }
87 
88         private ITaskItem[] _typeLibFiles = null;
89 
90         /// <summary>
91         /// merged array containing typeLibNames and typeLibFiles (internal for unit testing)
92         /// </summary>
93         internal List<ComReferenceInfo> allProjectRefs = null;
94 
95         /// <summary>
96         /// array containing all dependency references
97         /// </summary>
98         internal List<ComReferenceInfo> allDependencyRefs = null;
99 
100         /// <summary>
101         /// the directory wrapper files get generated into
102         /// </summary>
103         public string WrapperOutputDirectory
104         {
105             get
106             {
107                 return _wrapperOutputDirectory;
108             }
109             set
110             {
111                 _wrapperOutputDirectory = value;
112             }
113         }
114 
115         private string _wrapperOutputDirectory = null;
116 
117         /// <summary>
118         /// When set to true, the typelib version will be included in the wrapper name.  Default is false.
119         /// </summary>
120         public bool IncludeVersionInInteropName
121         {
122             get
123             {
124                 return _includeVersionInInteropName;
125             }
126 
127             set
128             {
129                 _includeVersionInInteropName = value;
130             }
131         }
132 
133         private bool _includeVersionInInteropName;
134 
135         /// <summary>
136         /// source of resolved .NET assemblies - we need this for ActiveX wrappers, since we can't resolve .NET assembly
137         /// references ourselves
138         /// </summary>
139         public ITaskItem[] ResolvedAssemblyReferences
140         {
141             get
142             {
143                 return _resolvedAssemblyReferences;
144             }
145             set
146             {
147                 _resolvedAssemblyReferences = value;
148             }
149         }
150 
151         private ITaskItem[] _resolvedAssemblyReferences = null;
152 
153         /// <summary>
154         /// container name for public/private keys
155         /// </summary>
156         public string KeyContainer
157         {
158             get
159             {
160                 return _keyContainer;
161             }
162             set
163             {
164                 _keyContainer = value;
165             }
166         }
167 
168         private string _keyContainer = null;
169 
170         /// <summary>
171         /// file containing public/private keys
172         /// </summary>
173         public string KeyFile
174         {
175             get
176             {
177                 return _keyFile;
178             }
179             set
180             {
181                 _keyFile = value;
182             }
183         }
184 
185         private string _keyFile = null;
186 
187         /// <summary>
188         /// delay sign wrappers?
189         /// </summary>
190         public bool DelaySign
191         {
192             get
193             {
194                 return _delaySign;
195             }
196             set
197             {
198                 _delaySign = value;
199             }
200         }
201 
202         private bool _delaySign = false;
203 
204         /// <summary>
205         /// Passes the TypeLibImporterFlags.PreventClassMembers flag to tlb wrapper generation
206         /// </summary>
207         public bool NoClassMembers
208         {
209             get
210             {
211                 return _noClassMembers;
212             }
213             set
214             {
215                 _noClassMembers = value;
216             }
217         }
218 
219         private bool _noClassMembers = false;
220 
221         /// <summary>
222         /// If true, do not log messages or warnings.  Default is false.
223         /// </summary>
224         public bool Silent
225         {
226             get
227             {
228                 return _silent;
229             }
230 
231             set
232             {
233                 _silent = value;
234             }
235         }
236 
237         private bool _silent = false;
238 
239         /// <summary>
240         /// The preferred target processor architecture. Passed to tlbimp.exe /machine flag after translation.
241         /// Should be a member of Microsoft.Build.Utilities.ProcessorArchitecture.
242         /// </summary>
243         public string TargetProcessorArchitecture
244         {
245             get
246             {
247                 return _targetProcessorArchitecture;
248             }
249 
250             set
251             {
252                 if (UtilitiesProcessorArchitecture.X86.Equals(value, StringComparison.OrdinalIgnoreCase))
253                 {
254                     _targetProcessorArchitecture = UtilitiesProcessorArchitecture.X86;
255                 }
256                 else if (UtilitiesProcessorArchitecture.MSIL.Equals(value, StringComparison.OrdinalIgnoreCase))
257                 {
258                     _targetProcessorArchitecture = UtilitiesProcessorArchitecture.MSIL;
259                 }
260                 else if (UtilitiesProcessorArchitecture.AMD64.Equals(value, StringComparison.OrdinalIgnoreCase))
261                 {
262                     _targetProcessorArchitecture = UtilitiesProcessorArchitecture.AMD64;
263                 }
264                 else if (UtilitiesProcessorArchitecture.IA64.Equals(value, StringComparison.OrdinalIgnoreCase))
265                 {
266                     _targetProcessorArchitecture = UtilitiesProcessorArchitecture.IA64;
267                 }
268                 else if (UtilitiesProcessorArchitecture.ARM.Equals(value, StringComparison.OrdinalIgnoreCase))
269                 {
270                     _targetProcessorArchitecture = UtilitiesProcessorArchitecture.ARM;
271                 }
272                 else
273                 {
274                     _targetProcessorArchitecture = value;
275                 }
276             }
277         }
278 
279         private string _targetProcessorArchitecture = null;
280 
281         /// <summary>
282         /// Property to allow multitargeting of ResolveComReferences:  If true, tlbimp.exe
283         /// from the appropriate target framework will be run out-of-proc to generate
284         /// the necessary wrapper assemblies. Aximp is always run out of proc.
285         /// </summary>
286         public bool ExecuteAsTool
287         {
288             get
289             {
290                 return _executeAsTool;
291             }
292             set
293             {
294                 _executeAsTool = value;
295             }
296         }
297 
298         private bool _executeAsTool = true;
299 
300         /// <summary>
301         /// paths to found/generated reference wrappers
302         /// </summary>
303         [Output]
304         public ITaskItem[] ResolvedFiles
305         {
306             get
307             {
308                 return _resolvedFiles;
309             }
310             set
311             {
312                 _resolvedFiles = value;
313             }
314         }
315 
316         private ITaskItem[] _resolvedFiles = null;
317 
318         /// <summary>
319         /// paths to found modules (needed for isolation)
320         /// </summary>
321         [Output]
322         public ITaskItem[] ResolvedModules
323         {
324             get
325             {
326                 return _resolvedModules;
327             }
328             set
329             {
330                 _resolvedModules = value;
331             }
332         }
333 
334         private ITaskItem[] _resolvedModules = null;
335 
336         /// <summary>
337         /// If ExecuteAsTool is true, this must be set to the SDK
338         /// tools path for the framework version being targeted.
339         /// </summary>
340         public string SdkToolsPath
341         {
342             get { return _sdkToolsPath; }
343             set { _sdkToolsPath = value; }
344         }
345 
346         private string _sdkToolsPath;
347 
348         /// <summary>
349         /// Cache file for COM component timestamps. If not present, every run will regenerate all the wrappers.
350         /// </summary>
351         public string StateFile
352         {
353             get { return _stateFile; }
354             set { _stateFile = value; }
355         }
356 
357         private string _stateFile = null;
358 
359         /// <summary>
360         /// The project target framework version.
361         ///
362         /// Default is empty. which means there will be no filtering for the reference based on their target framework.
363         /// </summary>
364         /// <value></value>
365         public string TargetFrameworkVersion
366         {
367             get { return _projectTargetFrameworkAsString; }
368             set { _projectTargetFrameworkAsString = value; }
369         }
370 
371         private string _projectTargetFrameworkAsString = String.Empty;
372         private Version _projectTargetFramework;
373 
374 
375         /// <summary>version 4.0</summary>
376         private static readonly Version s_targetFrameworkVersion_40 = new Version("4.0");
377 
378         private ResolveComReferenceCache _timestampCache = null;
379 
380         // Cache hashtables for different wrapper types
381         private Hashtable _cachePia = new Hashtable();
382         private Hashtable _cacheTlb = new Hashtable();
383         private Hashtable _cacheAx = new Hashtable();
384 
385         // Paths for the out-of-proc tools being used
386         private string _aximpPath;
387         private string _tlbimpPath;
388 
389         #endregion
390 
391         #region ITask members
392 
393         /// <summary>
394         /// Task entry point.
395         /// </summary>
396         /// <returns></returns>
Execute()397         public override bool Execute()
398         {
399             if (!VerifyAndInitializeInputs())
400             {
401                 return false;
402             }
403 
404             if (!ComputePathToAxImp() || !ComputePathToTlbImp())
405             {
406                 // unable to compute the path to tlbimp.exe, aximp.exe, or both and that is necessary to
407                 // continue forward, so return now.
408                 return false;
409             }
410 
411             allProjectRefs = new List<ComReferenceInfo>();
412             allDependencyRefs = new List<ComReferenceInfo>();
413 
414             _timestampCache = (ResolveComReferenceCache)StateFileBase.DeserializeCache(StateFile, Log, typeof(ResolveComReferenceCache));
415 
416             if (_timestampCache == null || (_timestampCache != null && !_timestampCache.ToolPathsMatchCachePaths(_tlbimpPath, _aximpPath)))
417             {
418                 if (!Silent)
419                 {
420                     Log.LogMessageFromResources(MessageImportance.Low, "ResolveComReference.NotUsingCacheFile", StateFile == null ? String.Empty : StateFile);
421                 }
422 
423                 _timestampCache = new ResolveComReferenceCache(_tlbimpPath, _aximpPath);
424             }
425             else if (!Silent)
426             {
427                 Log.LogMessageFromResources(MessageImportance.Low, "ResolveComReference.UsingCacheFile", StateFile == null ? String.Empty : StateFile);
428             }
429 
430             try
431             {
432                 ConvertAttrReferencesToComReferenceInfo(allProjectRefs, TypeLibNames);
433                 ConvertFileReferencesToComReferenceInfo(allProjectRefs, TypeLibFiles);
434 
435                 // add missing tlbimp references for aximp ones
436                 AddMissingTlbReferences();
437 
438                 // see if we have any typelib name clashes. Ignore the return value - we now remove the conflicting refs
439                 // and continue (first one wins)
440                 CheckForConflictingReferences();
441 
442                 SetFrameworkVersionFromString(_projectTargetFrameworkAsString);
443 
444                 // Process each task item. If one of them fails we still process the rest of them, but
445                 // remember that the task should return failure.
446                 // DESIGN CHANGE: we no longer fail the task when one or more references fail to resolve.
447                 // Unless we experience a catastrophic failure, we'll log warnings for those refs and proceed
448                 // (and return success)
449                 ArrayList moduleList = new ArrayList();
450                 ArrayList resolvedReferenceList = new ArrayList();
451 
452                 ComDependencyWalker dependencyWalker = new ComDependencyWalker(Marshal.ReleaseComObject);
453                 bool allReferencesResolvedSuccessfully = true;
454                 for (int pass = 0; pass < 4; pass++)
455                 {
456                     foreach (ComReferenceInfo projectRefInfo in allProjectRefs)
457                     {
458                         string wrapperType = projectRefInfo.taskItem.GetMetadata(ComReferenceItemMetadataNames.wrapperTool);
459 
460                         // first resolve all PIA refs, then regular tlb refs and finally ActiveX refs
461                         if ((pass == 0 && ComReferenceTypes.IsPia(wrapperType)) ||
462                             (pass == 1 && ComReferenceTypes.IsTlbImp(wrapperType)) ||
463                             (pass == 2 && ComReferenceTypes.IsPiaOrTlbImp(wrapperType)) ||
464                             (pass == 3 && ComReferenceTypes.IsAxImp(wrapperType)))
465                         {
466                             try
467                             {
468                                 if (!this.ResolveReferenceAndAddToList(dependencyWalker, projectRefInfo, resolvedReferenceList, moduleList))
469                                 {
470                                     allReferencesResolvedSuccessfully = false;
471                                 }
472                             }
473                             catch (ComReferenceResolutionException)
474                             {
475                                 // problem resolving this reference? continue so that we can display all error messages
476                             }
477                             catch (StrongNameException)
478                             {
479                                 // key extraction problem? No point in continuing, since all wrappers will hit the same problem.
480                                 // error message has already been logged
481                                 return false;
482                             }
483                             catch (FileLoadException ex)
484                             {
485                                 // This exception is thrown when we try to load a delay signed assembly without disabling
486                                 // strong name verification first. So print a nice information if we're generating
487                                 // delay signed wrappers, otherwise rethrow, since it's an unexpected exception.
488                                 if (DelaySign)
489                                 {
490                                     Log.LogErrorWithCodeFromResources(null, projectRefInfo.SourceItemSpec, 0, 0, 0, 0, "ResolveComReference.LoadingDelaySignedAssemblyWithStrongNameVerificationEnabled", ex.Message);
491 
492                                     // no point in printing the same thing multiple times...
493                                     return false;
494                                 }
495                                 else
496                                 {
497                                     Debug.Assert(false, "Unexpected exception in ResolveComReference.Execute. " +
498                                         "Please log a MSBuild bug specifying the steps to reproduce the problem.");
499                                     throw;
500                                 }
501                             }
502                             catch (ArgumentException ex)
503                             {
504                                 // This exception is thrown when we try to convert some of the Metadata from the project
505                                 // file and the conversion fails.  Most likely, the user needs to correct a type in the
506                                 // project file.
507                                 Log.LogErrorWithCodeFromResources("General.InvalidArgument", ex.Message);
508                                 return false;
509                             }
510                             catch (SystemException ex)
511                             {
512                                 Log.LogErrorWithCodeFromResources("ResolveComReference.FailedToResolveComReference",
513                                     projectRefInfo.attr.guid, projectRefInfo.attr.wMajorVerNum, projectRefInfo.attr.wMinorVerNum,
514                                     ex.Message);
515                             }
516                         }
517                     }
518                 }
519 
520                 SetCopyLocalToFalseOnGacOrNoPIAAssemblies(resolvedReferenceList, GlobalAssemblyCache.GetGacPath());
521 
522                 ResolvedModules = (ITaskItem[])moduleList.ToArray(typeof(ITaskItem));
523                 ResolvedFiles = (ITaskItem[])resolvedReferenceList.ToArray(typeof(ITaskItem));
524 
525                 // The Logs from AxImp and TlbImp aren't part of our log, but if the task failed, it will return false from
526                 // GenerateWrapper, which should get passed all the way back up here.
527                 return allReferencesResolvedSuccessfully && !Log.HasLoggedErrors;
528             }
529             finally
530             {
531                 if ((_timestampCache != null) && _timestampCache.Dirty)
532                 {
533                     _timestampCache.SerializeCache(StateFile, Log);
534                 }
535 
536                 Cleanup();
537             }
538         }
539 
540         #endregion
541 
542         #region Methods
543 
544         /// <summary>
545         /// Converts the string target framework value to a number.
546         /// Accepts both "v" prefixed and no "v" prefixed formats
547         /// if format is bad will log a message and return 0.
548         /// </summary>
549         /// <returns>Target framework version value</returns>
SetFrameworkVersionFromString(string version)550         internal void SetFrameworkVersionFromString(string version)
551         {
552             Version parsedVersion = null;
553             if (!String.IsNullOrEmpty(version))
554             {
555                 parsedVersion = VersionUtilities.ConvertToVersion(version);
556 
557                 if (parsedVersion == null && !Silent)
558                 {
559                     Log.LogMessageFromResources(MessageImportance.Normal, "ResolveComReference.BadTargetFrameworkFormat", version);
560                 }
561             }
562 
563             _projectTargetFramework = parsedVersion;
564             return;
565         }
566 
567         /// <summary>
568         /// Computes the path to TlbImp.exe for use in logging and for passing to the
569         /// nested TlbImp task.
570         /// </summary>
571         /// <returns>True if the path is found (or it doesn't matter because we're executing in memory), false otherwise</returns>
ComputePathToTlbImp()572         private bool ComputePathToTlbImp()
573         {
574             _tlbimpPath = null;
575 
576             if (String.IsNullOrEmpty(_sdkToolsPath))
577             {
578                 _tlbimpPath = GetPathToSDKFileWithCurrentlyTargetedArchitecture("TlbImp.exe", TargetDotNetFrameworkVersion.Version35, VisualStudioVersion.VersionLatest);
579 
580                 if (null == _tlbimpPath && ExecuteAsTool)
581                 {
582                     Log.LogErrorWithCodeFromResources("General.PlatformSDKFileNotFound", "TlbImp.exe",
583                         ToolLocationHelper.GetDotNetFrameworkSdkInstallKeyValue(TargetDotNetFrameworkVersion.Version35, VisualStudioVersion.VersionLatest),
584                         ToolLocationHelper.GetDotNetFrameworkSdkRootRegistryKey(TargetDotNetFrameworkVersion.Version35, VisualStudioVersion.VersionLatest));
585                 }
586             }
587             else
588             {
589                 _tlbimpPath = SdkToolsPathUtility.GeneratePathToTool(SdkToolsPathUtility.FileInfoExists, this.TargetProcessorArchitecture, SdkToolsPath, "TlbImp.exe", Log, ExecuteAsTool);
590             }
591 
592             if (null == _tlbimpPath && !ExecuteAsTool)
593             {
594                 // if TlbImp.exe is not installed, just use the filename
595                 _tlbimpPath = "TlbImp.exe";
596                 return true;
597             }
598 
599             if (_tlbimpPath != null)
600             {
601                 _tlbimpPath = Path.GetDirectoryName(_tlbimpPath);
602             }
603 
604             return _tlbimpPath != null;
605         }
606 
607         /// <summary>
608         /// Computes the path to AxImp.exe for use in logging and for passing to the
609         /// nested AxImp task.
610         /// </summary>
611         /// <returns>True if the path is found, false otherwise</returns>
ComputePathToAxImp()612         private bool ComputePathToAxImp()
613         {
614             // We always execute AxImp.exe out of proc
615             _aximpPath = null;
616 
617             if (String.IsNullOrEmpty(_sdkToolsPath))
618             {
619                 // In certain cases -- such as trying to build a Dev10 project on a machine that only has Dev11 installed --
620                 // it's possible to have ExecuteAsTool set to false (e.g. "use the current CLR") but still have SDKToolsPath
621                 // be empty (because it's referencing the 7.0A SDK in the registry, which doesn't exist).  In that case, we
622                 // want to look for VersionLatest.  However, if ExecuteAsTool is true (default value) and SDKToolsPath is
623                 // empty, then we can safely assume that we want to get the 3.5 version of the tool.
624                 TargetDotNetFrameworkVersion targetAxImpVersion = ExecuteAsTool ? TargetDotNetFrameworkVersion.Version35 : TargetDotNetFrameworkVersion.Latest;
625 
626                 // We want to use the copy of AxImp corresponding to our targeted architecture if possible.
627                 _aximpPath = GetPathToSDKFileWithCurrentlyTargetedArchitecture("AxImp.exe", targetAxImpVersion, VisualStudioVersion.VersionLatest);
628 
629                 if (null == _aximpPath)
630                 {
631                     Log.LogErrorWithCodeFromResources("General.PlatformSDKFileNotFound", "AxImp.exe",
632                         ToolLocationHelper.GetDotNetFrameworkSdkInstallKeyValue(targetAxImpVersion, VisualStudioVersion.VersionLatest),
633                         ToolLocationHelper.GetDotNetFrameworkSdkRootRegistryKey(targetAxImpVersion, VisualStudioVersion.VersionLatest));
634                 }
635             }
636             else
637             {
638                 _aximpPath = SdkToolsPathUtility.GeneratePathToTool(SdkToolsPathUtility.FileInfoExists, this.TargetProcessorArchitecture, SdkToolsPath, "AxImp.exe", Log, true /* log errors */);
639             }
640 
641             if (_aximpPath != null)
642             {
643                 _aximpPath = Path.GetDirectoryName(_aximpPath);
644             }
645 
646             return _aximpPath != null;
647         }
648 
649         /// <summary>
650         /// Try to get the path to the tool in the Windows SDK with the given .NET Framework version and
651         /// of the same architecture as we were currently given for TargetProcessorArchitecture.
652         /// </summary>
GetPathToSDKFileWithCurrentlyTargetedArchitecture(string file, TargetDotNetFrameworkVersion targetFrameworkVersion, VisualStudioVersion visualStudioVersion)653         private string GetPathToSDKFileWithCurrentlyTargetedArchitecture(string file, TargetDotNetFrameworkVersion targetFrameworkVersion, VisualStudioVersion visualStudioVersion)
654         {
655             string path = null;
656 
657             switch (this.TargetProcessorArchitecture)
658             {
659                 case UtilitiesProcessorArchitecture.ARM:
660                 case UtilitiesProcessorArchitecture.X86:
661                     path = ToolLocationHelper.GetPathToDotNetFrameworkSdkFile(file, targetFrameworkVersion, visualStudioVersion, DotNetFrameworkArchitecture.Bitness32);
662                     break;
663                 case UtilitiesProcessorArchitecture.AMD64:
664                 case UtilitiesProcessorArchitecture.IA64:
665                     path = ToolLocationHelper.GetPathToDotNetFrameworkSdkFile(file, targetFrameworkVersion, visualStudioVersion, DotNetFrameworkArchitecture.Bitness64);
666                     break;
667                 case UtilitiesProcessorArchitecture.MSIL:
668                 default:
669                     // just go with the default lookup
670                     break;
671             }
672 
673             if (path == null)
674             {
675                 // fall back to the default lookup (current architecture / x86) just in case it's found there ...
676                 path = ToolLocationHelper.GetPathToDotNetFrameworkSdkFile(file, targetFrameworkVersion, visualStudioVersion);
677             }
678 
679             return path;
680         }
681 
682         /// <summary>
683         /// Clean various caches and other state that should not be preserved between subsequent runs
684         /// </summary>
Cleanup()685         private void Cleanup()
686         {
687             // clear the wrapper caches - since references can change between runs, wrapper objects should not be reused
688             _cacheAx.Clear();
689             _cachePia.Clear();
690             _cacheTlb.Clear();
691 
692             // release COM interface pointers for dependency references
693             foreach (ComReferenceInfo dependencyRefInfo in allDependencyRefs)
694             {
695                 dependencyRefInfo.ReleaseTypeLibPtr();
696             }
697 
698             // release COM interface pointers for project references
699             foreach (ComReferenceInfo projectRefInfo in allProjectRefs)
700             {
701                 projectRefInfo.ReleaseTypeLibPtr();
702             }
703         }
704 
705         /*
706          * Method:  VerifyAndInitializeInputs
707          *
708          * Helper method. Verifies the input task items have correct metadata and initializes optional ones with
709          * default values if they're not present.
710          */
VerifyAndInitializeInputs()711         private bool VerifyAndInitializeInputs()
712         {
713             if ((KeyContainer != null && KeyContainer.Length != 0)
714                 && (KeyFile != null && KeyFile.Length != 0))
715             {
716                 Log.LogErrorWithCodeFromResources("ResolveComReference.CannotSpecifyBothKeyFileAndKeyContainer");
717                 return false;
718             }
719 
720             if (DelaySign)
721             {
722                 if ((KeyContainer == null || KeyContainer.Length == 0) &&
723                     (KeyFile == null || KeyFile.Length == 0))
724                 {
725                     Log.LogErrorWithCodeFromResources("ResolveComReference.CannotSpecifyDelaySignWithoutEitherKeyFileOrKeyContainer");
726                     return false;
727                 }
728             }
729 
730             // if no output directory specified, default to the project directory
731             if (WrapperOutputDirectory == null)
732             {
733                 WrapperOutputDirectory = String.Empty;
734             }
735 
736             int typeLibNamesLength = (TypeLibNames == null) ? 0 : TypeLibNames.GetLength(0);
737             int typeLibFilesLength = (TypeLibFiles == null) ? 0 : TypeLibFiles.GetLength(0);
738 
739             // nothing to do? we cannot tell the difference between not passing in anything and passing in empty list,
740             // so let's just exit.
741             if (typeLibFilesLength + typeLibNamesLength == 0)
742             {
743                 Log.LogErrorWithCodeFromResources("ResolveComReference.NoComReferencesSpecified");
744                 return false;
745             }
746 
747             bool metadataValid = true;
748 
749             for (int i = 0; i < typeLibNamesLength; i++)
750             {
751                 // verify the COM reference item contains all the required attributes
752                 string missingMetadata;
753 
754                 if (!VerifyReferenceMetadataForNameItem(TypeLibNames[i], out missingMetadata))
755                 {
756                     Log.LogErrorWithCodeFromResources(null, TypeLibNames[i].ItemSpec, 0, 0, 0, 0, "ResolveComReference.MissingOrUnknownComReferenceAttribute", missingMetadata, TypeLibNames[i].ItemSpec);
757 
758                     // don't exit immediately... check all the refs and display all errors
759                     metadataValid = false;
760                 }
761                 else
762                 {
763                     // Initialize optional attributes with default values if they're missing
764                     InitializeDefaultMetadataForNameItem(TypeLibNames[i]);
765                 }
766             }
767 
768             for (int i = 0; i < typeLibFilesLength; i++)
769             {
770                 // File COM references don't have any required metadata, so no verification necessary here
771                 // Initialize optional metadata with default values if they're missing
772                 InitializeDefaultMetadataForFileItem(TypeLibFiles[i]);
773             }
774 
775             return metadataValid;
776         }
777 
778         /*
779          * Method:  ConvertAttrReferencesToComReferenceInfo
780          *
781          * Helper method. Converts TypeLibAttr references to ComReferenceInfo objects.
782          * This method cannot fail, since we want to proceed with the task even if some references won't load.
783          */
ConvertAttrReferencesToComReferenceInfo(List<ComReferenceInfo> projectRefs, ITaskItem[] typeLibAttrs)784         private void ConvertAttrReferencesToComReferenceInfo(List<ComReferenceInfo> projectRefs, ITaskItem[] typeLibAttrs)
785         {
786             int typeLibAttrsLength = (typeLibAttrs == null) ? 0 : typeLibAttrs.GetLength(0);
787 
788             for (int i = 0; i < typeLibAttrsLength; i++)
789             {
790                 ComReferenceInfo projectRefInfo = new ComReferenceInfo();
791 
792                 try
793                 {
794                     if (projectRefInfo.InitializeWithTypeLibAttrs(Log, Silent, TaskItemToTypeLibAttr(typeLibAttrs[i]), typeLibAttrs[i], this.TargetProcessorArchitecture))
795                     {
796                         projectRefs.Add(projectRefInfo);
797                     }
798                     else
799                     {
800                         projectRefInfo.ReleaseTypeLibPtr();
801                     }
802                 }
803                 catch (COMException ex)
804                 {
805                     if (!Silent)
806                     {
807                         Log.LogWarningWithCodeFromResources("ResolveComReference.CannotLoadTypeLibItemSpec", typeLibAttrs[i].ItemSpec, ex.Message);
808                     }
809 
810                     projectRefInfo.ReleaseTypeLibPtr();
811                     // we don't want to fail the task if one of the references is not registered, so just continue
812                 }
813             }
814         }
815 
816         /*
817          * Method:  ConvertFileReferencesToComReferenceInfo
818          *
819          * Helper method. Converts TypeLibFiles references to ComReferenceInfo objects
820          * This method cannot fail, since we want to proceed with the task even if some references won't load.
821          */
ConvertFileReferencesToComReferenceInfo(List<ComReferenceInfo> projectRefs, ITaskItem[] tlbFiles)822         private void ConvertFileReferencesToComReferenceInfo(List<ComReferenceInfo> projectRefs, ITaskItem[] tlbFiles)
823         {
824             int tlbFilesLength = (tlbFiles == null) ? 0 : tlbFiles.GetLength(0);
825 
826             for (int i = 0; i < tlbFilesLength; i++)
827             {
828                 string refPath = tlbFiles[i].ItemSpec;
829 
830                 if (!Path.IsPathRooted(refPath))
831                     refPath = Path.Combine(Directory.GetCurrentDirectory(), refPath);
832 
833                 ComReferenceInfo projectRefInfo = new ComReferenceInfo();
834 
835                 try
836                 {
837                     if (projectRefInfo.InitializeWithPath(Log, Silent, refPath, tlbFiles[i], this.TargetProcessorArchitecture))
838                     {
839                         projectRefs.Add(projectRefInfo);
840                     }
841                     else
842                     {
843                         projectRefInfo.ReleaseTypeLibPtr();
844                     }
845                 }
846                 catch (COMException ex)
847                 {
848                     if (!Silent)
849                     {
850                         Log.LogWarningWithCodeFromResources("ResolveComReference.CannotLoadTypeLibItemSpec", tlbFiles[i].ItemSpec, ex.Message);
851                     }
852 
853                     projectRefInfo.ReleaseTypeLibPtr();
854                     // we don't want to fail the task if one of the references is not registered, so just continue
855                 }
856             }
857         }
858 
859         /// <summary>
860         /// Every ActiveX reference (aximp) requires a corresponding tlbimp reference. If the tlbimp reference is
861         /// missing from the project file we pretend it's there to save the user some useless typing.
862         /// </summary>
AddMissingTlbReferences()863         internal void AddMissingTlbReferences()
864         {
865             var newProjectRefs = new List<ComReferenceInfo>();
866 
867             foreach (ComReferenceInfo axRefInfo in allProjectRefs)
868             {
869                 // Try to find the matching tlbimp/pia reference for each aximp reference
870                 // There is an obscured case in this algorithm: there may be more than one match. Arbitrarily chooses the first.
871                 if (ComReferenceTypes.IsAxImp(axRefInfo.taskItem.GetMetadata(ComReferenceItemMetadataNames.wrapperTool)))
872                 {
873                     bool matchingTlbRefPresent = false;
874 
875                     foreach (ComReferenceInfo tlbRefInfo in allProjectRefs)
876                     {
877                         string tlbWrapperType = tlbRefInfo.taskItem.GetMetadata(ComReferenceItemMetadataNames.wrapperTool);
878 
879                         if (ComReferenceTypes.IsTlbImp(tlbWrapperType) || ComReferenceTypes.IsPia(tlbWrapperType) || ComReferenceTypes.IsPiaOrTlbImp(tlbWrapperType))
880                         {
881                             if (ComReference.AreTypeLibAttrEqual(axRefInfo.attr, tlbRefInfo.attr))
882                             {
883                                 axRefInfo.taskItem.SetMetadata(ComReferenceItemMetadataNames.tlbReferenceName, tlbRefInfo.typeLibName);
884 
885                                 // Check and demote EmbedInteropTypes to "false" for wrappers of ActiveX controls. The compilers won't embed
886                                 // the ActiveX control and so will transitively turn this wrapper into a reference as well. We need to know to
887                                 // make the wrapper CopyLocal=true later so switch to EmbedInteropTypes=false now.
888                                 string embedInteropTypes = tlbRefInfo.taskItem.GetMetadata(ItemMetadataNames.embedInteropTypes);
889                                 if (ConversionUtilities.CanConvertStringToBool(embedInteropTypes))
890                                 {
891                                     if (ConversionUtilities.ConvertStringToBool(embedInteropTypes))
892                                     {
893                                         if (!Silent)
894                                         {
895                                             Log.LogMessageFromResources(MessageImportance.High, "ResolveComReference.TreatingTlbOfActiveXAsNonEmbedded", tlbRefInfo.taskItem.ItemSpec, axRefInfo.taskItem.ItemSpec);
896                                         }
897 
898                                         tlbRefInfo.taskItem.SetMetadata(ItemMetadataNames.embedInteropTypes, "false");
899                                     }
900                                 }
901                                 axRefInfo.primaryOfAxImpRef = tlbRefInfo;
902                                 matchingTlbRefPresent = true;
903                                 break;
904                             }
905                         }
906                     }
907 
908                     // add the matching tlbimp ref if not already there
909                     if (!matchingTlbRefPresent)
910                     {
911                         if (!Silent)
912                         {
913                             Log.LogMessageFromResources(MessageImportance.Low, "ResolveComReference.AddingMissingTlbReference", axRefInfo.taskItem.ItemSpec);
914                         }
915 
916                         ComReferenceInfo newTlbRef = new ComReferenceInfo(axRefInfo);
917                         newTlbRef.taskItem.SetMetadata(ComReferenceItemMetadataNames.wrapperTool, ComReferenceTypes.primaryortlbimp);
918                         newTlbRef.taskItem.SetMetadata(ItemMetadataNames.embedInteropTypes, "false");
919                         axRefInfo.primaryOfAxImpRef = newTlbRef;
920 
921                         newProjectRefs.Add(newTlbRef);
922                         axRefInfo.taskItem.SetMetadata(ComReferenceItemMetadataNames.tlbReferenceName, newTlbRef.typeLibName);
923                     }
924                 }
925             }
926 
927             foreach (ComReferenceInfo refInfo in newProjectRefs)
928             {
929                 allProjectRefs.Add(refInfo);
930             }
931         }
932 
933         /// <summary>
934         /// Resolves the COM reference, and adds it to the appropriate item list.
935         /// </summary>
936         /// <param name="projectRefInfo"></param>
937         /// <param name="resolvedReferenceList"></param>
938         /// <param name="moduleList"></param>
939         /// <returns></returns>
ResolveReferenceAndAddToList( ComDependencyWalker dependencyWalker, ComReferenceInfo projectRefInfo, ArrayList resolvedReferenceList, ArrayList moduleList )940         private bool ResolveReferenceAndAddToList
941             (
942             ComDependencyWalker dependencyWalker,
943             ComReferenceInfo projectRefInfo,
944             ArrayList resolvedReferenceList,
945             ArrayList moduleList
946             )
947         {
948             ITaskItem referencePath;
949 
950             if (ResolveReference(dependencyWalker, projectRefInfo, WrapperOutputDirectory, out referencePath))
951             {
952                 resolvedReferenceList.Add(referencePath);
953 
954                 bool metadataFound = false;
955                 bool isolated = MetadataConversionUtilities.TryConvertItemMetadataToBool(projectRefInfo.taskItem, "Isolated", out metadataFound);
956 
957                 if (metadataFound && isolated)
958                 {
959                     string modulePath = projectRefInfo.strippedTypeLibPath;
960                     if (modulePath != null)
961                     {
962                         ITaskItem moduleItem = new TaskItem(modulePath);
963                         moduleItem.SetMetadata("Name", projectRefInfo.taskItem.ItemSpec);
964                         moduleList.Add(moduleItem);
965                     }
966                     else
967                     {
968                         return false;
969                     }
970                 }
971             }
972             else
973             {
974                 return false;
975             }
976 
977             return true;
978         }
979 
980         /*
981          * Method:  ResolveReference
982          *
983          * Helper COM resolution method. Creates an appropriate helper class for the given tool and calls
984          * the Resolve method on it.
985          */
ResolveReference(ComDependencyWalker dependencyWalker, ComReferenceInfo referenceInfo, string outputDirectory, out ITaskItem referencePathItem)986         internal bool ResolveReference(ComDependencyWalker dependencyWalker, ComReferenceInfo referenceInfo, string outputDirectory, out ITaskItem referencePathItem)
987         {
988             if (referenceInfo.referencePathItem == null)
989             {
990                 if (!Silent)
991                 {
992                     Log.LogMessageFromResources(MessageImportance.Low, "ResolveComReference.Resolving", referenceInfo.taskItem.ItemSpec, referenceInfo.taskItem.GetMetadata(ComReferenceItemMetadataNames.wrapperTool));
993                 }
994 
995                 List<string> dependencyPaths = ScanAndResolveAllDependencies(dependencyWalker, referenceInfo);
996 
997                 referenceInfo.dependentWrapperPaths = dependencyPaths;
998                 referencePathItem = new TaskItem();
999                 referenceInfo.referencePathItem = referencePathItem;
1000 
1001                 ComReferenceWrapperInfo wrapperInfo;
1002 
1003                 if (ResolveComClassicReference(referenceInfo, outputDirectory,
1004                     referenceInfo.taskItem.GetMetadata(ComReferenceItemMetadataNames.wrapperTool),
1005                     referenceInfo.taskItem.ItemSpec, true, referenceInfo.dependentWrapperPaths, out wrapperInfo))
1006                 {
1007                     referencePathItem.ItemSpec = wrapperInfo.path;
1008                     referenceInfo.taskItem.CopyMetadataTo(referencePathItem);
1009 
1010                     string fusionName = AssemblyName.GetAssemblyName(wrapperInfo.path).FullName;
1011                     referencePathItem.SetMetadata(ItemMetadataNames.fusionName, fusionName);
1012 
1013                     if (!Silent)
1014                     {
1015                         Log.LogMessageFromResources(MessageImportance.Low, "ResolveComReference.ResolvedReference", referenceInfo.taskItem.ItemSpec, wrapperInfo.path);
1016                     }
1017 
1018                     return true;
1019                 }
1020 
1021                 if (!Silent)
1022                 {
1023                     Log.LogWarningWithCodeFromResources("ResolveComReference.CannotFindWrapperForTypeLib", referenceInfo.taskItem.ItemSpec);
1024                 }
1025 
1026                 return false;
1027             }
1028             else
1029             {
1030                 bool successfullyResolved = !String.IsNullOrEmpty(referenceInfo.referencePathItem.ItemSpec);
1031                 referencePathItem = referenceInfo.referencePathItem;
1032 
1033                 return successfullyResolved;
1034             }
1035         }
1036 
1037         /*
1038          * Method:  IsExistingProjectReference
1039          *
1040          * If given typelib attributes are already a project reference, return that reference.
1041          */
IsExistingProjectReference(TYPELIBATTR typeLibAttr, string neededRefType, out ComReferenceInfo referenceInfo)1042         internal bool IsExistingProjectReference(TYPELIBATTR typeLibAttr, string neededRefType, out ComReferenceInfo referenceInfo)
1043         {
1044             for (int pass = 0; pass < 3; pass++)
1045             {
1046                 // First PIAs, then tlbimps, then aximp
1047                 // Only execute each pass if the needed ref type matches or is null
1048                 // Important: the condition for Ax wrapper is different, since we don't want to find Ax references
1049                 // for unknown wrapper types - "unknown" wrapper means we're only looking for a tlbimp or a primary reference
1050                 if ((pass == 0 && (ComReferenceTypes.IsPia(neededRefType) || neededRefType == null)) ||
1051                     (pass == 1 && (ComReferenceTypes.IsTlbImp(neededRefType) || neededRefType == null)) ||
1052                     (pass == 2 && (ComReferenceTypes.IsAxImp(neededRefType))))
1053                 {
1054                     foreach (ComReferenceInfo projectRefInfo in allProjectRefs)
1055                     {
1056                         string wrapperType = projectRefInfo.taskItem.GetMetadata(ComReferenceItemMetadataNames.wrapperTool);
1057 
1058                         // First PIAs, then tlbimps, then aximp
1059                         if ((pass == 0 && ComReferenceTypes.IsPia(wrapperType)) ||
1060                             (pass == 1 && ComReferenceTypes.IsTlbImp(wrapperType)) ||
1061                             (pass == 2 && ComReferenceTypes.IsAxImp(wrapperType)))
1062                         {
1063                             // found it? return the existing reference
1064                             if (ComReference.AreTypeLibAttrEqual(projectRefInfo.attr, typeLibAttr))
1065                             {
1066                                 referenceInfo = projectRefInfo;
1067                                 return true;
1068                             }
1069                         }
1070                     }
1071                 }
1072             }
1073 
1074             referenceInfo = null;
1075             return false;
1076         }
1077 
1078         /*
1079          * Method:  IsExistingDependencyReference
1080          *
1081          * If given typelib attributes are already a dependency reference (that is, was already
1082          * processed) return that reference.
1083          */
IsExistingDependencyReference(TYPELIBATTR typeLibAttr, out ComReferenceInfo referenceInfo)1084         internal bool IsExistingDependencyReference(TYPELIBATTR typeLibAttr, out ComReferenceInfo referenceInfo)
1085         {
1086             foreach (ComReferenceInfo dependencyRefInfo in allDependencyRefs)
1087             {
1088                 // found it? return the existing reference
1089                 if (ComReference.AreTypeLibAttrEqual(dependencyRefInfo.attr, typeLibAttr))
1090                 {
1091                     referenceInfo = dependencyRefInfo;
1092                     return true;
1093                 }
1094             }
1095 
1096             referenceInfo = null;
1097             return false;
1098         }
1099 
1100         /*
1101          * Method:  ResolveComClassicReference
1102          *
1103          * Resolves a COM classic reference given the type library attributes and the type of wrapper to use.
1104          * If wrapper type is not specified, this method will first look for an existing reference in the project,
1105          * fall back to looking for a PIA and finally try to generate a regular tlbimp wrapper.
1106          */
ResolveComClassicReference(ComReferenceInfo referenceInfo, string outputDirectory, string wrapperType, string refName, bool topLevelRef, List<string> dependencyPaths, out ComReferenceWrapperInfo wrapperInfo)1107         internal bool ResolveComClassicReference(ComReferenceInfo referenceInfo, string outputDirectory, string wrapperType, string refName, bool topLevelRef, List<string> dependencyPaths, out ComReferenceWrapperInfo wrapperInfo)
1108         {
1109             wrapperInfo = null;
1110 
1111             bool retVal = false;
1112 
1113             // only look for an existing PIA
1114             if (ComReferenceTypes.IsPia(wrapperType))
1115             {
1116                 retVal = ResolveComReferencePia(referenceInfo, refName, out wrapperInfo);
1117             }
1118             // find/generate a tlb wrapper
1119             else if (ComReferenceTypes.IsTlbImp(wrapperType))
1120             {
1121                 retVal = ResolveComReferenceTlb(referenceInfo, outputDirectory, refName, topLevelRef, dependencyPaths, out wrapperInfo);
1122             }
1123             // find/generate an Ax wrapper
1124             else if (ComReferenceTypes.IsAxImp(wrapperType))
1125             {
1126                 retVal = ResolveComReferenceAx(referenceInfo, outputDirectory, refName, out wrapperInfo);
1127             }
1128             // find/generate a pia/tlb wrapper (it's only possible to get here via a callback)
1129             else if (wrapperType == null || ComReferenceTypes.IsPiaOrTlbImp(wrapperType))
1130             {
1131                 // if this reference does not exist in the project, try looking for a PIA first
1132                 retVal = ResolveComReferencePia(referenceInfo, refName, out wrapperInfo);
1133                 if (!retVal)
1134                 {
1135                     // failing that, try a regular tlb wrapper
1136                     retVal = ResolveComReferenceTlb(referenceInfo, outputDirectory, refName, false /* dependency */, dependencyPaths, out wrapperInfo);
1137                 }
1138             }
1139             else
1140             {
1141                 ErrorUtilities.VerifyThrow(false, "Unknown wrapper type!");
1142             }
1143             referenceInfo.resolvedWrapper = wrapperInfo;
1144 
1145             // update the timestamp cache with the timestamp of the component we just processed
1146             _timestampCache[referenceInfo.strippedTypeLibPath] = File.GetLastWriteTime(referenceInfo.strippedTypeLibPath);
1147 
1148             return retVal;
1149         }
1150 
1151         /*
1152          * Method:  ResolveComClassicReference
1153          *
1154          * Resolves a COM classic reference given the type library attributes and the type of wrapper to use.
1155          * If wrapper type is not specified, this method will first look for an existing reference in the project,
1156          * fall back to looking for a PIA and finally try to generate a regular tlbimp wrapper.
1157          *
1158          * This is the method available for references to call back to resolve their dependencies
1159          */
IComReferenceResolver.ResolveComClassicReference(TYPELIBATTR typeLibAttr, string outputDirectory, string wrapperType, string refName, out ComReferenceWrapperInfo wrapperInfo)1160         bool IComReferenceResolver.ResolveComClassicReference(TYPELIBATTR typeLibAttr, string outputDirectory, string wrapperType, string refName, out ComReferenceWrapperInfo wrapperInfo)
1161         {
1162             // does this reference exist in the project or is it a dependency?
1163             bool topLevelRef = false;
1164 
1165             wrapperInfo = null;
1166 
1167             // remap the type lib to ADO 2.7 if necessary
1168             TYPELIBATTR oldAttr = typeLibAttr;
1169 
1170             if (ComReference.RemapAdoTypeLib(Log, Silent, ref typeLibAttr) && !Silent)
1171             {
1172                 // if successfully remapped the reference to ADO 2.7, notify the user
1173                 Log.LogMessageFromResources(MessageImportance.Low, "ResolveComReference.RemappingAdoTypeLib", oldAttr.wMajorVerNum, oldAttr.wMinorVerNum);
1174             }
1175 
1176             ComReferenceInfo referenceInfo;
1177 
1178             // find an existing ref in the project (taking the desired wrapperType into account, if any)
1179             if (IsExistingProjectReference(typeLibAttr, wrapperType, out referenceInfo))
1180             {
1181                 // IsExistingProjectReference should not return null...
1182                 Debug.Assert(referenceInfo != null, "IsExistingProjectReference should not return null");
1183                 topLevelRef = true;
1184                 wrapperType = referenceInfo.taskItem.GetMetadata(ComReferenceItemMetadataNames.wrapperTool);
1185             }
1186             // was this dependency already processed?
1187             else if (IsExistingDependencyReference(typeLibAttr, out referenceInfo))
1188             {
1189                 Debug.Assert(referenceInfo != null, "IsExistingDependencyReference should not return null");
1190 
1191                 // we've seen this dependency before, so we should know what its wrapper type is.
1192                 if (wrapperType == null || ComReferenceTypes.IsPiaOrTlbImp(wrapperType))
1193                 {
1194                     string typeLibKey = ComReference.UniqueKeyFromTypeLibAttr(typeLibAttr);
1195                     if (_cachePia.ContainsKey(typeLibKey))
1196                     {
1197                         wrapperType = ComReferenceTypes.primary;
1198                     }
1199                     else if (_cacheTlb.ContainsKey(typeLibKey))
1200                     {
1201                         wrapperType = ComReferenceTypes.tlbimp;
1202                     }
1203                 }
1204             }
1205             // if not found anywhere, create a new ComReferenceInfo object and resolve it.
1206             else
1207             {
1208                 try
1209                 {
1210                     referenceInfo = new ComReferenceInfo();
1211 
1212                     if (referenceInfo.InitializeWithTypeLibAttrs(Log, Silent, typeLibAttr, null, this.TargetProcessorArchitecture))
1213                     {
1214                         allDependencyRefs.Add(referenceInfo);
1215                     }
1216                     else
1217                     {
1218                         referenceInfo.ReleaseTypeLibPtr();
1219                         return false;
1220                     }
1221                 }
1222                 catch (COMException ex)
1223                 {
1224                     if (!Silent)
1225                     {
1226                         Log.LogWarningWithCodeFromResources("ResolveComReference.CannotLoadTypeLib", typeLibAttr.guid,
1227                             typeLibAttr.wMajorVerNum.ToString(CultureInfo.InvariantCulture),
1228                             typeLibAttr.wMinorVerNum.ToString(CultureInfo.InvariantCulture),
1229                             ex.Message);
1230                     }
1231 
1232                     referenceInfo.ReleaseTypeLibPtr();
1233 
1234                     // can't resolve an unregistered and unknown dependency, so return false
1235                     return false;
1236                 }
1237             }
1238 
1239             // if we don't have the reference name, use the typelib name
1240             if (refName == null)
1241             {
1242                 refName = referenceInfo.typeLibName;
1243             }
1244 
1245             return ResolveComClassicReference(referenceInfo, outputDirectory, wrapperType, refName, topLevelRef, referenceInfo.dependentWrapperPaths, out wrapperInfo);
1246         }
1247 
1248         /*
1249          * Method:  ResolveNetAssemblyReference
1250          *
1251          * Resolves a .NET assembly reference using the list of resolved managed references supplied to the task.
1252          *
1253          * This is the method available for references to call back to resolve their dependencies
1254          */
IComReferenceResolver.ResolveNetAssemblyReference(string assemblyName, out string assemblyPath)1255         bool IComReferenceResolver.ResolveNetAssemblyReference(string assemblyName, out string assemblyPath)
1256         {
1257             int commaIndex = assemblyName.IndexOf(',');
1258 
1259             // if we have a strong name, strip off everything but the assembly name
1260             if (commaIndex != -1)
1261                 assemblyName = assemblyName.Substring(0, commaIndex);
1262 
1263             assemblyName += ".dll";
1264 
1265             for (int i = 0; i < ResolvedAssemblyReferences.GetLength(0); i++)
1266             {
1267                 if (String.Compare(Path.GetFileName(ResolvedAssemblyReferences[i].ItemSpec), assemblyName, StringComparison.OrdinalIgnoreCase) == 0)
1268                 {
1269                     assemblyPath = ResolvedAssemblyReferences[i].ItemSpec;
1270                     return true;
1271                 }
1272             }
1273 
1274             assemblyPath = null;
1275             return false;
1276         }
1277 
1278         /*
1279          * Method:  ResolveComAssemblyReference
1280          *
1281          * Resolves a COM wrapper assembly reference based on the COM references resolved so far. This method is necessary
1282          * for Ax wrappers only, so all necessary references will be resolved by then (since we resolve them in
1283          * the following order: pia, tlbimp, aximp)
1284          *
1285          * This is the method available for references to call back to resolve their dependencies
1286          */
IComReferenceResolver.ResolveComAssemblyReference(string fullAssemblyName, out string assemblyPath)1287         bool IComReferenceResolver.ResolveComAssemblyReference(string fullAssemblyName, out string assemblyPath)
1288         {
1289             AssemblyNameExtension fullAssemblyNameEx = new AssemblyNameExtension(fullAssemblyName);
1290 
1291             foreach (ComReferenceWrapperInfo wrapperInfo in _cachePia.Values)
1292             {
1293                 // this should not happen, but it would be a non fatal error
1294                 Debug.Assert(wrapperInfo.path != null);
1295                 if (wrapperInfo.path == null)
1296                     continue;
1297 
1298                 // we have already verified all cached wrappers, so we don't expect this methods to throw anything
1299                 AssemblyNameExtension wrapperAssemblyNameEx = new AssemblyNameExtension(AssemblyName.GetAssemblyName(wrapperInfo.path));
1300 
1301                 if (fullAssemblyNameEx.Equals(wrapperAssemblyNameEx))
1302                 {
1303                     assemblyPath = wrapperInfo.path;
1304                     return true;
1305                 }
1306                 // The PIA might have been redirected, so check its original assembly name too
1307                 else if (fullAssemblyNameEx.Equals(wrapperInfo.originalPiaName))
1308                 {
1309                     assemblyPath = wrapperInfo.path;
1310                     return true;
1311                 }
1312             }
1313 
1314             foreach (ComReferenceWrapperInfo wrapperInfo in _cacheTlb.Values)
1315             {
1316                 // temporary wrapper? skip it.
1317                 if (wrapperInfo.path == null)
1318                     continue;
1319 
1320                 // we have already verified all cached wrappers, so we don't expect this methods to throw anything
1321                 AssemblyNameExtension wrapperAssemblyNameEx = new AssemblyNameExtension(AssemblyName.GetAssemblyName(wrapperInfo.path));
1322 
1323                 if (fullAssemblyNameEx.Equals(wrapperAssemblyNameEx))
1324                 {
1325                     assemblyPath = wrapperInfo.path;
1326                     return true;
1327                 }
1328             }
1329 
1330             foreach (ComReferenceWrapperInfo wrapperInfo in _cacheAx.Values)
1331             {
1332                 // this should not happen, but it would be a non fatal error
1333                 Debug.Assert(wrapperInfo.path != null);
1334                 if (wrapperInfo.path == null)
1335                     continue;
1336 
1337                 // we have already verified all cached wrappers, so we don't expect this methods to throw anything
1338                 AssemblyNameExtension wrapperAssemblyNameEx = new AssemblyNameExtension(AssemblyName.GetAssemblyName(wrapperInfo.path));
1339 
1340                 if (fullAssemblyNameEx.Equals(wrapperAssemblyNameEx))
1341                 {
1342                     assemblyPath = wrapperInfo.path;
1343                     return true;
1344                 }
1345             }
1346 
1347             assemblyPath = null;
1348             return false;
1349         }
1350 
1351         /// <summary>
1352         /// Helper function - resolves a PIA COM classic reference given the type library attributes.
1353         /// </summary>
1354         /// <param name="referenceInfo">Information about the reference to be resolved</param>
1355         /// <param name="refName">Name of reference</param>
1356         /// <param name="wrapperInfo">Information about wrapper locations</param>
1357         /// <returns>True if the reference was already found or successfully generated, false otherwise.</returns>
ResolveComReferencePia(ComReferenceInfo referenceInfo, string refName, out ComReferenceWrapperInfo wrapperInfo)1358         internal bool ResolveComReferencePia(ComReferenceInfo referenceInfo, string refName, out ComReferenceWrapperInfo wrapperInfo)
1359         {
1360             wrapperInfo = null;
1361             string typeLibKey = ComReference.UniqueKeyFromTypeLibAttr(referenceInfo.attr);
1362 
1363             // look in the PIA cache first
1364             if (_cachePia.ContainsKey(typeLibKey))
1365             {
1366                 wrapperInfo = (ComReferenceWrapperInfo)_cachePia[typeLibKey];
1367                 return true;
1368             }
1369 
1370             try
1371             {
1372                 // if not in the cache, we have no choice but to go looking for the PIA
1373                 PiaReference reference = new PiaReference(Log, Silent, referenceInfo, refName);
1374 
1375                 // if not found, fail (we do not fall back to tlbimp wrappers if we're looking specifically for a PIA)
1376                 if (!reference.FindExistingWrapper(out wrapperInfo, _timestampCache[referenceInfo.strippedTypeLibPath]))
1377                 {
1378                     return false;
1379                 }
1380 
1381                 // if found, add it to the PIA cache
1382                 _cachePia.Add(typeLibKey, wrapperInfo);
1383             }
1384             catch (Exception e) when (ExceptionHandling.IsIoRelatedException(e))
1385             {
1386                 return false;
1387             }
1388 
1389             return true;
1390         }
1391 
1392         /// <summary>
1393         /// Return the set of item specs for the resolved assembly references.
1394         /// </summary>
1395         /// <returns></returns>
GetResolvedAssemblyReferenceItemSpecs()1396         internal IEnumerable<string> GetResolvedAssemblyReferenceItemSpecs()
1397         {
1398             return (ResolvedAssemblyReferences == null) ? Array.Empty<string>(): ResolvedAssemblyReferences.Select(rar => rar.ItemSpec);
1399         }
1400 
1401         /// <summary>
1402         /// Helper function - resolves a regular tlb COM classic reference given the type library attributes.
1403         /// </summary>
1404         /// <param name="referenceInfo">Information about the reference to be resolved</param>
1405         /// <param name="outputDirectory">Directory the interop DLL should be written to</param>
1406         /// <param name="refName">Name of reference</param>
1407         /// <param name="topLevelRef">True if this is a top-level reference</param>
1408         /// <param name="wrapperInfo">Information about wrapper locations</param>
1409         /// <returns>True if the reference was already found or successfully generated, false otherwise.</returns>
ResolveComReferenceTlb(ComReferenceInfo referenceInfo, string outputDirectory, string refName, bool topLevelRef, List<string> dependencyPaths, out ComReferenceWrapperInfo wrapperInfo)1410         internal bool ResolveComReferenceTlb(ComReferenceInfo referenceInfo, string outputDirectory, string refName, bool topLevelRef, List<string> dependencyPaths, out ComReferenceWrapperInfo wrapperInfo)
1411         {
1412             wrapperInfo = null;
1413             string typeLibKey = ComReference.UniqueKeyFromTypeLibAttr(referenceInfo.attr);
1414 
1415             // look in the TLB cache first
1416             if (_cacheTlb.ContainsKey(typeLibKey))
1417             {
1418                 wrapperInfo = (ComReferenceWrapperInfo)_cacheTlb[typeLibKey];
1419                 return true;
1420             }
1421 
1422             // is it a temporary wrapper?
1423             bool isTemporary = false;
1424 
1425             // no top level (included in the project) refs can have temporary wrappers
1426             if (!topLevelRef)
1427             {
1428                 // wrapper is temporary if there's a top level tlb reference with the same typelib name, but different attributes
1429                 foreach (ComReferenceInfo projectRefInfo in allProjectRefs)
1430                 {
1431                     if (ComReferenceTypes.IsTlbImp(projectRefInfo.taskItem.GetMetadata(ComReferenceItemMetadataNames.wrapperTool)))
1432                     {
1433                         // conflicting typelib names for different typelibs? generate a temporary wrapper
1434                         if (!ComReference.AreTypeLibAttrEqual(referenceInfo.attr, projectRefInfo.attr) &&
1435                             String.Compare(referenceInfo.typeLibName, projectRefInfo.typeLibName, StringComparison.OrdinalIgnoreCase) == 0)
1436                         {
1437                             isTemporary = true;
1438                         }
1439                     }
1440                 }
1441             }
1442 
1443             try
1444             {
1445                 List<string> referencePaths = new List<string>(GetResolvedAssemblyReferenceItemSpecs());
1446 
1447                 if (dependencyPaths != null)
1448                 {
1449                     referencePaths.AddRange(dependencyPaths);
1450                 }
1451 
1452                 // not in the cache? see if anyone was kind enough to generate it for us
1453                 TlbReference reference = new TlbReference(Log, Silent, this, referencePaths, referenceInfo, refName,
1454                     outputDirectory, isTemporary, DelaySign, KeyFile, KeyContainer, this.NoClassMembers,
1455                     this.TargetProcessorArchitecture, IncludeVersionInInteropName, ExecuteAsTool, _tlbimpPath,
1456                     BuildEngine, EnvironmentVariables);
1457 
1458                 // wrapper doesn't exist or needs regeneration? generate it then
1459                 if (!reference.FindExistingWrapper(out wrapperInfo, _timestampCache[referenceInfo.strippedTypeLibPath]))
1460                 {
1461                     if (!reference.GenerateWrapper(out wrapperInfo))
1462                         return false;
1463                 }
1464 
1465                 // if found or successfully generated, cache it.
1466                 _cacheTlb.Add(typeLibKey, wrapperInfo);
1467             }
1468             catch (Exception e) when (ExceptionHandling.IsIoRelatedException(e))
1469             {
1470                 return false;
1471             }
1472 
1473             return true;
1474         }
1475 
1476         /// <summary>
1477         /// Helper function - resolves an ActiveX reference given the type library attributes.
1478         /// </summary>
1479         /// <param name="referenceInfo">Information about the reference to be resolved</param>
1480         /// <param name="outputDirectory">Directory the interop DLL should be written to</param>
1481         /// <param name="refName">Name of reference</param>
1482         /// <param name="wrapperInfo">Information about wrapper locations</param>
1483         /// <returns>True if the reference was already found or successfully generated, false otherwise.</returns>
ResolveComReferenceAx(ComReferenceInfo referenceInfo, string outputDirectory, string refName, out ComReferenceWrapperInfo wrapperInfo)1484         internal bool ResolveComReferenceAx(ComReferenceInfo referenceInfo, string outputDirectory, string refName, out ComReferenceWrapperInfo wrapperInfo)
1485         {
1486             wrapperInfo = null;
1487             string typeLibKey = ComReference.UniqueKeyFromTypeLibAttr(referenceInfo.attr);
1488 
1489             // look in the Ax cache first
1490             if (_cacheAx.ContainsKey(typeLibKey))
1491             {
1492                 wrapperInfo = (ComReferenceWrapperInfo)_cacheAx[typeLibKey];
1493                 return true;
1494             }
1495 
1496             try
1497             {
1498                 // not in the cache? see if anyone was kind enough to generate it for us
1499 
1500                 AxReference reference = new AxReference(Log, Silent, this, referenceInfo, refName, outputDirectory, DelaySign, KeyFile, KeyContainer, IncludeVersionInInteropName, _aximpPath, BuildEngine, EnvironmentVariables);
1501 
1502                 // wrapper doesn't exist or needs regeneration? generate it then
1503                 if (!reference.FindExistingWrapper(out wrapperInfo, _timestampCache[referenceInfo.strippedTypeLibPath]))
1504                 {
1505                     if (!reference.GenerateWrapper(out wrapperInfo))
1506                         return false;
1507                 }
1508 
1509                 // if found or successfully generated, cache it.
1510                 _cacheAx.Add(typeLibKey, wrapperInfo);
1511             }
1512             catch (Exception e) when (ExceptionHandling.IsIoRelatedException(e))
1513             {
1514                 return false;
1515             }
1516 
1517             return true;
1518         }
1519 
1520         #region VerifyReferenceMetadataForNameItem required metadata
1521 
1522         // Metadata required on a valid Com reference item
1523         private static readonly string[] s_requiredMetadataForNameItem = {
1524             ComReferenceItemMetadataNames.guid,
1525             ComReferenceItemMetadataNames.versionMajor,
1526             ComReferenceItemMetadataNames.versionMinor
1527         };
1528 
1529         #endregion
1530 
1531         /*
1532          * Method:  VerifyReferenceMetadataForNameItem
1533          *
1534          * Verifies that all required metadata on the COM reference item are there.
1535          */
VerifyReferenceMetadataForNameItem(ITaskItem reference, out string missingOrInvalidMetadata)1536         internal static bool VerifyReferenceMetadataForNameItem(ITaskItem reference, out string missingOrInvalidMetadata)
1537         {
1538             missingOrInvalidMetadata = "";
1539 
1540             // go through the list of required metadata and fail if one of them is not found
1541             foreach (string metadataName in s_requiredMetadataForNameItem)
1542             {
1543                 if (reference.GetMetadata(metadataName).Length == 0)
1544                 {
1545                     missingOrInvalidMetadata = metadataName;
1546                     return false;
1547                 }
1548             }
1549 
1550             // now verify they contain valid data
1551             Guid guid;
1552             if (!Guid.TryParse(reference.GetMetadata(ComReferenceItemMetadataNames.guid), out guid))
1553             {
1554                 // invalid guid format
1555                 missingOrInvalidMetadata = ComReferenceItemMetadataNames.guid;
1556                 return false;
1557             }
1558 
1559             try
1560             {
1561                 // invalid versionMajor format
1562                 missingOrInvalidMetadata = ComReferenceItemMetadataNames.versionMajor;
1563                 short.Parse(reference.GetMetadata(ComReferenceItemMetadataNames.versionMajor), NumberStyles.Integer, CultureInfo.InvariantCulture);
1564 
1565                 // invalid versionMinor format
1566                 missingOrInvalidMetadata = ComReferenceItemMetadataNames.versionMinor;
1567                 short.Parse(reference.GetMetadata(ComReferenceItemMetadataNames.versionMinor), NumberStyles.Integer, CultureInfo.InvariantCulture);
1568 
1569                 // only check lcid if specified
1570                 if (reference.GetMetadata(ComReferenceItemMetadataNames.lcid).Length > 0)
1571                 {
1572                     // invalid lcid format
1573                     missingOrInvalidMetadata = ComReferenceItemMetadataNames.lcid;
1574                     int.Parse(reference.GetMetadata(ComReferenceItemMetadataNames.lcid), NumberStyles.Integer, CultureInfo.InvariantCulture);
1575                 }
1576 
1577                 // only check wrapperTool if specified
1578                 if (reference.GetMetadata(ComReferenceItemMetadataNames.wrapperTool).Length > 0)
1579                 {
1580                     // invalid wrapperTool type
1581                     missingOrInvalidMetadata = ComReferenceItemMetadataNames.wrapperTool;
1582                     string wrapperTool = reference.GetMetadata(ComReferenceItemMetadataNames.wrapperTool);
1583 
1584                     if ((!ComReferenceTypes.IsAxImp(wrapperTool)) &&
1585                         (!ComReferenceTypes.IsTlbImp(wrapperTool)) &&
1586                         (!ComReferenceTypes.IsPia(wrapperTool)))
1587                     {
1588                         return false;
1589                     }
1590                 }
1591             }
1592             catch (OverflowException)
1593             {
1594                 return false;
1595             }
1596             catch (FormatException)
1597             {
1598                 return false;
1599             }
1600 
1601             // all metadata were found
1602             missingOrInvalidMetadata = String.Empty;
1603             return true;
1604         }
1605 
1606         /*
1607          * Method:  InitializeDefaultMetadataForNameItem
1608          *
1609          * Initializes optional metadata on given name item to their default values if they're not present
1610          */
InitializeDefaultMetadataForNameItem(ITaskItem reference)1611         internal static void InitializeDefaultMetadataForNameItem(ITaskItem reference)
1612         {
1613             // default value for lcid is 0
1614             if (reference.GetMetadata(ComReferenceItemMetadataNames.lcid).Length == 0)
1615                 reference.SetMetadata(ComReferenceItemMetadataNames.lcid, "0");
1616 
1617             // default value for wrapperTool is tlbimp
1618             if (reference.GetMetadata(ComReferenceItemMetadataNames.wrapperTool).Length == 0)
1619                 reference.SetMetadata(ComReferenceItemMetadataNames.wrapperTool, ComReferenceTypes.tlbimp);
1620         }
1621 
1622         /*
1623          * Method:  InitializeDefaultMetadataForFileItem
1624          *
1625          * Initializes optional metadata on given file item to their default values if they're not present
1626          */
InitializeDefaultMetadataForFileItem(ITaskItem reference)1627         internal static void InitializeDefaultMetadataForFileItem(ITaskItem reference)
1628         {
1629             // default value for wrapperTool is tlbimp
1630             if (reference.GetMetadata(ComReferenceItemMetadataNames.wrapperTool).Length == 0)
1631                 reference.SetMetadata(ComReferenceItemMetadataNames.wrapperTool, ComReferenceTypes.tlbimp);
1632         }
1633 
1634         /*
1635          * Method:  CheckForConflictingReferences
1636          *
1637          * Checks if we have any conflicting references.
1638          */
CheckForConflictingReferences()1639         internal bool CheckForConflictingReferences()
1640         {
1641             Hashtable namesForReferences = new Hashtable();
1642             ArrayList refsToBeRemoved = new ArrayList();
1643             bool noConflictsFound = true;
1644 
1645             for (int pass = 0; pass < 2; pass++)
1646             {
1647                 foreach (ComReferenceInfo projectRefInfo in allProjectRefs)
1648                 {
1649                     string wrapperType = projectRefInfo.taskItem.GetMetadata(ComReferenceItemMetadataNames.wrapperTool);
1650 
1651                     // only check aximp and tlbimp references
1652                     if ((pass == 0 && ComReferenceTypes.IsAxImp(wrapperType)) ||
1653                         (pass == 1 && ComReferenceTypes.IsTlbImp(wrapperType)))
1654                     {
1655                         // if we already have a reference with this name, compare attributes
1656                         if (namesForReferences.ContainsKey(projectRefInfo.typeLibName))
1657                         {
1658                             // if different type lib attributes, we have a conflict, remove the conflicting reference
1659                             // and continue processing
1660                             ComReferenceInfo conflictingRef = (ComReferenceInfo)namesForReferences[projectRefInfo.typeLibName];
1661 
1662                             if (!ComReference.AreTypeLibAttrEqual(projectRefInfo.attr, conflictingRef.attr))
1663                             {
1664                                 if (!Silent)
1665                                 {
1666                                     Log.LogWarningWithCodeFromResources("ResolveComReference.ConflictingReferences", projectRefInfo.taskItem.ItemSpec, conflictingRef.taskItem.ItemSpec);
1667                                 }
1668 
1669                                 // mark the reference for removal, can't do it here because we're iterating through the ref's container
1670                                 refsToBeRemoved.Add(projectRefInfo);
1671                                 noConflictsFound = false;
1672                             }
1673                         }
1674                         else
1675                         {
1676                             // store the current reference
1677                             namesForReferences.Add(projectRefInfo.typeLibName, projectRefInfo);
1678                         }
1679                     }
1680                 }
1681 
1682                 // use a new hashtable for different passes - refs to the same typelib with different wrapper types are OK
1683                 namesForReferences.Clear();
1684             }
1685 
1686             // now that we're outside the loop, we can safely remove the marked references
1687             foreach (ComReferenceInfo projectRefInfo in refsToBeRemoved)
1688             {
1689                 // remove and cleanup
1690                 allProjectRefs.Remove(projectRefInfo);
1691                 projectRefInfo.ReleaseTypeLibPtr();
1692             }
1693 
1694             return noConflictsFound;
1695         }
1696 
1697         /// <summary>
1698         /// Set the CopyLocal metadata to false on all assemblies that are located in the GAC.
1699         /// </summary>
1700         /// <param name="outputTaskItems">ArrayList of ITaskItems that will be outputted from the task</param>
1701         /// <param name="gacPath">The GAC root path</param>
SetCopyLocalToFalseOnGacOrNoPIAAssemblies(ArrayList outputTaskItems, string gacPath)1702         internal void SetCopyLocalToFalseOnGacOrNoPIAAssemblies(ArrayList outputTaskItems, string gacPath)
1703         {
1704             foreach (ITaskItem taskItem in outputTaskItems)
1705             {
1706                 if (taskItem.GetMetadata(ItemMetadataNames.msbuildReferenceSourceTarget).Length == 0)
1707                 {
1708                     taskItem.SetMetadata(ItemMetadataNames.msbuildReferenceSourceTarget, "ResolveComReference");
1709                 }
1710 
1711                 string embedInteropTypesMetadata = taskItem.GetMetadata(ItemMetadataNames.embedInteropTypes);
1712 
1713                 if (_projectTargetFramework != null && (_projectTargetFramework >= s_targetFrameworkVersion_40))
1714                 {
1715                     if ((embedInteropTypesMetadata != null) &&
1716                         (String.Compare(embedInteropTypesMetadata, "true", StringComparison.OrdinalIgnoreCase) == 0))
1717                     {
1718                         // Embed Interop Types forces CopyLocal to false
1719                         taskItem.SetMetadata(ItemMetadataNames.copyLocal, "false");
1720                         continue;
1721                     }
1722                 }
1723 
1724                 string privateMetadata = taskItem.GetMetadata(ItemMetadataNames.privateMetadata);
1725 
1726                 // if Private is not set on the original item, we set CopyLocal to false for GAC items
1727                 // and true for non-GAC items
1728                 if ((privateMetadata == null) || (privateMetadata.Length == 0))
1729                 {
1730                     if (String.Compare(taskItem.ItemSpec, 0, gacPath, 0, gacPath.Length, StringComparison.OrdinalIgnoreCase) == 0)
1731                     {
1732                         taskItem.SetMetadata(ItemMetadataNames.copyLocal, "false");
1733                     }
1734                     else
1735                     {
1736                         taskItem.SetMetadata(ItemMetadataNames.copyLocal, "true");
1737                     }
1738                 }
1739                 // if Private is set, it always takes precedence
1740                 else
1741                 {
1742                     taskItem.SetMetadata(ItemMetadataNames.copyLocal, privateMetadata);
1743                 }
1744             }
1745         }
1746 
1747         /// <summary>
1748         /// Scan all the dependencies of the main project references and preresolve them
1749         /// so that when we get asked about a previously unknown dependency in the form of a .NET assembly
1750         /// we know what to do with it.
1751         /// </summary>
ScanAndResolveAllDependencies(ComDependencyWalker dependencyWalker, ComReferenceInfo reference)1752         private List<string> ScanAndResolveAllDependencies(ComDependencyWalker dependencyWalker, ComReferenceInfo reference)
1753         {
1754             dependencyWalker.ClearDependencyList();
1755 
1756             if (!Silent)
1757             {
1758                 Log.LogMessageFromResources(MessageImportance.Low, "ResolveComReference.ScanningDependencies", reference.SourceItemSpec);
1759             }
1760 
1761             dependencyWalker.AnalyzeTypeLibrary(reference.typeLibPointer);
1762 
1763             if (!Silent)
1764             {
1765                 foreach (Exception ex in dependencyWalker.EncounteredProblems)
1766                 {
1767                     // A failure to resolve a reference due to something possibly being missing from disk is not
1768                     // an error; the user may not be actually consuming types from it
1769                     Log.LogWarningWithCodeFromResources("ResolveComReference.FailedToScanDependencies",
1770                         reference.SourceItemSpec, ex.Message);
1771                 }
1772             }
1773 
1774             dependencyWalker.EncounteredProblems.Clear();
1775 
1776             HashSet<string> dependentPaths = new HashSet<string>();
1777             TYPELIBATTR[] dependentAttrs = dependencyWalker.GetDependencies();
1778 
1779             foreach (TYPELIBATTR dependencyTypeLibAttr in dependentAttrs)
1780             {
1781                 // We don't need to even try to resolve if the dependency reference is ourselves.
1782                 if (!ComReference.AreTypeLibAttrEqual(dependencyTypeLibAttr, reference.attr))
1783                 {
1784                     ComReferenceInfo existingReference;
1785 
1786                     if (IsExistingProjectReference(dependencyTypeLibAttr, null, out existingReference))
1787                     {
1788                         ITaskItem resolvedItem;
1789 
1790                         // If we're resolving another project reference, empty out the type cache -- if the dependencies are buried,
1791                         // caching the analyzed types can make it so that we don't recognize our dependencies' dependencies.
1792                         dependencyWalker.ClearAnalyzedTypeCache();
1793 
1794                         if (ResolveReference(dependencyWalker, existingReference, WrapperOutputDirectory, out resolvedItem))
1795                         {
1796                             // Add the resolved dependency
1797                             dependentPaths.Add(resolvedItem.ItemSpec);
1798 
1799                             // and anything it depends on
1800                             foreach (string dependentPath in existingReference.dependentWrapperPaths)
1801                             {
1802                                 dependentPaths.Add(dependentPath);
1803                             }
1804                         }
1805                     }
1806                     else
1807                     {
1808                         if (!Silent)
1809                         {
1810                             Log.LogMessageFromResources(MessageImportance.Low, "ResolveComReference.ResolvingDependency",
1811                                 dependencyTypeLibAttr.guid, dependencyTypeLibAttr.wMajorVerNum, dependencyTypeLibAttr.wMinorVerNum);
1812                         }
1813 
1814                         ComReferenceWrapperInfo wrapperInfo;
1815 
1816                         ((IComReferenceResolver)this).ResolveComClassicReference(dependencyTypeLibAttr, WrapperOutputDirectory,
1817                             null /* unknown wrapper type */, null /* unknown name */, out wrapperInfo);
1818 
1819                         if (!Silent)
1820                         {
1821                             Log.LogMessageFromResources(MessageImportance.Low, "ResolveComReference.ResolvedDependentComReference",
1822                                 dependencyTypeLibAttr.guid, dependencyTypeLibAttr.wMajorVerNum, dependencyTypeLibAttr.wMinorVerNum,
1823                                 wrapperInfo.path);
1824                         }
1825 
1826                         dependentPaths.Add(wrapperInfo.path);
1827                     }
1828                 }
1829             }
1830 
1831             return dependentPaths.ToList<string>();
1832         }
1833 
1834         /*
1835          * Method:  TaskItemToTypeLibAttr
1836          *
1837          * Gets the TLIBATTR structure based on the reference we have.
1838          * Sets guid, versions major & minor, lcid.
1839          */
TaskItemToTypeLibAttr(ITaskItem taskItem)1840         internal static TYPELIBATTR TaskItemToTypeLibAttr(ITaskItem taskItem)
1841         {
1842             TYPELIBATTR attr = new TYPELIBATTR();
1843 
1844             // copy metadata from Reference to our TYPELIBATTR
1845             attr.guid = new Guid(taskItem.GetMetadata(ComReferenceItemMetadataNames.guid));
1846             attr.wMajorVerNum = short.Parse(taskItem.GetMetadata(ComReferenceItemMetadataNames.versionMajor), NumberStyles.Integer, CultureInfo.InvariantCulture);
1847             attr.wMinorVerNum = short.Parse(taskItem.GetMetadata(ComReferenceItemMetadataNames.versionMinor), NumberStyles.Integer, CultureInfo.InvariantCulture);
1848             attr.lcid = int.Parse(taskItem.GetMetadata(ComReferenceItemMetadataNames.lcid), NumberStyles.Integer, CultureInfo.InvariantCulture);
1849             return attr;
1850         }
1851 
1852         #endregion
1853     }
1854 }
1855 
1856 #endif
1857