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