Code Snippet: Ensuring a file is in a project

Every couple of weeks, I'll get a question about getting an arbitrary file on disk into a project in VS without asking VS to open in a document window directly. It seems to be pretty hard to find information for (when I needed to do it, it took a few email threads with various other teams to figure it out), so here's a code snippet I use for this. It works by adding the file to the miscellaneous files project (if it isn't already in another project) and then returning the hierarchy/itemid for the file, which can be used with other methods involving the shell, opening documents, locking documents, etc.


///
<summary>

///
If the given file isn't in a project, put it in the miscellaneous files project, and return

///
the project/itemid of the file.

///
</summary>

///
<returns>hresult (failure code)</returns>

static int
EnsureFileInAProject(string
fileName,
out
IVsUIHierarchy hierarchy, out uint itemId)

{

    IVsUIShellOpenDocument shellOpenDocument = ServiceProvider.GlobalProvider.GetService(typeof(SVsUIShellOpenDocument))
as
IVsUIShellOpenDocument;

    IOleServiceProvider unused;

   
int
pDocInAProject;

   
// Before we can take a lock, we need to make sure the given doc data is in a project.

   
// First, ask the shell if it already is

   
if
(ErrorHandler.Failed(shellOpenDocument.IsDocumentInAProject(fileName,
out
hierarchy,
out
itemId,
out
unused,
out
pDocInAProject)))

    {

        Debug.Fail("IsDocumentInAProject failed, which is unexpected.");

       
return
VSConstants.E_FAIL;

    }

   
// We need to add it to the miscellaneous files project if it isn't in a project.

   
if
(pDocInAProject == (int)__VSDOCINPROJECT.DOCINPROJ_DocNotInProject)

    {

       
var
externalFilesManager = Shell.Package.GetGlobalService(typeof(SVsExternalFilesManager))
as
IVsExternalFilesManager;

       
if
(externalFilesManager == null)

        {

            Debug.Fail("Can't find the miscellaneous files project to use.");

           
return
VSConstants.E_FAIL;

        }

       
// Make sure the misc files project doesn't actually *open* the document

       
uint
createFlags = (uint)__VSCREATEDOCWIN.CDW_RDTFLAGS_MASK | (uint)_VSRDTFLAGS.RDT_PlaceHolderDoc;

       
Guid
emptyGuid = Guid.Empty;

       
int
unusedPos;

        IVsWindowFrame unusedFrame;

       
if
(ErrorHandler.Failed(externalFilesManager.AddDocument(createFlags, fileName, IntPtr.Zero,
IntPtr.Zero,
ref
emptyGuid,
null, ref emptyGuid,
null, null, out unusedPos,
out
unusedFrame)))

        {

            Debug.Fail("Couldn't add the document to miscellaneous files project.");

           
return
VSConstants.E_FAIL;

        }

        IVsProject miscProject;

       
if
(ErrorHandler.Failed(externalFilesManager.GetExternalFilesProject(out
miscProject)) ||

            (hierarchy = miscProject as IVsUIHierarchy) ==
null
||

            ErrorHandler.Failed(hierarchy.ParseCanonicalName(fileName,
out
itemId)))

        {

            Debug.Fail("Couldn't get the misc files project or item we just added.");

           
return
VSConstants.E_FAIL;

        }

    }

   
return
VSConstants.S_OK;

}