1
0
Files
wfassoc/wfassoc/wfassoc.c

344 lines
11 KiB
C

#include "wfassoc.h"
#include <strsafe.h>
#include <ShlObj.h>
// private function and variable declearions.
// the function with _AL tail mean that the value returned by function is allocated from heap and should be free manually.
// otherwise, the tail of _NAL mean this function will not allocate any new memeory.
/// <summary>
/// Convert multi byte string to wide char string
/// Notice: this function will allocate memory for returns and it should be released safely.
/// </summary>
/// <param name="source">The string will be converted</param>
/// <param name="error">The error happend during converting</param>
/// <returns>The string has been converted. If return NULL, it mean that the converting failed.</returns>
WCHAR* ConvMultiByteToWideChar_AL(CHAR* source);
WCHAR* GetAppNameFromAppPath_NAL(WCHAR* path);
WCHAR* Strcat_AL(WCHAR* str1, WCHAR* str2);
WCHAR* GetPathFromAppPath_AL(WCHAR* path);
WCHAR* IterateSupportedTypesString_NAL(WCHAR* typesString, WCHAR* prev);
void WFPrintflnInDebug();
// some effective reg function
LSTATUS WFRegOpenKeyWithCreation(HKEY hkey, LPCWSTR lpSubKey, PHKEY phkResult);
LSTATUS WFRegSetStringValue(HKEY hkey, LPCWSTR lpValueName, const WCHAR* data);
#define LEGACY_PROGID_FORMATA "%s.%s.%d"
#define LEGACY_PROGID_FORMATW L"%s.%s.%d"
#define WFALLOC(type,count) (type*)malloc(sizeof(type)*count);
#define SAFE_EXEC_WIN32(wfError, recvError, skipLabel, function) recvError=function; if(recvError!=ERROR_SUCCESS) {wfError = WFERROR_WIN32_ERROR;goto skipLabel;}
#define SAFE_EXEC_STRALLOC(wfError, skipLabel, vStr, function) vStr=function; if(vStr==NULL) {wfError = WFERROR_ALLOC;goto skipLabel;}
#define SAFE_FREE_PTR(obj) if(obj!=NULL){free(obj);obj=NULL;}
#define SAFE_FREE_HKEY(hkey) if(hkey!=NULL&&hkey!=INVALID_HANDLE_VALUE){RegCloseKey(hkey);hkey=NULL;}
// public function implements.
WFERROR WFInstallApplicationW(WFAPP_PROFILEW* profile) {
if (profile->WFVersion != WFVERSION) return WFERROR_INVALID_VERSION;
WFERROR error = WFERROR_OK;
LSTATUS win32_error;
WCHAR* itemSupportedTypes = NULL;
// generate necessary strings
WCHAR* strAppPath = NULL,
* strPath = NULL,
* strApplications = NULL,
* strProgID = NULL,
* strOpenWithList = NULL;
SAFE_EXEC_STRALLOC(error, final_process,
strPath, GetPathFromAppPath_AL(profile->AppPath)
);
SAFE_EXEC_STRALLOC(error, final_process,
strAppPath, Strcat_AL(
L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\",
GetAppNameFromAppPath_NAL(profile->AppPath)
)
);
SAFE_EXEC_STRALLOC(error, final_process,
strProgID, Strcat_AL(
L"SOFTWARE\\Classes\\Applications\\",
profile->ProgID
)
);
SAFE_EXEC_STRALLOC(error, final_process,
strApplications, Strcat_AL(
L"SOFTWARE\\Classes\\Applications\\",
GetAppNameFromAppPath_NAL(profile->AppPath)
)
);
// generate necessary HKEY
HKEY nodeAppPath = NULL,
nodeApplications = NULL,
nodeApplications_Verb = NULL,
nodeApplications_SupportedTypes = NULL,
nodeProgID = NULL,
nodeProgID_Verb = NULL,
nodeClasses = NULL,
nodeExt = NULL,
nodeExt_OpenWithProgIds = NULL,
nodeExt_OpenWithList = NULL;
// register in `App Paths`
SAFE_EXEC_WIN32(error, win32_error, final_process,
WFRegOpenKeyWithCreation(profile->RegisterForAllUsers ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER, strAppPath, &nodeAppPath)
);
SAFE_EXEC_WIN32(error, win32_error, final_process,
WFRegOpenKeyWithCreation(profile->RegisterForAllUsers ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER, strApplications, &nodeApplications)
);
SAFE_EXEC_WIN32(error, win32_error, final_process,
WFRegOpenKeyWithCreation(nodeApplications, L"shell\\open\\command", &nodeApplications_Verb)
);
SAFE_EXEC_WIN32(error, win32_error, final_process,
WFRegOpenKeyWithCreation(nodeApplications, L"SupportedTypes", &nodeApplications_SupportedTypes)
);
SAFE_EXEC_WIN32(error, win32_error, final_process,
WFRegOpenKeyWithCreation(profile->RegisterForAllUsers ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER, strProgID, &nodeProgID)
);
SAFE_EXEC_WIN32(error, win32_error, final_process,
WFRegOpenKeyWithCreation(nodeProgID, L"shell\\open\\command", &nodeProgID_Verb)
);
SAFE_EXEC_WIN32(error, win32_error, final_process,
WFRegOpenKeyWithCreation(profile->RegisterForAllUsers ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER, L"SOFTWARE\\Classes", &nodeClasses)
);
// operate HKEY
SAFE_EXEC_WIN32(error, win32_error, final_process,
WFRegSetStringValue(nodeAppPath, NULL, profile->AppPath) // visit Default key
);
SAFE_EXEC_WIN32(error, win32_error, final_process,
WFRegSetStringValue(nodeAppPath, L"Path", strPath)
);
SAFE_EXEC_WIN32(error, win32_error, final_process,
WFRegSetStringValue(nodeApplications_Verb, NULL, profile->AppCommand) // visit Default key
);
SAFE_EXEC_WIN32(error, win32_error, final_process,
WFRegSetStringValue(nodeProgID_Verb, NULL, profile->AppCommand) // visit Default key
);
while ((itemSupportedTypes = IterateSupportedTypesString_NAL(profile->SupportedTypes, itemSupportedTypes)) != NULL) {
SAFE_EXEC_WIN32(error, win32_error, final_process,
WFRegSetStringValue(nodeApplications_SupportedTypes, itemSupportedTypes, NULL) // register supported type with blank item
);
SAFE_EXEC_WIN32(error, win32_error, final_process,
WFRegOpenKeyWithCreation(nodeClasses, itemSupportedTypes, &nodeExt)
);
if (profile->SetAsDefault) {
SAFE_EXEC_WIN32(error, win32_error, final_process,
WFRegSetStringValue(nodeExt, NULL, profile->ProgID) // visit Default key
);
}
if (profile->ShowInOpenWithMenu) {
if (profile->UseOpenWithList) {
// use Windows XP node
SAFE_EXEC_STRALLOC(error, final_process,
strOpenWithList, Strcat_AL(
L"OpenWithList\\",
GetAppNameFromAppPath_NAL(profile->AppPath)
)
);
SAFE_EXEC_WIN32(error, win32_error, final_process,
WFRegOpenKeyWithCreation(nodeExt, itemSupportedTypes, &nodeExt_OpenWithList);
);
SAFE_FREE_PTR(strOpenWithList);
SAFE_FREE_HKEY(nodeExt_OpenWithList);
} else {
// use Windows Vista node
SAFE_EXEC_WIN32(error, win32_error, final_process,
WFRegOpenKeyWithCreation(nodeExt, itemSupportedTypes, &nodeExt_OpenWithProgIds);
);
SAFE_EXEC_WIN32(error, win32_error, final_process,
WFRegSetStringValue(nodeExt_OpenWithProgIds, profile->ProgID, NULL)
);
SAFE_FREE_HKEY(nodeExt_OpenWithProgIds);
}
}
SAFE_FREE_HKEY(nodeExt);
}
final_process:
// free HKEY and strings
SAFE_FREE_HKEY(nodeAppPath);
SAFE_FREE_HKEY(nodeApplications);
SAFE_FREE_HKEY(nodeApplications_Verb);
SAFE_FREE_HKEY(nodeApplications_SupportedTypes);
SAFE_FREE_HKEY(nodeProgID);
SAFE_FREE_HKEY(nodeProgID_Verb);
SAFE_FREE_HKEY(nodeClasses);
SAFE_FREE_HKEY(nodeExt);
SAFE_FREE_HKEY(nodeExt_OpenWithProgIds);
SAFE_FREE_HKEY(nodeExt_OpenWithList);
SAFE_FREE_PTR(strAppPath);
SAFE_FREE_PTR(strPath);
SAFE_FREE_PTR(strApplications);
SAFE_FREE_PTR(strProgID);
// order uninstall to wipe out all written data
// if function failed
if (error != WFERROR_OK)
WFUninstallApplicationW(profile);
// active changes
SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL);
// return error
return error;
}
WFERROR WFInstallApplicationA(WFAPP_PROFILEA* profile) {
return WFERROR_OK;
}
WFERROR WFUninstallApplicationW(WFAPP_PROFILEW* profile) {
if (profile->WFVersion != WFVERSION) return WFERROR_INVALID_VERSION;
return WFERROR_OK;
}
WFERROR WFUninstallApplicationA(WFAPP_PROFILEA* profile) {
return WFERROR_OK;
}
WFERROR WFGenerateProgIDW(WCHAR* vendor, WCHAR* component, INT version, WCHAR* result, INT* result_length) {
if (result_length == NULL) return WFERROR_NULLPTR;
if (result == NULL) {
*result_length = _snwprintf(NULL, 0, LEGACY_PROGID_FORMATW, vendor, component, version) + 1;
} else {
int write_result = _snwprintf(result, *result_length, LEGACY_PROGID_FORMATW, vendor, component, version);
if (write_result < 0 || write_result >= *result_length) return WFERROR_INSUFFICIENT_BUFFER;
}
return WFERROR_OK;
}
WFERROR WFGenerateProgIDA(CHAR* vendor, CHAR* component, INT version, CHAR* result, INT* result_length) {
if (result_length == NULL) return WFERROR_NULLPTR;
if (result == NULL) {
*result_length = _snprintf(NULL, 0, LEGACY_PROGID_FORMATA, vendor, component, version) + 1;
} else {
int write_result = _snprintf(result, *result_length, LEGACY_PROGID_FORMATA, vendor, component, version);
if (write_result < 0 || write_result >= *result_length) return WFERROR_INSUFFICIENT_BUFFER;
}
return WFERROR_OK;
}
// private function and variable implements.
WCHAR* ConvMultiByteToWideChar_AL(CHAR* source) {
WCHAR* dest = NULL;
size_t sourceLength = strlen(source);
int destLength = MultiByteToWideChar(CP_ACP, 0, source, sourceLength, NULL, 0);
if (destLength <= 0) return NULL;
destLength += 10;
dest = WFALLOC(WCHAR, destLength);
if (dest == NULL) return NULL;
memset(dest, 0, sizeof(WCHAR) * destLength);
int error = MultiByteToWideChar(CP_ACP, 0, source, sourceLength, dest, destLength);
if (error <= 0) {
free(dest);
return NULL;
}
return dest;
}
WCHAR* GetAppNameFromAppPath_NAL(WCHAR* path) {
WCHAR* lastSlash = path;
while (*path != L'\0') {
if (*path == L'\\' || *path == L'/') {
lastSlash = path;
}
path++;
}
return ++lastSlash;
}
WCHAR* GetPathFromAppPath_AL(WCHAR* path) {
size_t length = wsclen(path);
WCHAR* dest = NULL;
dest = WFALLOC(WCHAR, length);
if (dest == NULL) return NULL;
wcscpy(dest, path);
// get the last slash.
// if this path do not contain slash, then lastSlash == dest, the path is not existed, return full path instead.
// otherwise, set lastSlash is zero to indicate the end of string
WCHAR* lastSlash = GetAppNameFromAppPath(dest);
if (lastSlash != dest) {
*(lastSlash - 1) = L'\0';
}
return dest;
}
WCHAR* Strcat_AL(WCHAR* str1, WCHAR* str2) {
size_t length = wcslen(str1) + wcslen(str2) + 10;
WCHAR* dest = NULL;
dest = WFALLOC(WCHAR, length);
if (dest == NULL) return NULL;
wcscpy(dest, str1);
wcscat(dest, str2);
return dest;
}
WCHAR* IterateSupportedTypesString_NAL(WCHAR* typesString, WCHAR* prev) {
if (typesString == NULL || *typesString == L'\0') return NULL;
if (prev == NULL) return typesString;
// skip prev string
while (*prev != L'\0') {
prev++;
}
// if the next string is start with zero, it mean that the full string is over and return NULL to terminate analyse.
// otherwise return next string
prev++;
if (*prev == L'\0') return NULL;
else return prev;
}
void WFPrintflnInDebug() {
}
LSTATUS WFRegOpenKeyWithCreation(HKEY hkey, LPCWSTR lpSubKey, PHKEY phkResult) {
return RegCreateKeyExW(
hkey,
lpSubKey,
0,
NULL,
REG_OPTION_NON_VOLATILE,
KEY_ALL_ACCESS,
NULL,
phkResult,
NULL
);
}
LSTATUS WFRegSetStringValue(HKEY hkey, LPCWSTR lpValueName, const WCHAR* data) {
return RegSetValueExW(
hkey,
lpValueName,
0,
REG_SZ,
data,
data == NULL ? 0 : ((wcslen(data) + 1) * sizeof(WCHAR))
);
}