rolandjitsu rolandjitsu - 13 days ago 5
ASP.NET (C#) Question

include file in project from command line

Is there a way to include a file in project from command line in vs2012 ?

The reason why I'm asking is because is very frustrating to include any new file I add to the project folder whenever I use some other IDE ( like ST3 ) or when I save a file from Photoshop, etc.

I'm using Grunt for doing a lot of minifying, concatenation, running ngmin on my angular scripts, etc. There's a grunt-shell plugin that allows grunt tasks to run shell commands ( I'm already using it for unlocking locked files by TFS ). So I was thinking that I could create a task that would do the include in project for me for any new file I add ( by watching a certain folder with grunt-watch ).

Answer

Here is a solution using PowerShell. It is a little long, but I promise it works. I tested quite a bit.

First, the easy part. Here's how you run the script from the command prompt.

powershell -File C:\AddExistingItem.ps1 -solutionPath "C:\Test.sln" -projectName "TestAddItem" -item "C:\Test.txt"

Now the scary part, AddExistingItem.ps1:

param([String]$solutionPath, [String]$projectName, [String]$item)

#BEGIN: section can be removed if executing from within a PowerShell window
$source = @" 

namespace EnvDteUtils
{ 
    using System; 
    using System.Runtime.InteropServices; 

    public class MessageFilter : IOleMessageFilter 
    { 
        // 
        // Class containing the IOleMessageFilter 
        // thread error-handling functions. 

        // Start the filter. 
        public static void Register() 
        { 
            IOleMessageFilter newFilter = new MessageFilter();  
            IOleMessageFilter oldFilter = null;  
            CoRegisterMessageFilter(newFilter, out oldFilter); 
        } 

        // Done with the filter, close it. 
        public static void Revoke() 
        { 
            IOleMessageFilter oldFilter = null;  
            CoRegisterMessageFilter(null, out oldFilter); 
        } 

        // 
        // IOleMessageFilter functions. 
        // Handle incoming thread requests. 
        int IOleMessageFilter.HandleInComingCall(int dwCallType,  
          System.IntPtr hTaskCaller, int dwTickCount, System.IntPtr  
          lpInterfaceInfo)  
        { 
            //Return the flag SERVERCALL_ISHANDLED. 
            return 0; 
        } 

        // Thread call was rejected, so try again. 
        int IOleMessageFilter.RetryRejectedCall(System.IntPtr  
          hTaskCallee, int dwTickCount, int dwRejectType) 
        { 
            if (dwRejectType == 2) 
            // flag = SERVERCALL_RETRYLATER. 
            { 
                // Retry the thread call immediately if return >=0 &  
                // <100. 
                return 99; 
            } 
            // Too busy; cancel call. 
            return -1; 
        } 

        int IOleMessageFilter.MessagePending(System.IntPtr hTaskCallee,  
          int dwTickCount, int dwPendingType) 
        { 
            //Return the flag PENDINGMSG_WAITDEFPROCESS. 
            return 2;  
        } 

        // Implement the IOleMessageFilter interface. 
        [DllImport("Ole32.dll")] 
        private static extern int  
          CoRegisterMessageFilter(IOleMessageFilter newFilter, out  
          IOleMessageFilter oldFilter); 
    } 

    [ComImport(), Guid("00000016-0000-0000-C000-000000000046"),  
    InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] 
    interface IOleMessageFilter  
    { 
        [PreserveSig] 
        int HandleInComingCall(  
            int dwCallType,  
            IntPtr hTaskCaller,  
            int dwTickCount,  
            IntPtr lpInterfaceInfo); 

        [PreserveSig] 
        int RetryRejectedCall(  
            IntPtr hTaskCallee,  
            int dwTickCount, 
            int dwRejectType); 

        [PreserveSig] 
        int MessagePending(  
            IntPtr hTaskCallee,  
            int dwTickCount, 
            int dwPendingType); 
    } 
} 
"@ 

Add-Type -TypeDefinition $source      

[EnvDTEUtils.MessageFilter]::Register()
#END: section can be removed if executing from within a PowerShell window

$IDE = New-Object -ComObject VisualStudio.DTE

$IDE.Solution.Open("$solutionPath")

$project = $IDE.Solution.Projects | ? { $_.Name -eq "$projectName" }
$project.ProjectItems.AddFromFile("$item") | Out-Null
$project.Save()

$IDE.Quit()

#BEGIN: section can be removed if executing from within a PowerShell window
[EnvDTEUtils.MessageFilter]::Revoke()
#END: section can be removed if executing from within a PowerShell window

95% of the code is only there to let you run from the command prompt. If you are writing and running code directly in PowerShell you can leave it out and go straight to $IDE = New-Object -ComObject VisualStudio.DTE.

Here is a blog post explaining why that scary stuff is needed.
And here is another one on the same thing but in C#.

Another thing worth noting. I tried using the EnvDTE assemblies, like you would in .net, but I kept getting a COM registration error. Once I started using the COM objects directly everything worked. I don't know enough about COM to really venture a guess as to why this is.

EDIT

If you receive this error when trying to run the script from the command prompt:

Execution Policy Error

Then you need to run this first (you should only need to run it once ever.)

powershell -command "Set-ExecutionPolicy -ExecutionPolicy RemoteSigned"

Here is a good, in-depth explanation of what that command is doing.

Comments