/// <summary> /// Does the actual work of changing the caption after all of the verifications have been done /// that it's Ok to move the file. /// </summary> /// <param name="newCaption">The new caption.</param> /// <param name="newPath">The new absolute path.</param> public override void MoveNodeOnCaptionChange(string newCaption, string newPath) { string oldCaption = this.Caption; string oldPath = this.AbsolutePath; string oldRelativePath = this.RelativePath; bool updatedWindowCaptions = false; bool removedNode = false; Node newNode = null; // If we are currently selected, cache the value so we can restore the selection // after the addition. bool wasSelected = this.Selected; // Tell the environment to stop listening to file change events on the old file. using (FileChangeNotificationSuspender notificationSuspender = new FileChangeNotificationSuspender(oldPath)) { // Make sure the environment says we can start the rename. if (!this.Hierarchy.AttachedProject.Tracker.CanRenameFile(oldPath, newPath)) { return; } // Move the file on the file system to match the new name. if (!this.IsVirtual && File.Exists(oldPath) && !File.Exists(newPath)) { Tracer.WriteLineInformation(classType, "MoveNodeOnCaptionChange", "Renaming the file '{0}' to '{1}'.", oldPath, newPath); string newDirectoryName = Path.GetDirectoryName(newPath); if (!Directory.Exists(newDirectoryName)) { Directory.CreateDirectory(newDirectoryName); } File.Move(oldPath, newPath); } try { // Update all of the windows that currently have this file opened in an editor. this.UpdateOpenWindowCaptions(newCaption); updatedWindowCaptions = true; // We have to remove the node and re-add it so that we can have the sorting preserved. // Also, if the extension has changed then we'll have to recreate a new type-specific // FileNode. The easy way is to remove ourself from the project then tell the project // to add an existing file. string newRelativePath = PackageUtility.MakeRelative(this.Hierarchy.RootNode.AbsoluteDirectory, newPath); // Remove ourself from the hierarchy. this.Parent.Children.Remove(this); removedNode = true; // We have now been removed from the hierarchy. Do NOT call any virtual methods or // methods that depend on our state after this point. // Re-add ourself as a new incarnation (different object). Our life ends here. newNode = this.Hierarchy.AddExistingFile(newRelativePath, true); if (newNode != null) { // We need to set our hierarchy Id to match the new hierachy Id in case Visual Studio // calls back into us for something. this.SetHierarchyId(newNode.HierarchyId); // Select the new node if we were previously selected. if (wasSelected) { newNode.Select(); } // Tell the RDT to rename the document. Context.RunningDocumentTable.RenameDocument(oldPath, newPath, newNode.HierarchyId); } } catch (Exception e) { if (ErrorUtility.IsExceptionUnrecoverable(e)) { throw; } // Rollback the file move Tracer.WriteLineWarning(classType, "MoveNodeOnCaptionChange", "There was an error in renaming the document. Exception: {0}", e); File.Move(newPath, oldPath); // Remove the node that we just added. if (newNode != null) { newNode.RemoveFromProject(); } // Re-add a new node since we've already removed the old node. if (removedNode || this.Parent == null) { newNode = this.Hierarchy.AddExistingFile(oldRelativePath, true); this.SetHierarchyId(newNode.HierarchyId); if (wasSelected) { newNode.Select(); } } // Rollback the caption update on open windows if (updatedWindowCaptions) { this.UpdateOpenWindowCaptions(oldCaption); } // Rethrow the exception throw; } // Tell the environment that we're done renaming the document. this.Hierarchy.AttachedProject.Tracker.OnFileRenamed(oldPath, newPath); // Update the property browser. IVsUIShell vsUIShell = (IVsUIShell)this.Hierarchy.ServiceProvider.GetServiceOrThrow(typeof(SVsUIShell), typeof(IVsUIShell), classType, "MoveNodeOnCaptionChange"); vsUIShell.RefreshPropertyBrowser(0); } }
/// <summary> /// Canonicalizes the specified path as a file path. If it should be canonicalized as a folder path /// then the subclass should override. /// </summary> /// <param name="absolutePath">The path to canonicalize.</param> /// <returns>The canonicalized path.</returns> protected virtual string CanonicalizePath(string absolutePath) { return(PackageUtility.CanonicalizeFilePath(absolutePath)); }
/// <summary> /// Shows the Visual Studio open file dialog. /// </summary> /// <param name="dialogTitle">The title for the dialog box.</param> /// <param name="filter">The filter for the dialog.</param> /// <param name="initialDirectory">The initial starting directory. Can be null to use the current directory.</param> /// <returns>An array of paths to the chosen files or an empty array if the user canceled the dialog.</returns> public string[] ShowOpenFileDialog(string dialogTitle, string filter, string initialDirectory) { ArrayList fileNames = new ArrayList(); int bufferSize = NativeMethods.MAX_PATH; // Get the HWND to use for the modal file dialog. IntPtr hwnd; IVsUIShell uiShell = this.ServiceProvider.GetVsUIShell(classType, "ShowOpenFileDialog"); NativeMethods.ThrowOnFailure(uiShell.GetDialogOwnerHwnd(out hwnd)); // Create a native string buffer for the file name. IntPtr pwzFileName = Marshal.StringToHGlobalUni(new string('\0', bufferSize)); try { // Fill in open file options structure. VSOPENFILENAMEW[] openFileOptions = new VSOPENFILENAMEW[1]; openFileOptions[0].lStructSize = (uint)Marshal.SizeOf(typeof(VSOPENFILENAMEW)); openFileOptions[0].hwndOwner = hwnd; openFileOptions[0].pwzDlgTitle = dialogTitle; openFileOptions[0].pwzFileName = pwzFileName; openFileOptions[0].nMaxFileName = (uint)bufferSize; openFileOptions[0].pwzFilter = filter; openFileOptions[0].pwzInitialDir = initialDirectory; openFileOptions[0].dwFlags = (uint)(VsOpenFileDialogFlags.AllowMultiSelect); // Open the Visual Studio open dialog. int hr = uiShell.GetOpenFileNameViaDlg(openFileOptions); bool canceled = (hr == NativeMethods.OLE_E_PROMPTSAVECANCELLED); if (NativeMethods.Failed(hr) && !canceled) { NativeMethods.ThrowOnFailure(hr); } // Get the file name(s). if (openFileOptions[0].pwzFileName != IntPtr.Zero && !canceled) { // We want to get the entire buffered string because if multiple files were selected then it has // the following format: directory\0file1\0file2\0...fileN\0\0. Note that it ends with two null // terminators. string rawDialogPath = Marshal.PtrToStringUni(openFileOptions[0].pwzFileName, bufferSize); // These will hold our currently parsed values. StringBuilder directory = new StringBuilder(); StringBuilder fileName = new StringBuilder(); bool parsingDirectory = true; // Walk over the raw string to pull out the directory and the file names. for (int i = 0; i < rawDialogPath.Length; i++) { char c = rawDialogPath[i]; char nextC = (i + 1 < rawDialogPath.Length ? rawDialogPath[i + 1] : '\0'); // If we've hit a null termination, then we have to stop parsing for a second and add an // item to our array. if (c != '\0') { if (parsingDirectory) { directory.Append(c); } else { fileName.Append(c); } } else { if (parsingDirectory) { parsingDirectory = false; } else { // We've seen another file, so let's add the absolute path to our array. string absolutePath = Path.Combine(directory.ToString(), fileName.ToString()); absolutePath = PackageUtility.CanonicalizeFilePath(absolutePath); fileNames.Add(absolutePath); // Clear the file name StringBuilder for the next round. fileName.Length = 0; } // If we are at the double null termination then we can quit parsing. if (nextC == '\0') { // If the user only selected one file, then our parsed directory should be the full file name. if (fileNames.Count == 0) { fileNames.Add(directory.ToString()); } break; } } } } } finally { // Release the string buffer. if (pwzFileName != IntPtr.Zero) { Marshal.FreeHGlobal(pwzFileName); } } return((string[])fileNames.ToArray(typeof(string))); }
//========================================================================================= // Constructors //========================================================================================= /// <summary> /// Static class initializer. /// </summary> static Tracer() { // Get the full path to the log file. We'll create a directory in the user's // temp directory for the Votive log. This is to fix SourceForge bug #1122213 // where a non-administrator would have insufficient access to write to the // same directory as the assembly (which is Program Files by default). string tempDir = Path.Combine(Path.GetTempPath(), DirectoryName); if (!Directory.Exists(tempDir)) { try { Directory.CreateDirectory(tempDir); } catch (Exception e) { initializationException = e; return; } } logPath = Path.Combine(tempDir, FileName); // Delete the existing log file by creating a zero-length log file. if (File.Exists(logPath)) { FileStream stream = null; try { stream = File.Open(logPath, FileMode.Truncate, FileAccess.Write, FileShare.Write); } catch (Exception e) { initializationException = e; return; } finally { if (stream != null) { stream.Close(); } } } // We'll use the default listener as our logger. Create it if it doesn't exist. DefaultTraceListener listener = (DefaultTraceListener)Trace.Listeners["Default"]; if (listener == null) { listener = new DefaultTraceListener(); Trace.Listeners.Add(listener); } listener.LogFileName = logPath; Trace.IndentSize = 0; Trace.AutoFlush = true; try { // Write the first line to the trace log to make sure we can write to it. string firstLine = PackageUtility.SafeStringFormatInvariant("Trace started on {0}.", DateTime.Now.ToString(HeaderDateFormat, CultureInfo.InvariantCulture)); Trace.WriteLine(firstLine); } catch (Exception e) { initializationException = e; } }
int IVsProject3.AddItem(uint itemidLoc, VSADDITEMOPERATION dwAddItemOperation, string pszItemName, uint cFilesToOpen, string[] rgpszFilesToOpen, IntPtr hwndDlgOwner, VSADDRESULT[] pResult) { bool canceled = false; bool wereErrors = false; pResult[0] = VSADDRESULT.ADDRESULT_Failure; // Get the parent node to which it should be added. FolderNode parentNode = this.GetNode(itemidLoc, true) as FolderNode; if (parentNode == null) { string message = this.NativeResources.GetString(ResId.IDS_E_ADDITEMTOPROJECT, Path.GetFileName(this.FilePath)); Context.ShowErrorMessageBox(message); Tracer.Fail("The specified parent {0} is not a FolderNode so we can't add an item to it.", itemidLoc); return(NativeMethods.E_UNEXPECTED); } // Loop through the files that are to be added and add them, one by one. foreach (string sourcePath in rgpszFilesToOpen) { string destPath = null; Node addedNode = null; switch (dwAddItemOperation) { case VSADDITEMOPERATION.VSADDITEMOP_CLONEFILE: destPath = Path.Combine(parentNode.AbsoluteDirectory, pszItemName); addedNode = this.AddCopyOfFile(sourcePath, destPath, out canceled); break; case VSADDITEMOPERATION.VSADDITEMOP_OPENFILE: if (PackageUtility.IsRelative(this.RootDirectory, sourcePath)) { destPath = PackageUtility.MakeRelative(this.RootDirectory, sourcePath); addedNode = this.AddExistingFile(destPath, true); } else { destPath = Path.Combine(parentNode.AbsoluteDirectory, Path.GetFileName(sourcePath)); addedNode = this.AddCopyOfFile(sourcePath, destPath, out canceled); } break; case VSADDITEMOPERATION.VSADDITEMOP_LINKTOFILE: Tracer.Fail("NYI: VSADDITEMOPERATION.VSADDITEMOP_LINKTOFILE."); throw new NotImplementedException("Linking to files is not supported yet."); case VSADDITEMOPERATION.VSADDITEMOP_RUNWIZARD: Tracer.Fail("NYI: VSADDITEMOPERATION.VSADDITEMOP_RUNWIZARD."); throw new NotImplementedException("Running a wizard is not supported yet."); default: Tracer.Fail("Unknown VSADDDITEMOPERATION '{0}'", dwAddItemOperation); throw new ArgumentException(PackageUtility.SafeStringFormatInvariant("The dwAddItemOperation contains an unknown and unsupported value '{0}'.", dwAddItemOperation), "dwAddItemOperation"); } // There were errors if the node is still null at this point. wereErrors = (addedNode == null); } pResult[0] = (canceled ? VSADDRESULT.ADDRESULT_Cancel : (wereErrors ? VSADDRESULT.ADDRESULT_Failure : VSADDRESULT.ADDRESULT_Success)); return(NativeMethods.S_OK); }
/// <summary> /// Canonicalizes the specified path as a directory path. /// </summary> /// <param name="absolutePath">The path to canonicalize.</param> /// <returns>The canonicalized path.</returns> protected override string CanonicalizePath(string absolutePath) { return(PackageUtility.CanonicalizeFilePath(absolutePath)); }
public static void TraceRunningDocuments() { // Get the RDT (Running Document Table) IVsRunningDocumentTable rdt = Package.Instance.Context.ServiceProvider.GetService(typeof(IVsRunningDocumentTable)) as IVsRunningDocumentTable; if (rdt == null) { Tracer.WriteLineWarning(classType, "TraceRunningDocuments", "Cannot get an instance of IVsRunningDocumentTable to use for enumerating the running documents."); return; } // Get the enumerator for the currently running documents. IEnumRunningDocuments enumerator; int hr = rdt.GetRunningDocumentsEnum(out enumerator); if (NativeMethods.Failed(hr)) { Tracer.WriteLineWarning(classType, "TraceRunningDocuments", "Cannot get an instance of IEnumRunningDocuments to use for enumerating the running documents."); return; } // Enumerate. StringCollection traceLines = new StringCollection(); uint[] cookies = new uint[1]; uint fetchCount; while (true) { hr = enumerator.Next(1, cookies, out fetchCount); if (NativeMethods.Failed(hr)) { Tracer.WriteLineWarning(classType, "TraceRunningDocuments", "The enumeration failed for the running documents. Hr=0x{0:X}", hr); return; } if (fetchCount == 0) { break; } uint cookie = cookies[0]; // We shouldn't be getting a nil cookie. if (cookie == DocumentInfo.NullCookie) { Tracer.WriteLineWarning(classType, "TraceRunningDocuments", "There is a null cookie value in the RDT, which shouldn't be happening."); } else { // Now we have a document cookie, so let's get some information about it. DocumentInfo docInfo = Package.Instance.Context.RunningDocumentTable.FindByCookie(cookie); string traceMessage; if (docInfo == null) { traceMessage = PackageUtility.SafeStringFormatInvariant("The document with cookie '{0}' could not be found in the RDT. There's something weird going on.", cookie); } else { // Here's where we actually do the trace finally. traceMessage = PackageUtility.SafeStringFormatInvariant("RDT document: Cookie={0} Path={1} IsOpen={2} IsDirty={3}", docInfo.Cookie, docInfo.AbsolutePath, docInfo.IsOpen, docInfo.IsDirty); } // We don't want to trace immediately because we want all of these lines to appear together. If we // trace immediately, then the messages will be split up. traceLines.Add(traceMessage); } } // Now trace all of the messages at once. foreach (string traceMessage in traceLines) { Tracer.WriteLine(classType, "TraceRunningDocuments", Tracer.Level.Information, traceMessage); } }