I've been trying to track down why my Office2010 plugin leaves a null pointer exception during uninstall and the 2007 version does not. (Edit: 2007 is at same state as 2010 - FAIL)
To narrow it down I have put in a couple of eventlog traps, meaning if code reaches this point - I should get something in the Eventlog. No such luck. Now either I written the eventlog trap wrong or code doesn't reach that point.
In the CustomSetupActions - ClickOnceInstaller.cs
public void Uninstall(System.Collections.IDictionary savedState)
{
// write something to eventlog
// This is not being fired, the exception doesn't reach here or writing to eventlog fails.
if (!EventLog.SourceExists("OfficePlugin"))
{
EventLog.CreateEventSource("OfficePlugin", "Application");
}
EventLog.WriteEntry
("OfficePlugin"
, string.Format("Uninstalling: (bug hunting)"), EventLogEntryType.Information);
string deploymentLocation = (string)savedState["deploymentLocation"];
if (deploymentLocation != null)
{
string arguments = String.Format(
"/S /U \"{0}\"", deploymentLocation);
ExecuteVSTOInstaller(arguments);
}
}
As for the ExecuteVSTOInstaller(string arguments)
2007 refers to: string subPath = #"Microsoft Shared\VSTO\9.0\VSTOInstaller.exe";
2010 refers to: string subPath = #"Microsoft Shared\VSTO\10.0\VSTOInstaller.exe";
If the first trap had fired, this is where I would have placed the trap afterwards.
--
I have another method that handles the registration db
RegisterOffice2010AddIn.cs
public void UnRegisterAddIn(string applicationName, string addInName)
{
Next line is precisely the same eventlog trap as I just used/shown.
Difference between the two (2007 vs 2010).
private const string UserSettingsLocation =
#"Software\Microsoft\Office\12.0\User Settings";
vs
private const string UserSettingsLocation =
#"Software\Microsoft\Office\14.0\User Settings";
I can't think of any other place that might be interesting to place the trap. I have a CustomInstaller which doesn't do anything besides Dispose(bool disposing) and InitializeComponent()
Development:
Action start 14:21:00: PublishFeatures.
Action ended 14:21:00: PublishFeatures. Return value 1.
Action start 14:21:00: PublishProduct.
Action ended 14:21:00: PublishProduct. Return value 1.
Action start 14:21:00: InstallExecute.
MSI (c) (B8:BC) [14:21:01:013]: Font created. Charset: Req=0, Ret=0, Font: Req=MS Shell Dlg, Ret=MS Shell Dlg
Error 1001. Error 1001. An exception occurred while uninstalling. This exception will be ignored and the uninstall will continue. However, the application might not be fully uninstalled after the uninstall is complete. --> Object reference not set to an instance of an object.
DEBUG: Error 2769: Custom Action _EE8A0D36_BE55_421F_9A55_95470C001D87.uninstall did not close 1 MSIHANDLEs.
The installer has encountered an unexpected error installing this package. This may indicate a problem with this package. The error code is 2769. The arguments are: _EE8A0D36_BE55_421F_9A55_95470C001D87.uninstall, 1,
Action ended 14:21:05: InstallExecute. Return value 3.
Action ended 14:21:06: INSTALL. Return value 3.
Googling the Error 2769 - gives an answer of "[TARGETDIR]\" , but I dont reference TargetDir, I reference deploymentLocation. And Yes I have tried adding the "\" to places I could think of. Including the setup - Registry - HKLM\Software\MS\Office\12.0\ ...etc... \Addins\Excel/Word/Outlook and the Manifest keyvalue. Gave no feedback, good or bad, errors or otherwise. I'm at a loss what else to try to hunt this one down.
I have a guess it is referencing to this, in the VDPROJ:
{
"Name" = "8:UnregisterOutlookAddIn"
"Condition" = "8:"
"Object" = "8:_73E71A44EB72485AB7367745F7D57F49"
"FileType" = "3:1"
"InstallAction" = "3:4"
"Arguments" = "8:"
"EntryPoint" = "8:"
"Sequence" = "3:3"
"Identifier" = "8:_EE8A0D36_BE55_421F_9A55_95470C001D87"
"InstallerClass" = "11:TRUE"
"CustomActionData" = "8:/addinName=\"OUR.Outlook.Outlook2010AddIn\" /application=\"Outlook\""
}
I found it throws two exception - the secondary under CustomSetupActions and UnregisterAddIn and the primary under ClickOnceInstaller and Uninstall. Howcome I mention them as 2ndary and primary. Well it will do the exception in CustomAction and then the killing one in ClickOnce. I've eliminated the one in CustomActions and I now only have to worry about the ClickOnce. From what I can gather the ClickOnce implements the interface specified in the Setup Project (Install, Rollback, Commit, Uninstall). I only have to figure out how it can run amiss the Uninstall method.
Disclaimer: Unless ofcourse I'm mistaken and is barking up the wrong tree.
Change to WiX. It became a workaround as the original is still true.
Related
I am attempting to check-out a single file via workspace.PendEdit with an exclusive lock LockLevel.CheckOut. The following function succeeds (no errors) but it seems to have no effect on the file in TFS (not checked-out and no lock).
public static void Lock(string filePath)
{
var workspace = GetWorkspace(filePath);
workspace.PendEdit(new[] {filePath}, RecursionType.None, null, LockLevel.CheckOut);
}
I am suspecting that this has something to do with my TFS Workspace being local. However, Visual Studio 2015 seems to have no problem establishing a lock on the file via [Source Control Explorer]->[Right Click Selected File]->[Advanced]->[Lock]. What am I doing that's different than what VS is doing? Am I missing something?
You should use RecursionType.Full not RecursionType.None.
workspace.PendEdit(new[] {filePath}, RecursionType.Full, null, LockLevel.CheckOut);
The PendEdit() method return the number of files that were checked out/locked for the filePath you specify. The RecursionType.Full will recurse to the last child of the path.
Update:
Please try to install this TFS nuget package(https://www.nuget.org/packages/Microsoft.TeamFoundationServer.ExtendedClient/) for your API project and test if this issue still exists. If it works, no matter what version of VS you use, this issue won't appear.
After much trial and error I ended up implementing an event handler for NonFatalError like this:
private static void VersionControlServer_NonFatalError(object sender, ExceptionEventArgs e)
{
if (e.Failure != null && e.Failure.Severity == SeverityType.Error)
throw new ApplicationException("An internal TFS error occurred. See failure message for details:\r\n"+e.Failure.Message);
}
Once the event handler was hooked up to the versionControlServer object via versionControlServer.NonFatalError += VersionControlServer_NonFatalError; I was able to see what was going on with my exclusive check-outs. As it turned out, TFS was failing silently with the following error:
TF400022: The item $/Fake/Server/Path/project.config cannot be locked for checkout in workspace MYWORKSPACE;Dan Lastname. Checkout locks are not supported in local workspaces.
The solution was to change the LockLevel from LockLevel.CheckOut to LockLevel.Checkin. Its a slightly different type of lock but its sufficient for my needs and that's the type of lock VS is using when you attempt to lock a file in a local workspace. So here is my original function with the tiny change in LockLevel that made all the difference.
public static void Lock(string filePath)
{
var workspace = GetWorkspace(filePath);
workspace.PendEdit(new[] {filePath}, RecursionType.None, null, LockLevel.Checkin);
}
I've encountered a problem when I try to retrieve the valid states of all features within an MSI package using the Microsoft.Deployment.WindowsInstaller.Installer class.
I want to copy the ValidStates property of each FeatureInfo within a Session. However when doing so I get a "Handle is in an invalid state." exception.
If I print each of these values out using Console.WriteLine() or step through the code in Visual Studio there is no exception.
I am at a loss as to what is preventing me from doing this.
Thanks in advance!
My Code:
var featureDictionary = new Dictionary<string, string[]>();
if (string.IsNullOrWhiteSpace(mPath))
return featureDictionary;
try
{
Installer.SetInternalUI(InstallUIOptions.Silent);
using (var session = Installer.OpenPackage(mPath, true))
{
foreach (var feature in session.Features)
{
try
{
var states = feature.ValidStates.Select((state) => state.ToString());
featureDictionary.Add(feature.Name, states.ToArray());
}
catch (InstallerException ex)
{
Debug.WriteLine(ex.Message);
}
}
}
}
catch (InstallerException) { }
return featureDictionary;
The basic problem appears to be that you are opening the MSI as a file. Since you haven't posted its declaration, or how it is set, I'm assuming that mpath means it's a path to the file. Your OpenPackage method parameters seem to indicate this too. You're getting that error because you are trying to open the MSI file as a file during the actual install, and failing.
The way to get hold of the database for the running install is to use Session.Database.
You can't open the running MSI as a file during the install perhaps for the same reason you can't run an MSI file that you have open with Orca, a simple file sharing violation. When you step through with Visual Studio you're simply accessing the static file and getting default values and the file isn't being used for an install. The other issue is that there can only be one Session object per process (as the OpenPackage docs say) and you are attempting to get a second one while there is already a Session object associated with the handle of the install.
As a custom action it needs to be sequenced after CostFinalize.
Windows Installer conditional expressions such as !feature-state will tell you what state the feature is in, because it's usually better to avoid code where Windows Installer will just give you the answer.
I am attempting to create a copy of an existing dtsx file so I can change a few variables based on input from the user. I am able to make a copy of the file, look at the variables, and set the variables to the correct input. However, when I go to look at the file in Visual Studio I get a few errors.
Microsoft Visual Studio is unable to load this document: The package failed to load to to error 0xC0010014 "One or more error occurred. There should be more specific errors preceding this one that explains the details of the errors. This message is used as a return value from functions that encounter errors". This occurs when CPackage::LoadFromXML fails.
The errors contained in the error list:
Error 3 Error loading test.dtsx: Error loading value "<DTS:Property xmlns:DTS="www.microsoft.com/SqlServer/Dts" DTS:Name="PackageFormatVersion">6</DTS:Property>" from node "DTS:Property". E:\test.dtsx 1 1
The version number in the package is not valid. The version number cannot be greater than current version number.
I looked into these errors and I saw potential issues with the server year and visual studio year. Both of these are the 2008 version.
My code:
string pkgPath = #"\\server\TestFolder\test.dtsx"
app = new Microsoft.SqlServer.Dts.Runtime.Application();
pkg = app.LoadPackage(pkgPath, null);
Console.WriteLine(pkg.Variables["filename"].Value.ToString());
pkg.Variables["filename"].Value = "testFile";
Console.WriteLine(pkg.Variables["filename"].Value.ToString());
app.SaveToXml(pkgPath, pkg, null);
If I open the file I am using to make a copy of in Visual Studio, it works no problem -- something strange is happening when I do app.SaveToXML();
Any ideas or suggestion would be wonderful.
To run this as a process from DTEXEC, it would look something like below. Please check out these two links for further details about ProcessStartInfo and how to use /SET so you can add that to your argument. Test this from the command line first because the syntax can be finicky.
https://msdn.microsoft.com/en-us/library/system.diagnostics.processstartinfo(v=vs.110).aspx
https://technet.microsoft.com/en-us/library/ms162810(v=sql.105).aspx
Using System.Diagnostics;
string args = #"/F'c:\MyPackage.dtsx' /SET'\package.variables[myvariable].Value;myvalue'";
ProcessStartInfo executePackage = new ProcessStartInfo("dtexec", args);
executePackage.UseShellExecute = false;
executePackage.RedirectStandardError = true;
executePackage.RedirectStandardOutput = true;
executePackage.CreateNoWindow = true;
StringBuilder output = new StringBuilder();
Process executing = Process.Start(executePackage);
while(!executing.StandardOutput.EndOfStream)
{
output.AppendLine(executing.StandardOutput.ReadLine();
}
executing.WaitForExit():
I have tried to create a custom action for a Visual Studio Installer project to modify the permissions for a config file.
The Installer.cs is as follows:
public override void Commit(IDictionary savedState)
{
base.Commit(savedState);
// Get path of our installation (e.g. TARGETDIR)
//string configPath = System.IO.Path.GetDirectoryName(Context.Parameters["AssemblyPath"]) + #"\config.xml";
string configPath = #"C:\Program Files\Blueberry\Serial Number Reservation\config.xml";
// Get a FileSecurity object that represents the current security settings.
FileSecurity fSecurity = File.GetAccessControl(configPath);
//Get SID for 'Everyone' - WellKnownSidType works in non-english systems
SecurityIdentifier everyone = new SecurityIdentifier(WellKnownSidType.WorldSid, null);
// Add the FileSystemAccessRule to the security settings.
fSecurity.AddAccessRule(new FileSystemAccessRule(everyone, FileSystemRights.Modify | FileSystemRights.Synchronize, InheritanceFlags.ContainerInherit | InheritanceFlags.ObjectInherit, PropagationFlags.None, AccessControlType.Allow));
// Set the new access settings.
File.SetAccessControl(configPath, fSecurity);
}
public override void Install(IDictionary stateSaver)
{
base.Install(stateSaver);
}
public override void Rollback(IDictionary savedState)
{
base.Rollback(savedState);
}
public override void Uninstall(IDictionary savedState)
{
base.Uninstall(savedState);
}
Then I add the Primary Output (Installer class = true) into the Commit section of the setup project's Custom Actions.
When I run the installer, I get the following error:
Error 1001: Could not find file 'c:\mypath\myapp.InstallState'
Scouring the web I've found a few examples of similar experiences, but none of the solutions offered have worked for me.
Any ideas?
You can find a solution here
To quote:
The problem is that the MSI infrastructure is looking for the installation state file which is usually created during the Install
phase. If the custom action does not participate in the Install phase,
no file is created.
The solution is to add the custom action to both the Install and the
Commit phases, although it does nothing during the install phase.
I had this problem when I didn't specify a custom action in my installer project for all four overrides (Install, Uninstall, Commit, and Rollback). As soon as I specified my project output as the custom action for all four, the issue went away.
The only overrides in my installer class that did anything were Commit and Uninstall; I think that Install was in charge of creating the InstallState file in the first place, and since it was never called the InstallState file was never created.
Sometimes this happens when the installer class is not created correctly. Here is a tutorial which may help you: http://devcity.net/Articles/339/1/article.aspx
Make sure that your custom action follows the tutorial recommendations.
Sometimes, "Debugger.Launch();" is put at those overwritten functions for debugging. If you build the installer with the statement there, and during your installation, a dialog will popup to ask you whether debug is needed, if you press 'cancel debugging', you'll get this error dialog. Because you added the 'Debugger.Launch()' at your function, then that function will be considered as 'missed' by installer. So, don't forget to remove it.
Try installing this as in an administrator command prompt. This worked for me.!
For me, the issue was as simple as just adding a closing quote around one of the textbox names in my CustomActionData string.
I was using the "Textboxes (A)" and "Textboxes (B)" windows in the User Interface section. A has 1 box, EDITA1, where I get the path to a file, and B has 2 boxes, EDITB1 and EDITB2, for some database parameters. My CustomActionData string looked like this:
/filepath="[EDITA1]" /host="[EDITB1] /port="[EDITB2]"
It should have been:
/filepath="[EDITA1]" /host="[EDITB1]" /port="[EDITB2]"
(closing quote on [EDITB1])
I used the Install override in my Installer class to get the values (i.e. string filepath = Context.Parameters["filepath"];) and used it to write them to an INI file for my app to use once installed. I put the "Primary output" under all of the phases in the Custom Actions GUI, but did nothing with the InstallerClass property (default: True) and only set the CustomActionData string on the Install one. I didn't even include override functions in my Installer class, since I was doing nothing that was custom in the other phases.
create your post install executable as you would a console app ex: "mypostinstallapp.exe".
install the "mypostinstallapp.exe" with your msi product. maybe put it with the Application Folder or a shared folder if you want to use it with multiple installs.
in the custom actions (rightclick the msi project and view custom actions) and add an action in the Commit section. assuming you want it to run towards the end.
then select your "mypostinstallapp.exe" from the actions view and in its properties set InstallerClass False.
what I do in "mypostinstallapp.exe" is look for a cmd file and run it as a process.
so i can add stuff to the cmd file and try to forget about the custom action process.
Make sure in the output properties you check installer class to false
You can set a register key to the installation path. Click on the Setup Project, View Register, and set the value as: [ProgramFiles64Folder][Manufacturer][ProductName].
Then you can create a Console App to get in to the register and take this information and run your program. Just need to add as a custom action on Commit the Console App you created.
Try setting Installer class = false instead of true in the properties for your custom action. That fixed this problem for me.
I have a Visual Studio 2010 solution consisting of 2 projects:
Core, a C# class library project which handles the functionality and data access
UI, an ASP.NET 4 website (.NET Framework 4) that references the Core, and calls functionality in the Core.
My exception handler is set in Global.asax (Application_Error.)
When an exception occurs in the UI, everything works perfectly, I get filename, line number, etc.
This is not the case for exceptions that occur in the Core.
For this, I get a stacktrace like:
{FillUserCount at offset 2376 in file:line:column <filename unknown>:0:0}
P.S. The Core.dll and Core.pdb are present in the UI Bin folder.
In Visual Studio -> Tools -> Options -> Debugging -> "Enable just my code" is unchecked and "Enable source server support" is checked.
Is there a way to get stackframe info (filename, class, method, line number) also for errors that occured in my referenced project ?
The solution to get this working was to create a new web site and re-referencing the Core project.
Still, the prerequisites that Jon Skeet mentioned remain the same:
the projects have to be built in Debug configuration, and not Release
while referencing a project, make sure PDB files are copied
on code side, when retrieving info about the exception that occured, and using StackTrace, make sure you create the instance with the second parameter set to true (true to capture the file name, line number, and column number; otherwise, false.)
This is my final working code. It may help others:
Exception ex = Server.GetLastError().GetBaseException();
StackTrace trace = new StackTrace(ex, true);
if (trace.FrameCount == 0)
return;
StackFrame stackFrame = trace.GetFrame(0);
string className, fileName, functionName, message;
int line = 0;
// if for some reason, filename is being retrieved as null
if (stackFrame.GetFileName() == null)
{
className = ex.TargetSite.ReflectedType.Name;
fileName = ex.TargetSite.ReflectedType.Name;
functionName = ex.TargetSite.Name;
message = ex.Message;
}
else
{
// Collect data where exception occured
string[] splitFile = stackFrame.GetFileName().Split('\\');
className = splitFile[splitFile.Length - 1];
fileName = stackFrame.GetFileName();
functionName = stackFrame.GetMethod().Name;
message = ex.Message;
line = stackFrame.GetFileLineNumber();
}
How are you referencing Core? If you've added a reference to the "Release" DLL, that would explain it... if you've just added a reference to the project and you're running a build with the "Debug" configuration then it should be okay.