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:

Jeff Roberts said...

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

cbloom said...

4dos can look for an MSVC?

KeYeR said...

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

cbloom said...

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

-> associate cpp,hpp with vs08

?

Namtar said...

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 ...

Tom said...

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...

Tom said...

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

Tom said...

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".

cbloom said...

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

Sam said...

Woah, assoc and ftype. That is awesome!

Tom Johnstone said...

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 );
}

cbloom said...

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.

Tom Johnstone said...

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);
}

cbloom said...

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.

Tom Johnstone said...

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

Tom Johnstone said...

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

cbloom said...

" 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)

Tom Johnstone said...

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;
}

Tom Johnstone said...

vcsendto.bat-

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

old rants