8/06/2010

08-06-10 - Visual Studio File Associations

This is my annoyance of the moment. I rarely double-click CPP/H files, but once in a while it's handy.

On my work machine, I currently have VC 2003,2005,2008, and 2010 installed. Which one should the CPP/H file open in?

The right answer is obviously : whichever one is currently running.

And of course there's no way to do that.

Almost every day recently I've had a moment where I am working away in VC ver. A , and I double click some CPP file, and it doesn't pop up, and I'm like "WTF", and then I hear my disk grinding, and I'm like "oh nooes!" , and then the splash box pops up announcing VC ver. B ; thanks a lot guys, I'm really glad you started a new instance of your gargantuan hog of a program so that I could view one file.

Actually if I don't have a DevStudio currently running, then I'd like CPP/H to just open in notepad. Maybe I have to write my own tool to open CPP/H files. But even setting the file associations to point at my own tool is a nightmare. Maybe I have to write my own tool to set file associations. Grumble.

(for the record : I have 2008 for Xenon, 2005 is where I do most of my work, I keep 2003 to be able to build some old code that I haven't ported to 2005 templates yet, and 2010 to check it out for the future).

19 comments:

  1. Can't you just associate c and h to a batch 4dos batch file that looks for the MSVC that's running?

    ReplyDelete
  2. Easier. Why not to create a batch file for each VS you are using, that will modify the register to change file association for the one its going about to run, like

    vs08.bat
    -> associate cpp,hpp with vs08
    -> run vs08

    vs10.bat
    -> associate cpp,hpp with vs10
    -> run vs10

    should be quite simple

    ReplyDelete
  3. That's a pretty good idea. How do I do :

    -> associate cpp,hpp with vs08

    ?

    ReplyDelete
  4. You can use "assoc" to change file associations from the command line.

    I don't have VisualStudio on this machine to check the exact syntax but running "assoc .cpp" will show you the current association. Just changing the version number is usually enough ...

    ReplyDelete
  5. Try ASSOC and FTYPE. Looks like ASSOC maps extension to type, and FTYPE maps type to application. I guess you could create one type for each version of VS, then use ASSOC to change the mapping.

    A quick google suggests http://ss64.com/nt/assoc.html has some info...

    ReplyDelete
  6. BTW Here's what my PC says for .cpp:

    [0][ C:\bin\4nt ]assoc .cpp
    .cpp=VisualStudio.cpp.8.0

    [0][ C:\bin\4nt ]ftype VisualStudio.cpp.8.0
    VisualStudio.cpp.8.0="C:\vs2005\Common7\IDE\devenv.exe" /dde

    ReplyDelete
  7. Goddamnit, blogger ate my first post with my second one. My first post was basically "see ASSOC and FTYPE, the mapping is 2-step, oh and here's a link: http://ss64.com/nt/assoc.html".

    ReplyDelete
  8. Whoah , I had no idea assoc & ftype existed. Awesome.

    ReplyDelete
  9. Woah, assoc and ftype. That is awesome!

    ReplyDelete
  10. That doesn't support the default "open in notepad" though. That same problem has been pissing me off for a while, so I wrote a little app which reroutes file opens-

    #include <windows.h>
    #include <tlhelp32.h>
    #include <stdio.h>

    #include <string>

    // Forward declarations:
    BOOL ListProcessModules( DWORD dwPID, const char* filename );
    BOOL ListProcessThreads(const char* filename);

    void main( int argc, const char** argv )
    {
    if (argc != 2)
    {
    printf("usage: %s [filename]\n", argv[0]);
    return;
    }

    if (!ListProcessThreads(argv[1]))
    {
    std::string path;
    path += "start notepad ";
    path += argv[1];
    system(path.c_str());
    }
    }

    BOOL ListProcessModules( DWORD dwPID, const char* filename )
    {
    HANDLE hModuleSnap = INVALID_HANDLE_VALUE;
    MODULEENTRY32 me32;

    // Take a snapshot of all modules in the specified process.
    hModuleSnap = CreateToolhelp32Snapshot( TH32CS_SNAPMODULE, dwPID );
    if( hModuleSnap == INVALID_HANDLE_VALUE )
    {
    return( TRUE );
    }

    // Set the size of the structure before using it.
    me32.dwSize = sizeof( MODULEENTRY32 );

    // Retrieve information about the first module,
    // and exit if unsuccessful
    if( !Module32First( hModuleSnap, &me32 ) )
    {
    CloseHandle( hModuleSnap ); // Must clean up the snapshot object!
    return( TRUE );
    }

    // Now walk the module list of the process,
    // and display information about each module
    do
    {
    if (strstr(me32.szModule, "devenv.exe"))
    {
    std::string path = "start \"";
    path += me32.szExePath;
    path += "\" ";
    path += filename;
    printf(path.c_str());
    system(path.c_str());
    CloseHandle( hModuleSnap );
    return FALSE;
    }
    } while( Module32Next( hModuleSnap, &me32 ) );

    // Do not forget to clean up the snapshot object.
    CloseHandle( hModuleSnap );
    return( TRUE );
    }

    BOOL ListProcessThreads(const char* filename)
    {
    HANDLE hThreadSnap = INVALID_HANDLE_VALUE;
    THREADENTRY32 te32;

    // Take a snapshot of all running threads
    hThreadSnap = CreateToolhelp32Snapshot( TH32CS_SNAPTHREAD, 0 );
    if( hThreadSnap == INVALID_HANDLE_VALUE )
    return( TRUE );

    // Fill in the size of the structure before using it.
    te32.dwSize = sizeof(THREADENTRY32 );

    // Retrieve information about the first thread,
    // and exit if unsuccessful
    if( !Thread32First( hThreadSnap, &te32 ) )
    {
    CloseHandle( hThreadSnap ); // Must clean up the snapshot object!
    return( TRUE );
    }

    // Now walk the thread list of the system,
    // and display information about each thread
    // associated with the specified process
    do
    {
    if (!ListProcessModules(te32.th32OwnerProcessID, filename))
    return TRUE;
    } while( Thread32Next(hThreadSnap, &te32 ) );

    // Don't forget to clean up the snapshot object.
    CloseHandle( hThreadSnap );
    return( FALSE );
    }

    ReplyDelete
  11. Well done sir, I just built it and it seems to work dandy.

    Yeah the assoc/ftype thing is awesome but doesn't actually solve the problem in question.

    I mean, say I run vs08.bat , then vs10.bat , then close my vs10, it's associated to the wrong thing. Buggy, fragile.

    ReplyDelete
  12. This version's a little bit quicker. I was finding it a little bit laggy when running through VM Ware on my laptop-


    #include <tlhelp32.h> 
    #include <stdio.h> 
    #include <string>

    #pragma comment(lib, "User32.lib")

    // Forward declarations: 
    BOOL ListProcessModules( DWORD dwPID, const char* filename ); 
    BOOL CALLBACK EnumWindowsProc(__in HWND hwnd, __in LPARAM lParam);

    void main( int argc, const char** argv )
    {
        if (argc != 2)
        {
            printf("usage: %s [filename]\n", argv[0]);
            return;
        }
        
        if (EnumWindows(&EnumWindowsProc, (LPARAM)argv[1]))
        {
            std::string path;
            path += "start notepad ";
            path += argv[1];
            system(path.c_str());
        }
    }

    BOOL ListProcessModules( DWORD dwPID, const char* filename ) 

        HANDLE hModuleSnap = INVALID_HANDLE_VALUE; 
        MODULEENTRY32 me32; 

        // Take a snapshot of all modules in the specified process. 
        hModuleSnap = CreateToolhelp32Snapshot( TH32CS_SNAPMODULE, dwPID ); 
        if( hModuleSnap == INVALID_HANDLE_VALUE ) 
        { 
            return( TRUE ); 
        } 

        // Set the size of the structure before using it. 
        me32.dwSize = sizeof( MODULEENTRY32 ); 

        // Retrieve information about the first module, 
        // and exit if unsuccessful 
        if( !Module32First( hModuleSnap, &me32 ) ) 
        { 
            CloseHandle( hModuleSnap ); // Must clean up the snapshot object! 
            return( TRUE ); 
        } 

        // Now walk the module list of the process, 
        // and display information about each module 
        do 
        {
            if (!strcmp(me32.szModule, "devenv.exe"))
            {
                std::string path = "start \"";
                path += me32.szExePath;
                path += "\" ";
                path += filename;
                printf(path.c_str());
                system(path.c_str());
                CloseHandle( hModuleSnap ); 
                return FALSE;
            }
        } while( Module32Next( hModuleSnap, &me32 ) ); 

        // Do not forget to clean up the snapshot object. 
        CloseHandle( hModuleSnap ); 
        return( TRUE ); 
    }

    BOOL CALLBACK EnumWindowsProc(__in HWND hwnd, __in LPARAM lParam)
    {
        DWORD threadId;
        GetWindowThreadProcessId(hwnd, &threadId);
        const char* filename = (const char*)lParam;
        return ListProcessModules(threadId, filename);
    }

    ReplyDelete
  13. Why don't you just use TH32CS_SNAPPROCESS ?

    I'm turning this into a more generic utility I'll call SendTo , which will look for any process name and send the file to it, and errorlevel return if not found, so you can then run the default from a batch file.

    ReplyDelete
  14. Well I guess you don't really need to enumerate all processes, just the ones with windows

    ReplyDelete
  15. In fact you only need to check the first module of each process too, as that is guaranteed to be the exe

    ReplyDelete
  16. " Well I guess you don't really need to enumerate all processes, just the ones with windows "

    Yeah but the number of processes is like an order of magnitude smaller than the number of windows typically (a few hundred vs a few thousand)

    ReplyDelete
  17. sendto.cpp-

    #include <Windows.h>
    #include <tlhelp32.h>
    #include <stdio.h>
    #include <string>
    BOOL ListProcessModules( DWORD dwPID, const char* processName, const char* filename )
    {
    HANDLE hModuleSnap = INVALID_HANDLE_VALUE;
    MODULEENTRY32 me32;
    // Take a snapshot of all modules in the specified process.
    hModuleSnap = CreateToolhelp32Snapshot( TH32CS_SNAPMODULE, dwPID );
    if( hModuleSnap == INVALID_HANDLE_VALUE )
    {
    return( TRUE );
    }
    // Set the size of the structure before using it.
    me32.dwSize = sizeof( MODULEENTRY32 );
    // Retrieve information about the first module,
    // and exit if unsuccessful
    if( !Module32First( hModuleSnap, &me32 ) )
    {
    CloseHandle( hModuleSnap ); // Must clean up the snapshot object!
    return( TRUE );
    }
    CloseHandle( hModuleSnap ); // Must clean up the snapshot object!
    if (strstr(processName, me32.szModule) == processName)
    {
    std::string path;
    path += "start \"\" /B \"";
    path += me32.szExePath;
    path += "\" ";
    path += processName + strlen(me32.szModule);
    path += " \"";
    path += filename;
    path += "\"";
    printf("%s\n", path.c_str());
    system(path.c_str());
    return FALSE;
    }
    return( TRUE );
    }
    BOOL ListProcessThreads(const char* processName, const char* filename)
    {
    HANDLE hThreadSnap = INVALID_HANDLE_VALUE;
    THREADENTRY32 te32;
    // Take a snapshot of all running threads
    hThreadSnap = CreateToolhelp32Snapshot( TH32CS_SNAPTHREAD, 0 );
    if( hThreadSnap == INVALID_HANDLE_VALUE )
    return( TRUE );
    // Fill in the size of the structure before using it.
    te32.dwSize = sizeof(THREADENTRY32 );
    // Retrieve information about the first thread,
    // and exit if unsuccessful
    if( !Thread32First( hThreadSnap, &te32 ) )
    {
    CloseHandle( hThreadSnap ); // Must clean up the snapshot object!
    return( TRUE );
    }
    // Now walk the thread list of the system,
    // and display information about each thread
    // associated with the specified process
    do
    {
    if (!ListProcessModules(te32.th32OwnerProcessID, processName, filename))
    return TRUE;
    } while( Thread32Next(hThreadSnap, &te32 ) );
    // Don't forget to clean up the snapshot object.
    CloseHandle( hThreadSnap );
    return( FALSE );
    }
    int main( int argc, const char** argv )
    {
    if (argc != 3)
    {
    printf("usage: %s [process name] [filename]\n", argv[0]);
    return -1;
    }
    if (!ListProcessThreads(argv[1], argv[2]))
    {
    return 1;
    }
    return 0;
    }

    ReplyDelete
  18. vcsendto.bat-

    @echo off
    "%~dp0sendto" "devenv.exe /Edit" %1
    if ERRORLEVEL 1 start notepad %1

    ReplyDelete