1
0

feat: add c++ wfassoc wrapper

This commit is contained in:
2026-05-19 20:34:12 +08:00
parent aececd8e5d
commit 72a8c13c1f
3 changed files with 287 additions and 6 deletions

View File

@@ -5,7 +5,7 @@
# This module requires the user to set wfassoc_ROOT to the installation # This module requires the user to set wfassoc_ROOT to the installation
# directory of wfassoc. The directory structure under wfassoc_ROOT must be: # directory of wfassoc. The directory structure under wfassoc_ROOT must be:
# bin/ - contains wfassoc_cdylib.dll # bin/ - contains wfassoc_cdylib.dll
# include/ - contains wfassoc.h # include/ - contains wfassoc.h and wfassoc++.h
# lib/ - contains wfassoc_cdylib.dll.lib (import library) # lib/ - contains wfassoc_cdylib.dll.lib (import library)
# #
# This module defines the following variables: # This module defines the following variables:
@@ -36,7 +36,7 @@ set(wfassoc_LIB_DIR ${wfassoc_ROOT}/lib)
set(wfassoc_BIN_DIR ${wfassoc_ROOT}/bin) set(wfassoc_BIN_DIR ${wfassoc_ROOT}/bin)
# Find header files # 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}) set(wfassoc_INCLUDE_DIRS ${wfassoc_INCLUDE_DIR})
else() else()
message(SEND_ERROR "Missing wfassoc header files in ${wfassoc_INCLUDE_DIR}") message(SEND_ERROR "Missing wfassoc header files in ${wfassoc_INCLUDE_DIR}")

View File

@@ -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 <memory>
#include <stdexcept>
#include <string>
#include <utility>
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>&& 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<IconRc> ResolveIcon() {
Token token = _INVALID_TOKEN();
_Check(wfassoc::WFProgramResolveIcon(_token, &token));
if (token == _INVALID_TOKEN()) {
return nullptr;
}
return std::make_unique<IconRc>(token);
}
size_t ExtsLen() {
size_t len = 0;
_Check(wfassoc::WFProgramExtsLen(_token, &len));
return len;
}
std::unique_ptr<Ext> GetExt(size_t index) {
Token token = _INVALID_TOKEN();
_Check(wfassoc::WFProgramGetExt(_token, index, &token));
return std::make_unique<Ext>(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<ExtStatus> 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<ExtStatus>(token);
}
private:
Token _token;
};
} // namespace wfassocpp
#endif // WFASSOCPP_H_

View File

@@ -4,12 +4,12 @@
* *
* This header provides a C-compatible API for managing Windows file associations, * This header provides a C-compatible API for managing Windows file associations,
* including schema creation, program registration, and extension management. * 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 #pragma once
#ifndef __WFASSOC_H__ #ifndef WFASSOC_H_
#define __WFASSOC_H__ #define WFASSOC_H_
#ifdef __cplusplus #ifdef __cplusplus
#include <cstddef> #include <cstddef>
@@ -537,4 +537,4 @@ bool WFExtGetDottedInner(Token in_ext, CStyleString *out_inner);
} // namespace wfassoc } // namespace wfassoc
#endif // __cplusplus #endif // __cplusplus
#endif // __WFASSOC_H__ #endif // WFASSOC_H_