UnGAC MSBuild Assemblies on Install or Update#5626
UnGAC MSBuild Assemblies on Install or Update#5626benvillalobos merged 24 commits intodotnet:masterfrom
Conversation
src/Microsoft.Build.UnGAC/Program.cs
Outdated
| string s = output.ReadLine(); | ||
|
|
||
| // Store all instances of gacutil for now. | ||
| while (!String.IsNullOrEmpty(s)) | ||
| { | ||
| allInstancesOfGACUtil.Add(s); | ||
| s = output.ReadLine(); | ||
| } |
There was a problem hiding this comment.
| string s = output.ReadLine(); | |
| // Store all instances of gacutil for now. | |
| while (!String.IsNullOrEmpty(s)) | |
| { | |
| allInstancesOfGACUtil.Add(s); | |
| s = output.ReadLine(); | |
| } | |
| // Store all instances of gacutil for now. | |
| for (string loc = output.ReadLine(); !String.IsNullOrEmpty(loc); loc = output.ReadLine()) | |
| { | |
| allInstancesOfGACUtil.Add(s); | |
| } |
Does this terminate appropriately? If so, I think there's a ReadAllLines function that might do the same. (I might have made that up 😄.)
Also, do we really care about any gacutils after the first?
There was a problem hiding this comment.
Does this terminate appropriately? If so, I think there's a ReadAllLines function that might do the same. (I might have made that up 😄.)
It does and has always, which kind of worries me. I would do ReadAllLines but have experienced lockups from that as well. Perhaps I can clear the buffer (as I do below) so I can safely pull everything.
Also, do we really care about any gacutils after the first?
Honestly I don't think so. I did it because "why not" while I had them there. The final version will almost certainly not store all instances.
src/Microsoft.Build.UnGAC/Program.cs
Outdated
| input.WriteLine("where /F /R \"C:\\Program Files (x86)\\Microsoft SDKs\\Windows\" gacutil.exe"); | ||
|
|
||
| // Throw away the first line - it's the command we just used | ||
| output.ReadLine(); |
There was a problem hiding this comment.
This would probably make it messier rather than cleaner, but you should be able to set echo to off to suppress this.
src/Microsoft.Build.UnGAC/Program.cs
Outdated
| StreamReader output; | ||
| StreamWriter input; | ||
|
|
||
| Process proc = new Process |
There was a problem hiding this comment.
This seems heavy. Is there a way to run it in-proc?
There was a problem hiding this comment.
I'm not aware of one. I considered using an exec task but we need this to be an exe. If someone knows I'd love to know!
There was a problem hiding this comment.
You can access the GAC via the Fusion interface, for example:
https://docs.microsoft.com/en-us/dotnet/framework/unmanaged-api/fusion/iassemblycache-uninstallassembly-method
Not sure if a COM interop wrapper exists or you'll have to build one yourself. I guess a big reason to go this route would be if gacutil.exe is not guaranteed to be present on the machine. Otherwise, if you know for sure there is gacutil.exe and you can find it, it may be easier to just execute it. I'm assuming we don't care about performance here.
There was a problem hiding this comment.
Good idea. There are a few that exist, but either the links to them are dead or they're not supported anymore. I found https://github.com/EasyHook/EasyHook
http://blog.wibeck.org/2008/10/working-with-gac-from-code/ points to an API wrapper that unfortunately 404's today.
There's also https://www.codeproject.com/Articles/8285/GAC-API-Interface
There was a problem hiding this comment.
This is likely of interest:
msbuild/src/Tasks/NativeMethods.cs
Lines 344 to 351 in f2c4bfd
I don't think we can depend on gacutil.exe existing: the VS install may be what installs it. We could maybe express a dependency on "some .NET SDK" and use that, but it's probably easier to use the COM object.
it's probably easier to use the COM object
😲
There was a problem hiding this comment.
I just spent a good chunk of time trying to get https://docs.microsoft.com/en-us/dotnet/api/system.enterpriseservices.internal.publish.gacremove?view=netframework-4.8 working to no avail (passing in file path, assembly name, pointing directly to dll, etc). MattCav suggested it was preferable to use some sort of API and keep the code simple so that's the route I'll go.
There was a problem hiding this comment.
Reusing NativeMethods was the way to go. The program does need to be running as admin for it to work, however.
src/Microsoft.Build.UnGAC/Program.cs
Outdated
|
|
||
| proc.Close(); | ||
| proc.Dispose(); | ||
| Console.ReadKey(); |
There was a problem hiding this comment.
The readkey is temporary so I can see the console output before the window closes.
src/Microsoft.Build.UnGAC/Program.cs
Outdated
|
|
||
| namespace Microsoft.Build.UnGAC | ||
| { | ||
| class Program |
There was a problem hiding this comment.
Does this work, and is this the right place for this? I'm surprised first that this runs at install time rather than running every time the user builds and second that you had to make a new project to make something that runs automatically. Could you explain that a little more?
There was a problem hiding this comment.
What will allow it to run at install time is the exe being pointed to from src/Package/MSBuild.VSSetup/exe.swr, which src/Package/MSBuild.VSSetup/files.swr will point at. And files.swr is already being picked up by microbuild.
|
Current issue:
|
MSBuild.Dev.sln
Outdated
| # Visual Studio Version 16 | ||
| VisualStudioVersion = 16.0.30320.27 |
There was a problem hiding this comment.
Nit: Go ahead and revert this entirely since there's no meaningful change.
| <TargetFramework>net45</TargetFramework> | ||
| <!-- Set as an exe because this project publishes its own output. --> | ||
| <OutputType>Exe</OutputType> | ||
| <!-- Forcing the 'vsix' output to be json output. --> |
There was a problem hiding this comment.
Add
... workaround for dotnet/arcade#xxxx
(and file the "support exe packages" bug there ;))
…nd thus prevent MSBuild from installing
Much cleaner!
…sed to arcade as a semicolon delimited list, which arcade can't deal with
Reverted MSBuild.Dev.sln
3d48453 to
49900b2
Compare
| <FileSignInfo Include="MessagePack.dll" CertificateName="3PartySHA2" /> | ||
| <FileSignInfo Include="MessagePack.Annotations.dll" CertificateName="3PartySHA2" /> | ||
| <ItemsToSign Include="$(VisualStudioSetupOutputPath)DevDivPackages\*.nupkg" /> | ||
| <FileSignInfo Include="MessagePack.Annotations.dll" CertificateName="3PartySHA2" /> |
There was a problem hiding this comment.
I'm a little confused by this; do we not need to sign DevDivPackages*.nupkg anymore?
There was a problem hiding this comment.
We didn't sign it in the first place; this line was just in error.
| <Project Sdk="Microsoft.NET.Sdk"> | ||
| <PropertyGroup> | ||
| <!-- Setup runs on net45 and may not have installed newer yet. --> | ||
| <TargetFramework>net45</TargetFramework> |
There was a problem hiding this comment.
Does that mean it doesn't work on mac/linux? Is there a way we can make it work on mac/linux?
There was a problem hiding this comment.
The GAC is a thing only on .NET Framework (on Windows). Things do not break because of the GAC on Mac/Linux, because there's no GAC.
| /// It runs at VS install-time as well as repair-time. | ||
| /// It is intended to run as best effort. Meaning that if it fails, we avoid throwing and instead log it. | ||
| /// </summary> | ||
| class Program |
| { | ||
| string[] assembliesToUnGAC = | ||
| { | ||
| "Microsoft.Build, Version=15.1.0.0", |
There was a problem hiding this comment.
Are these the only versions people GAC?
There was a problem hiding this comment.
They're the only versions that we use, so they're the only ones that need to be removed from the GAC. Prior to VS 15, our assemblies were GACed by design; after they're not and they're all 15.1.0.0.
| // If we hit an error with an assembly, keep trying the others. | ||
| if ((hresult >> 31) == 1) | ||
| { | ||
| Console.WriteLine($"Could not remove {assembly} from the GAC. HResult: {hresult}"); |
There was a problem hiding this comment.
Does it write this if the assembly just wasn't in the GAC?
There was a problem hiding this comment.
It should, but I'm modifying this now to not check whether it was successful. It will just print out that it tried, and what the result was.
rainersigwald
left a comment
There was a problem hiding this comment.
I'd like to see your insertion-automation changes before signing off.
| <FileSignInfo Include="MessagePack.dll" CertificateName="3PartySHA2" /> | ||
| <FileSignInfo Include="MessagePack.Annotations.dll" CertificateName="3PartySHA2" /> | ||
| <ItemsToSign Include="$(VisualStudioSetupOutputPath)DevDivPackages\*.nupkg" /> | ||
| <FileSignInfo Include="MessagePack.Annotations.dll" CertificateName="3PartySHA2" /> |
There was a problem hiding this comment.
We didn't sign it in the first place; this line was just in error.
|
@rainersigwald you can see it here: https://dev.azure.com/devdiv/DevDiv/_releaseProgress?releaseId=815300&_a=release-pipeline-progress - Microsoft.Build.vsman{$(MSBuild_ExtApisPackageVersion)+$(Build.SourceVersion)}=https://vsdrop.corp.microsoft.com/file/v1/Products/DevDiv/dotnet/msbuild/$(Build.SourceBranchName)/$(Build.BuildNumber);Microsoft.Build.vsman
+ Microsoft.Build.vsman{$(MSBuild_ExtApisPackageVersion)+$(Build.SourceVersion)}=https://vsdrop.corp.microsoft.com/file/v1/Products/DevDiv/dotnet/msbuild/$(Build.SourceBranchName)/$(Build.BuildNumber);Microsoft.Build.vsman,Microsoft.Build.UnGAC.vsman=https://vsdrop.corp.microsoft.com/file/v1/Products/DevDiv/dotnet/msbuild/$(Build.SourceBranchName)/$(Build.BuildNumber);Microsoft.Build.UnGAC.vsmanNote that I removed the version from the ungac vsman, and there comma separating |
|
@benvillalobos that change looks good. I think this is good to go. Please change the real insertion job after you merge it. |
* Added .swr file. Try catching the program to ensure it doesn't fail and thus prevent MSBuild from installing * Moved exe.swr to its own directory. Otherwise both .swr files are passed to arcade as a semicolon delimited list, which arcade can't deal with * Sign UnGAC.exe

Fixes #5183