/** * @file wfassoc.h * @brief Windows File Association C API header * * This header provides a C-compatible API for managing Windows file associations, * including schema creation, program registration, and extension management. * The API is designed to at least work with both C99 and C++17 compilers. */ #pragma once #ifndef WFASSOC_H_ #define WFASSOC_H_ #ifdef __cplusplus #include #include #else // __cplusplus #include #include #include #endif // __cplusplus #ifdef __cplusplus namespace wfassoc { #endif // __cplusplus #ifdef __cplusplus /** Type representing a null-terminated UTF-8 C-style string */ using CStyleString = const char*; /** * @brief Type representing a handle/token for managed objects * * This library use object pool to manage any objects created during calling. * And we expose this type as an opaque handle for visiting your created object. */ using Token = uint64_t; #else // __cplusplus typedef const char *CStyleString; typedef uint64_t Token; #endif // __cplusplus // Special treat for ICON because it may be defined by Windows header. #ifndef _WINDEF_ #ifdef __cplusplus /** * @brief Type representing an icon handle (opaque pointer) * * This type is equivalent with Win32 HICON type. */ using HICON = void*; #else // __cplusplus typedef void *HICON; #endif // __cplusplus #endif #ifdef __cplusplus /** Invalid icon handle value */ constexpr HICON INVALID_HICON = nullptr; /** Invalid index value used for error conditions */ constexpr size_t INVALID_INDEX = static_cast(-1); #else // __cplusplus static const HICON INVALID_HICON = NULL; static const size_t INVALID_INDEX = ((size_t)-1); #endif // __cplusplus #ifdef __cplusplus /** * @brief Registration scope for file associations * * Determines whether a program is registered for the current user or system-wide. */ enum class Scope : uint32_t { /** Current user scope */ User = 0u, /** System-wide scope */ System = 1u }; /** * @brief View mode for querying file association status * * Determines how the association status is viewed/queried. */ enum class View : uint32_t { /** User-level view */ User = 0u, /** System-level view */ System = 1u, /** Combined hybrid view of both user and system */ Hybrid = 2u }; #else // __cplusplus typedef uint32_t Scope; /** Current user scope */ static const Scope SCOPE_USER = 0u; /** System-wide scope */ static const Scope SCOPE_SYSTEM = 1u; typedef uint32_t View; /** User-level view */ static const View VIEW_USER = 0u; /** System-level view */ static const View VIEW_SYSTEM = 1u; /** Combined hybrid view of both user and system */ static const View VIEW_HYBRID = 2u; #endif // __cplusplus #ifdef __cplusplus extern "C" { #endif // __cplusplus /** * @brief Initialize the wfassoc library * * This function must be called before using the MOST of any other wfassoc functions. * * @return true on success, false on failure. */ bool WFStartup(void); /** * @brief Shutdown the wfassoc library * * Cleans up all allocated resources and object pools. * Should be called when done using the library. * * @return true on success, false on failure. */ bool WFShutdown(void); /** * @brief Get the last error message * * Returns a human-readable error message describing the last error that occurred. * The returned error message string is valid until the next API call. * * For most functions located in this library, except some special function indicated in their notes, * they return boolean value indicating whether function is successful or not. * Once they fail, you can call this function to get a human-readable error message. * * The execution of this function do not need to be wrapped by WFStartup() and WFShutdown(). * * The string this function return use different buffer with function return string value. * So you don't worry about that calling this function may invalidate function function return string value. * * @return Null-terminated UTF-8 string containing the error message. * If no error has occurred, the string is empty. * There is no possibility of a NULL return value. */ CStyleString WFGetLastError(void); /** * @brief Check if the current process has administrative privileges * * This function will not throw any error. * There is no necessity to call WFGetLastError() after this function. * The return value only indicates whether the current process has administrative privileges or not. * * The execution of this function do not need to be wrapped by WFStartup() and WFShutdown(). * * @return true if running with admin privileges, false otherwise */ bool WFHasPrivilege(void); /** * @brief Get an invalid token value * * In theory, invalid token value should be a constant value. * However, due to the library I used in Rust side, this value only can be fetched at runtime. * So I expose this function to make programmer can fetch this constant value. * Theoretically, you just need to fetch this function only once at the beginning of your program. * * The execution of this function do not need to be wrapped by WFStartup() and WFShutdown(). * * @return An invalid token value */ Token WFInvalidToken(void); /** * @brief Create a new Schema object * * A Schema is a sketchpad of a complete program. * For the user of this library, they should create a Schema object first. * Then convert it to a Program object for following operations. * * @param[out] out_schema Pointer to receive the Schema token. * The receiver take the ownership of this Schema object. * And it should be freed by calling WFSchemaDestroy() when it is no longer needed, * or consumed by creating a Program object via WFProgramCreate(). * @return true on success, false on failure */ bool WFSchemaCreate(Token *out_schema); /** * @brief Destroy a Schema object * * Releases resources associated with the Schema object. * * Usually you do not need to call this function, * because the convertion function from Schema to Program will consume given Schema object to produce Program object. * * @param[in] in_schema Schema token to destroy * @return true on success, false on failure */ bool WFSchemaDestroy(Token in_schema); /** * @brief Set the program identifier for a Schema * * @param[in] in_schema Schema token * @param[in] in_value Null-terminated UTF-8 string containing the identifier. * This identifier should not be empty, must start with alphabet character, * and follow with alphabet characters, digits, underline, or hyphens. * @return true on success, false on failure */ bool WFSchemaSetIdentifier(Token in_schema, CStyleString in_value); /** * @brief Set the program path for a Schema * * @param[in] in_schema Schema token * @param[in] in_value Null-terminated UTF-8 string containing the program path. * This path should be the fully qualified path to the application. * @return true on success, false on failure */ bool WFSchemaSetPath(Token in_schema, CStyleString in_value); /** * @brief Set the program CLSID for a Schema * * @param[in] in_schema Schema token * @param[in] in_value Null-terminated UTF-8 string containing the CLSID. * This CLSID string should be in the format of @c {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx} . * Please note that curly braces are required. * @return true on success, false on failure */ bool WFSchemaSetClsid(Token in_schema, CStyleString in_value); /** * @brief Set the program name for a Schema (optional) * * @param[in] in_schema Schema token * @param[in] in_value Null-terminated UTF-8 string containing the name, or NULL to clear * @return true on success, false on failure */ bool WFSchemaSetName(Token in_schema, CStyleString in_value); /** * @brief Set the program icon for a Schema (optional) * * @param[in] in_schema Schema token * @param[in] in_value Null-terminated UTF-8 string containing the icon path, or NULL to clear * @return true on success, false on failure */ bool WFSchemaSetIcon(Token in_schema, CStyleString in_value); /** * @brief Set the program behavior for a Schema (optional) * * @param[in] in_schema Schema token * @param[in] in_value Null-terminated UTF-8 string containing the behavior command, or NULL to clear * @return true on success, false on failure */ bool WFSchemaSetBehavior(Token in_schema, CStyleString in_value); /** * @brief Add a string resource entry to a Schema * * @param[in] in_schema Schema token * @param[in] in_name Null-terminated UTF-8 string containing the name of this entry * @param[in] in_value Null-terminated UTF-8 string containing the value of this entry. * It can be a plain string or a reference string to resource. * @return true on success, false on failure */ bool WFSchemaAddStr(Token in_schema, CStyleString in_name, CStyleString in_value); /** * @brief Add an icon registry entry to a Schema * * @param[in] in_schema Schema token * @param[in] in_name Null-terminated UTF-8 string containing the name of this entry * @param[in] in_value Null-terminated UTF-8 string containing the value of this entry. * It can be a path to icon or a reference string to resource. * @return true on success, false on failure */ bool WFSchemaAddIcon(Token in_schema, CStyleString in_name, CStyleString in_value); /** * @brief Add a behavior registry entry to a Schema * * @param[in] in_schema Schema token * @param[in] in_name Null-terminated UTF-8 string containing the name of this entry * @param[in] in_value Null-terminated UTF-8 string containing the value of this entry. * It should be a valid command line string which use \c %1, \c %2, etc. to represent parameters. * @return true on success, false on failure */ bool WFSchemaAddBehavior(Token in_schema, CStyleString in_name, CStyleString in_value); /** * @brief Add a file extension to a Schema * * @param[in] in_schema Schema token * @param[in] in_ext Null-terminated UTF-8 string containing the file extension name (without leading dot). * @param[in] in_ext_name Null-terminated UTF-8 string containing the name pointing to associated name for this extension. * This name should be registered by calling WFSchemaAddStr(). * @param[in] in_ext_icon Null-terminated UTF-8 string containing the name pointing to associated icon for this extension. * This name should be registered by calling WFSchemaAddIcon(). * @param[in] in_ext_behavior Null-terminated UTF-8 string containing the name pointing to associated behavior for this extension. * This name should be registered by calling WFSchemaAddBehavior(). * @return true on success, false on failure */ bool WFSchemaAddExt(Token in_schema, CStyleString in_ext, CStyleString in_ext_name, CStyleString in_ext_icon, CStyleString in_ext_behavior); /** * @brief Create a Program object from a Schema * * Please note that this function will consume the Schema object. * It means that the Schema object cannot be used after this call. * And you do not need to call WFSchemaDestroy() for this Schema object after this call. * * Please note that the given Schema object will always be consumed, * no matter this function return success or failure. * * @param[in] in_schema Schema token (will be consumed) * @param[out] out_program Pointer to receive the Program token. * The receiver take the ownership of this Program object. * And it should be freed by calling WFProgramDestroy() when it is no longer needed. * @return true on success, false on failure */ bool WFProgramCreate(Token in_schema, Token *out_program); /** * @brief Destroy a Program object * * Releases resources associated with the Program. * * @param[in] in_program Program token to destroy * @return true on success, false on failure */ bool WFProgramDestroy(Token in_program); /** * @brief Resolve the provided program name of this Program * * The name will be user specified first, * then fallback to program manifest file specified name, * and finally fallback to the file name of executable. * * @param[in] in_program Program token * @param[out] out_name Pointer to receive the resolved name. * There is no possibility that this value is NULL. * This string will be freed at the next API call. Please make a copy immediately if you need to use it longer. * @return true on success, false on failure */ bool WFProgramResolveName(Token in_program, CStyleString *out_name); /** * @brief Resolve the Program icon resource * * The icon will be user specified first, * the fallback to the first icon of program, * and finally fallback to the system default executable icon. * * @param[in] in_program Program token * @param[out] out_icon_rc Pointer to receive the icon resource token. * The caller take the ownership of created icon resource object. * And it should be freed by calling WFIconRcDestroy() when it is no longer needed. * @return true on success, false on failure */ bool WFProgramResolveIcon(Token in_program, Token *out_icon_rc); /** * @brief Get the number of file extensions in the Program * * @param[in] in_program Program token * @param[out] out_len Pointer to receive the number of extensions * @return true on success, false on failure */ bool WFProgramExtsLen(Token in_program, size_t *out_len); /** * @brief Find a file extension by its body (extension string) * * @param[in] in_program Program token * @param[in] in_body Null-terminated UTF-8 string containing the file extension name (without leading dot) to find. * @param[out] out_index Pointer to receive the file extension index, or INVALID_INDEX if not found. * @return true on success, false on failure */ bool WFProgramFindExt(Token in_program, CStyleString in_body, size_t *out_index); /** * @brief Resolve this program provided extension's details by index * * @param[in] in_program Program token * @param[in] in_index Index of the extension to resolve * @param[out] out_self_ext_status Pointer to receive the self extension status token. * The caller take the ownership of created self extension status object. * And it should be freed by calling WFSelfExtStatusDestroy() when it is no longer needed. * @return true on success, false on failure */ bool WFProgramResolveExt(Token in_program, size_t in_index, Token *out_self_ext_status); /** * @brief Register the Program in the specified scope * * @param[in] in_program Program token * @param[in] in_scope Registration scope * @return true on success, false on failure */ bool WFProgramRegister(Token in_program, Scope in_scope); /** * @brief Unregister the Program from the specified scope * * @param[in] in_program Program token * @param[in] in_scope Registration scope * @return true on success, false on failure */ bool WFProgramUnregister(Token in_program, Scope in_scope); /** * @brief Check if the Program is registered in the specified scope * * @param[in] in_program Program token * @param[in] in_scope Registration scope for checking * @param[out] out_is_registered Pointer to receive the registration status. * True if the Program is registered in the specified scope, false otherwise. * @return true on success, false on failure */ bool WFProgramIsRegistered(Token in_program, Scope in_scope, bool *out_is_registered); /** * @brief Link a file extension in the specified scope * * @param[in] in_program Program token * @param[in] in_scope Registration scope * @param[in] in_index Index of the extension to link * @return true on success, false on failure */ bool WFProgramLinkExt(Token in_program, Scope in_scope, size_t in_index); /** * @brief Unlink a file extension in the specified scope * * @param[in] in_program Program token * @param[in] in_scope Registration scope * @param[in] in_index Index of the extension to unlink * @return true on success, false on failure */ bool WFProgramUnlinkExt(Token in_program, Scope in_scope, size_t in_index); /** * @brief Query the status of a file extension * * @param[in] in_program Program token * @param[in] in_view View viewpoint. * @param[in] in_index Index of the extension to query * @param[out] out_ext_status Pointer to receive the extension status token, or invalid token if not found. * If the extension is not found, it usually means that this extension is not registered in the specified scope. * The caller take the ownership of created extension status object. * And it should be freed by calling WFExtStatusDestroy() when it is no longer needed. * @return true on success, false on failure */ bool WFProgramQueryExt(Token in_program, View in_view, size_t in_index, Token *out_ext_status); /** * @brief Destroy an extension status object * * @param[in] in_ext_status Extension status token to destroy * @return true on success, false on failure */ bool WFExtStatusDestroy(Token in_ext_status); /** * @brief Get the display name from an extension status object * * The display will be user specified first, * the fallback to its ProgId verbatim. * * @param[in] in_ext_status Extension status token * @param[out] out_name Pointer to receive the name. * There is no possibility that this value is NULL. * We will try to use localized name first, then use raw ProgId name if localized name is not available. * This string will be freed at the next API call. Please make a copy immediately if you need to use it longer. * @return true on success, false on failure */ bool WFExtStatusGetName(Token in_ext_status, CStyleString *out_name); /** * @brief Get the icon from an extension status object * * The icon will be user specified first, * the fallback to the system default file icon. * * @param[in] in_ext_status Extension status token * @param[out] out_icon Pointer to receive the icon handle. * This icon handle will be freed once this icon resource object is destroyed. * Please make a copy immediately if you need to use it longer. * @return true on success, false on failure */ bool WFExtStatusGetIcon(Token in_ext_status, HICON *out_icon); /** * @brief Destroy a self extension status object * * @param[in] in_self_ext_status Self extension status token to destroy * @return true on success, false on failure */ bool WFSelfExtStatusDestroy(Token in_self_ext_status); /** * @brief Get the display name from a self extension status object * * The display will be user specified first, * the fallback to its ProgId verbatim. * * @param[in] in_self_ext_status Self extension status token * @param[out] out_name Pointer to receive the name string. * There is no possibility that this value is NULL. * This string will be freed at the next API call. Please make a copy immediately if you need to use it longer. * @return true on success, false on failure */ bool WFSelfExtStatusGetName(Token in_self_ext_status, CStyleString *out_name); /** * @brief Get the icon from a self extension status object * * The icon will be user specified first, * the fallback to the system default file icon. * * @param[in] in_self_ext_status Self extension status token * @param[out] out_icon Pointer to receive the icon handle. * This icon handle will be freed once this self extension status object is destroyed. * Please make a copy immediately if you need to use it longer. * @return true on success, false on failure */ bool WFSelfExtStatusGetIcon(Token in_self_ext_status, HICON *out_icon); /** * @brief Get the extension string (without leading dot) from a self extension status object * * @param[in] in_self_ext_status Self extension status token * @param[out] out_inner Pointer to receive the file extension name (without leading dot). * There is no possibility that this value is NULL. * This string will be freed at the next API call. Please make a copy immediately if you need to use it longer. * @return true on success, false on failure */ bool WFSelfExtStatusGetExt(Token in_self_ext_status, CStyleString *out_inner); /** * @brief Get the dotted extension string (with leading dot) from a self extension status object * * @param[in] in_self_ext_status Self extension status token * @param[out] out_inner Pointer to receive the file extension string (with leading dot). * There is no possibility that this value is NULL. * This string will be freed at the next API call. Please make a copy immediately if you need to use it longer. * @return true on success, false on failure */ bool WFSelfExtStatusGetDottedExt(Token in_self_ext_status, CStyleString *out_inner); /** * @brief Destroy an icon resource object * * @param[in] in_icon_rc Icon resource token to destroy * @return true on success, false on failure */ bool WFIconRcDestroy(Token in_icon_rc); /** * @brief Get the icon handle from an icon resource object * * @param[in] in_icon_rc Icon resource token * @param[out] out_icon Pointer to receive the icon handle. * There is no possibility that this value is INVALID_HICON. * This icon handle will be freed once this icon resource object is destroyed. * Please make a copy immediately if you need to use it longer. * @return true on success, false on failure */ bool WFIconRcGetIcon(Token in_icon_rc, HICON *out_icon); #ifdef __cplusplus } // extern "C" #endif // __cplusplus #ifdef __cplusplus } // namespace wfassoc #endif // __cplusplus #endif // WFASSOC_H_