From 357201d6c962c22e9c8bbebc6ab2cea8e258d7f2 Mon Sep 17 00:00:00 2001 From: yyc12345 Date: Mon, 28 Feb 2022 14:40:27 +0800 Subject: [PATCH] add install code WITHOUT safety check! --- wfassoc/wfassoc.c | 269 +++++++++++++++++++++++++++++++++++++++++++++- wfassoc/wfassoc.h | 13 ++- 2 files changed, 274 insertions(+), 8 deletions(-) diff --git a/wfassoc/wfassoc.c b/wfassoc/wfassoc.c index 46007a3..64fafe0 100644 --- a/wfassoc/wfassoc.c +++ b/wfassoc/wfassoc.c @@ -1,7 +1,10 @@ #include "wfassoc.h" #include +#include // 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. /// /// Convert multi byte string to wide char string @@ -10,18 +13,186 @@ /// The string will be converted /// The error happend during converting /// The string has been converted. If return NULL, it mean that the converting failed. -WCHAR* ConvMultiByteToWideChar(CHAR* source); +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 SAFE_FREE(obj) if(obj!=NULL)free(obj); +#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; - return WFERROR_OK; + 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) { @@ -63,7 +234,7 @@ WFERROR WFGenerateProgIDA(CHAR* vendor, CHAR* component, INT version, CHAR* resu // private function and variable implements. -WCHAR* ConvMultiByteToWideChar(CHAR* source) { +WCHAR* ConvMultiByteToWideChar_AL(CHAR* source) { WCHAR* dest = NULL; size_t sourceLength = strlen(source); @@ -71,7 +242,7 @@ WCHAR* ConvMultiByteToWideChar(CHAR* source) { if (destLength <= 0) return NULL; destLength += 10; - dest = (WCHAR*)malloc(sizeof(WCHAR) * destLength); + dest = WFALLOC(WCHAR, destLength); if (dest == NULL) return NULL; memset(dest, 0, sizeof(WCHAR) * destLength); @@ -82,4 +253,92 @@ WCHAR* ConvMultiByteToWideChar(CHAR* source) { } 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)) + ); } \ No newline at end of file diff --git a/wfassoc/wfassoc.h b/wfassoc/wfassoc.h index fe7056c..be46107 100644 --- a/wfassoc/wfassoc.h +++ b/wfassoc/wfassoc.h @@ -6,12 +6,12 @@ // quick marco for developer and should not be used in wfassoc self -#ifdef UNICODE +#if defined(_UNICODE) #define WFAPP_PROFILE WFAPP_PROFILEW #define WFInstallApplication WFInstallApplicationW #define WFUninstallApplication WFUninstallApplicationW #define WFGenerateProgID WFGenerateProgIDW -#else +#elif defined(_MBCS) #define WFAPP_PROFILE WFAPP_PROFILEA #define WFInstallApplication WFInstallApplicationA #define WFUninstallApplication WFUninstallApplicationA @@ -47,7 +47,10 @@ typedef enum _WFERROR { /// /// The encoding of string has error. /// - WFERROR_ENCODING = 4 + WFERROR_ENCODING = 4, + WFERROR_WIN32_ERROR = 5, + WFERROR_ASSERT_ERROR = 6, + WFERROR_ALLOC = 7 }WFERROR; /// @@ -78,6 +81,10 @@ typedef struct _WFAPP_PROFILEW { /// `.jpg\0.png\0.gif\0` /// WCHAR* SupportedTypes; + BOOL RegisterForAllUsers; + BOOL SetAsDefault; + BOOL ShowInOpenWithMenu; + BOOL UseOpenWithList; }WFAPP_PROFILEW; ///