Link to home
Start Free TrialLog in
Avatar of johnfox147
johnfox147

asked on

Proxy msi.dll causes installer error 1304

The product I’ve inherited has a need to know when installs are being done by the system msiexec process (and be able to trace it to being initiated by a particular user-initiated msiexec, and when they are finished, etc.). So the current solution for that is to replace msi.dll with a proxy dll. The Windows msi.dll is renamed to msisys.dll and we put our proxy in place as msi.dll. In the proxy a handful of functions are hooked and the rest are passed through to msisys.dll using the techniques used on this link:  http://www.codeproject.com/Articles/17863/Using-Pragmas-to-Create-a-Proxy-DLL

This works great for most install packages but I’ve found a couple of install packages that fail to complete when my proxy msi.dll is in place. If I put back the original Windows msi.dll then the install packages work fine. They only fail to complete when my proxy dll is in place. One of these is for some not-well-known 3rd party software but the other is ‘Visual Studio 2010 Isolated Shell’ from Microsoft.

The problem with these install packages when my proxy dll is in place is that they fail with a 1304 error. Specifically for the ‘Visual Studio 2010 Isolated Shell’ install the error is:  
Error 1304.Error writing to file: Microsoft.VisualStudio.Modeling.Sdk.10.0.dll.  Verify that you have access to that directory.
I’ve noticed that the two installer packages with this problem are both of the type that do not allow using the included .msi file with msiexec.exe – they complain that you must use the provided setup.exe program. This behaves the same on both WinXP x86 and Win7 x64.

Things I’ve verified about the proxy msi.dll:

Every function in the original Windows msi.dll is either provided as a hook function or passed-through using a pragma comment
The VS_VERSION_INFO for the proxy msi.dll is identical to the original Windows msi.dll
The ACLs on the proxy msi.dll are identical to the original Windows msi.dll
There are no Alternate Data Streams on either the original Windows msi.dll nor on my proxy msi.dll

I’ve exhausted ideas on what makes my proxy dll look different enough to msiexec.exe to make it fail with the 1304 error. If anyone has any ideas I would greatly appreciate it. Thanks.
Avatar of CSI-Windows_com
CSI-Windows_com
Flag of United States of America image

It's possible you might be running into Windows Resource Protection (WRP).  You can verify by checking if the permissions on the resource only allow full access to the "TrustedInstaller" account.

