Notifications
Clear all

[Closed] A little challenge… Max File Opener

Here’s a little challenge for those that can make EXE’s…

It would be nice if there was an application you could associate .max files with that would inspect the max file and see which version of max the file was saved in and then gave you options of which version of max to open the file with.

The current max file opening system is such a mess when you have multiple versions of max installed!

9 Replies

getting the version is pretty trivial, this should do it…


  #include <Windows.h>
  #include <stdio.h>
  #include <tchar.h>
  #include <conio.h>
  
  #define PROPSET_SUMINFO			0x000000ff
  #define PROPSET_DOCSUMINFO		0x0000ff00
  #define PROPSET_USERDEF			0x00ff0000
  
  #define ALL_PROPERTIES			0xffffffff	// All props
  #define TITLE_PROP				0x00000001	// Summary Info
  #define SUBJECT_PROP			0x00000002	// Summary Info
  #define AUTHOR_PROP				0x00000004	// Summary Info
  #define KEYWORDS_PROP			0x00000008	// Summary Info
  #define COMMENTS_PROP			0x00000010	// Summary Info
  #define MANAGER_PROP			0x00000100	// Document Summary Info
  #define COMPANY_PROP			0x00000200	// Document Summary Info
  #define CATEGORY_PROP			0x00000400	// Document Summary Info
  #define EXT_DEPEND_PROP			0x00000800	// Document Summary Info
  #define PLUGINS_PROP			0x00001000	// Document Summary Info
  #define OBJECTS_PROP			0x00002000	// Document Summary Info
  #define MATERIALS_PROP			0x00004000	// Document Summary Info
  #define USER_PROP				0x00010000	// User Defined Properties
  
  #define PID_TITLE				0x00000002
  #define PID_SUBJECT				0x00000003
  #define PID_AUTHOR				0x00000004
  #define PID_KEYWORDS			0x00000005
  #define PID_COMMENTS			0x00000006
  
  #define PID_MANAGER				0x0000000E
  #define PID_COMPANY				0x0000000F
  #define PID_CATEGORY			0x00000002
  #define PID_HEADINGPAIR			0x0000000C
  #define PID_DOCPARTS			0x0000000D
  
  
  #define testfile "D:/Max/Scenes/Golf/models/default/ball.max"
  
  int main(int argc, char* argv[])
  {
  	LPSTORAGE				pStorage = NULL;
  	IPropertySetStorage*	pPropertySetStorage = NULL;
  	IPropertyStorage*		pDocumentSummaryInfoStorage = NULL;
  
  	wchar_t	 wfilename[_MAX_PATH];
  
  	MultiByteToWideChar(CP_ACP, 0, (char*)argv[1], -1, wfilename, _MAX_PATH);
  
  	HRESULT	res = StgOpenStorage(wfilename, (LPSTORAGE)0, STGM_DIRECT|STGM_READ|STGM_SHARE_EXCLUSIVE,	NULL,0,&pStorage);
  	if (res!=S_OK) return 0;
  
  	if(S_OK != pStorage->QueryInterface(IID_IPropertySetStorage, (void**)&pPropertySetStorage)) 
  	{
  		pStorage->Release();
  		return 0;
  	}
  
  	if (S_OK == pPropertySetStorage->Open(FMTID_DocSummaryInformation, STGM_READ|STGM_SHARE_EXCLUSIVE, &pDocumentSummaryInfoStorage)) 
  	{
  		PROPSPEC	PropSpec[2] = {{PRSPEC_PROPID,PID_HEADINGPAIR},{PRSPEC_PROPID,PID_DOCPARTS}};
  		PROPVARIANT	PropVar[2];
  
  		HRESULT hr = pDocumentSummaryInfoStorage->ReadMultiple(2, PropSpec, PropVar);
  		if (S_OK == hr) 
  		{
  			if ((PropVar[0].vt == (VT_VARIANT | VT_VECTOR)) && (PropVar[1].vt == (VT_LPSTR | VT_VECTOR))) 
  			{
  				CAPROPVARIANT*	pHeading = &PropVar[0].capropvar;
  				CALPSTR*		pDocPart = &PropVar[1].calpstr;
  				printf("%s
", pDocPart->pElems[0]); // prints the maxversion string
  				printf("%s
", pDocPart->pElems[2]); // prints the build version string
  			}
  		}
  		FreePropVariantArray(2, PropVar);
  		pDocumentSummaryInfoStorage->Release();
  	}
  
  	pPropertySetStorage->Release();
  	pStorage->Release();
  
  	_getch();
  	return 0;
  }

you can associate max files with the above command line app and it will read and display the version (though early max version files don’t display the build version but the number of verts). you can use sscanf to extract the version from the string. find the version of max or higher that the file was create with (a list of file strings for each version should do it, just checking they exist ) then use then use createprocess window proc to launch max passing the filename as the command line arg.

this will get you most the way there…

// MaxVersion.cpp : Defines the entry point for the console application.
   //
   
   #include <Windows.h>
   #include <stdio.h>
   #include <tchar.h>
   #include <conio.h>
   
   #define PROPSET_SUMINFO			0x000000ff
   #define PROPSET_DOCSUMINFO		0x0000ff00
   #define PROPSET_USERDEF			0x00ff0000
   
   #define ALL_PROPERTIES			0xffffffff	// All props
   #define TITLE_PROP				0x00000001	// Summary Info
   #define SUBJECT_PROP			0x00000002	// Summary Info
   #define AUTHOR_PROP				0x00000004	// Summary Info
   #define KEYWORDS_PROP			0x00000008	// Summary Info
   #define COMMENTS_PROP			0x00000010	// Summary Info
   #define MANAGER_PROP			0x00000100	// Document Summary Info
   #define COMPANY_PROP			0x00000200	// Document Summary Info
   #define CATEGORY_PROP			0x00000400	// Document Summary Info
   #define EXT_DEPEND_PROP			0x00000800	// Document Summary Info
   #define PLUGINS_PROP			0x00001000	// Document Summary Info
   #define OBJECTS_PROP			0x00002000	// Document Summary Info
   #define MATERIALS_PROP			0x00004000	// Document Summary Info
   #define USER_PROP				0x00010000	// User Defined Properties
   
   #define PID_TITLE				0x00000002
   #define PID_SUBJECT				0x00000003
   #define PID_AUTHOR				0x00000004
   #define PID_KEYWORDS			0x00000005
   #define PID_COMMENTS			0x00000006
   
   #define PID_MANAGER				0x0000000E
   #define PID_COMPANY				0x0000000F
   #define PID_CATEGORY			0x00000002
   #define PID_HEADINGPAIR			0x0000000C
   #define PID_DOCPARTS			0x0000000D
   
   //const char* maxversions
   
   inline bool FileExists(const char* filename)
   {
   	if(FILE *file = fopen(filename, "r")) 
   	{
   		fclose(file);
   		return true;
   	} 
   	else 
   		return false;
   }
   
   const wchar_t* max12 = L"C:/Program Files (x86)/Autodesk/3ds Max 2010/3dsmax.exe";
   
   bool launchMax(const wchar_t* max, wchar_t* filename)
   {
   	STARTUPINFO StartupInfo;
   	PROCESS_INFORMATION ProcessInfo;
   
   	memset(&StartupInfo, 0, sizeof(StartupInfo));
   	StartupInfo.cb = sizeof(StartupInfo);
   	memset(&ProcessInfo, 0, sizeof(ProcessInfo));
   
   	wchar_t cmdline[512];
   	wcscpy(cmdline,max);
   	wcscat(cmdline,L" ");
   	wcscat(cmdline,filename);
   			  
   	if(!CreateProcess(NULL, cmdline, NULL, NULL,  NULL, NULL, NULL, NULL, &StartupInfo, &ProcessInfo))
   		return false;
   
   	return true;
   }
   
   //********************************************************************************************
   
   int main(int argc, char* argv[])
   {
   	LPSTORAGE				pStorage = NULL;
   	IPropertySetStorage*	pPropertySetStorage = NULL;
   	IPropertyStorage*		pDocumentSummaryInfoStorage = NULL;
   
   	float version;
   	wchar_t	 wfilename[_MAX_PATH];
   
   	char* filename = (char*)argv[1];
   	MultiByteToWideChar(CP_ACP, 0, (char*)argv[1], -1, wfilename, _MAX_PATH);
   
   	HRESULT	res = StgOpenStorage(wfilename, (LPSTORAGE)0, STGM_DIRECT|STGM_READ|STGM_SHARE_EXCLUSIVE, NULL,0,&pStorage);
   	if(res != S_OK)
   		return 0;
   
   	if(S_OK != pStorage->QueryInterface(IID_IPropertySetStorage, (void**)&pPropertySetStorage)) 
   	{
   		pStorage->Release();
   		return 0;
   	}
   
   	if (S_OK == pPropertySetStorage->Open(FMTID_DocSummaryInformation, STGM_READ|STGM_SHARE_EXCLUSIVE, &pDocumentSummaryInfoStorage)) 
   	{
   		PROPSPEC	PropSpec[2] = {{PRSPEC_PROPID,PID_HEADINGPAIR},{PRSPEC_PROPID,PID_DOCPARTS}};
   		PROPVARIANT	PropVar[2];
   
   		HRESULT hr = pDocumentSummaryInfoStorage->ReadMultiple(2, PropSpec, PropVar);
   		if(S_OK == hr) 
   		{
   			if((PropVar[0].vt == (VT_VARIANT | VT_VECTOR)) && (PropVar[1].vt == (VT_LPSTR | VT_VECTOR))) 
   			{
   				CAPROPVARIANT*	pHeading = &PropVar[0].capropvar;
   				CALPSTR*		pDocPart = &PropVar[1].calpstr;
   			   version = (float)atof(&pDocPart->pElems[0][16]); // hoping this doesn't change in future versions
   				printf("%s
", pDocPart->pElems[0]); // prints the maxversion string
   				printf("%s
", pDocPart->pElems[2]); // prints the build version string
   				
   			}
   		}
   		FreePropVariantArray(2, PropVar);
   		pDocumentSummaryInfoStorage->Release();
   	}
   
   	pPropertySetStorage->Release();
   	pStorage->Release();
   
   
   // choose max here etc (the easy version as this should be quite involved process) :)
   
   	if((int)version == 12)
   		launchMax(max12, wfilename);
   
   //_getch();
   	return 0;
   }

edited as earlier verions use “3ds max Version:” and later versions use “3ds Max Version:” :hmm:

this seems to work ok

// MaxVersion.cpp 
    
    #include <Windows.h>
    #include <stdio.h>
    #include <tchar.h>
    #include <conio.h>
    
    #pragma warning(disable : 4996)
    
    #define PID_HEADINGPAIR			0x0000000C
    #define PID_DOCPARTS			0x0000000D
    
    //********************************************************************************************
    
    inline bool FileExists(const wchar_t* filename)
    {
    	if(FILE* file = _wfopen(filename, L"r")) 
    	{
    		fclose(file);
    		return true;
    	} 
    	else 
    		return false;
    }
    
    //********************************************************************************************
    // max apps on you machine go here
    
    const wchar_t* maxversions[] = {
    	L"C:/Program Files (x86)/Autodesk/3ds Max 2010/3dsmax.exe",
    	L"C:/Program Files (x86)/Autodesk/3ds Max 2011/3dsmax.exe",
    	L"C:/Program Files (x86)/Autodesk/3ds Max 2012/3dsmax.exe",
    	L"C:/Program Files/Autodesk/3ds Max 2014/3dsmax.exe",
    	L"C:/Program Files/Autodesk/3ds Max 2015/3dsmax.exe" };
    
    // look up table for preferred usage app eg 0-12 by max 2010, 2013 by 2014 etc
    
    static int versionprefs[] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,3,3,4 };
    
    //********************************************************************************************
    
    bool launchMax(const wchar_t* max, wchar_t* filename)
    {
    	STARTUPINFO StartupInfo;
    	PROCESS_INFORMATION ProcessInfo;
    
    	memset(&StartupInfo, 0, sizeof(StartupInfo));
    	StartupInfo.cb = sizeof(StartupInfo);
    	memset(&ProcessInfo, 0, sizeof(ProcessInfo));
    
    	wchar_t cmdline[512];
    	wcscpy_s(cmdline,max);
    	wcscat_s(cmdline,L" ");
    	wcscat_s(cmdline,filename);
   
   // add any cmdline customization here
    			  
    	if(!CreateProcess(NULL, cmdline, NULL, NULL,  NULL, NULL, NULL, NULL, &StartupInfo, &ProcessInfo))
    		return false;
    	return true;
    }
    
    //********************************************************************************************
    
    int main(int argc, char* argv[])
    {
    	LPSTORAGE				pStorage = NULL;
    	IPropertySetStorage*	pPropertySetStorage = NULL;
    	IPropertyStorage*		pDocumentSummaryInfoStorage = NULL;
    
    	int version;
    	wchar_t	 wfilename[_MAX_PATH];
    
    	MultiByteToWideChar(CP_ACP, 0, argv[1], -1, wfilename, _MAX_PATH);
    
    	HRESULT	res = StgOpenStorage(wfilename, (LPSTORAGE)0, STGM_DIRECT|STGM_READ|STGM_SHARE_EXCLUSIVE, NULL,0,&pStorage);
    	if(res != S_OK)
    		return 0;
    
    	if(S_OK != pStorage->QueryInterface(IID_IPropertySetStorage, (void**)&pPropertySetStorage)) 
    	{
    		pStorage->Release();
    		return 0;
    	}
    
    	if (S_OK == pPropertySetStorage->Open(FMTID_DocSummaryInformation, STGM_READ|STGM_SHARE_EXCLUSIVE, &pDocumentSummaryInfoStorage)) 
    	{
    		PROPSPEC	PropSpec[2] = {{PRSPEC_PROPID,PID_HEADINGPAIR},{PRSPEC_PROPID,PID_DOCPARTS}};
    		PROPVARIANT	PropVar[2];
    
    		HRESULT hr = pDocumentSummaryInfoStorage->ReadMultiple(2, PropSpec, PropVar);
    		if(S_OK == hr) 
    		{
    			if((PropVar[0].vt == (VT_VARIANT | VT_VECTOR)) && (PropVar[1].vt == (VT_LPSTR | VT_VECTOR))) 
    			{
    				CAPROPVARIANT*	pHeading = &PropVar[0].capropvar;
    				CALPSTR*		pDocPart = &PropVar[1].calpstr;
    				version = (int)atof(&pDocPart->pElems[0][16]);
    			}
    		}
    		FreePropVariantArray(2, PropVar);
    		pDocumentSummaryInfoStorage->Release();
    	}
    
    	pPropertySetStorage->Release();
    	pStorage->Release();
    
    // dispatch to the preferred max version
    
    	if(FileExists(maxversions[versionprefs[version]]))
    		launchMax(maxversions[versionprefs[version]], wfilename);
    
    	return 0;
    }
    
    

I don’t think i’d have as the default app though is nice to have it as a right click open with option.

slight variation… the original is based on a console app by changing the build properties
Linker::system::subsystem to Windows (/SUBSYSTEM:WINDOWS) and changing the code to…

// MaxVersion.cpp 

#include <Windows.h>
#include <stdio.h>
#include <tchar.h>
#include <conio.h>

#pragma warning(disable : 4996)

#define PID_HEADINGPAIR			0x0000000C
#define PID_DOCPARTS			0x0000000D

//********************************************************************************************

inline bool FileExists(const wchar_t* filename)
{
	if(FILE* file = _wfopen(filename, L"r")) 
	{
		fclose(file);
		return true;
	} 
	else 
		return false;
}

//********************************************************************************************
// max apps on your machine go here

const wchar_t* maxversions[] = {
	L"C:/Program Files (x86)/Autodesk/3ds Max 2010/3dsmax.exe",
	L"C:/Program Files (x86)/Autodesk/3ds Max 2011/3dsmax.exe",
	L"C:/Program Files (x86)/Autodesk/3ds Max 2012/3dsmax.exe",
	L"C:/Program Files/Autodesk/3ds Max 2014/3dsmax.exe",
	L"C:/Program Files/Autodesk/3ds Max 2015/3dsmax.exe" };

// look up table for preferred usage app eg 0-12 by max 2010, 2013 by 2014 etc

static int versionprefs[] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,3,3,4 };

//********************************************************************************************

bool launchMax(const wchar_t* max, wchar_t* filename)
{
	STARTUPINFO StartupInfo;
	PROCESS_INFORMATION ProcessInfo;

	memset(&StartupInfo, 0, sizeof(StartupInfo));
	StartupInfo.cb = sizeof(StartupInfo);
	memset(&ProcessInfo, 0, sizeof(ProcessInfo));

	wchar_t cmdline[512];
	wcscpy_s(cmdline,max);
	wcscat_s(cmdline,L" ");
	wcscat_s(cmdline,filename);
			  
	if(!CreateProcess(NULL, cmdline, NULL, NULL,  NULL, NULL, NULL, NULL, &StartupInfo, &ProcessInfo))
		return false;
	return true;
}

//********************************************************************************************

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
	LPWSTR *szArgList;
	int argCount;
 
	szArgList = CommandLineToArgvW(GetCommandLine(), &argCount);

	LPSTORAGE				pStorage = NULL;
	IPropertySetStorage*	pPropertySetStorage = NULL;
	IPropertyStorage*		pDocumentSummaryInfoStorage = NULL;

	int version;

	wchar_t* wfilename = szArgList[1];

	HRESULT	res = StgOpenStorage(wfilename, (LPSTORAGE)0, STGM_DIRECT|STGM_READ|STGM_SHARE_EXCLUSIVE, NULL,0,&pStorage);
	if(res != S_OK)
		return 0;

	if(S_OK != pStorage->QueryInterface(IID_IPropertySetStorage, (void**)&pPropertySetStorage)) 
	{
		pStorage->Release();
		return 0;
	}

	if (S_OK == pPropertySetStorage->Open(FMTID_DocSummaryInformation, STGM_READ|STGM_SHARE_EXCLUSIVE, &pDocumentSummaryInfoStorage)) 
	{
		PROPSPEC	PropSpec[2] = {{PRSPEC_PROPID,PID_HEADINGPAIR},{PRSPEC_PROPID,PID_DOCPARTS}};
		PROPVARIANT	PropVar[2];

		HRESULT hr = pDocumentSummaryInfoStorage->ReadMultiple(2, PropSpec, PropVar);
		if(S_OK == hr) 
		{
			if((PropVar[0].vt == (VT_VARIANT | VT_VECTOR)) && (PropVar[1].vt == (VT_LPSTR | VT_VECTOR))) 
			{
				CAPROPVARIANT*	pHeading = &PropVar[0].capropvar;
				CALPSTR*		pDocPart = &PropVar[1].calpstr;
				version = (int)atof(&pDocPart->pElems[0][16]);
			}
		}
		FreePropVariantArray(2, PropVar);
		pDocumentSummaryInfoStorage->Release();
	}

	pPropertySetStorage->Release();
	pStorage->Release();

// dispatch to the preferred max version

	if(FileExists(maxversions[versionprefs[version]]))
		launchMax(maxversions[versionprefs[version]], wfilename);

	return 0;
}

there will be no console window opened so it appears more “seamless”

an exe (was compiled on windows 7 should work on xp & vista) for anyone to try, with the the “prefs” moved into an inifile (so win98 )

to use copy the exe & ini into the same folder edit the inifile for your system e.g. which max exes you want to use and which version they will open with. right click on any max file an select open with… choose default program and browse for the maxversion.exe ( i reset the default back to max afterwards) the app should then open the file in the “correct” version of max.

the source code, moved some of it into stl strings as it’s a bit “cleaner”…

// MaxVersion.cpp 

#include <Windows.h>
#include <stdio.h>
#include <tchar.h>
#include <conio.h>

#include <string>
#include <sstream>
#include <vector>
using namespace std;


#pragma warning(disable : 4996)

#define PID_HEADINGPAIR			0x0000000C
#define PID_DOCPARTS			0x0000000D

const wchar_t* INIFILE = L"./MaxVersion.ini";

template<typename T> std::wstring toString(const T& t)
{
  std::wstringstream stringStream;
  stringStream << t;
  return stringStream.str();
}

//********************************************************************************************

inline bool FileExists(const wstring& filename)
{
	if(FILE* file = _wfopen(filename.c_str(), L"r")) 
	{
		fclose(file);
		return true;
	} 
	else 
		return false;
}

//*****************************************************************************************
// max apps on your machine go here

static vector<wstring> maxexes;
static vector<int> versions;

//**************************************************************************************

bool launchMax(const wstring& max, const wstring& filename)
{
	STARTUPINFO StartupInfo;
	PROCESS_INFORMATION ProcessInfo;

	memset(&StartupInfo, 0, sizeof(StartupInfo));
	StartupInfo.cb = sizeof(StartupInfo);
	memset(&ProcessInfo, 0, sizeof(ProcessInfo));

	wstring cmdline = max;
	cmdline += L" ";
	cmdline += filename;
	   
	if(!CreateProcess(NULL,(LPWSTR)cmdline.c_str(), NULL, NULL,  NULL, NULL, NULL, NULL, &StartupInfo, &ProcessInfo))
		return false;
	return true;
}

//**************************************************************************************

bool readInifile(const wstring& inifile)
{
	if(!FileExists(inifile))
		return false;

	wchar_t buf[MAX_PATH];
	int count = GetPrivateProfileString(L"exe",L"execount",L"0", buf, MAX_PATH,inifile.c_str());
	int execount = _wtoi(buf);
	maxexes.resize(execount);

	for(int i = 0; i < execount; ++i)
	{
		wstring key(L"exe_");
		key += toString(i);
		count = GetPrivateProfileString(L"exe",key.c_str(),L"", buf, MAX_PATH,inifile.c_str());
		maxexes[i] = buf;
	}

	count = GetPrivateProfileString(L"versions",L"numversions",L"0", buf, MAX_PATH,inifile.c_str());
	int vercount = _wtoi(buf);
	versions.resize(vercount);

	for(int i = 0; i < vercount; ++i)
	{
		wstring key(L"ver_");
		key += toString(i);
		count = GetPrivateProfileString(L"versions",key.c_str(),L"", buf, MAX_PATH,inifile.c_str());
		versions[i] = _wtoi(buf);
	}
	return true;
}

//*****************************************************************************************

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
	if(!readInifile(INIFILE))
		return 0;

	LPWSTR *szArgList;
	int argCount, version;
 
	szArgList = CommandLineToArgvW(GetCommandLine(), &argCount);

	LPSTORAGE				pStorage = NULL;
	IPropertySetStorage*	pPropertySetStorage = NULL;
	IPropertyStorage*		pDocumentSummaryInfoStorage = NULL;

	wstring wfilename = szArgList[1];

	HRESULT	res = StgOpenStorage(wfilename.c_str(), (LPSTORAGE)0, STGM_DIRECT|STGM_READ|STGM_SHARE_EXCLUSIVE, NULL,0,&pStorage);
	if(res != S_OK)
		return 0;

	if(S_OK != pStorage->QueryInterface(IID_IPropertySetStorage, (void**)&pPropertySetStorage)) 
	{
		pStorage->Release();
		return 0;
	}

	if (S_OK == pPropertySetStorage->Open(FMTID_DocSummaryInformation, STGM_READ|STGM_SHARE_EXCLUSIVE, &pDocumentSummaryInfoStorage)) 
	{
		PROPSPEC	PropSpec[2] = {{PRSPEC_PROPID,PID_HEADINGPAIR},{PRSPEC_PROPID,PID_DOCPARTS}};
		PROPVARIANT	PropVar[2];

		HRESULT hr = pDocumentSummaryInfoStorage->ReadMultiple(2, PropSpec, PropVar);
		if(S_OK == hr) 
		{
			if((PropVar[0].vt == (VT_VARIANT | VT_VECTOR)) && (PropVar[1].vt == (VT_LPSTR | VT_VECTOR))) 
			{
				CAPROPVARIANT*	pHeading = &PropVar[0].capropvar;
				CALPSTR*		pDocPart = &PropVar[1].calpstr;
				version = (int)atof(&pDocPart->pElems[0][16]);
			}
		}
		FreePropVariantArray(2, PropVar);
		pDocumentSummaryInfoStorage->Release();
	}

	pPropertySetStorage->Release();
	pStorage->Release();

// dispatch to the preferred max version

	if(FileExists(maxexes[versions[version]]))
		launchMax(maxexes[versions[version]], wfilename);

	return 0;
}

just curios… what is the reason to open a max file with lowest available max version?

I dunno, maybe he only has plugins for certain versions and not others

we render on a network where max 2012 is installed with all the needed plugins. Sometimes we download files for interiors/exteriors made in 2013/2014 version. We have to open them in 2014 then save them as 2012 version. And sometimes, after conversion to lower version of max, and opening in 2012, I get an error – missing plugins – mass FX or smth like this. then I use this script: romevemissingplugins and only after this I can copy the object from this max file to the project max file where I work. It may happen I repeat these steps 5-15 times each day.