in vsintegration/src/FSharp.ProjectSystem.Base/Project/HierarchyNode.cs [2726:2861]
public virtual int SaveItem(VSSAVEFLAGS saveFlag, string silentSaveAsName, uint itemid, IntPtr docData, out int cancelled)
{
cancelled = 0;
if (this.ProjectMgr == null || this.ProjectMgr.IsClosed)
{
return VSConstants.E_FAIL;
}
// Validate itemid
if (itemid == VSConstants.VSITEMID_ROOT || itemid == VSConstants.VSITEMID_SELECTION)
{
return VSConstants.E_INVALIDARG;
}
HierarchyNode node = this.ProjectMgr.NodeFromItemId(itemid);
if (node == null)
{
return VSConstants.E_FAIL;
}
string existingFileMoniker = node.GetMkDocument();
// We can only perform save if the document is open
if (docData == IntPtr.Zero)
{
string errorMessage = string.Format(CultureInfo.CurrentCulture, SR.GetString(SR.CanNotSaveFileNotOpeneInEditor, CultureInfo.CurrentUICulture), node.Url);
throw new InvalidOperationException(errorMessage);
}
string docNew = String.Empty;
int returnCode = VSConstants.S_OK;
IPersistFileFormat ff = null;
IVsPersistDocData dd = null;
IVsUIShell shell = this.projectMgr.Site.GetService(typeof(SVsUIShell)) as IVsUIShell;
if (shell == null)
{
return VSConstants.E_FAIL;
}
try
{
//Save docdata object.
//For the saveas action a dialog is show in order to enter new location of file.
//In case of a save action and the file is readonly a dialog is also shown
//with a couple of options, SaveAs, Overwrite or Cancel.
ff = Marshal.GetObjectForIUnknown(docData) as IPersistFileFormat;
if (ff == null)
{
return VSConstants.E_FAIL;
}
if (VSSAVEFLAGS.VSSAVE_SilentSave == saveFlag)
{
returnCode = shell.SaveDocDataToFile(saveFlag, ff, silentSaveAsName, out docNew, out cancelled);
ErrorHandler.ThrowOnFailure(returnCode);
}
else
{
dd = Marshal.GetObjectForIUnknown(docData) as IVsPersistDocData;
if (dd == null)
{
return VSConstants.E_FAIL;
}
returnCode = dd.SaveDocData(saveFlag, out docNew, out cancelled);
ErrorHandler.ThrowOnFailure(returnCode);
}
// We can be unloaded after the SaveDocData() call if the save caused a designer to add a file and this caused
// the project file to be reloaded (QEQS caused a newer version of the project file to be downloaded). So we check
// here.
if (this.ProjectMgr == null || this.ProjectMgr.IsClosed)
{
cancelled = 1;
return (int)OleConstants.OLECMDERR_E_CANCELED;
}
else
{
// if a SaveAs occurred we need to update to the fact our item's name has changed.
// this includes the following:
// 1. call RenameDocument on the RunningDocumentTable
// 2. update the full path name for the item in our hierarchy
// 3. a directory-based project may need to transfer the open editor to the
// MiscFiles project if the new file is saved outside of the project directory.
// This is accomplished by calling IVsExternalFilesManager::TransferDocument
// we have three options for a saveas action to be performed
// 1. the flag was set (the save as command was triggered)
// 2. a silent save specifying a new document name
// 3. a save command was triggered but was not possible because the file has a read only attrib. Therefore
// the user has chosen to do a save as in the dialog that showed up
bool emptyOrSamePath = String.IsNullOrEmpty(docNew) || NativeMethods.IsSamePath(existingFileMoniker, docNew);
bool saveAs = ((saveFlag == VSSAVEFLAGS.VSSAVE_SaveAs)) ||
((saveFlag == VSSAVEFLAGS.VSSAVE_SilentSave) && !emptyOrSamePath) ||
((saveFlag == VSSAVEFLAGS.VSSAVE_Save) && !emptyOrSamePath);
if (saveAs)
{
returnCode = node.AfterSaveItemAs(docData, docNew);
// If it has been cancelled recover the old name.
if ((returnCode == (int)OleConstants.OLECMDERR_E_CANCELED || returnCode == VSConstants.E_ABORT))
{
// Cleanup.
this.DeleteFromStorage(docNew);
if (this is ProjectNode && FSLib.Shim.FileSystem.SafeExists(docNew))
{
File.Delete(docNew);
}
if (ff != null)
{
returnCode = shell.SaveDocDataToFile(VSSAVEFLAGS.VSSAVE_SilentSave, ff, existingFileMoniker, out docNew, out cancelled);
}
}
else if (returnCode != VSConstants.S_OK)
{
ErrorHandler.ThrowOnFailure(returnCode);
}
}
}
}
catch (COMException e)
{
Trace.WriteLine("Exception :" + e.Message);
returnCode = e.ErrorCode;
// Try to recover
if (ff != null)
{
shell.SaveDocDataToFile(VSSAVEFLAGS.VSSAVE_SilentSave, ff, existingFileMoniker, out docNew, out cancelled);
}
}
return returnCode;
}