diff --git a/wfassoc/utils.c b/wfassoc/utils.c deleted file mode 100644 index ab9c7e6..0000000 --- a/wfassoc/utils.c +++ /dev/null @@ -1,166 +0,0 @@ -#include "utils.h" - -#pragma region WFString - -WFERROR WFString_Alloc(WFString** strl, wchar_t* raw_data) { - WFERROR ec; - if ((ec = WFString_Alloc(strl, 0)) != WFERROR_OK) return ec; - if ((ec = WFString_SetData(*strl, raw_data)) != WFERROR_OK) return ec; - return WFERROR_OK; -} - -WFERROR WFString_Alloc(WFString** strl, char* raw_data, BOOL is_utf8) { - return WFERROR(); -} - -WFERROR WFString_Alloc(WFString** strl, uint32_t size) { - *strl = WFNEW(WFString); - if (*strl == NULL) return WFERROR_ALLOC; - - // only init capacity and raw data here, other field will be refreshed in rezise func - WFERROR ec; - (*strl)->mCapacity = 0; - (*strl)->mRawData = NULL; - ec = WFString_Resize(*strl, size); - - // return resize result - return ec; -} - -WFERROR WFString_Free(WFString* strl) { - if (strl == NULL) return WFERROR_OK; - if (strl->mRawData == NULL) return WFERROR_NULLPTR; - free(strl->mRawData); - free(strl); - return WFERROR_OK; -} - -WFERROR WFString_Resize(WFString* strl, uint32_t new_size) { - // if remain data is not enough, we need alloc new one - if (new_size >= strl->mCapacity) { - strl->mCapacity = new_size * 2; - strl->mRealCapacity = strl->mCapacity + 1; - - // alloc buffer - if (strl->mRawData != NULL) - strl->mRawData = realloc(strl->mRawData, strl->mRealCapacity * sizeof(wchar_t)); - else - strl->mRawData = malloc(strl->mRealCapacity * sizeof(wchar_t)); - if (strl->mRawData == NULL) return WFERROR_ALLOC; - } - - // set length as new length - strl->mLength = new_size; - strl->mRealLength = strl->mLength + 1; - - // set the last one is zero - strl->mRawData[strl->mRealLength - 1] = 0; - return WFERROR_OK; -} - -WFERROR WFString_GetData(WFString* strl, wchar_t** pdata) { - if (strl == NULL) return WFERROR_NULLPTR; - *pdata = strl->mRawData; - return WFERROR_OK; -} - -WFERROR WFString_SetData(WFString* strl, wchar_t* data) { - // get length and resize buffer - WFERROR ec; - uint32_t size = wcslen(data); - ec = WFString_Resize(strl, size); - if (ec != WFERROR_OK) return ec; - - // copy data - if (size != 0) - memcpy(strl->mRawData, data, sizeof(wchar_t) * size); - - return WFERROR_OK; -} - -WFERROR WFString_GetLength(WFString* strl, uint32_t* len) { - if (strl == NULL) return WFERROR_NULLPTR; - *len = strl->mLength; - return WFERROR_OK; -} - -WFERROR WFString_Printf(WFString* strl, wchar_t* format, ...) { - return WFERROR(); -} - -#pragma endregion - -#pragma region WFLinkedList - -WFERROR WFLinkedList_Alloc(WFLinkedList** list) { - *list = WFNEW(WFLinkedList); - if (*list == NULL) return WFERROR_ALLOC; - return WFERROR_OK; -} - -WFERROR WFLinkedList_Free(WFLinkedList* list) { - return WFLinkedList_Free_Full(list, NULL); -} - -WFERROR WFLinkedList_Free_Full(WFLinkedList* list, WFLinkedListNode_FreeDataFunc free_func) { - if (list == NULL) return WFERROR_OK; - - // iterate full list and remove data and node - WFLinkedListNode* node = list->mHead, * free_node = NULL; - while (node != NULL) { - // free raw data - if (free_func != NULL) (*free_func)(node->mRawData); - // move to next node and free current node - free_node = node; - node = node->mNext; - free(free_node); - } - - // free list self - free(list); - - return WFERROR_OK; -} - -WFERROR WFLinkedList_Add(WFLinkedList* list, void* data) { - if (list == NULL) return WFERROR_NULLPTR; - - WFLinkedListNode* new_item = WFNEW(WFLinkedListNode); - if (new_item == NULL) return WFERROR_ALLOC; - new_item->mNext = NULL; - new_item->mRawData = data; - - if (list->mLength == 0) { - list->mHead = list->mTail = new_item; - } else { - list->mTail->mNext = new_item; - list->mTail = new_item; - } - ++list->mLength; - return WFERROR_OK; -} - -WFERROR WFLinkedList_NodeIterator(WFLinkedList* list, WFLinkedListNode** node_ptr) { - if (list == NULL) return WFERROR_NULLPTR; - - if (*node_ptr == NULL) { - // if node_ptr is NULL, it mean that wo should iterate this list from head - *node_ptr = list->mHead; - } else { - // otherwise, move to next node - *node_ptr = (*node_ptr)->mNext; - } - - // if the header is null, return end of tail error to notice caller stop iterate - if (*node_ptr == NULL) return WFERROR_END_OF_TAIL; - else return WFERROR_OK; -} - -WFERROR WFLinkedListNode_GetData(WFLinkedListNode* node, void** pdata) { - if (node == NULL) return WFERROR_NULLPTR; - - *pdata = node->mRawData; - return WFERROR_OK; -} - -#pragma endregion diff --git a/wfassoc/utils.h b/wfassoc/utils.h deleted file mode 100644 index 6809560..0000000 --- a/wfassoc/utils.h +++ /dev/null @@ -1,44 +0,0 @@ -#if !defined(_YYCDLL_WFASSOC_UTILS_H__IMPORTED_) -#define _YYCDLL_WFASSOC_UTILS_H__IMPORTED_ - -#include "wfassoc.h" -#include - -typedef struct _WFString { - wchar_t* mRawData; - uint32_t mLength; - uint32_t mCapacity; - - uint32_t mRealLength; - uint32_t mRealCapacity; -}WFString; - -WFERROR WFString_Alloc(WFString** strl, wchar_t* raw_data); -WFERROR WFString_Alloc(WFString** strl, char* raw_data, BOOL is_utf8); -WFERROR WFString_Alloc(WFString** strl, uint32_t size); -WFERROR WFString_Free(WFString* strl); -WFERROR WFString_Resize(WFString* strl, uint32_t new_size); -WFERROR WFString_GetData(WFString* strl, wchar_t** pdata); -WFERROR WFString_SetData(WFString* strl, wchar_t* data); -WFERROR WFString_GetLength(WFString* strl, uint32_t* len); -WFERROR WFString_Printf(WFString* strl, wchar_t* format, ...); - -typedef WFERROR(*WFLinkedListNode_FreeDataFunc)(void* data); -typedef struct _WFLinkedListNode { - void* mRawData; - WFLinkedListNode* mNext; -}WFLinkedListNode; -typedef struct _WFLinkedList { - WFLinkedListNode* mHead; - WFLinkedListNode* mTail; - uint32_t mLength; -}WFLinkedList; - -WFERROR WFLinkedList_Alloc(WFLinkedList** list); -WFERROR WFLinkedList_Free(WFLinkedList* list); -WFERROR WFLinkedList_Free_Full(WFLinkedList* list, WFLinkedListNode_FreeDataFunc free_func); -WFERROR WFLinkedList_Add(WFLinkedList* list, void* data); -WFERROR WFLinkedList_NodeIterator(WFLinkedList* list, WFLinkedListNode** node_ptr); -WFERROR WFLinkedListNode_GetData(WFLinkedListNode* node, void** pdata); - -#endif \ No newline at end of file diff --git a/wfassoc/wfassoc.c b/wfassoc/wfassoc.c deleted file mode 100644 index 64fafe0..0000000 --- a/wfassoc/wfassoc.c +++ /dev/null @@ -1,344 +0,0 @@ -#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 -/// Notice: this function will allocate memory for returns and it should be released safely. -/// -/// 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_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)) - ); -} \ No newline at end of file diff --git a/wfassoc/wfassoc.h b/wfassoc/wfassoc.h deleted file mode 100644 index 37b0b5a..0000000 --- a/wfassoc/wfassoc.h +++ /dev/null @@ -1,152 +0,0 @@ -#if !defined(_YYCDLL_WFASSOC_H__IMPORTED_) -#define _YYCDLL_WFASSOC_H__IMPORTED_ - -#include -#include - -// quick marco for developer and should not be used in wfassoc self - -#if defined(_UNICODE) -#define WFAPP_PROFILE WFAPP_PROFILEW -#define WFInstallApplication WFInstallApplicationW -#define WFUninstallApplication WFUninstallApplicationW -#define WFGenerateProgID WFGenerateProgIDW -#elif defined(_MBCS) -#define WFAPP_PROFILE WFAPP_PROFILEA -#define WFInstallApplication WFInstallApplicationA -#define WFUninstallApplication WFUninstallApplicationA -#define WFGenerateProgID WFGenerateProgIDA -#endif - -// useful macro - -#define WFNEW(type) ((type*)malloc(sizeof(type))) -#define WFVERSION 0 -#define WFSUCCESS(expr) (!expr) -#define WFFAILED(expr) expr - -/// -/// wfassoc Error Enum -/// -typedef enum _WFERROR { - /// - /// All operation done successfully - /// - WFERROR_OK = 0, - /// - /// The filed `WFVersion` in Profile Struct is not matched. It usually mean that currently used DLL is not matched with the DLL when compiling this application. - /// - WFERROR_INVALID_VERSION = 1, - /// - /// The buffer in some operations is insufficient, please try expanding buffer. - /// - WFERROR_INSUFFICIENT_BUFFER = 2, - /// - /// Some essential pointer variable is NULL. - /// - WFERROR_NULLPTR = 3, - /// - /// The encoding of string has error. - /// - WFERROR_ENCODING = 4, - WFERROR_WIN32_ERROR = 5, - WFERROR_ASSERT_ERROR = 6, - WFERROR_ALLOC = 7, - WFERROR_END_OF_TAIL = 8 -}WFERROR; - -/// -/// wfassoc Wide Character Set Profile Struct -/// -typedef struct _WFAPP_PROFILEW { - /// - /// wfassoc version. Fill it with `WFVERSION` in your application. This field is used for version checking. - /// - INT WFVersion; - /// - /// The path to locate your application. - /// - WCHAR* AppPath; - /// - /// The command will be executed when opening files. - /// - WCHAR* AppCommand; - /// - /// Your application's ProgID. - /// For more detail about how to create your ProgID, please browse: https://docs.microsoft.com/en-us/windows/win32/shell/fa-progids - /// We also provide a generator for creating legacy ProgID. Use `WFGenerateProgID` to create a legacy ProgID. - /// - WCHAR* ProgID; - /// - /// Your application supported file extensions. - /// The structure of this field is connecting all supported extensions with dot(.) end to end. For example: - /// `.jpg\0.png\0.gif\0` - /// - WCHAR* SupportedTypes; - BOOL RegisterForAllUsers; - BOOL SetAsDefault; - BOOL ShowInOpenWithMenu; - BOOL UseOpenWithList; -}WFAPP_PROFILEW; - -/// -/// wfassoc Narrow Character Set Profile Struct -/// -typedef struct _WFAPP_PROFILEA { - /// - /// wfassoc version. Fill it with `WFVERSION` in your application. This field is used for version checking. - /// - INT WFVersion; - /// - /// The path to locate your application. - /// - CHAR* AppPath; - /// - /// The command will be executed when opening files. - /// - CHAR* AppCommand; - /// - /// Your application's ProgID. - /// For more detail about how to create your ProgID, please browse: https://docs.microsoft.com/en-us/windows/win32/shell/fa-progids - /// We also provide a generator for creating legacy ProgID. Use `WFGenerateProgID` to create a legacy ProgID. - /// - CHAR* ProgID; - /// - /// Your application supported file extensions. - /// The structure of this field is connecting all supported extensions with dot(.) end to end. For example: - /// `.jpg\0.png\0.gif\0` - /// - CHAR* SupportedTypes; -}WFAPP_PROFILEA; - -/// -/// Install Application via Wide Character -/// -/// -/// -WFERROR WFInstallApplicationW(WFAPP_PROFILEW* profile); -WFERROR WFInstallApplicationA(WFAPP_PROFILEA* profile); -WFERROR WFUninstallApplicationW(WFAPP_PROFILEW* profile); -WFERROR WFUninstallApplicationA(WFAPP_PROFILEA* profile); -/// -/// Generate Legacy ProgID -/// -/// Vendor. Such as `Word`, `Excel`, `PowerPoint`. -/// Component. Such as `Document`, `Sheet`, `Diagram`. -/// Version. Such as `0`, `1`, `114514`. -/// Pointer to output ProgID. If this variable is NULL, function will calculate proper length of receiving buffer and return it via `result_length`. -/// Pointer to a int variable containing buffer's length. If `result` is not NULL, it should be the length of `result`. -/// -WFERROR WFGenerateProgIDW(WCHAR* vendor, WCHAR* component, INT version, WCHAR* result, INT* result_length); -/// -/// Generate Legacy ProgID -/// -/// Vendor. Such as `Word`, `Excel`, `PowerPoint`. -/// Component. Such as `Document`, `Sheet`, `Diagram`. -/// Version. Such as `0`, `1`, `114514`. -/// Pointer to output ProgID. If this variable is NULL, function will calculate proper length of receiving buffer and return it via `result_length`. -/// Pointer to a int variable containing buffer's length. If `result` is not NULL, it should be the length of `result`. -/// -WFERROR WFGenerateProgIDA(CHAR* vendor, CHAR* component, INT version, CHAR* result, INT* result_length); - -#endif diff --git a/wfassoc/wfassoc.vcxproj b/wfassoc/wfassoc.vcxproj index 4ed7e3a..dee1b62 100644 --- a/wfassoc/wfassoc.vcxproj +++ b/wfassoc/wfassoc.vcxproj @@ -147,12 +147,14 @@ - - + + + - - + + + diff --git a/wfassoc/wfassoc.vcxproj.filters b/wfassoc/wfassoc.vcxproj.filters index 706fb9d..5e8d923 100644 --- a/wfassoc/wfassoc.vcxproj.filters +++ b/wfassoc/wfassoc.vcxproj.filters @@ -15,18 +15,24 @@ - + 头文件 - + + 头文件 + + 头文件 - + 源文件 - + + 源文件 + + 源文件 diff --git a/wfassoc/wfassoc_core.c b/wfassoc/wfassoc_core.c new file mode 100644 index 0000000..14b0254 --- /dev/null +++ b/wfassoc/wfassoc_core.c @@ -0,0 +1,407 @@ +#include "wfassoc_core.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 +/// Notice: this function will allocate memory for returns and it should be released safely. +/// +/// The string will be converted +/// The error happend during converting +/// The string has been converted. If return NULL, it mean that the converting failed. +//WFERROR ConvMultiByteToWideChar(CHAR* source); +WFERROR WFSplitAppPath(WFString* app_path, WFString* app_name, WFString* base_path); +//WFERROR Strcat(WCHAR* str1, WCHAR* str2); +//WFERROR WFGetBasePathFromAppPath(WFString* app_path, WFString* base_path); +WFERROR WFSplitSupportedTypesString(wchar_t* typesString, WFLinkedList* list); + +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_FORMAT 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;goto skipLabel;} +#define SAFE_EXEC_WF_LABEL(wfError, skipLabel, function) if((wfError=function)!=WFERROR_OK) {goto skipLabel;} +#define SAFE_EXEC_WF_RETURN(wfError, function) if((wfError=function)!=WFERROR_OK) {return wfError;} +#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 WFInstallApplication(WFAPP_PROFILE* profile) { + if (profile->WFVersion != WFVERSION) return WFERROR_INVALID_VERSION; + + WFERROR wf_error = WFERROR_OK; + LSTATUS win32_error = ERROR_SUCCESS; + //WCHAR* itemSupportedTypes = NULL; + + // ================================== + // generate necessary data + + // init string + WFString* strProgID = NULL, + * strAppPath = NULL, + * strAppBasePath = NULL, + * strAppFileName = NULL; + + SAFE_EXEC_WF_LABEL(wf_error, final_process, + WFString_Alloc(&strProgID) + ); + SAFE_EXEC_WF_LABEL(wf_error, final_process, + WFString_Alloc_Wchar(&strAppPath, profile->AppPath) + ); + SAFE_EXEC_WF_LABEL(wf_error, final_process, + WFString_Alloc(&strAppBasePath) + ); + SAFE_EXEC_WF_LABEL(wf_error, final_process, + WFString_Alloc(&strAppFileName) + ); + + // write string + SAFE_EXEC_WF_LABEL(wf_error, final_process, + WFString_Printf(strProgID, LEGACY_PROGID_FORMAT, profile->ProgID_Vendor, profile->ProgID_Component, profile->ProgID_Version) + ); + SAFE_EXEC_WF_LABEL(wf_error, final_process, + WFSplitAppPath(strAppPath, strAppFileName, strAppBasePath) + ); + + WFString* regpathAppPaths = NULL, + * regpathApplicationsRealName = NULL, + * regpathApplicationsProgId = NULL; + + SAFE_EXEC_WF_LABEL(wf_error, final_process, + WFString_Alloc_Wchar(®pathAppPaths, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\") + ); + SAFE_EXEC_WF_LABEL(wf_error, final_process, + WFString_Concat_String(regpathAppPaths, strAppFileName) + ); + + SAFE_EXEC_WF_LABEL(wf_error, final_process, + WFString_Alloc_Wchar(®pathApplicationsRealName, L"SOFTWARE\\Classes\\Applications\\") + ); + SAFE_EXEC_WF_LABEL(wf_error, final_process, + WFString_Concat_String(regpathApplicationsRealName, strAppFileName) + ); + + SAFE_EXEC_WF_LABEL(wf_error, final_process, + WFString_Alloc_Wchar(®pathApplicationsProgId, L"SOFTWARE\\Classes\\Applications\\") + ); + SAFE_EXEC_WF_LABEL(wf_error, final_process, + WFString_Concat_String(regpathApplicationsProgId, strProgID) + ); + + + //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; +//} + +WFERROR WFSplitAppPath(WFString* app_path, WFString* app_name, WFString* base_path) { + WFERROR ec; + wchar_t* lastSlash, *src, *ptr; + SAFE_EXEC_WF_RETURN(ec, WFString_GetData(app_path, &ptr)); + src = lastSlash = ptr; + + while (*ptr != L'\0') { + if (*ptr == L'\\' || *ptr == L'/') { + lastSlash = ptr; + } + ptr++; + } + + SAFE_EXEC_WF_RETURN(ec, WFString_SubString(app_path, base_path, 0, lastSlash - src)); + SAFE_EXEC_WF_RETURN(ec, WFString_SetData(app_name, ++lastSlash)); + return WFERROR_OK; +} + +//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; +//} + +WFERROR WFSplitSupportedTypesString(wchar_t* typesString, WFLinkedList* list) { + WFERROR ec; + wchar_t* ptr; + WFString* strl; + uint32_t len_str; + ptr = typesString; + + while (*ptr != L'\0') { + // add into list + SAFE_EXEC_WF_RETURN(ec, WFString_Alloc(&strl, ptr)); + SAFE_EXEC_WF_RETURN(ec, WFLinkedList_Add(list, strl)); + + // shift to next string + SAFE_EXEC_WF_RETURN(ec, WFString_GetLength(strl, &len_str)); + ptr += len_str + 1; + } + + return WFERROR_OK; +} + +//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_core.h b/wfassoc/wfassoc_core.h new file mode 100644 index 0000000..7417d21 --- /dev/null +++ b/wfassoc/wfassoc_core.h @@ -0,0 +1,60 @@ +#if !defined(_YYCDLL_WFASSOC_H__IMPORTED_) +#define _YYCDLL_WFASSOC_H__IMPORTED_ + +#include +#include +#include "wfassoc_utils.h" + +// quick marco for developer and should not be used in wfassoc self + +//#if defined(_UNICODE) +//#define WFAPP_PROFILE WFAPP_PROFILEW +//#define WFInstallApplication WFInstallApplicationW +//#define WFUninstallApplication WFUninstallApplicationW +//#define WFGenerateProgID WFGenerateProgIDW +//#elif defined(_MBCS) +//#define WFAPP_PROFILE WFAPP_PROFILEA +//#define WFInstallApplication WFInstallApplicationA +//#define WFUninstallApplication WFUninstallApplicationA +//#define WFGenerateProgID WFGenerateProgIDA +//#endif + +/// +/// Install Application via Wide Character +/// +/// +/// +WFERROR WFInstallApplication(WFAPP_PROFILE* profile); +WFERROR WFUninstallApplication(WFAPP_PROFILE* profile); + +WFERROR WFRegisterAppPath(WFAPP_INTERNAL_PROFILE* internal_profile); +WFERROR WFRegisterApplication(WFAPP_INTERNAL_PROFILE* internal_profile); +WFERROR WFRegisterExtensions(WFAPP_INTERNAL_PROFILE* internal_profile); + +//WFERROR WFProfile_Alloc(WFAPP_PROFILE** profile); +//WFERROR WFProfile_Free(WFAPP_PROFILE* profile); +//WFERROR WFProfile_SetProgIDA(WFAPP_PROFILE** profile, char* vendor, char* component, uint32_t version, BOOL is_utf8); +//WFERROR WFProfile_SetProgIDW(WFAPP_PROFILE** profile, wchar_t* vendor, wchar_t* component, uint32_t version); + +/// +/// Generate Legacy ProgID +/// +/// Vendor. Such as `Word`, `Excel`, `PowerPoint`. +/// Component. Such as `Document`, `Sheet`, `Diagram`. +/// Version. Such as `0`, `1`, `114514`. +/// Pointer to output ProgID. If this variable is NULL, function will calculate proper length of receiving buffer and return it via `result_length`. +/// Pointer to a int variable containing buffer's length. If `result` is not NULL, it should be the length of `result`. +/// +//WFERROR WFGenerateProgIDW(WCHAR* vendor, WCHAR* component, INT version, WCHAR* result, INT* result_length); +/// +/// Generate Legacy ProgID +/// +/// Vendor. Such as `Word`, `Excel`, `PowerPoint`. +/// Component. Such as `Document`, `Sheet`, `Diagram`. +/// Version. Such as `0`, `1`, `114514`. +/// Pointer to output ProgID. If this variable is NULL, function will calculate proper length of receiving buffer and return it via `result_length`. +/// Pointer to a int variable containing buffer's length. If `result` is not NULL, it should be the length of `result`. +/// +//WFERROR WFGenerateProgIDA(CHAR* vendor, CHAR* component, INT version, CHAR* result, INT* result_length); + +#endif diff --git a/wfassoc/wfassoc_private.c b/wfassoc/wfassoc_private.c new file mode 100644 index 0000000..2a97d8a --- /dev/null +++ b/wfassoc/wfassoc_private.c @@ -0,0 +1 @@ +#include "wfassoc_private.h" \ No newline at end of file diff --git a/wfassoc/wfassoc_private.h b/wfassoc/wfassoc_private.h new file mode 100644 index 0000000..74c67e4 --- /dev/null +++ b/wfassoc/wfassoc_private.h @@ -0,0 +1,9 @@ +#if !defined(_YYCDLL_WFASSOC_PRIVATE_H__IMPORTED_) +#define _YYCDLL_WFASSOC_PRIVATE_H__IMPORTED_ + +typedef struct _WFAPP_RAWDATA { + uint32_t mVersion; + +}WFAPP_RAWDATA; + +#endif \ No newline at end of file diff --git a/wfassoc/wfassoc_utils.c b/wfassoc/wfassoc_utils.c new file mode 100644 index 0000000..e66ae83 --- /dev/null +++ b/wfassoc/wfassoc_utils.c @@ -0,0 +1,256 @@ +#include "wfassoc_utils.h" + +#pragma region WFString + +WFERROR WFString_Alloc_Wchar(WFString** strl, const wchar_t* raw_data) { + if (raw_data == NULL) return WFERROR_NULLPTR; + + WFERROR ec; + if ((ec = WFString_Alloc(strl)) != WFERROR_OK) return ec; + if ((ec = WFString_SetData(*strl, raw_data)) != WFERROR_OK) return ec; + return WFERROR_OK; +} + +WFERROR WFString_Alloc_Char(WFString** strl, const char* raw_data, BOOL is_utf8) { + if (raw_data == NULL) return WFERROR_NULLPTR; + + // init string + WFERROR ec; + if ((ec = WFString_Alloc(strl)) != WFERROR_OK) return ec; + + // compute expected string length + uint32_t sourceLength = strlen(raw_data); + int destLength = MultiByteToWideChar(is_utf8 ? CP_UTF8 : CP_ACP, 0, raw_data, sourceLength, NULL, 0); + if (destLength <= 0) return WFERROR_CRT; + --destLength; // remove terminal char + + // resize string + if ((ec = WFString_Resize(*strl, destLength)) != WFERROR_OK) return ec; + + // clear buffer and write data + wchar_t* buffer; + if ((ec = WFString_GetData(*strl, &buffer)) != WFERROR_OK) return ec; + memset(buffer, 0, sizeof(WCHAR) * destLength); + int crt_error = MultiByteToWideChar(is_utf8 ? CP_UTF8 : CP_ACP, 0, raw_data, sourceLength, buffer, destLength); + if (crt_error <= 0) return WFERROR_CRT; + + return WFERROR_OK; +} + +WFERROR WFString_Alloc_Capacity(WFString** strl, uint32_t size) { + *strl = WFNEW(WFString); + if (*strl == NULL) return WFERROR_ALLOC; + + // init struct data + (*strl)->mCapacity = size; + (*strl)->mRealCapacity = size + 1; + (*strl)->mLength = 0; + (*strl)->mRealLength = 1; + + (*strl)->mRawData = malloc((*strl)->mRealCapacity * sizeof(wchar_t)); + if ((*strl)->mRawData == NULL) return WFERROR_ALLOC; + + (*strl)->mRawData[(*strl)->mRealLength - 1] = 0; + + return WFERROR_OK; +} + +WFERROR WFString_Alloc(WFString** strl) { + return WFString_Alloc_Capacity(strl, 256); +} + +WFERROR WFString_Free(WFString* strl) { + if (strl == NULL) return WFERROR_OK; + if (strl->mRawData == NULL) return WFERROR_NULLPTR; + free(strl->mRawData); + free(strl); + return WFERROR_OK; +} + +WFERROR WFString_Resize(WFString* strl, uint32_t new_size) { + // if remain data is not enough, we need alloc new one + if (new_size >= strl->mCapacity) { + strl->mCapacity = new_size * 2; + strl->mRealCapacity = strl->mCapacity + 1; + + // alloc buffer + if (strl->mRawData != NULL) + strl->mRawData = realloc(strl->mRawData, strl->mRealCapacity * sizeof(wchar_t)); + else + strl->mRawData = malloc(strl->mRealCapacity * sizeof(wchar_t)); + if (strl->mRawData == NULL) return WFERROR_ALLOC; + } + + // set length as new length + strl->mLength = new_size; + strl->mRealLength = strl->mLength + 1; + + // set the last one is zero + strl->mRawData[strl->mRealLength - 1] = 0; + return WFERROR_OK; +} + +WFERROR WFString_GetData(WFString* strl, wchar_t** pdata) { + if (strl == NULL) return WFERROR_NULLPTR; + *pdata = strl->mRawData; + return WFERROR_OK; +} + +WFERROR WFString_SetData(WFString* strl, const wchar_t* data) { + if (strl == NULL || data == NULL) return WFERROR_NULLPTR; + + // get length and resize buffer + WFERROR ec; + uint32_t size = wcslen(data); + ec = WFString_Resize(strl, size); + if (ec != WFERROR_OK) return ec; + + // copy data + if (size != 0) + memcpy(strl->mRawData, data, sizeof(wchar_t) * size); + + return WFERROR_OK; +} + +WFERROR WFString_GetLength(WFString* strl, uint32_t* len) { + if (strl == NULL) return WFERROR_NULLPTR; + *len = strl->mLength; + return WFERROR_OK; +} + +WFERROR WFString_Printf(WFString* strl, const wchar_t* format, ...) { + if (strl == NULL) return WFERROR_NULLPTR; + + // get expected size + va_list argptr; + va_start(argptr, format); + uint32_t count = _vsnwprintf(NULL, 0, format, argptr); + //count++; + va_end(argptr); + + // resize string and get buffer ptr + WFERROR ec; + wchar_t* buffer; + if ((ec = WFString_Resize(strl, count)) != WFERROR_OK) return ec; + if ((ec = WFString_GetData(strl, &buffer)) != WFERROR_OK) return ec; + + // write data to buffer + buffer[count - 1] = L'\0'; + va_start(argptr, format); + int write_result = _vsnwprintf(buffer, count, format, argptr); + va_end(argptr); + if (write_result < 0 || write_result >= count) return WFERROR_CRT; + + return WFERROR_OK; +} + +WFERROR WFString_Concat_Wchar(WFString* strl, const wchar_t* extra) { + if (strl == NULL) return WFERROR_NULLPTR; + if (extra == NULL) return WFERROR_OK; + + uint32_t count = wcslen(extra); + uint32_t oldlen = strl->mLength; + + // if extra strl count is 0, do not copy any data. + if (count == 0) return WFERROR_OK; + + WFERROR ec; + if ((ec = WFString_Resize(strl, oldlen + count)) != WFERROR_OK) return ec; + memcpy(strl->mRawData + oldlen, extra, sizeof(wchar_t) * count); + return WFERROR_OK; +} +WFERROR WFString_Concat_String(WFString* strl, WFString* extra) { + if (strl == NULL) return WFERROR_NULLPTR; + if (extra == NULL) return WFERROR_OK; + + return WFString_Concat_Wchar(strl, extra->mRawData); +} + +WFERROR WFString_SubString(WFString* strl, WFString* substring, uint32_t start_index, uint32_t length) { + if (strl == NULL) return WFERROR_NULLPTR; + if (start_index >= strl->mLength || length > strl->mLength - start_index) return WFERROR_INVALID_ARGUMENTS; + + WFERROR ec; + if ((ec = WFString_Resize(substring, length)) != WFERROR_OK) return ec; + if (length != 0) { + memcpy(substring->mRawData, strl->mRawData + start_index, sizeof(wchar_t) * length); + } + return WFERROR_OK; +} + +#pragma endregion + +#pragma region WFLinkedList + +WFERROR WFLinkedList_Alloc(WFLinkedList** list) { + *list = WFNEW(WFLinkedList); + if (*list == NULL) return WFERROR_ALLOC; + return WFERROR_OK; +} + +WFERROR WFLinkedList_Free(WFLinkedList* list) { + return WFLinkedList_Free_Full(list, NULL); +} + +WFERROR WFLinkedList_Free_Full(WFLinkedList* list, WFLinkedListNode_FreeDataFunc free_func) { + if (list == NULL) return WFERROR_OK; + + // iterate full list and remove data and node + WFLinkedListNode* node = list->mHead, * free_node = NULL; + while (node != NULL) { + // free raw data + if (free_func != NULL) (*free_func)(node->mRawData); + // move to next node and free current node + free_node = node; + node = node->mNext; + free(free_node); + } + + // free list self + free(list); + + return WFERROR_OK; +} + +WFERROR WFLinkedList_Add(WFLinkedList* list, void* data) { + if (list == NULL) return WFERROR_NULLPTR; + + WFLinkedListNode* new_item = WFNEW(WFLinkedListNode); + if (new_item == NULL) return WFERROR_ALLOC; + new_item->mNext = NULL; + new_item->mRawData = data; + + if (list->mLength == 0) { + list->mHead = list->mTail = new_item; + } else { + list->mTail->mNext = new_item; + list->mTail = new_item; + } + ++list->mLength; + return WFERROR_OK; +} + +WFERROR WFLinkedList_NodeIterator(WFLinkedList* list, WFLinkedListNode** node_ptr) { + if (list == NULL) return WFERROR_NULLPTR; + + if (*node_ptr == NULL) { + // if node_ptr is NULL, it mean that wo should iterate this list from head + *node_ptr = list->mHead; + } else { + // otherwise, move to next node + *node_ptr = (*node_ptr)->mNext; + } + + // if the header is null, return end of tail error to notice caller stop iterate + if (*node_ptr == NULL) return WFERROR_END_OF_TAIL; + else return WFERROR_OK; +} + +WFERROR WFLinkedListNode_GetData(WFLinkedListNode* node, void** pdata) { + if (node == NULL) return WFERROR_NULLPTR; + + *pdata = node->mRawData; + return WFERROR_OK; +} + +#pragma endregion diff --git a/wfassoc/wfassoc_utils.h b/wfassoc/wfassoc_utils.h new file mode 100644 index 0000000..fe20966 --- /dev/null +++ b/wfassoc/wfassoc_utils.h @@ -0,0 +1,136 @@ +#if !defined(_YYCDLL_WFASSOC_UTILS_H__IMPORTED_) +#define _YYCDLL_WFASSOC_UTILS_H__IMPORTED_ + +#include +#include + +// useful macro + +#define WFNEW(type) ((type*)malloc(sizeof(type))) +#define WFNEW_ARRAY(type) ((type*)malloc(sizeof(type)*count);) +#define WFVERSION 0 +#define WFSUCCESS(expr) (!expr) +#define WFFAILED(expr) expr + +/// +/// wfassoc Error Enum +/// +typedef enum _WFERROR { + /// + /// All operation done successfully + /// + WFERROR_OK = 0, + /// + /// The filed `WFVersion` in Profile Struct is not matched. It usually mean that currently used DLL is not matched with the DLL when compiling this application. + /// + WFERROR_INVALID_VERSION = 1, + /// + /// The buffer in some operations is insufficient, please try expanding buffer. + /// + WFERROR_INSUFFICIENT_BUFFER = 2, + /// + /// Some essential pointer variable is NULL. + /// + WFERROR_NULLPTR = 3, + WFERROR_WIN32 = 5, + WFERROR_ALLOC = 6, + WFERROR_END_OF_TAIL = 7, + WFERROR_CRT = 8, + WFERROR_INVALID_ARGUMENTS = 9 +}WFERROR; + +/// +/// wfassoc Profile Struct +/// +typedef struct _WFAPP_PROFILE { + /// + /// wfassoc version. Fill it with `WFVERSION` in your application. This field is used for version checking. + /// + uint32_t WFVersion; + /// + /// The path to locate your application. + /// + wchar_t* AppPath; + /// + /// The command will be executed when opening files. + /// + wchar_t* AppCommand; + /// + /// Your application's ProgID. + /// For more detail about how to create your ProgID, please browse: https://docs.microsoft.com/en-us/windows/win32/shell/fa-progids + /// We also provide a generator for creating legacy ProgID. Use `WFGenerateProgID` to create a legacy ProgID. + /// + wchar_t* ProgID_Vendor; + wchar_t* ProgID_Component; + uint32_t ProgID_Version; + /// + /// Your application supported file extensions. + /// The structure of this field is connecting all supported extensions with dot(.) end to end. For example: + /// `.jpg\0.png\0.gif\0` + /// + wchar_t* SupportedTypes; + BOOL RegisterForAllUsers; + BOOL SetAsDefault; + BOOL ShowInOpenWithMenu; + BOOL UseOpenWithList; +}WFAPP_PROFILE; + +typedef struct _WFAPP_INTERNAL_PROFILE { + WFString* AppPath; + WFString* AppBasePath; + WFString* AppFileName; + + WFString* ProgID; + + WFString* AppCommand; + + WFLinkedList* SupportedTypes; + + BOOL RegisterForAllUsers; + BOOL SetAsDefault; + BOOL ShowInOpenWithMenu; + BOOL UseOpenWithList; +}WFAPP_INTERNAL_PROFILE; + +typedef struct _WFString { + wchar_t* mRawData; + uint32_t mLength; + uint32_t mCapacity; + + uint32_t mRealLength; + uint32_t mRealCapacity; +}WFString; + +WFERROR WFString_Alloc_Wchar(WFString** strl, const wchar_t* raw_data); +WFERROR WFString_Alloc_Char(WFString** strl, const char* raw_data, BOOL is_utf8); +WFERROR WFString_Alloc_Capacity(WFString** strl, uint32_t size); +WFERROR WFString_Alloc(WFString** strl); +WFERROR WFString_Free(WFString* strl); +WFERROR WFString_Resize(WFString* strl, uint32_t new_size); +WFERROR WFString_GetData(WFString* strl, wchar_t** pdata); +WFERROR WFString_SetData(WFString* strl, const wchar_t* data); +WFERROR WFString_GetLength(WFString* strl, uint32_t* len); +WFERROR WFString_Printf(WFString* strl, const wchar_t* format, ...); +WFERROR WFString_Concat_Wchar(WFString* strl, const wchar_t* extra); +WFERROR WFString_Concat_String(WFString* strl, WFString* extra); +WFERROR WFString_SubString(WFString* strl, WFString* substring, uint32_t start_index, uint32_t length); + +typedef WFERROR(*WFLinkedListNode_FreeDataFunc)(void* data); +typedef struct _WFLinkedListNode { + void* mRawData; + WFLinkedListNode* mNext; +}WFLinkedListNode; +typedef struct _WFLinkedList { + WFLinkedListNode* mHead; + WFLinkedListNode* mTail; + uint32_t mLength; +}WFLinkedList; + +WFERROR WFLinkedList_Alloc(WFLinkedList** list); +WFERROR WFLinkedList_Free(WFLinkedList* list); +WFERROR WFLinkedList_Free_Full(WFLinkedList* list, WFLinkedListNode_FreeDataFunc free_func); +WFERROR WFLinkedList_Add(WFLinkedList* list, void* data); +WFERROR WFLinkedList_NodeIterator(WFLinkedList* list, WFLinkedListNode** node_ptr); +WFERROR WFLinkedListNode_GetData(WFLinkedListNode* node, void** pdata); + +#endif \ No newline at end of file