Windows Installer does not have this error occur is that it has certain application compatibility shims applied to it.  You can see that the WRPMitigation Shim is applied if you use Compatibility Administrator from the Application Compatibility Toolkit (http://www.microsoft.com/en-us/download/details.aspx?id=7352).  Look up the shims on msiexec.exe and regsrv32.exe.

WRPMitigation sends a "success" message when ever a process is denied access to a resource because it is under Windows Resource Protection (WRP) - it lies and said the update went fine.

I took a look with compatibility administrator and the WRPMitigation shim against msiexec.exe does have a specification to includes ALL dlls in the process - in theory this should cover you.

Setup.exes also have a host of mitigations applied to them, but they are only visible if you convert the appcompat sdb to XML (http://blogs.msdn.com/b/heaths/archive/2007/11/02/sdb2xml.aspx).  If those are applied as AppCompat layers they may trickle down to msiexec as well.

Would be helpful to have a verbose MSI log of an installation that failed in this way.

You might also try Rohitab API Monitor to see if you can decipher what is happening: http://www.rohitab.com/apimonitor

I would comment that what you are trying to do is likely to run into problems do to some of the deep system level items as mentioned above.  I'm not sure that this is the best way to accomplish what you are wishing to do.

Have you tested under Windows 7?  msi.dll is under WRP and if you rename it there have got to be a lot of products that would take that as the actions of a virus.
Avatar of johnfox147
johnfox147

ASKER

Thank you much for responding with good suggestions.

WRP seems like a reasonable thing to investigate, but I am seeing identical behavior on WinXP and my understanding is that WRP is only on Vista and later. Wouldn't that rule out it being an issue with WRP?

I agree that a verbose MSI log would be helpful. I tried to go that route but can't figure out how to do it since trying to install the .msi file with msiexec fails with a complaint that the provided setup.exe must be used. I would love find a way to turn on verbose MSI logging for this type of installer.

The suggestion of Rohitab API Monitor looks to be an excellent one. That's a pretty powerful tool and I'm hoping it can help me get to the bottom of my issue. It definitely gives insight into what I was looking for -- namely which functions in msi.dll are being called when the install fails.
You are correct - if it tests identically on XP, then it is not WRP.

Verbose logging can be turned for your execution scenario using the registry group policy keys.

I am attaching a .REG file that:
*) Turns on verbose logging
*) Turns on debug logging - the biggest benefit is seeing exactly what command line the setup.exe passed to the MSI
*) Turns off Package Based logging (packages running on MSI 4.0 and later can enable logging by including the "MsiLogging" property in the property table - disabling this prevents you from getting less than the verbose log due to this property being in a test package.

The log will be in %TEMP% of the user who starts the setup (EXE or MSI) and named MSI?????.LOG where "?" is alphanumeric.

This approach also generates a verbose log for self-healing.
ASKER CERTIFIED SOLUTION
Avatar of CSI-Windows_com
CSI-Windows_com
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Outstanding. Thank you. I'm not seeing the .reg attachment however -- that looks to be a valuable tool and would be much appreciated.

I also appreciate the suggested alternate design to use WMI. I will definitely investigate that but am still holding out hope that I can patch up my proxy dll for the time being and then look to replace with a WMI solution after.
Excellent info on how to set the registry to always do verbose logging of all installs!  I'm attaching two logs:

MSI17857.LOG is running the install program with the original msi.dll in place and the install succeeds (at least beyond where it otherwise fails and then I manually canceled it).

MSIbefc.LOG is running the same install program with my proxy msi.dll in place and the install fails with the following error popped in a dialog:

Error writing to file: ControlMicrosystem.ClearSCADA.DDK.dll.  Verify that you have access to that directory.

Search for the string 'DDKASSEMBLY_ControlMicrosystems.ClearSCADA.DDK.dll' to see where it succeeds in the first and fails in the second.

Thanks so much for the fantastic help!  Even if you are not able to pinpoint anything from these logs -- you've gotten me so far along towards the solution.
MSI17857.LOG
MSIbefc.LOG
Does your msi.dll use .NET in any way?

What are the permissions on that file or folder?  You must view it via the command prompt and find it in c:\windows\assembly.

Could you please zip the larger log file - it won't download.  I can't check if it is also getting this error: "MSI (s) (EC:0C) [18:52:18:920]: Assembly Error:The assembly is built by a runtime newer than the currently loaded runtime, and cannot be loaded."
No use of .NET

Larger file is reattached as a zip. It does not have the error you referenced.
MSI17857.zip
I removed my proxy msi.dll and let the install finish to completion. The dll that gives the error with my proxy in place has the following metadata values (which I got using depends.exe):

Image Ver:         0.0
Linker Ver:         10.0
OS Ver:               5.1
Subsystem Ver:  5.1

The permissions on that file are not any different than any other file on the system so I don't think it's that.

But in looking at the error from the log again ("The assembly is built by a runtime newer than the currently loaded runtime") I'm wondering if msiexec has a way of figuring out which version of .NET is currently on the system and that gets answered somehow with data in msi.dll and my version of msi.dll doesn't have that set correctly.  I'll be looking into that deeper...
Keep in mind the error says "than currently loaded" - that would be mscoree.dll.

If I were you I would use procexp.exe (http://live.sysinternals.com/procexp.exe) and when the error presents, examine and save the list of DLLs loaded in the process.

Then run the good one and do the same - will have to be quicker as you won't have an error to hold things up - maybe leave the final completion dialog up.
I've narrowed the problem down to the fact that the value for MsiNetAssemblySupport is wrong when my poxy msi.dll is in place.  When I run with my proxy msi.dll in place, the value is 2.0.50727.42 and when I run with the original msi.dll in place, the value is 4.0.30319.1.

I have .NET 2.0 and .NET 4.0 both installed on this system. When the original msi.dll is used, the MsiNetAssemblySupport is properly set for .NET 4.0.  But when my proxy msi.dll is used, that property is incorrect set for .NET 2.0.

The docs on MsiNetAssemblySupport say it is determined by looking at the version of fusion.dll. So I just need to figure out why my proxy dll is picking up the wrong version of fusion.dll when it does this check. I'm not changing the module load libraries or their ordering at all.
I would advise you to be cautious about trusting "the docs".

First verify that version determination is done by looking at a file.

Procmon might be the tool of choice at this point (http://live.sysinternals.com/procmon.exe) - I use it to trace COM activation - from the registry right through to module loading.

Use a filter to look for fusion.dll.

If you spend the time to setup debug symbols for procmon, you can get a stack trace of any particular row with a right click - I think you'll be able to scan for MsiNetAssemblySupport to confirm what it is doing.

Looking through the records may also reveal where your DLL get's the bad steer from.
Yes -- I already used procmon to verify that msiexec.exe is loading the wrong version of fusion.dll

I'm now thinking that my proxy msi.dll is targeted to .NET 2.0 and that's why it's picking up that version rather than the latest (.NET 4.0).
I thought that your msi.dll didn't use .NET?  Is there a way that a non-.NET DLL "targets" a version of .NET - I'm not knowledgeable on this, but very curious.
Yes -- my msi.dll doesn't do anything with .NET. But that's what I'm thinking -- that it is still somehow "targeting" an earlier version of .NET.

I so far have tried building my dll using VS 2010 which uses .NET 4 by default (did not work) and also putting an application config file in place which specifies .NET 4 (did not work).

Next up is trying to look at the .NET logs to see if I can figure it out.  Will definitely post here if I do.
The non-.NET equivalent of looking in fusion logs is sxstrace.exe.  It is included on every system.  Here is a shell script that automates the generation and parsing for you: http://csi-windows.com/toolkit/manifestutils
Sorry I haven't followed up in a few days. To summarize, the problem has been identified as the fact that msiexec.exe is loading the wrong version of fusion.dll. It loads the .NET 2 version of fusion.dll instead of the .NET 4 version of fusion.dll. Both versions of .NET are installed and it's not taking the latest when my proxy msi.dll is in place. If I put the original msi.dll back in place, msiexec.exe now loads the correct version of fusion.dll.

Nothing in the fusion logs nor in the output of sxstrace shows any information about the algorithm used to find the fusion.dll for the latest version of .NET.

So I still don't know the true root cause of the problem. If I had access to the source code for msiexec.exe perhaps I could understand the algorith being used for finding the proper version of fusion.dll and where that is going wrong.

I'm planning on pursuing a workaround of manually setting the MsiNetAssemblySupport property rather than relying on msiexec to determine it from the version number of the fusion.dll that it has loaded. Of course that means I'll need to provide code that can reliably determine the latest version of .NET that is installed but I can't see any other way to solve this problem given that I must use the proxy msi.dll for what my product is doing.
I would sure punt to WMI event notification queries if I were you - this isn't likely to be the first of these obscure workarounds you are going to have to do.

At least do a feasibility on it and find out if all you need to do can be done with the event notification query data.
Thanks for all of your help to this point. I checked into possibly using WMI but unfortunately simply detecting that an install has started is not sufficient for what I need to do. I need to be able to know that an install is about to start, then do some processing in my app, then allow the install to proceed. But the WMI events are not synchronous and would not allow me to stall the install until I'm ready for it.
Ok - good that you checked it out.

I just recently read that .NET runtime execution engine (mscoree.dll?) is loaded by a standard COM activation (the author was making the point that COM is still very integrated into windows).  Have you tried tracing your COM activations for mscoree.dll?
No. But that's a good suggestion. Maybe it is something about the COM activation. I'll check into it.
I'm not sure that's it. msiexec.exe is loading mscoree.dll and mscoreei.dll from .NET 4. But fusion.dll and mscorwks.dll are from .NET 2.
So that may point to it being a decision within the .NET framework that thinks you need .NET 2 instead of .NET 4.

What about a stack trace on the load module of mscorwks.dll?
I appreciate all the help to this point. I still cannot understand why msiexec.exe is loading the .NET 2 version of fusion.dll when .NET 4 is also installed. If I remove my msi.dll proxy and replace the original msi.dll, then the .NET 4 version of fusion.dll is loaded.

I am conceding with a workaround that I have found to work:  Just before any install begins, I check the value of the MsiNetAssemblySupport property and if it is set for .NET 2 and I also know that .NET 4 or higher is installed (by checking the file system), then I temporarily rename fusion.dll in the .NET 2 framework directory and then put it back as soon as the install is complete. This seems to allow the install to proceed and does not interfere with .NET 2 programs from running.
I would use a lot of caution with that work around!

It the very least I would load up a .NET 2 app (any one)  and then run an install using your rename technique.  I think you'll be barred from renaming.  Keep in mind there may be .NET services running against the 2.0 engine, making this an even more complex scenario.

Have you traced the fusion logs of the msiexec.exe process in both cases to see if you can determine the bit of data that the activation context is using to determine which to load?
Thanks for the caution. I can still install .NET 2 apps no problem. There is nothing at all in the fusion logs about loading fusion.dll from msiexec. The only way I can see to ever find the true root cause would be to get access to the source code for msiexec to see how it loads fusion.dll.
What I am referring to is the use of fusion.dll by .NET 2 apps while you are trying to rename or have it renamed - either it being locked and not being able to rename it, or .NET 2 trying to startup while it is renamed.

You'll also want to make sure your code is rock solid to replace fusion.dll if MSI does a rollback and possibly not rename it for every possible MSIEXEC run scenario (e.g. self-healing).

My suspicion is that a limited test run on a single dev machine with no other apps will not surface all the issues this might create and that customers would not be very likely to suspect your software to report bugs if they start having general .NET problems.
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
My issue of how to hook the functions in msi.dll by using a proxy dll is still unsolved. The CSI-Windows_com's suggestion to use a completely different approach is how I ended up ultimately resolving this. I included my own last posts as an explanation of what workaround I ended up using. CIS-Windows_com suggested using WMI. I ended up using a kernel minifilter driver to track the needed events.