344 lines
11 KiB
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))
|
|
);
|
|
} |