diff --git a/wfassoc-cdylib/codegen/Findwfassoc.cmake b/wfassoc-cdylib/codegen/Findwfassoc.cmake index 7bab1ca..b2ee06d 100644 --- a/wfassoc-cdylib/codegen/Findwfassoc.cmake +++ b/wfassoc-cdylib/codegen/Findwfassoc.cmake @@ -5,7 +5,7 @@ # This module requires the user to set wfassoc_ROOT to the installation # directory of wfassoc. The directory structure under wfassoc_ROOT must be: # bin/ - contains wfassoc_cdylib.dll -# include/ - contains wfassoc.h +# include/ - contains wfassoc.h and wfassoc++.h # lib/ - contains wfassoc_cdylib.dll.lib (import library) # # This module defines the following variables: @@ -36,7 +36,7 @@ set(wfassoc_LIB_DIR ${wfassoc_ROOT}/lib) set(wfassoc_BIN_DIR ${wfassoc_ROOT}/bin) # Find header files -if(EXISTS ${wfassoc_INCLUDE_DIR}/wfassoc.h) +if(EXISTS ${wfassoc_INCLUDE_DIR}/wfassoc.h AND EXISTS ${wfassoc_INCLUDE_DIR}/wfassoc++.h) set(wfassoc_INCLUDE_DIRS ${wfassoc_INCLUDE_DIR}) else() message(SEND_ERROR "Missing wfassoc header files in ${wfassoc_INCLUDE_DIR}") diff --git a/wfassoc-cdylib/codegen/wfassoc++.h b/wfassoc-cdylib/codegen/wfassoc++.h new file mode 100644 index 0000000..366f90c --- /dev/null +++ b/wfassoc-cdylib/codegen/wfassoc++.h @@ -0,0 +1,281 @@ +/** + * @file wfassoc++.h + * @brief Windows File Association C++ API header + * + * This header provides C++ API for managing Windows file associations, + * based on its C-compatible API. + * The API is designed to work with at least C++17. + */ + +#pragma once +#ifndef WFASSOCPP_H_ +#define WFASSOCPP_H_ + +#include "wfassoc.h" +#include +#include +#include +#include + +namespace wfassocpp { + +using wfassoc::CStyleString; +using wfassoc::Token; +using wfassoc::HICON; +using wfassoc::INVALID_HICON; +using wfassoc::INVALID_INDEX; +using wfassoc::Scope; +using wfassoc::View; + +/// @private +inline void _Check(bool result) { + if (!result) { + throw std::runtime_error(wfassoc::WFGetLastError()); + } +} + +/// @private +inline Token _INVALID_TOKEN() { + static Token v = wfassoc::WFInvalidToken(); + return v; +} + +class Schema { +public: + Schema() { _Check(wfassoc::WFSchemaCreate(&_token)); } + ~Schema() { + if (_token != _INVALID_TOKEN()) { + wfassoc::WFSchemaDestroy(_token); + } + } + Schema(const Schema&) = delete; + Schema& operator=(const Schema&) = delete; + Schema(Schema&& other) noexcept : _token(other._token) { other._token = _INVALID_TOKEN(); } + Schema& operator=(Schema&& other) noexcept { + if (this != &other) { + if (_token != _INVALID_TOKEN()) { + wfassoc::WFSchemaDestroy(_token); + } + _token = other._token; + other._token = _INVALID_TOKEN(); + } + return *this; + } + + void SetIdentifier(const char* value) { _Check(wfassoc::WFSchemaSetIdentifier(_token, value)); } + void SetPath(const char* value) { _Check(wfassoc::WFSchemaSetPath(_token, value)); } + void SetClsid(const char* value) { _Check(wfassoc::WFSchemaSetClsid(_token, value)); } + void SetName(const char* value) { _Check(wfassoc::WFSchemaSetName(_token, value)); } + void SetIcon(const char* value) { _Check(wfassoc::WFSchemaSetIcon(_token, value)); } + void SetBehavior(const char* value) { _Check(wfassoc::WFSchemaSetBehavior(_token, value)); } + void AddStr(const char* name, const char* value) { _Check(wfassoc::WFSchemaAddStr(_token, name, value)); } + void AddIcon(const char* name, const char* value) { _Check(wfassoc::WFSchemaAddIcon(_token, name, value)); } + void AddBehavior(const char* name, const char* value) { _Check(wfassoc::WFSchemaAddBehavior(_token, name, value)); } + void AddExt(const char* ext, const char* ext_name, const char* ext_icon, const char* ext_behavior) { + _Check(wfassoc::WFSchemaAddExt(_token, ext, ext_name, ext_icon, ext_behavior)); + } + +private: + friend class Program; + /// @private + Token Release() noexcept { + Token t = _token; + _token = _INVALID_TOKEN(); + return t; + } + Token _token; +}; + +class IconRc { +public: + explicit IconRc(Token token) : _token(token) {} + ~IconRc() { + if (_token != _INVALID_TOKEN()) { + wfassoc::WFIconRcDestroy(_token); + } + } + IconRc(const IconRc&) = delete; + IconRc& operator=(const IconRc&) = delete; + IconRc(IconRc&& other) noexcept : _token(other._token) { other._token = _INVALID_TOKEN(); } + IconRc& operator=(IconRc&& other) noexcept { + if (this != &other) { + if (_token != _INVALID_TOKEN()) { + wfassoc::WFIconRcDestroy(_token); + } + _token = other._token; + other._token = _INVALID_TOKEN(); + } + return *this; + } + + HICON GetIcon() { + HICON icon = nullptr; + _Check(wfassoc::WFIconRcGetIcon(_token, &icon)); + return icon; + } + +private: + Token _token; +}; + +class ExtStatus { +public: + explicit ExtStatus(Token token) : _token(token) {} + ~ExtStatus() { + if (_token != _INVALID_TOKEN()) { + wfassoc::WFExtStatusDestroy(_token); + } + } + ExtStatus(const ExtStatus&) = delete; + ExtStatus& operator=(const ExtStatus&) = delete; + ExtStatus(ExtStatus&& other) noexcept : _token(other._token) { other._token = _INVALID_TOKEN(); } + ExtStatus& operator=(ExtStatus&& other) noexcept { + if (this != &other) { + if (_token != _INVALID_TOKEN()) { + wfassoc::WFExtStatusDestroy(_token); + } + _token = other._token; + other._token = _INVALID_TOKEN(); + } + return *this; + } + + std::string GetName() { + const char* name = nullptr; + _Check(wfassoc::WFExtStatusGetName(_token, &name)); + return name ? std::string(name) : std::string(); + } + + HICON GetIcon() { + HICON icon = nullptr; + _Check(wfassoc::WFExtStatusGetIcon(_token, &icon)); + return icon; + } + +private: + Token _token; +}; + +class Ext { +public: + explicit Ext(Token token) : _token(token) {} + ~Ext() { + if (_token != _INVALID_TOKEN()) { + wfassoc::WFExtDestroy(_token); + } + } + Ext(const Ext&) = delete; + Ext& operator=(const Ext&) = delete; + Ext(Ext&& other) noexcept : _token(other._token) { other._token = _INVALID_TOKEN(); } + Ext& operator=(Ext&& other) noexcept { + if (this != &other) { + if (_token != _INVALID_TOKEN()) { + wfassoc::WFExtDestroy(_token); + } + _token = other._token; + other._token = _INVALID_TOKEN(); + } + return *this; + } + + std::string GetInner() { + const char* inner = nullptr; + _Check(wfassoc::WFExtGetInner(_token, &inner)); + return std::string(inner); + } + + std::string GetDottedInner() { + const char* inner = nullptr; + _Check(wfassoc::WFExtGetDottedInner(_token, &inner)); + return std::string(inner); + } + +private: + Token _token; +}; + +class Program { +public: + explicit Program(std::unique_ptr&& schema) { + _Check(wfassoc::WFProgramCreate(schema->Release(), &_token)); + } + ~Program() { + if (_token != _INVALID_TOKEN()) { + wfassoc::WFProgramDestroy(_token); + } + } + Program(const Program&) = delete; + Program& operator=(const Program&) = delete; + Program(Program&& other) noexcept : _token(other._token) { other._token = _INVALID_TOKEN(); } + Program& operator=(Program&& other) noexcept { + if (this != &other) { + if (_token != _INVALID_TOKEN()) { + wfassoc::WFProgramDestroy(_token); + } + _token = other._token; + other._token = _INVALID_TOKEN(); + } + return *this; + } + + std::string ResolveName() { + const char* name = nullptr; + _Check(wfassoc::WFProgramResolveName(_token, &name)); + return name ? std::string(name) : std::string(); + } + + std::unique_ptr ResolveIcon() { + Token token = _INVALID_TOKEN(); + _Check(wfassoc::WFProgramResolveIcon(_token, &token)); + if (token == _INVALID_TOKEN()) { + return nullptr; + } + return std::make_unique(token); + } + + size_t ExtsLen() { + size_t len = 0; + _Check(wfassoc::WFProgramExtsLen(_token, &len)); + return len; + } + + std::unique_ptr GetExt(size_t index) { + Token token = _INVALID_TOKEN(); + _Check(wfassoc::WFProgramGetExt(_token, index, &token)); + return std::make_unique(token); + } + + size_t FindExt(const char* body) { + size_t index = INVALID_INDEX; + _Check(wfassoc::WFProgramFindExt(_token, body, &index)); + return index; + } + + void Register(Scope scope) { _Check(wfassoc::WFProgramRegister(_token, scope)); } + void Unregister(Scope scope) { _Check(wfassoc::WFProgramUnregister(_token, scope)); } + + bool IsRegistered(Scope scope) { + bool result = false; + _Check(wfassoc::WFProgramIsRegistered(_token, scope, &result)); + return result; + } + + void LinkExt(Scope scope, size_t index) { _Check(wfassoc::WFProgramLinkExt(_token, scope, index)); } + void UnlinkExt(Scope scope, size_t index) { _Check(wfassoc::WFProgramUnlinkExt(_token, scope, index)); } + + std::unique_ptr QueryExt(View view, size_t index) { + Token token = _INVALID_TOKEN(); + _Check(wfassoc::WFProgramQueryExt(_token, view, index, &token)); + if (token == _INVALID_TOKEN()) { + return nullptr; + } + return std::make_unique(token); + } + +private: + Token _token; +}; + +} // namespace wfassocpp + +#endif // WFASSOCPP_H_ diff --git a/wfassoc-cdylib/codegen/wfassoc.h b/wfassoc-cdylib/codegen/wfassoc.h index 816739b..4c58e9b 100644 --- a/wfassoc-cdylib/codegen/wfassoc.h +++ b/wfassoc-cdylib/codegen/wfassoc.h @@ -4,12 +4,12 @@ * * 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 work with both C and C++ compilers. + * The API is designed to at least work with both C99 and C++17 compilers. */ #pragma once -#ifndef __WFASSOC_H__ -#define __WFASSOC_H__ +#ifndef WFASSOC_H_ +#define WFASSOC_H_ #ifdef __cplusplus #include @@ -537,4 +537,4 @@ bool WFExtGetDottedInner(Token in_ext, CStyleString *out_inner); } // namespace wfassoc #endif // __cplusplus -#endif // __WFASSOC_H__ +#endif // WFASSOC_H_