Thoughts about setup and deployment issues, WiX, XNA, the .NET Framework and Visual Studio
All postings are provided AS IS
with no warranties, and confer no rights. Additionally, views expressed
herein are my own and not those of my employer, Microsoft.
I have built an installer using the Visual Studio 2005 setup project wizard. It installs correctly on Windows XP but fails on Windows Vista. I investigated and found that the failure on Windows Vista is caused by a custom action that fails with an access denied error. However, I am running the setup with elevated privileges in Windows Vista. How can I fix my setup so it will work correctly on Windows Vista?
As Robert Flaming described in this blog post, it is necessary to set the NoImpersonate flag for custom actions that modify the system in Windows Vista. This was an uninforced architectural intent that is now enforced in Windows Vista.
Unfortunately, there is not a way to directly set this flag for a custom action in the UI for the setup project in the Visual Studio IDE. In Visual Studio 2005, you can use a post-build step that modifies the MSI to set this bit using a strategy previously described in this blog post.
You can use the following steps to set the NoImpersonate bit in a Visual Studio 2005 setup project:
<update date="1/22/2007"> Updated command line in step 7. The variable I had specified previously was incorrect. It actually needs to be spelled wrong using "ouput" instead of "output" in the $(BuiltOuputPath) variable </update>
<update date="3/18/2009"> Fixed broken link to the sample script. </update>
Hi Cinni Patel - The name of the variable needs to be $(BuiltOuputPath) instead of $(BuiltOutputPath). The word Ouput is misspelled intentionally in the instructions in this blog post because that is what Visual Studio expects it to be named behind the scenes (this is a bug that was not fixed before VS 2005 shipped). Also, you listed the name of your .js file as CustomAction_NoImpersonate1.js - I'm not sure if the 1 is a typo or if you renamed the file when you downloaded it, but I'd suggest double-checking that as well.
Can you please try updating your post-build event command line and see if that helps?
If that doesn't help, then as a next step, I'd suggest looking at your build output to make sure that the $(ProjectDir) and $(BuiltOuputPath) variables are being resolved to the correct paths when the post-build step runs. You might need to put the .js file in a different directory to get it to run correctly.
Thanks for your article.
However, I have a problem. I want to run postbuild event with 2 conditions because i want to run script "CustomAction_NoImpersonate.js" if not terminalserver and an another script if terminalserver. Can you help me to do this ?
Thank you very much.
(Sorry for my english)
Hi Yoyo1977 - I think what you'll want to do for this scenario is create a new build configuration in Visual Studio, and assign different post-build steps to each of the build configurations. That will allow you to run a different script depending on which configuration you are building. You can find more information about VS build configurations in the MSDN topic at http://msdn.microsoft.com/library/kkz9kefa.aspx.
No I don't want to do this.
I just want to assign a different postbuildevent in the property of the setup when i'm in the situation of Terminal Server.
cscript.exe "$(ProjectDir)CustomAction.js" "$(BuiltOuputPath)" for non terminal server
cscript.exe "$(ProjectDir)CustomAction_Terminal.js" "$(BuiltOuputPath)" for terminal server.
Hi Yoyo1977 - To do this, you will need to define an MSBuild property and set it to different values for the terminal server case and the non-terminal server case. Then you can add a conditional statement to your post-build command that looks like this:
if $(TerminalServer)==False cscript.exe "$(ProjectDir)CustomAction.js" "$(BuiltOuputPath)"
if $(TerminalServer)==True cscript.exe "$(ProjectDir)CustomAction_Terminal.js" "$(BuiltOuputPath)"
The trick for you will be to figure out how you figure out when to set the TerminalServer property to true or false. I don't know what you mean by "in the situation of Terminal Server" in your description, so you'll have to set that based on what your scenario requires.
Also, please note that I'm not an MSBuild syntax expert. There are a lot of blogs and forum posts that you can find by doing a search for "Visual Stuido conditional post-build events" so you may want to review some of the other examples that are out there as you refine your syntax.
I am following the steps that you have described above
i need to install the setup in vista but i
getting the below error
The installer has encountered an unexpected error installing this package. This may indicate a problem with this package. The error code is 2869
My postbuiltevent command is
cscript.exe "$(ProjectDir)CustomAction_NoImpersonate.js" "$(BuiltOuputPath)"
My development projectname is Test1 and setup project name is websetup1.where to put the js file. and how to check js file
Hi Sasireka - You can look at the build output in Visual Studio to see the exact paths that $(ProjectDir) and $(BuiltOuputPath) get resolved to when your post-build command gets run. You can use that information to make sure that you're putting the .js file in the correct directory on your system.
I'm using your script file referencing the post build event with the incorrectly spelt text.
In my custom action (an C# EXE), I display a dialog with a text box:
textbox.Text = Environment.UserName;
However when I run the MSI, the textbox.Text value is "SYSTEM"
Is there something wrong with the script, I was expecting the value to be my <WindowsAccountName>
Hi Craig_aus - No, there's nothing wrong with the script - that is exactly what I would expect to see. Setting the NoImpersonate custom action attribute means that it will not use impersonation. That means that it will run with the same credentials as Windows Installer runs under (which is the local SYSTEM account).
I am facing the same issue "error code 2869" when installing my service on vista.
I am not able to download the zip file "CustomAction_NoImpersonate.zip" from the "sample script(http://astebner.sts.winisp.net/Tools/CustomAction_NoImpersonate.zip)" link that you have given in this post.
Can you please fix this issue so that I can download the file and implement your steps?
Or else you can also email that file to me at email@example.com.
Really appreciate your help on this.
Thanks & Regards,
Hi Sowji250 - My old WinISP file server was retired a few weeks ago, and I've been fixing up broken links as I find them. I just updated this blog post with the new location of this sample script. You can download it from http://cid-27e6a35d1a492af7.skydrive.live.com/self.aspx/Blog%7C_Tools/CustomAction%7C_NoImpersonate.zip.
Hi, This posting is great. Thank you so much for making this available.
I got your script working in JS, but have tried porting it to C# so that I can get it to run as part of some other code already running as part of a post-processing operation on an automated build process. (I've actually modified it a bit as well, to UNSET the NoImpersonate bit and SET TSAware.)
My C# code looks like this:
public static void EditMSIDatabase(string msiFile)
Installer installer = (Installer)Activator.CreateInstance(
Database database = installer.OpenDatabase(msiFile, MsiOpenDatabaseMode.msiOpenDatabaseModeDirect);
string sql = "SELECT `Action`, `Type`, `Source`, `Target` FROM `CustomAction`";
View view = database.OpenView(sql);
Record record = view.Fetch();
while (record != null)
if ((record.get_IntegerData(2) & (int)ExecutionContext.InScript) > 0)
int initalValue = record.get_IntegerData(2);
record.set_IntegerData(2, record.get_IntegerData(2) & ~(int) ExecutionContext.NoImpersonate);
record.set_IntegerData(2, record.get_IntegerData(2) | (int) ExecutionContext.TSAware);
record = view.Fetch();
public enum ExecutionContext
InScript = 0x00000400,
Rollback = 0x00000100,
Commit = 0x00000200,
NoImpersonate = 0x00000800,
TSAware = 0x00008000,
When I run the code, it gets as far as the call to view.Modify, and then throws an exception.
Unhandled Exception: System.ApplicationException: A COM exception was thrown while trying to write to the MSI file ---> System.Runtime.InteropServices.COMExcept
ion (0x80004005): Modify,Mode,Record
at System.RuntimeType.ForwardCallToInvokeMember(String memberName, BindingFlags flags, Object target, Int32 aWrapperTypes, MessageData& msgData)
at WindowsInstaller.View.Modify(MsiViewModify Mode, Record Record)
at AgileBuildPostProcessor.MSIFixer.EditMSIDatabase(String msiFile) in C:\Projects\AileBuildPostProcessor\AileBuildPostProcessor\MSIFixer.cs:line 36
Any ideas what might be causing this and how to work around it? Any help would be appreciated.
Hi Aufgang - I don't see anything that jumps out at me in looking at your code. You may want to look at some of the samples in the Windows Installer SDK as a starting point
There is also a managed code wrapper for the MSI APIs in WiX v3.0 (http://wix.sourceforge.net) that I'd suggest taking a look at. It is called the Deployment Tools Foundation (DTF) and there is a good help file that comes with it as well. To look at that, you just need to install one of the builds of WiX v3.0 from http://wix.sourceforge.net/releases/.
Hi, I apply your suggestion and re-built my msi in vstudio 2008. The msi still doesn't work on vista and throws the same error:
"There is a problem with this Windows Installer package. A program required for this install to complete could not be run. Contact your support personnel or package vendor."
Other reason causes this error?
Hi Lovecreatesbeauty@hotmail.com - This error essentially means that the custom action is failing, but doesn't provide any details about the root cause. There could be a lot of possible causes for this type of error, depending on what your custom action is designed to do. You will probably need to try to debug your custom action in more detail to narrow this down further. You can do this by adding logging to your custom action or adding a dialog box at the beginning of your custom action so you can attach a debugger. You could also try using the MsiBreak environment variable (described at http://msdn.microsoft.com/library/aa368264.aspx).