Compare commits
13 Commits
883cba901c
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 930a84a0f6 | |||
| 447d94fdd6 | |||
| f0bd2c0b73 | |||
| 119a4d0341 | |||
| 53b40a4d2f | |||
| 0533cdab23 | |||
| 8c61aa1e1d | |||
| 77924b5937 | |||
| 2c811503a2 | |||
| 18a55272a3 | |||
| 1658127bdd | |||
| 72a8c13c1f | |||
| aececd8e5d |
@@ -1,3 +0,0 @@
|
|||||||
# Pineapple Picture Association
|
|
||||||
|
|
||||||
TODO
|
|
||||||
241
example/qwfassoc/PROMPT.txt
Normal file
241
example/qwfassoc/PROMPT.txt
Normal file
@@ -0,0 +1,241 @@
|
|||||||
|
我要求你使用Qt Widget编写一个GUI程序。该GUI程序是wfassoc的一个可视化界面。wfassoc是一个由Rust编写,并暴露出C接口,可以操作Windows注册表,来管理应用程序的注册和卸载,以及文件关联的动态链接库。
|
||||||
|
|
||||||
|
我要求你在@example/qwfassoc/TASKS.md 中先做好详细的规划,而不是上来就写代码。我会安排其他人来负责执行你做的规划。
|
||||||
|
|
||||||
|
# 项目要求
|
||||||
|
|
||||||
|
- 使用Qt Widget编写界面,而不是QML。
|
||||||
|
- 使用UI文件而不是C++语句来构建界面。
|
||||||
|
- 我使用的是Qt 6,使用CMake作为构建系统,不要使用Qt的qmake。
|
||||||
|
- 使用toml11作为TOML读取库。
|
||||||
|
|
||||||
|
# 界面要求
|
||||||
|
|
||||||
|
这是一个基于 Qt Widgets 的标准对话框界面描述。你可以按照以下层级结构来构建代码:
|
||||||
|
|
||||||
|
## 主窗口容器 (Main Window)
|
||||||
|
|
||||||
|
* 类: `QDialog`。
|
||||||
|
* 窗口标题: "xxx选项"。xxx在应用程序初始化时,通过wfassoc的Program提供的接口,运行时获取。
|
||||||
|
* 窗口图标:在应用程序初始化时,通过wfassoc的Program提供的接口,运行时获取。
|
||||||
|
* 大小限制:固定大小480x600
|
||||||
|
|
||||||
|
## 选项卡 (Top Tabs)
|
||||||
|
|
||||||
|
* 组件: `QTabWidget`。
|
||||||
|
* 标签页 (Tabs): 从左到右依次添加以下标签页:
|
||||||
|
1. 应用程序
|
||||||
|
2. 文件关联
|
||||||
|
* 大小:选项卡占据对话框全部内容
|
||||||
|
|
||||||
|
## "应用程序"选项卡内容
|
||||||
|
|
||||||
|
该选项卡内部使用垂直布局 (`QVBoxLayout`),包含一个主要的分组区域:
|
||||||
|
|
||||||
|
### 区域内容
|
||||||
|
|
||||||
|
* 容器: `QGroupBox`。
|
||||||
|
* 区域标题:安装与卸载
|
||||||
|
* 布局: 垂直布局 (`QVBoxLayout`)。
|
||||||
|
* 上半部分
|
||||||
|
* 水平布局(`QHBoxLayout`)。
|
||||||
|
* 左侧: 一个 `QLabel` 显示图标,该图标表示要设定的应用程序的图标。在应用程序初始化时,通过wfassoc的Program提供的接口,运行时获取。
|
||||||
|
* 右侧: 一个 `QLabel` 显示文本"在此安装或卸载xxx",表示要设定的应用程序的名称。xxx在应用程序初始化时,通过wfassoc的Program提供的接口,运行时获取。 (文本需要设置自动换行 `setWordWrap(true)`)。
|
||||||
|
* 下半部分
|
||||||
|
* 水平布局(`QHBoxLayout`)。
|
||||||
|
* 内容为两个 `QPushButton`,文本分别为:
|
||||||
|
* 安装:为当前对象(系统或当前用户,由应用程序初始化时从命令行参数获取)安装应用。如果应用程序已经安装,则不可点击。
|
||||||
|
* 卸载:为当前对象(系统或当前用户,由应用程序初始化时从命令行参数获取)卸载应用。如果应用程序没有安装,则不可点击。
|
||||||
|
|
||||||
|
## "文件关联"选项卡内容
|
||||||
|
|
||||||
|
在这个选项页内部,使用一个垂直布局 (`QVBoxLayout`) 来排列以下控件:
|
||||||
|
|
||||||
|
* 说明文本:
|
||||||
|
* 组件: `QLabel`。
|
||||||
|
* 文本: "使用 xxx 关联的文件类型:"。xxx在应用程序初始化时,通过wfassoc的Program提供的接口,运行时获取。
|
||||||
|
|
||||||
|
* 功能按钮行:
|
||||||
|
* 布局: `QHBoxLayout` (水平布局)。
|
||||||
|
* 组件: 两个 `QPushButton`。
|
||||||
|
* 文本: 两个按钮的文本都是 "+"。
|
||||||
|
* 位置: 位于列表上方,用于“全选”操作(第一次点击,将所有没关联的文件扩展名(显示为空白)设置为应用程序提供的打开方式。如果没有空白内容,或第二次点击,将所有文件全部设置为应用程序提供的打开方式)。
|
||||||
|
|
||||||
|
* 文件类型列表 (核心组件):
|
||||||
|
* 组件: `QTableWidget` (表格控件)。
|
||||||
|
* 列数: 3列。
|
||||||
|
* 表头 (Header):
|
||||||
|
* 第1列标题: "类型"
|
||||||
|
* 第2列标题: "uuu" (uuu在运行时进行获取,为当前用户名)
|
||||||
|
* 第3列标题: "所有用户"
|
||||||
|
* 行内容示例:
|
||||||
|
* 第一列:一个文件类型图标,右边跟着对应的文本。表示当前文件扩展名,和当前混合视图(hybrid)下的图标。图标和文本均从wfassoc函数获取。
|
||||||
|
* 第二列:用户视图(user)下的名称。文本从wfassoc函数获取。
|
||||||
|
* 第三列:系统视图(system)下的名称。文本从wfassoc函数获取。
|
||||||
|
* 滚动条: 右侧有一个垂直滚动条 (`QScrollBar`),表示内容超出可视区域。
|
||||||
|
* 操作方式:
|
||||||
|
* 第二列和第三列的元素可点击。
|
||||||
|
* 如果元素为空白或其它打开方式,则点击后设置为当前应用程序指定的打开方式(link)。
|
||||||
|
* 如果是自身打开方式,点击后设置为空白(unlink)
|
||||||
|
* 点击后,第一列的图标需要改变,也因此你需要暂存当前应用程序提供打开方式的图标。如果第二第三列均为空,则不显示图标(仍然占位,显示为空白)。
|
||||||
|
* 点击操作并不会实时操作注册表,程序需要暂存用户的需求,然后在点击确认或应用按钮后再统一执行。
|
||||||
|
|
||||||
|
* 底部按钮栏
|
||||||
|
* 布局: `QHBoxLayout` (水平布局),通常右对齐或使用 `QDialogButtonBox`。
|
||||||
|
* 组件: 三个 `QPushButton`。
|
||||||
|
* 按钮文本 (从左到右):
|
||||||
|
1. "确定" (通常设为默认按钮 `setDefault(true)`)。
|
||||||
|
2. "取消"。
|
||||||
|
3. "应用":点击后应用修改,并留在页面 (如果没有修改,则不可用)。
|
||||||
|
|
||||||
|
额外注意:
|
||||||
|
|
||||||
|
* 如果应用程序没有安装,则本页面下所有内容均不启用。
|
||||||
|
* 如果启动时命令行指定以为当前用户安装的模式启动,则系统那一栏所有按钮都不可用
|
||||||
|
|
||||||
|
# 代码要求
|
||||||
|
|
||||||
|
- 有关wfassoc的接口,请查阅@wfassoc-cdylib/codegen/wfassoc++.h 我要求你使用这个头文件中提供的内容来进行编写。
|
||||||
|
- wfassoc++.h文件中没有注释,如果你想查看注释,请访问@wfassoc-cdylib/codegen/wfassoc.h 文件。wfassoc++.h是wfassoc.h的C++包装。
|
||||||
|
- wfassoc.h所暴露的接口是由Rust编写的,通常查看wfassoc.h可满足所有需求。如果仍有不确定的内容,可查看其对应Rust项目的源码,位于@wfassoc-cdylib/src 目录下。或更进一步地查看其依赖的源码,位于@wfassoc/src 目录下。
|
||||||
|
- 该GUI程序需要接受两个必要的命令行参数,请使用Qt内置的命令行解析器进行解析:
|
||||||
|
- `-c`或`--manifest`:指定要配置的应用程序的清单文件。
|
||||||
|
- `-f`或`--for`:指定应用程序要安装到的
|
||||||
|
- 清单文件的样例是@example/manifest/ppic.toml
|
||||||
|
- 在应用程序加载时,或者执行操作时,如果发生错误(例如底层wfassoc发生错误,丢失命令行选项等,则弹出对话框报错,然后立即退出程序)
|
||||||
|
- 程序基本流程:
|
||||||
|
- 接受命令行,检查命令行参数是否合法
|
||||||
|
- 加载命令行指定的manifest文件,并使用sanitizer检查错误。你可以阅读@wfassoc-exec/src/manifest.rs 文件来看看我是如何在Rust中检查它的。
|
||||||
|
- 按照给定的manifest文件,使用wfassoc库构建schema,然后再构建program。
|
||||||
|
- 初始化窗口。
|
||||||
|
- 调用wfassoc program提供的函数,检查应用程序是否安装,设置窗口的安装部分的按钮enable。
|
||||||
|
- 调用wfassoc program提供的函数,遍历所有文件扩展名和关联情况,设置窗口的文件关联表格。
|
||||||
|
- 用户可以在"应用程序"选项卡中注册和卸载应用程序,点击后弹出窗口表示安装或卸载成功,然后检测是否安装,并刷新各个控件的enable状态。
|
||||||
|
- 用户可以在"文件关联"选项卡中设置是否以当前应用程序打开某些扩展名。用户可以点击全选按钮或单元格来进行设置,应用程序暂存修改,等用户点击确认或应用后再应用修改。如果点击的是应用,则刷新当前页面。
|
||||||
|
|
||||||
|
# 额外要求
|
||||||
|
|
||||||
|
- 不要尝试去编译来检查错误。我会安排其他人来检查程序是否能正常运行,并汇报回来,你再修改。
|
||||||
|
- 你不需要关心能否找到Qt,wfassoc和toml11这些库。
|
||||||
|
- 对于wfassoc,你只需要将@wfassoc-cdylib/codegen/Findwfassoc.cmake 复制到@example/qwfassoc/cmake 目录下,并在此目录下编写一个README.md,表明这个文件是从哪里复制来的即可。然后把复制的cmake文件所在目录加入find_package目录,然后使用find_package寻找wfassoc即可。至于去哪里找这个库,我会安排其他人来做。
|
||||||
|
- 对于Qt和toml11我会安排其他人来做,你只需要用find_package来找他们就行,不需要操心能不能找到。
|
||||||
|
- 如果你对某项需求有疑问,请问我,而不是进行猜测。
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### 总结代码结构示意 (伪代码):
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
QDialog *dialog = new QDialog();
|
||||||
|
dialog->setWindowTitle("选项");
|
||||||
|
|
||||||
|
QVBoxLayout *mainLayout = new QVBoxLayout(dialog);
|
||||||
|
|
||||||
|
// 1. Tab Widget
|
||||||
|
QTabWidget *tabWidget = new QTabWidget();
|
||||||
|
tabWidget->addTab(new QWidget(), "系统");
|
||||||
|
tabWidget->addTab(new QWidget(), "7-Zip");
|
||||||
|
// ... 其他 tabs
|
||||||
|
|
||||||
|
// 2. System Tab Content
|
||||||
|
QWidget *systemTab = tabWidget->widget(0);
|
||||||
|
QVBoxLayout *systemLayout = new QVBoxLayout(systemTab);
|
||||||
|
|
||||||
|
// Label
|
||||||
|
QLabel *label = new QLabel("使用 7-Zip 关联的文件类型:");
|
||||||
|
systemLayout->addWidget(label);
|
||||||
|
|
||||||
|
// Buttons (+)
|
||||||
|
QHBoxLayout *btnLayout = new QHBoxLayout();
|
||||||
|
QPushButton *btnPlus1 = new QPushButton("+");
|
||||||
|
QPushButton *btnPlus2 = new QPushButton("+");
|
||||||
|
btnLayout->addWidget(btnPlus1);
|
||||||
|
btnLayout->addWidget(btnPlus2);
|
||||||
|
systemLayout->addLayout(btnLayout);
|
||||||
|
|
||||||
|
// List (TreeWidget)
|
||||||
|
QTreeWidget *treeWidget = new QTreeWidget();
|
||||||
|
treeWidget->setColumnCount(3);
|
||||||
|
treeWidget->setHeaderLabels(QStringList() << "类型" << "yyc12345" << "所有用户");
|
||||||
|
// 添加 items...
|
||||||
|
systemLayout->addWidget(treeWidget);
|
||||||
|
|
||||||
|
// 3. Bottom Buttons
|
||||||
|
QHBoxLayout *bottomLayout = new QHBoxLayout();
|
||||||
|
bottomLayout->addStretch(); // 推挤按钮到右边
|
||||||
|
QPushButton *btnOK = new QPushButton("确定");
|
||||||
|
QPushButton *btnCancel = new QPushButton("取消");
|
||||||
|
QPushButton *btnApply = new QPushButton("应用(A)");
|
||||||
|
btnApply->setEnabled(false); // 禁用
|
||||||
|
QPushButton *btnHelp = new QPushButton("帮助");
|
||||||
|
|
||||||
|
bottomLayout->addWidget(btnOK);
|
||||||
|
bottomLayout->addWidget(btnCancel);
|
||||||
|
bottomLayout->addWidget(btnApply);
|
||||||
|
bottomLayout->addWidget(btnHelp);
|
||||||
|
|
||||||
|
mainLayout->addWidget(tabWidget);
|
||||||
|
mainLayout->addLayout(bottomLayout);
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
这是一个标准的 Windows 风格属性对话框,可以通过以下 Qt Widgets 结构来描述:
|
||||||
|
|
||||||
|
### 1. 主窗口容器
|
||||||
|
* 类: `QDialog`。
|
||||||
|
* 标题: "系统属性"。
|
||||||
|
* 布局: 垂直布局 (`QVBoxLayout`)。
|
||||||
|
|
||||||
|
### 2. 顶部选项卡 (Tabs)
|
||||||
|
* 组件: `QTabWidget`。
|
||||||
|
* 标签页: 包含 "计算机名", "硬件", "高级", "系统保护", "远程"。
|
||||||
|
* 当前选中: "硬件" 标签页。
|
||||||
|
|
||||||
|
### 3. "硬件" 选项卡内容
|
||||||
|
该选项卡内部使用垂直布局 (`QVBoxLayout`),包含两个主要的分组区域(视觉上类似 `QGroupBox` 或带有边框的 `QFrame`):
|
||||||
|
|
||||||
|
#### 区域 A: 设备管理器 (上半部分)
|
||||||
|
* 容器: 一个带有边框的容器。
|
||||||
|
* 布局: 水平布局 (`QHBoxLayout`)。
|
||||||
|
* 左侧: 一个 `QLabel` 显示电脑图标。
|
||||||
|
* 中间: 一个 `QLabel` 显示说明文本:"设备管理器列出所有安装在计算机上的硬件设备。请使用设备管理器来更改设备的属性。" (文本需要设置自动换行 `setWordWrap(true)`)。
|
||||||
|
* 右侧/底部: 一个 `QPushButton`,文本为 "设备管理器(D)"。
|
||||||
|
|
||||||
|
#### 区域 B: 设备安装设置 (下半部分)
|
||||||
|
* 容器: 一个带有边框的容器。
|
||||||
|
* 布局: 垂直布局 (`QVBoxLayout`)。
|
||||||
|
* 顶部行: 水平布局 (`QHBoxLayout`)。
|
||||||
|
* 左侧: 一个 `QLabel` 显示列表/勾选图标。
|
||||||
|
* 右侧: 一个 `QLabel` 显示说明文本:"选择 Windows 是否下载制造商提供的可用于你的设备的应用和自定义图标。" (文本需要设置自动换行)。
|
||||||
|
* 底部: 一个 `QPushButton`,文本为 "设备安装设置(S)",靠右对齐。
|
||||||
|
|
||||||
|
### 4. 底部按钮栏
|
||||||
|
* 布局: 水平布局 (`QHBoxLayout`),右对齐 (通常通过 `addStretch()` 实现)。
|
||||||
|
* 组件: 三个 `QPushButton`。
|
||||||
|
* "确定"
|
||||||
|
* "取消"
|
||||||
|
* "应用(A)" (注意:截图中该按钮呈灰色,代码中需设置 `setEnabled(false)`)。
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
你制定的计划有一些问题,请按照下述标出的问题一一修正:
|
||||||
|
|
||||||
|
- 编写的代码和说明文件需要使用英文注释。
|
||||||
|
- 是manifest而非manifesto,表示清单文件,请修正这个拼写错误。
|
||||||
|
- 我看到你在mainwindow篇章中编写了大量的C++代码,这没有必要。你是计划者而非执行者。你需要把需要在这个头文件中实现什么?该怎么做?需要使用哪些wfassoc函数,这些函数该怎么调用?在哪里查看他们怎么调用?详细的告诉将要执行这些任务的执行者,而不是直接为他们编写好代码。你在manifest部分的任务规划就非常符合这种范式。
|
||||||
|
|
||||||
3
example/qwfassoc/README.md
Normal file
3
example/qwfassoc/README.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# Q WFAssoc
|
||||||
|
|
||||||
|
TODO
|
||||||
1313
example/qwfassoc/TASKS.md
Normal file
1313
example/qwfassoc/TASKS.md
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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}")
|
||||||
5
example/qwfassoc/cmake/README.md
Normal file
5
example/qwfassoc/cmake/README.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
# cmake 模块说明
|
||||||
|
|
||||||
|
此目录下的 `Findwfassoc.cmake` 是从项目根目录 `wfassoc-cdylib/codegen/Findwfassoc.cmake` 复制而来。
|
||||||
|
|
||||||
|
该文件提供 `wfassoc::wfassoc` imported target。使用前需要设置 `wfassoc_ROOT` 变量指向 wfassoc 安装目录。
|
||||||
405
legacy/.gitignore
vendored
405
legacy/.gitignore
vendored
@@ -1,405 +0,0 @@
|
|||||||
# Custom ignore
|
|
||||||
# ignore build directory
|
|
||||||
builds/
|
|
||||||
Debug_MB/
|
|
||||||
Debug_UNICODE/
|
|
||||||
Release_UNICODE/
|
|
||||||
|
|
||||||
## Ignore Visual Studio temporary files, build results, and
|
|
||||||
## files generated by popular Visual Studio add-ons.
|
|
||||||
##
|
|
||||||
## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore
|
|
||||||
|
|
||||||
# User-specific files
|
|
||||||
*.rsuser
|
|
||||||
*.suo
|
|
||||||
*.user
|
|
||||||
*.userosscache
|
|
||||||
*.sln.docstates
|
|
||||||
|
|
||||||
# User-specific files (MonoDevelop/Xamarin Studio)
|
|
||||||
*.userprefs
|
|
||||||
|
|
||||||
# Mono auto generated files
|
|
||||||
mono_crash.*
|
|
||||||
|
|
||||||
# Build results
|
|
||||||
[Dd]ebug/
|
|
||||||
[Dd]ebugPublic/
|
|
||||||
[Rr]elease/
|
|
||||||
[Rr]eleases/
|
|
||||||
x64/
|
|
||||||
x86/
|
|
||||||
[Ww][Ii][Nn]32/
|
|
||||||
[Aa][Rr][Mm]/
|
|
||||||
[Aa][Rr][Mm]64/
|
|
||||||
bld/
|
|
||||||
[Bb]in/
|
|
||||||
[Oo]bj/
|
|
||||||
[Ll]og/
|
|
||||||
[Ll]ogs/
|
|
||||||
|
|
||||||
# Visual Studio 2015/2017 cache/options directory
|
|
||||||
.vs/
|
|
||||||
# Uncomment if you have tasks that create the project's static files in wwwroot
|
|
||||||
#wwwroot/
|
|
||||||
|
|
||||||
# Visual Studio 2017 auto generated files
|
|
||||||
Generated\ Files/
|
|
||||||
|
|
||||||
# MSTest test Results
|
|
||||||
[Tt]est[Rr]esult*/
|
|
||||||
[Bb]uild[Ll]og.*
|
|
||||||
|
|
||||||
# NUnit
|
|
||||||
*.VisualState.xml
|
|
||||||
TestResult.xml
|
|
||||||
nunit-*.xml
|
|
||||||
|
|
||||||
# Build Results of an ATL Project
|
|
||||||
[Dd]ebugPS/
|
|
||||||
[Rr]eleasePS/
|
|
||||||
dlldata.c
|
|
||||||
|
|
||||||
# Benchmark Results
|
|
||||||
BenchmarkDotNet.Artifacts/
|
|
||||||
|
|
||||||
# .NET Core
|
|
||||||
project.lock.json
|
|
||||||
project.fragment.lock.json
|
|
||||||
artifacts/
|
|
||||||
|
|
||||||
# ASP.NET Scaffolding
|
|
||||||
ScaffoldingReadMe.txt
|
|
||||||
|
|
||||||
# StyleCop
|
|
||||||
StyleCopReport.xml
|
|
||||||
|
|
||||||
# Files built by Visual Studio
|
|
||||||
*_i.c
|
|
||||||
*_p.c
|
|
||||||
*_h.h
|
|
||||||
*.ilk
|
|
||||||
*.meta
|
|
||||||
*.obj
|
|
||||||
*.iobj
|
|
||||||
*.pch
|
|
||||||
*.pdb
|
|
||||||
*.ipdb
|
|
||||||
*.pgc
|
|
||||||
*.pgd
|
|
||||||
*.rsp
|
|
||||||
*.sbr
|
|
||||||
*.tlb
|
|
||||||
*.tli
|
|
||||||
*.tlh
|
|
||||||
*.tmp
|
|
||||||
*.tmp_proj
|
|
||||||
*_wpftmp.csproj
|
|
||||||
*.log
|
|
||||||
*.tlog
|
|
||||||
*.vspscc
|
|
||||||
*.vssscc
|
|
||||||
.builds
|
|
||||||
*.pidb
|
|
||||||
*.svclog
|
|
||||||
*.scc
|
|
||||||
|
|
||||||
# Chutzpah Test files
|
|
||||||
_Chutzpah*
|
|
||||||
|
|
||||||
# Visual C++ cache files
|
|
||||||
ipch/
|
|
||||||
*.aps
|
|
||||||
*.ncb
|
|
||||||
*.opendb
|
|
||||||
*.opensdf
|
|
||||||
*.sdf
|
|
||||||
*.cachefile
|
|
||||||
*.VC.db
|
|
||||||
*.VC.VC.opendb
|
|
||||||
|
|
||||||
# Visual Studio profiler
|
|
||||||
*.psess
|
|
||||||
*.vsp
|
|
||||||
*.vspx
|
|
||||||
*.sap
|
|
||||||
|
|
||||||
# Visual Studio Trace Files
|
|
||||||
*.e2e
|
|
||||||
|
|
||||||
# TFS 2012 Local Workspace
|
|
||||||
$tf/
|
|
||||||
|
|
||||||
# Guidance Automation Toolkit
|
|
||||||
*.gpState
|
|
||||||
|
|
||||||
# ReSharper is a .NET coding add-in
|
|
||||||
_ReSharper*/
|
|
||||||
*.[Rr]e[Ss]harper
|
|
||||||
*.DotSettings.user
|
|
||||||
|
|
||||||
# TeamCity is a build add-in
|
|
||||||
_TeamCity*
|
|
||||||
|
|
||||||
# DotCover is a Code Coverage Tool
|
|
||||||
*.dotCover
|
|
||||||
|
|
||||||
# AxoCover is a Code Coverage Tool
|
|
||||||
.axoCover/*
|
|
||||||
!.axoCover/settings.json
|
|
||||||
|
|
||||||
# Coverlet is a free, cross platform Code Coverage Tool
|
|
||||||
coverage*.json
|
|
||||||
coverage*.xml
|
|
||||||
coverage*.info
|
|
||||||
|
|
||||||
# Visual Studio code coverage results
|
|
||||||
*.coverage
|
|
||||||
*.coveragexml
|
|
||||||
|
|
||||||
# NCrunch
|
|
||||||
_NCrunch_*
|
|
||||||
.*crunch*.local.xml
|
|
||||||
nCrunchTemp_*
|
|
||||||
|
|
||||||
# MightyMoose
|
|
||||||
*.mm.*
|
|
||||||
AutoTest.Net/
|
|
||||||
|
|
||||||
# Web workbench (sass)
|
|
||||||
.sass-cache/
|
|
||||||
|
|
||||||
# Installshield output folder
|
|
||||||
[Ee]xpress/
|
|
||||||
|
|
||||||
# DocProject is a documentation generator add-in
|
|
||||||
DocProject/buildhelp/
|
|
||||||
DocProject/Help/*.HxT
|
|
||||||
DocProject/Help/*.HxC
|
|
||||||
DocProject/Help/*.hhc
|
|
||||||
DocProject/Help/*.hhk
|
|
||||||
DocProject/Help/*.hhp
|
|
||||||
DocProject/Help/Html2
|
|
||||||
DocProject/Help/html
|
|
||||||
|
|
||||||
# Click-Once directory
|
|
||||||
publish/
|
|
||||||
|
|
||||||
# Publish Web Output
|
|
||||||
*.[Pp]ublish.xml
|
|
||||||
*.azurePubxml
|
|
||||||
# Note: Comment the next line if you want to checkin your web deploy settings,
|
|
||||||
# but database connection strings (with potential passwords) will be unencrypted
|
|
||||||
*.pubxml
|
|
||||||
*.publishproj
|
|
||||||
|
|
||||||
# Microsoft Azure Web App publish settings. Comment the next line if you want to
|
|
||||||
# checkin your Azure Web App publish settings, but sensitive information contained
|
|
||||||
# in these scripts will be unencrypted
|
|
||||||
PublishScripts/
|
|
||||||
|
|
||||||
# NuGet Packages
|
|
||||||
*.nupkg
|
|
||||||
# NuGet Symbol Packages
|
|
||||||
*.snupkg
|
|
||||||
# The packages folder can be ignored because of Package Restore
|
|
||||||
**/[Pp]ackages/*
|
|
||||||
# except build/, which is used as an MSBuild target.
|
|
||||||
!**/[Pp]ackages/build/
|
|
||||||
# Uncomment if necessary however generally it will be regenerated when needed
|
|
||||||
#!**/[Pp]ackages/repositories.config
|
|
||||||
# NuGet v3's project.json files produces more ignorable files
|
|
||||||
*.nuget.props
|
|
||||||
*.nuget.targets
|
|
||||||
|
|
||||||
# Microsoft Azure Build Output
|
|
||||||
csx/
|
|
||||||
*.build.csdef
|
|
||||||
|
|
||||||
# Microsoft Azure Emulator
|
|
||||||
ecf/
|
|
||||||
rcf/
|
|
||||||
|
|
||||||
# Windows Store app package directories and files
|
|
||||||
AppPackages/
|
|
||||||
BundleArtifacts/
|
|
||||||
Package.StoreAssociation.xml
|
|
||||||
_pkginfo.txt
|
|
||||||
*.appx
|
|
||||||
*.appxbundle
|
|
||||||
*.appxupload
|
|
||||||
|
|
||||||
# Visual Studio cache files
|
|
||||||
# files ending in .cache can be ignored
|
|
||||||
*.[Cc]ache
|
|
||||||
# but keep track of directories ending in .cache
|
|
||||||
!?*.[Cc]ache/
|
|
||||||
|
|
||||||
# Others
|
|
||||||
ClientBin/
|
|
||||||
~$*
|
|
||||||
*~
|
|
||||||
*.dbmdl
|
|
||||||
*.dbproj.schemaview
|
|
||||||
*.jfm
|
|
||||||
*.pfx
|
|
||||||
*.publishsettings
|
|
||||||
orleans.codegen.cs
|
|
||||||
|
|
||||||
# Including strong name files can present a security risk
|
|
||||||
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
|
|
||||||
#*.snk
|
|
||||||
|
|
||||||
# Since there are multiple workflows, uncomment next line to ignore bower_components
|
|
||||||
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
|
|
||||||
#bower_components/
|
|
||||||
|
|
||||||
# RIA/Silverlight projects
|
|
||||||
Generated_Code/
|
|
||||||
|
|
||||||
# Backup & report files from converting an old project file
|
|
||||||
# to a newer Visual Studio version. Backup files are not needed,
|
|
||||||
# because we have git ;-)
|
|
||||||
_UpgradeReport_Files/
|
|
||||||
Backup*/
|
|
||||||
UpgradeLog*.XML
|
|
||||||
UpgradeLog*.htm
|
|
||||||
ServiceFabricBackup/
|
|
||||||
*.rptproj.bak
|
|
||||||
|
|
||||||
# SQL Server files
|
|
||||||
*.mdf
|
|
||||||
*.ldf
|
|
||||||
*.ndf
|
|
||||||
|
|
||||||
# Business Intelligence projects
|
|
||||||
*.rdl.data
|
|
||||||
*.bim.layout
|
|
||||||
*.bim_*.settings
|
|
||||||
*.rptproj.rsuser
|
|
||||||
*- [Bb]ackup.rdl
|
|
||||||
*- [Bb]ackup ([0-9]).rdl
|
|
||||||
*- [Bb]ackup ([0-9][0-9]).rdl
|
|
||||||
|
|
||||||
# Microsoft Fakes
|
|
||||||
FakesAssemblies/
|
|
||||||
|
|
||||||
# GhostDoc plugin setting file
|
|
||||||
*.GhostDoc.xml
|
|
||||||
|
|
||||||
# Node.js Tools for Visual Studio
|
|
||||||
.ntvs_analysis.dat
|
|
||||||
node_modules/
|
|
||||||
|
|
||||||
# Visual Studio 6 build log
|
|
||||||
*.plg
|
|
||||||
|
|
||||||
# Visual Studio 6 workspace options file
|
|
||||||
*.opt
|
|
||||||
|
|
||||||
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
|
|
||||||
*.vbw
|
|
||||||
|
|
||||||
# Visual Studio 6 auto-generated project file (contains which files were open etc.)
|
|
||||||
*.vbp
|
|
||||||
|
|
||||||
# Visual Studio 6 workspace and project file (working project files containing files to include in project)
|
|
||||||
*.dsw
|
|
||||||
*.dsp
|
|
||||||
|
|
||||||
# Visual Studio 6 technical files
|
|
||||||
*.ncb
|
|
||||||
*.aps
|
|
||||||
|
|
||||||
# Visual Studio LightSwitch build output
|
|
||||||
**/*.HTMLClient/GeneratedArtifacts
|
|
||||||
**/*.DesktopClient/GeneratedArtifacts
|
|
||||||
**/*.DesktopClient/ModelManifest.xml
|
|
||||||
**/*.Server/GeneratedArtifacts
|
|
||||||
**/*.Server/ModelManifest.xml
|
|
||||||
_Pvt_Extensions
|
|
||||||
|
|
||||||
# Paket dependency manager
|
|
||||||
.paket/paket.exe
|
|
||||||
paket-files/
|
|
||||||
|
|
||||||
# FAKE - F# Make
|
|
||||||
.fake/
|
|
||||||
|
|
||||||
# CodeRush personal settings
|
|
||||||
.cr/personal
|
|
||||||
|
|
||||||
# Python Tools for Visual Studio (PTVS)
|
|
||||||
__pycache__/
|
|
||||||
*.pyc
|
|
||||||
|
|
||||||
# Cake - Uncomment if you are using it
|
|
||||||
# tools/**
|
|
||||||
# !tools/packages.config
|
|
||||||
|
|
||||||
# Tabs Studio
|
|
||||||
*.tss
|
|
||||||
|
|
||||||
# Telerik's JustMock configuration file
|
|
||||||
*.jmconfig
|
|
||||||
|
|
||||||
# BizTalk build output
|
|
||||||
*.btp.cs
|
|
||||||
*.btm.cs
|
|
||||||
*.odx.cs
|
|
||||||
*.xsd.cs
|
|
||||||
|
|
||||||
# OpenCover UI analysis results
|
|
||||||
OpenCover/
|
|
||||||
|
|
||||||
# Azure Stream Analytics local run output
|
|
||||||
ASALocalRun/
|
|
||||||
|
|
||||||
# MSBuild Binary and Structured Log
|
|
||||||
*.binlog
|
|
||||||
|
|
||||||
# NVidia Nsight GPU debugger configuration file
|
|
||||||
*.nvuser
|
|
||||||
|
|
||||||
# MFractors (Xamarin productivity tool) working folder
|
|
||||||
.mfractor/
|
|
||||||
|
|
||||||
# Local History for Visual Studio
|
|
||||||
.localhistory/
|
|
||||||
|
|
||||||
# Visual Studio History (VSHistory) files
|
|
||||||
.vshistory/
|
|
||||||
|
|
||||||
# BeatPulse healthcheck temp database
|
|
||||||
healthchecksdb
|
|
||||||
|
|
||||||
# Backup folder for Package Reference Convert tool in Visual Studio 2017
|
|
||||||
MigrationBackup/
|
|
||||||
|
|
||||||
# Ionide (cross platform F# VS Code tools) working folder
|
|
||||||
.ionide/
|
|
||||||
|
|
||||||
# Fody - auto-generated XML schema
|
|
||||||
FodyWeavers.xsd
|
|
||||||
|
|
||||||
# VS Code files for those working on multiple tools
|
|
||||||
.vscode/*
|
|
||||||
!.vscode/settings.json
|
|
||||||
!.vscode/tasks.json
|
|
||||||
!.vscode/launch.json
|
|
||||||
!.vscode/extensions.json
|
|
||||||
*.code-workspace
|
|
||||||
|
|
||||||
# Local History for Visual Studio Code
|
|
||||||
.history/
|
|
||||||
|
|
||||||
# Windows Installer files from build outputs
|
|
||||||
*.cab
|
|
||||||
*.msi
|
|
||||||
*.msix
|
|
||||||
*.msm
|
|
||||||
*.msp
|
|
||||||
|
|
||||||
# JetBrains Rider
|
|
||||||
*.sln.iml
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
# wfassoc
|
|
||||||
|
|
||||||
**W**indows **F**ile **Assoc**iation Library
|
|
||||||
|
|
||||||
**Work In Progress**
|
|
||||||
|
|
||||||
## Introduction
|
|
||||||
|
|
||||||
* wfassoc: Main library
|
|
||||||
* wfassoc_example: A full example about how to use this library
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
|
|
||||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
|
||||||
# Visual Studio Version 16
|
|
||||||
VisualStudioVersion = 16.0.31702.278
|
|
||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
|
||||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wfassoc", "wfassoc\wfassoc.vcxproj", "{4AAC8F0C-3E1C-4584-B682-05BBF96A813F}"
|
|
||||||
EndProject
|
|
||||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wfassoc_example", "wfassoc_example\wfassoc_example.vcxproj", "{AD275AD7-CBD5-41CA-AB24-BB707B3F7534}"
|
|
||||||
EndProject
|
|
||||||
Global
|
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
|
||||||
Debug_MB|x64 = Debug_MB|x64
|
|
||||||
Debug_MB|x86 = Debug_MB|x86
|
|
||||||
Debug_UNICODE|x64 = Debug_UNICODE|x64
|
|
||||||
Debug_UNICODE|x86 = Debug_UNICODE|x86
|
|
||||||
Release_UNICODE|x64 = Release_UNICODE|x64
|
|
||||||
Release_UNICODE|x86 = Release_UNICODE|x86
|
|
||||||
EndGlobalSection
|
|
||||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
|
||||||
{4AAC8F0C-3E1C-4584-B682-05BBF96A813F}.Debug_MB|x64.ActiveCfg = Debug|x64
|
|
||||||
{4AAC8F0C-3E1C-4584-B682-05BBF96A813F}.Debug_MB|x64.Build.0 = Debug|x64
|
|
||||||
{4AAC8F0C-3E1C-4584-B682-05BBF96A813F}.Debug_MB|x86.ActiveCfg = Debug|Win32
|
|
||||||
{4AAC8F0C-3E1C-4584-B682-05BBF96A813F}.Debug_MB|x86.Build.0 = Debug|Win32
|
|
||||||
{4AAC8F0C-3E1C-4584-B682-05BBF96A813F}.Debug_UNICODE|x64.ActiveCfg = Debug|x64
|
|
||||||
{4AAC8F0C-3E1C-4584-B682-05BBF96A813F}.Debug_UNICODE|x64.Build.0 = Debug|x64
|
|
||||||
{4AAC8F0C-3E1C-4584-B682-05BBF96A813F}.Debug_UNICODE|x86.ActiveCfg = Debug|Win32
|
|
||||||
{4AAC8F0C-3E1C-4584-B682-05BBF96A813F}.Debug_UNICODE|x86.Build.0 = Debug|Win32
|
|
||||||
{4AAC8F0C-3E1C-4584-B682-05BBF96A813F}.Release_UNICODE|x64.ActiveCfg = Release|x64
|
|
||||||
{4AAC8F0C-3E1C-4584-B682-05BBF96A813F}.Release_UNICODE|x64.Build.0 = Release|x64
|
|
||||||
{4AAC8F0C-3E1C-4584-B682-05BBF96A813F}.Release_UNICODE|x86.ActiveCfg = Release|Win32
|
|
||||||
{4AAC8F0C-3E1C-4584-B682-05BBF96A813F}.Release_UNICODE|x86.Build.0 = Release|Win32
|
|
||||||
{AD275AD7-CBD5-41CA-AB24-BB707B3F7534}.Debug_MB|x64.ActiveCfg = Debug_UNICODE|x64
|
|
||||||
{AD275AD7-CBD5-41CA-AB24-BB707B3F7534}.Debug_MB|x64.Build.0 = Debug_UNICODE|x64
|
|
||||||
{AD275AD7-CBD5-41CA-AB24-BB707B3F7534}.Debug_MB|x86.ActiveCfg = Debug_MB|Win32
|
|
||||||
{AD275AD7-CBD5-41CA-AB24-BB707B3F7534}.Debug_MB|x86.Build.0 = Debug_MB|Win32
|
|
||||||
{AD275AD7-CBD5-41CA-AB24-BB707B3F7534}.Debug_UNICODE|x64.ActiveCfg = Debug_UNICODE|x64
|
|
||||||
{AD275AD7-CBD5-41CA-AB24-BB707B3F7534}.Debug_UNICODE|x64.Build.0 = Debug_UNICODE|x64
|
|
||||||
{AD275AD7-CBD5-41CA-AB24-BB707B3F7534}.Debug_UNICODE|x86.ActiveCfg = Debug_UNICODE|Win32
|
|
||||||
{AD275AD7-CBD5-41CA-AB24-BB707B3F7534}.Debug_UNICODE|x86.Build.0 = Debug_UNICODE|Win32
|
|
||||||
{AD275AD7-CBD5-41CA-AB24-BB707B3F7534}.Release_UNICODE|x64.ActiveCfg = Release_UNICODE|x64
|
|
||||||
{AD275AD7-CBD5-41CA-AB24-BB707B3F7534}.Release_UNICODE|x64.Build.0 = Release_UNICODE|x64
|
|
||||||
{AD275AD7-CBD5-41CA-AB24-BB707B3F7534}.Release_UNICODE|x86.ActiveCfg = Release_UNICODE|Win32
|
|
||||||
{AD275AD7-CBD5-41CA-AB24-BB707B3F7534}.Release_UNICODE|x86.Build.0 = Release_UNICODE|Win32
|
|
||||||
EndGlobalSection
|
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
|
||||||
HideSolutionNode = FALSE
|
|
||||||
EndGlobalSection
|
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
|
||||||
SolutionGuid = {1AD32362-840E-4399-A7D5-26A0B67A614D}
|
|
||||||
EndGlobalSection
|
|
||||||
EndGlobal
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
LIBRARY wfassoc
|
|
||||||
EXPORTS
|
|
||||||
|
|
||||||
WFInstallApplicationW
|
|
||||||
WFInstallApplicationA
|
|
||||||
WFUninstallApplicationW
|
|
||||||
WFUninstallApplicationA
|
|
||||||
WFGenerateProgIDW
|
|
||||||
WFGenerateProgIDA
|
|
||||||
@@ -1,165 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
|
||||||
<ItemGroup Label="ProjectConfigurations">
|
|
||||||
<ProjectConfiguration Include="Debug|Win32">
|
|
||||||
<Configuration>Debug</Configuration>
|
|
||||||
<Platform>Win32</Platform>
|
|
||||||
</ProjectConfiguration>
|
|
||||||
<ProjectConfiguration Include="Release|Win32">
|
|
||||||
<Configuration>Release</Configuration>
|
|
||||||
<Platform>Win32</Platform>
|
|
||||||
</ProjectConfiguration>
|
|
||||||
<ProjectConfiguration Include="Debug|x64">
|
|
||||||
<Configuration>Debug</Configuration>
|
|
||||||
<Platform>x64</Platform>
|
|
||||||
</ProjectConfiguration>
|
|
||||||
<ProjectConfiguration Include="Release|x64">
|
|
||||||
<Configuration>Release</Configuration>
|
|
||||||
<Platform>x64</Platform>
|
|
||||||
</ProjectConfiguration>
|
|
||||||
</ItemGroup>
|
|
||||||
<PropertyGroup Label="Globals">
|
|
||||||
<VCProjectVersion>16.0</VCProjectVersion>
|
|
||||||
<Keyword>Win32Proj</Keyword>
|
|
||||||
<ProjectGuid>{4aac8f0c-3e1c-4584-b682-05bbf96a813f}</ProjectGuid>
|
|
||||||
<RootNamespace>wfassoc</RootNamespace>
|
|
||||||
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
|
||||||
</PropertyGroup>
|
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
|
||||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
|
||||||
<UseDebugLibraries>true</UseDebugLibraries>
|
|
||||||
<PlatformToolset>v142</PlatformToolset>
|
|
||||||
<CharacterSet>NotSet</CharacterSet>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
|
||||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
|
||||||
<UseDebugLibraries>false</UseDebugLibraries>
|
|
||||||
<PlatformToolset>v142</PlatformToolset>
|
|
||||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
|
||||||
<CharacterSet>NotSet</CharacterSet>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
|
||||||
<ConfigurationType>Application</ConfigurationType>
|
|
||||||
<UseDebugLibraries>true</UseDebugLibraries>
|
|
||||||
<PlatformToolset>v142</PlatformToolset>
|
|
||||||
<CharacterSet>Unicode</CharacterSet>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
|
||||||
<ConfigurationType>Application</ConfigurationType>
|
|
||||||
<UseDebugLibraries>false</UseDebugLibraries>
|
|
||||||
<PlatformToolset>v142</PlatformToolset>
|
|
||||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
|
||||||
<CharacterSet>Unicode</CharacterSet>
|
|
||||||
</PropertyGroup>
|
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
|
||||||
<ImportGroup Label="ExtensionSettings">
|
|
||||||
</ImportGroup>
|
|
||||||
<ImportGroup Label="Shared">
|
|
||||||
</ImportGroup>
|
|
||||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
|
||||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
|
||||||
</ImportGroup>
|
|
||||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
|
||||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
|
||||||
</ImportGroup>
|
|
||||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
|
||||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
|
||||||
</ImportGroup>
|
|
||||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
|
||||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
|
||||||
</ImportGroup>
|
|
||||||
<PropertyGroup Label="UserMacros" />
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
|
||||||
<LinkIncremental>true</LinkIncremental>
|
|
||||||
<OutDir>$(SolutionDir)builds\Debug\</OutDir>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
|
||||||
<LinkIncremental>false</LinkIncremental>
|
|
||||||
<OutDir>$(SolutionDir)builds\Release\</OutDir>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
|
||||||
<LinkIncremental>true</LinkIncremental>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
|
||||||
<LinkIncremental>false</LinkIncremental>
|
|
||||||
</PropertyGroup>
|
|
||||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
|
||||||
<ClCompile>
|
|
||||||
<WarningLevel>Level3</WarningLevel>
|
|
||||||
<SDLCheck>true</SDLCheck>
|
|
||||||
<PreprocessorDefinitions>WIN32;_CRT_SECURE_NO_WARNINGS;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
|
||||||
<ConformanceMode>true</ConformanceMode>
|
|
||||||
<CompileAs>CompileAsC</CompileAs>
|
|
||||||
</ClCompile>
|
|
||||||
<Link>
|
|
||||||
<SubSystem>Console</SubSystem>
|
|
||||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
|
||||||
<ModuleDefinitionFile>wfassoc.def</ModuleDefinitionFile>
|
|
||||||
</Link>
|
|
||||||
</ItemDefinitionGroup>
|
|
||||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
|
||||||
<ClCompile>
|
|
||||||
<WarningLevel>Level3</WarningLevel>
|
|
||||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
|
||||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
|
||||||
<SDLCheck>true</SDLCheck>
|
|
||||||
<PreprocessorDefinitions>WIN32;_CRT_SECURE_NO_WARNINGS;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
|
||||||
<ConformanceMode>true</ConformanceMode>
|
|
||||||
<CompileAs>CompileAsC</CompileAs>
|
|
||||||
</ClCompile>
|
|
||||||
<Link>
|
|
||||||
<SubSystem>Console</SubSystem>
|
|
||||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
|
||||||
<OptimizeReferences>true</OptimizeReferences>
|
|
||||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
|
||||||
<ModuleDefinitionFile>wfassoc.def</ModuleDefinitionFile>
|
|
||||||
</Link>
|
|
||||||
</ItemDefinitionGroup>
|
|
||||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
|
||||||
<ClCompile>
|
|
||||||
<WarningLevel>Level3</WarningLevel>
|
|
||||||
<SDLCheck>true</SDLCheck>
|
|
||||||
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
|
||||||
<ConformanceMode>true</ConformanceMode>
|
|
||||||
</ClCompile>
|
|
||||||
<Link>
|
|
||||||
<SubSystem>Console</SubSystem>
|
|
||||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
|
||||||
<ModuleDefinitionFile>wfassoc.def</ModuleDefinitionFile>
|
|
||||||
</Link>
|
|
||||||
</ItemDefinitionGroup>
|
|
||||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
|
||||||
<ClCompile>
|
|
||||||
<WarningLevel>Level3</WarningLevel>
|
|
||||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
|
||||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
|
||||||
<SDLCheck>true</SDLCheck>
|
|
||||||
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
|
||||||
<ConformanceMode>true</ConformanceMode>
|
|
||||||
</ClCompile>
|
|
||||||
<Link>
|
|
||||||
<SubSystem>Console</SubSystem>
|
|
||||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
|
||||||
<OptimizeReferences>true</OptimizeReferences>
|
|
||||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
|
||||||
<ModuleDefinitionFile>wfassoc.def</ModuleDefinitionFile>
|
|
||||||
</Link>
|
|
||||||
</ItemDefinitionGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<ClInclude Include="wfassoc_utils.h" />
|
|
||||||
<ClInclude Include="wfassoc_core.h" />
|
|
||||||
<ClInclude Include="wfassoc_private.h" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<ClCompile Include="wfassoc_private.c" />
|
|
||||||
<ClCompile Include="wfassoc_utils.c" />
|
|
||||||
<ClCompile Include="wfassoc_core.c" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<None Include="wfassoc.def" />
|
|
||||||
</ItemGroup>
|
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
|
||||||
<ImportGroup Label="ExtensionTargets">
|
|
||||||
</ImportGroup>
|
|
||||||
</Project>
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
|
||||||
<ItemGroup>
|
|
||||||
<Filter Include="源文件">
|
|
||||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
|
||||||
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
|
||||||
</Filter>
|
|
||||||
<Filter Include="头文件">
|
|
||||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
|
||||||
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
|
|
||||||
</Filter>
|
|
||||||
<Filter Include="资源文件">
|
|
||||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
|
||||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
|
||||||
</Filter>
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<ClInclude Include="wfassoc_core.h">
|
|
||||||
<Filter>头文件</Filter>
|
|
||||||
</ClInclude>
|
|
||||||
<ClInclude Include="wfassoc_utils.h">
|
|
||||||
<Filter>头文件</Filter>
|
|
||||||
</ClInclude>
|
|
||||||
<ClInclude Include="wfassoc_private.h">
|
|
||||||
<Filter>头文件</Filter>
|
|
||||||
</ClInclude>
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<ClCompile Include="wfassoc_core.c">
|
|
||||||
<Filter>源文件</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="wfassoc_utils.c">
|
|
||||||
<Filter>源文件</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="wfassoc_private.c">
|
|
||||||
<Filter>源文件</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<None Include="wfassoc.def">
|
|
||||||
<Filter>源文件</Filter>
|
|
||||||
</None>
|
|
||||||
</ItemGroup>
|
|
||||||
</Project>
|
|
||||||
@@ -1,407 +0,0 @@
|
|||||||
#include "wfassoc_core.h"
|
|
||||||
#include <strsafe.h>
|
|
||||||
#include <ShlObj.h>
|
|
||||||
|
|
||||||
// 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.
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Convert multi byte string to wide char string
|
|
||||||
/// Notice: this function will allocate memory for returns and it should be released safely.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="source">The string will be converted</param>
|
|
||||||
/// <param name="error">The error happend during converting</param>
|
|
||||||
/// <returns>The string has been converted. If return NULL, it mean that the converting failed.</returns>
|
|
||||||
//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))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
#if !defined(_YYCDLL_WFASSOC_H__IMPORTED_)
|
|
||||||
#define _YYCDLL_WFASSOC_H__IMPORTED_
|
|
||||||
|
|
||||||
#include <Windows.h>
|
|
||||||
#include <sal.h>
|
|
||||||
#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
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Install Application via Wide Character
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="profile"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
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);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Generate Legacy ProgID
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="vendor">Vendor. Such as `Word`, `Excel`, `PowerPoint`.</param>
|
|
||||||
/// <param name="component">Component. Such as `Document`, `Sheet`, `Diagram`.</param>
|
|
||||||
/// <param name="version">Version. Such as `0`, `1`, `114514`.</param>
|
|
||||||
/// <param name="result">Pointer to output ProgID. If this variable is NULL, function will calculate proper length of receiving buffer and return it via `result_length`.</param>
|
|
||||||
/// <param name="result_length">Pointer to a int variable containing buffer's length. If `result` is not NULL, it should be the length of `result`.</param>
|
|
||||||
/// <returns></returns>
|
|
||||||
//WFERROR WFGenerateProgIDW(WCHAR* vendor, WCHAR* component, INT version, WCHAR* result, INT* result_length);
|
|
||||||
/// <summary>
|
|
||||||
/// Generate Legacy ProgID
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="vendor">Vendor. Such as `Word`, `Excel`, `PowerPoint`.</param>
|
|
||||||
/// <param name="component">Component. Such as `Document`, `Sheet`, `Diagram`.</param>
|
|
||||||
/// <param name="version">Version. Such as `0`, `1`, `114514`.</param>
|
|
||||||
/// <param name="result">Pointer to output ProgID. If this variable is NULL, function will calculate proper length of receiving buffer and return it via `result_length`.</param>
|
|
||||||
/// <param name="result_length">Pointer to a int variable containing buffer's length. If `result` is not NULL, it should be the length of `result`.</param>
|
|
||||||
/// <returns></returns>
|
|
||||||
//WFERROR WFGenerateProgIDA(CHAR* vendor, CHAR* component, INT version, CHAR* result, INT* result_length);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
#include "wfassoc_private.h"
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
#if !defined(_YYCDLL_WFASSOC_PRIVATE_H__IMPORTED_)
|
|
||||||
#define _YYCDLL_WFASSOC_PRIVATE_H__IMPORTED_
|
|
||||||
|
|
||||||
typedef struct _WFAPP_RAWDATA {
|
|
||||||
uint32_t mVersion;
|
|
||||||
|
|
||||||
}WFAPP_RAWDATA;
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,256 +0,0 @@
|
|||||||
#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
|
|
||||||
@@ -1,136 +0,0 @@
|
|||||||
#if !defined(_YYCDLL_WFASSOC_UTILS_H__IMPORTED_)
|
|
||||||
#define _YYCDLL_WFASSOC_UTILS_H__IMPORTED_
|
|
||||||
|
|
||||||
#include <Windows.h>
|
|
||||||
#include <inttypes.h>
|
|
||||||
|
|
||||||
// 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
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// wfassoc Error Enum
|
|
||||||
/// </summary>
|
|
||||||
typedef enum _WFERROR {
|
|
||||||
/// <summary>
|
|
||||||
/// All operation done successfully
|
|
||||||
/// </summary>
|
|
||||||
WFERROR_OK = 0,
|
|
||||||
/// <summary>
|
|
||||||
/// 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.
|
|
||||||
/// </summary>
|
|
||||||
WFERROR_INVALID_VERSION = 1,
|
|
||||||
/// <summary>
|
|
||||||
/// The buffer in some operations is insufficient, please try expanding buffer.
|
|
||||||
/// </summary>
|
|
||||||
WFERROR_INSUFFICIENT_BUFFER = 2,
|
|
||||||
/// <summary>
|
|
||||||
/// Some essential pointer variable is NULL.
|
|
||||||
/// </summary>
|
|
||||||
WFERROR_NULLPTR = 3,
|
|
||||||
WFERROR_WIN32 = 5,
|
|
||||||
WFERROR_ALLOC = 6,
|
|
||||||
WFERROR_END_OF_TAIL = 7,
|
|
||||||
WFERROR_CRT = 8,
|
|
||||||
WFERROR_INVALID_ARGUMENTS = 9
|
|
||||||
}WFERROR;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// wfassoc Profile Struct
|
|
||||||
/// </summary>
|
|
||||||
typedef struct _WFAPP_PROFILE {
|
|
||||||
/// <summary>
|
|
||||||
/// wfassoc version. Fill it with `WFVERSION` in your application. This field is used for version checking.
|
|
||||||
/// </summary>
|
|
||||||
uint32_t WFVersion;
|
|
||||||
/// <summary>
|
|
||||||
/// The path to locate your application.
|
|
||||||
/// </summary>
|
|
||||||
wchar_t* AppPath;
|
|
||||||
/// <summary>
|
|
||||||
/// The command will be executed when opening files.
|
|
||||||
/// </summary>
|
|
||||||
wchar_t* AppCommand;
|
|
||||||
/// <summary>
|
|
||||||
/// 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.
|
|
||||||
/// </summary>
|
|
||||||
wchar_t* ProgID_Vendor;
|
|
||||||
wchar_t* ProgID_Component;
|
|
||||||
uint32_t ProgID_Version;
|
|
||||||
/// <summary>
|
|
||||||
/// 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`
|
|
||||||
/// </summary>
|
|
||||||
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
|
|
||||||
@@ -1,81 +0,0 @@
|
|||||||
#include "../wfassoc/wfassoc.h"
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <tchar.h>
|
|
||||||
|
|
||||||
#define COMMAND_MAX_LENGTH 128
|
|
||||||
|
|
||||||
WFAPP_PROFILE* create_profile();
|
|
||||||
TCHAR* create_progId();
|
|
||||||
void free_profile(WFAPP_PROFILE* profile);
|
|
||||||
|
|
||||||
int main(int argc, char* args[]) {
|
|
||||||
|
|
||||||
// alloc application profile
|
|
||||||
WFAPP_PROFILE* profile = create_profile();
|
|
||||||
if (profile == NULL) {
|
|
||||||
puts("Error: Fail to allocating profile structure.\n");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// alloc input string and check it
|
|
||||||
char* input_str = malloc(sizeof(char) * (COMMAND_MAX_LENGTH + 1));
|
|
||||||
if (input_str == NULL) return;
|
|
||||||
|
|
||||||
// accept input
|
|
||||||
while (TRUE) {
|
|
||||||
gets_s(input_str, COMMAND_MAX_LENGTH);
|
|
||||||
if (!strcmp(input_str, "install")) {
|
|
||||||
_tprintf(TEXT("%s\n"), profile->ProgID);
|
|
||||||
} else if (!strcmp(input_str, "uninstall")) {
|
|
||||||
_tprintf(TEXT("%s\n"), profile->ProgID);
|
|
||||||
} else if (!strcmp(input_str, "quit")) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//free input string
|
|
||||||
free(input_str);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
WFAPP_PROFILE* create_profile() {
|
|
||||||
WFAPP_PROFILE* profile = (WFAPP_PROFILE*)malloc(sizeof(WFAPP_PROFILE));
|
|
||||||
if (profile == NULL) return NULL;
|
|
||||||
memset(profile, 0, sizeof(WFAPP_PROFILE));
|
|
||||||
|
|
||||||
profile->WFVersion = WFVERSION;
|
|
||||||
profile->AppPath = TEXT("E:\\pineapple-picture\\ppic.exe");
|
|
||||||
profile->AppCommand = TEXT("E:\\pineapple-picture\\ppic.exe \"%1\"");
|
|
||||||
profile->SupportedTypes = TEXT(".png\0.jpg\0");
|
|
||||||
profile->ProgID = create_progId();
|
|
||||||
if (profile->ProgID == NULL) {
|
|
||||||
free_profile(profile);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return profile;
|
|
||||||
}
|
|
||||||
|
|
||||||
TCHAR* create_progId() {
|
|
||||||
TCHAR* progid = NULL;
|
|
||||||
int progIdSize;
|
|
||||||
if (WFFAILED(WFGenerateProgID(TEXT("PineapplePicture"), TEXT("Image"), 0, NULL, &progIdSize))) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
progid = (TCHAR*)malloc(sizeof(TCHAR) * progIdSize);
|
|
||||||
if (progid == NULL) return NULL;
|
|
||||||
memset(progid, 0, sizeof(TCHAR) * progIdSize);
|
|
||||||
|
|
||||||
if (WFFAILED(WFGenerateProgID(TEXT("PineapplePicture"), TEXT("Image"), 0, progid, &progIdSize))) {
|
|
||||||
free(progid);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return progid;
|
|
||||||
}
|
|
||||||
|
|
||||||
void free_profile(WFAPP_PROFILE* profile) {
|
|
||||||
if (profile->ProgID != NULL) free(profile->ProgID);
|
|
||||||
free(profile);
|
|
||||||
}
|
|
||||||
@@ -1,214 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
|
||||||
<ItemGroup Label="ProjectConfigurations">
|
|
||||||
<ProjectConfiguration Include="Debug_MB|Win32">
|
|
||||||
<Configuration>Debug_MB</Configuration>
|
|
||||||
<Platform>Win32</Platform>
|
|
||||||
</ProjectConfiguration>
|
|
||||||
<ProjectConfiguration Include="Debug_MB|x64">
|
|
||||||
<Configuration>Debug_MB</Configuration>
|
|
||||||
<Platform>x64</Platform>
|
|
||||||
</ProjectConfiguration>
|
|
||||||
<ProjectConfiguration Include="Debug_UNICODE|Win32">
|
|
||||||
<Configuration>Debug_UNICODE</Configuration>
|
|
||||||
<Platform>Win32</Platform>
|
|
||||||
</ProjectConfiguration>
|
|
||||||
<ProjectConfiguration Include="Release_UNICODE|Win32">
|
|
||||||
<Configuration>Release_UNICODE</Configuration>
|
|
||||||
<Platform>Win32</Platform>
|
|
||||||
</ProjectConfiguration>
|
|
||||||
<ProjectConfiguration Include="Debug_UNICODE|x64">
|
|
||||||
<Configuration>Debug_UNICODE</Configuration>
|
|
||||||
<Platform>x64</Platform>
|
|
||||||
</ProjectConfiguration>
|
|
||||||
<ProjectConfiguration Include="Release_UNICODE|x64">
|
|
||||||
<Configuration>Release_UNICODE</Configuration>
|
|
||||||
<Platform>x64</Platform>
|
|
||||||
</ProjectConfiguration>
|
|
||||||
</ItemGroup>
|
|
||||||
<PropertyGroup Label="Globals">
|
|
||||||
<VCProjectVersion>16.0</VCProjectVersion>
|
|
||||||
<Keyword>Win32Proj</Keyword>
|
|
||||||
<ProjectGuid>{ad275ad7-cbd5-41ca-ab24-bb707b3f7534}</ProjectGuid>
|
|
||||||
<RootNamespace>wfassocexample</RootNamespace>
|
|
||||||
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
|
||||||
</PropertyGroup>
|
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug_UNICODE|Win32'" Label="Configuration">
|
|
||||||
<ConfigurationType>Application</ConfigurationType>
|
|
||||||
<UseDebugLibraries>true</UseDebugLibraries>
|
|
||||||
<PlatformToolset>v142</PlatformToolset>
|
|
||||||
<CharacterSet>Unicode</CharacterSet>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug_MB|Win32'" Label="Configuration">
|
|
||||||
<ConfigurationType>Application</ConfigurationType>
|
|
||||||
<UseDebugLibraries>true</UseDebugLibraries>
|
|
||||||
<PlatformToolset>v142</PlatformToolset>
|
|
||||||
<CharacterSet>MultiByte</CharacterSet>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release_UNICODE|Win32'" Label="Configuration">
|
|
||||||
<ConfigurationType>Application</ConfigurationType>
|
|
||||||
<UseDebugLibraries>false</UseDebugLibraries>
|
|
||||||
<PlatformToolset>v142</PlatformToolset>
|
|
||||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
|
||||||
<CharacterSet>Unicode</CharacterSet>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug_UNICODE|x64'" Label="Configuration">
|
|
||||||
<ConfigurationType>Application</ConfigurationType>
|
|
||||||
<UseDebugLibraries>true</UseDebugLibraries>
|
|
||||||
<PlatformToolset>v142</PlatformToolset>
|
|
||||||
<CharacterSet>Unicode</CharacterSet>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug_MB|x64'" Label="Configuration">
|
|
||||||
<ConfigurationType>Application</ConfigurationType>
|
|
||||||
<UseDebugLibraries>true</UseDebugLibraries>
|
|
||||||
<PlatformToolset>v142</PlatformToolset>
|
|
||||||
<CharacterSet>Unicode</CharacterSet>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release_UNICODE|x64'" Label="Configuration">
|
|
||||||
<ConfigurationType>Application</ConfigurationType>
|
|
||||||
<UseDebugLibraries>false</UseDebugLibraries>
|
|
||||||
<PlatformToolset>v142</PlatformToolset>
|
|
||||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
|
||||||
<CharacterSet>Unicode</CharacterSet>
|
|
||||||
</PropertyGroup>
|
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
|
||||||
<ImportGroup Label="ExtensionSettings">
|
|
||||||
</ImportGroup>
|
|
||||||
<ImportGroup Label="Shared">
|
|
||||||
</ImportGroup>
|
|
||||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug_UNICODE|Win32'">
|
|
||||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
|
||||||
</ImportGroup>
|
|
||||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug_MB|Win32'" Label="PropertySheets">
|
|
||||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
|
||||||
</ImportGroup>
|
|
||||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release_UNICODE|Win32'">
|
|
||||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
|
||||||
</ImportGroup>
|
|
||||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug_UNICODE|x64'">
|
|
||||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
|
||||||
</ImportGroup>
|
|
||||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug_MB|x64'" Label="PropertySheets">
|
|
||||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
|
||||||
</ImportGroup>
|
|
||||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release_UNICODE|x64'">
|
|
||||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
|
||||||
</ImportGroup>
|
|
||||||
<PropertyGroup Label="UserMacros" />
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug_UNICODE|Win32'">
|
|
||||||
<LinkIncremental>true</LinkIncremental>
|
|
||||||
<OutDir>$(SolutionDir)builds\Debug\</OutDir>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug_MB|Win32'">
|
|
||||||
<LinkIncremental>true</LinkIncremental>
|
|
||||||
<OutDir>$(SolutionDir)builds\Debug\</OutDir>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release_UNICODE|Win32'">
|
|
||||||
<LinkIncremental>false</LinkIncremental>
|
|
||||||
<OutDir>$(SolutionDir)builds\Release\</OutDir>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug_UNICODE|x64'">
|
|
||||||
<LinkIncremental>true</LinkIncremental>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug_MB|x64'">
|
|
||||||
<LinkIncremental>true</LinkIncremental>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release_UNICODE|x64'">
|
|
||||||
<LinkIncremental>false</LinkIncremental>
|
|
||||||
</PropertyGroup>
|
|
||||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug_UNICODE|Win32'">
|
|
||||||
<ClCompile>
|
|
||||||
<WarningLevel>Level3</WarningLevel>
|
|
||||||
<SDLCheck>true</SDLCheck>
|
|
||||||
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
|
||||||
<ConformanceMode>true</ConformanceMode>
|
|
||||||
<CompileAs>CompileAsC</CompileAs>
|
|
||||||
</ClCompile>
|
|
||||||
<Link>
|
|
||||||
<SubSystem>Console</SubSystem>
|
|
||||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
|
||||||
</Link>
|
|
||||||
</ItemDefinitionGroup>
|
|
||||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug_MB|Win32'">
|
|
||||||
<ClCompile>
|
|
||||||
<WarningLevel>Level3</WarningLevel>
|
|
||||||
<SDLCheck>true</SDLCheck>
|
|
||||||
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
|
||||||
<ConformanceMode>true</ConformanceMode>
|
|
||||||
<CompileAs>CompileAsC</CompileAs>
|
|
||||||
</ClCompile>
|
|
||||||
<Link>
|
|
||||||
<SubSystem>Console</SubSystem>
|
|
||||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
|
||||||
</Link>
|
|
||||||
</ItemDefinitionGroup>
|
|
||||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release_UNICODE|Win32'">
|
|
||||||
<ClCompile>
|
|
||||||
<WarningLevel>Level3</WarningLevel>
|
|
||||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
|
||||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
|
||||||
<SDLCheck>true</SDLCheck>
|
|
||||||
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
|
||||||
<ConformanceMode>true</ConformanceMode>
|
|
||||||
<CompileAs>CompileAsC</CompileAs>
|
|
||||||
</ClCompile>
|
|
||||||
<Link>
|
|
||||||
<SubSystem>Console</SubSystem>
|
|
||||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
|
||||||
<OptimizeReferences>true</OptimizeReferences>
|
|
||||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
|
||||||
</Link>
|
|
||||||
</ItemDefinitionGroup>
|
|
||||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug_UNICODE|x64'">
|
|
||||||
<ClCompile>
|
|
||||||
<WarningLevel>Level3</WarningLevel>
|
|
||||||
<SDLCheck>true</SDLCheck>
|
|
||||||
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
|
||||||
<ConformanceMode>true</ConformanceMode>
|
|
||||||
</ClCompile>
|
|
||||||
<Link>
|
|
||||||
<SubSystem>Console</SubSystem>
|
|
||||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
|
||||||
</Link>
|
|
||||||
</ItemDefinitionGroup>
|
|
||||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug_MB|x64'">
|
|
||||||
<ClCompile>
|
|
||||||
<WarningLevel>Level3</WarningLevel>
|
|
||||||
<SDLCheck>true</SDLCheck>
|
|
||||||
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
|
||||||
<ConformanceMode>true</ConformanceMode>
|
|
||||||
</ClCompile>
|
|
||||||
<Link>
|
|
||||||
<SubSystem>Console</SubSystem>
|
|
||||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
|
||||||
</Link>
|
|
||||||
</ItemDefinitionGroup>
|
|
||||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release_UNICODE|x64'">
|
|
||||||
<ClCompile>
|
|
||||||
<WarningLevel>Level3</WarningLevel>
|
|
||||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
|
||||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
|
||||||
<SDLCheck>true</SDLCheck>
|
|
||||||
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
|
||||||
<ConformanceMode>true</ConformanceMode>
|
|
||||||
</ClCompile>
|
|
||||||
<Link>
|
|
||||||
<SubSystem>Console</SubSystem>
|
|
||||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
|
||||||
<OptimizeReferences>true</OptimizeReferences>
|
|
||||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
|
||||||
</Link>
|
|
||||||
</ItemDefinitionGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<ProjectReference Include="..\wfassoc\wfassoc.vcxproj">
|
|
||||||
<Project>{4aac8f0c-3e1c-4584-b682-05bbf96a813f}</Project>
|
|
||||||
</ProjectReference>
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<ClCompile Include="main.c" />
|
|
||||||
</ItemGroup>
|
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
|
||||||
<ImportGroup Label="ExtensionTargets">
|
|
||||||
</ImportGroup>
|
|
||||||
</Project>
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
|
||||||
<ItemGroup>
|
|
||||||
<Filter Include="源文件">
|
|
||||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
|
||||||
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
|
||||||
</Filter>
|
|
||||||
<Filter Include="头文件">
|
|
||||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
|
||||||
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
|
|
||||||
</Filter>
|
|
||||||
<Filter Include="资源文件">
|
|
||||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
|
||||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
|
||||||
</Filter>
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<ClCompile Include="main.c">
|
|
||||||
<Filter>源文件</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
</ItemGroup>
|
|
||||||
</Project>
|
|
||||||
101
wfassoc-cdylib/cbindgen/Findwfassoc.cmake
Normal file
101
wfassoc-cdylib/cbindgen/Findwfassoc.cmake
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
# Findwfassoc.cmake
|
||||||
|
# ----------------
|
||||||
|
# Find wfassoc library and headers.
|
||||||
|
#
|
||||||
|
# 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 and wfassoc++.h
|
||||||
|
# lib/ - contains wfassoc_cdylib.dll.lib (import library)
|
||||||
|
#
|
||||||
|
# This module defines the following variables:
|
||||||
|
# wfassoc_FOUND - True if wfassoc was found
|
||||||
|
# wfassoc_INCLUDE_DIRS - Path to wfassoc include directory
|
||||||
|
# wfassoc_LIBRARIES - Path to wfassoc import library
|
||||||
|
# wfassoc_DLL - Path to wfassoc DLL
|
||||||
|
# wfassoc_ROOT - The root directory (user-provided)
|
||||||
|
#
|
||||||
|
# This module also creates the following imported targets:
|
||||||
|
# wfassoc::wfassoc - Main wfassoc library (includes both include and link)
|
||||||
|
#
|
||||||
|
|
||||||
|
set(wfassoc_FOUND FALSE)
|
||||||
|
|
||||||
|
# Require user to set wfassoc_ROOT
|
||||||
|
if(NOT wfassoc_ROOT)
|
||||||
|
message(FATAL_ERROR "wfassoc_ROOT must be set to the installation directory of wfassoc")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Check existence of required subdirectories
|
||||||
|
if(NOT EXISTS ${wfassoc_ROOT})
|
||||||
|
message(FATAL_ERROR "wfassoc_ROOT directory does not exist: ${wfassoc_ROOT}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(wfassoc_INCLUDE_DIR ${wfassoc_ROOT}/include)
|
||||||
|
set(wfassoc_LIB_DIR ${wfassoc_ROOT}/lib)
|
||||||
|
set(wfassoc_BIN_DIR ${wfassoc_ROOT}/bin)
|
||||||
|
|
||||||
|
# Find header files
|
||||||
|
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}")
|
||||||
|
return()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Find import library (.lib)
|
||||||
|
find_file(wfassoc_LIBRARIES
|
||||||
|
NAMES wfassoc_cdylib.dll.lib
|
||||||
|
PATHS ${wfassoc_LIB_DIR}
|
||||||
|
NO_DEFAULT_PATH
|
||||||
|
DOC "wfassoc import library"
|
||||||
|
)
|
||||||
|
|
||||||
|
if(NOT wfassoc_LIBRARIES)
|
||||||
|
message(SEND_ERROR "Missing wfassoc import library (wfassoc_cdylib.dll.lib) in ${wfassoc_LIB_DIR}")
|
||||||
|
return()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Find DLL file
|
||||||
|
find_file(wfassoc_DLL
|
||||||
|
NAMES wfassoc_cdylib.dll
|
||||||
|
PATHS ${wfassoc_BIN_DIR}
|
||||||
|
NO_DEFAULT_PATH
|
||||||
|
DOC "wfassoc dynamic library"
|
||||||
|
)
|
||||||
|
|
||||||
|
if(NOT wfassoc_DLL)
|
||||||
|
message(SEND_ERROR "Missing wfassoc DLL (wfassoc_cdylib.dll) in ${wfassoc_BIN_DIR}")
|
||||||
|
return()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Everything found
|
||||||
|
set(wfassoc_FOUND TRUE)
|
||||||
|
|
||||||
|
# Mark variables as advanced for ccmake/cmake-gui
|
||||||
|
mark_as_advanced(wfassoc_INCLUDE_DIRS wfassoc_LIBRARIES wfassoc_DLL)
|
||||||
|
|
||||||
|
# Create imported target for wfassoc
|
||||||
|
if(wfassoc_FOUND AND NOT TARGET wfassoc::wfassoc)
|
||||||
|
add_library(wfassoc::wfassoc SHARED IMPORTED)
|
||||||
|
|
||||||
|
# Set include directories
|
||||||
|
set_target_properties(wfassoc::wfassoc PROPERTIES
|
||||||
|
INTERFACE_INCLUDE_DIRECTORIES ${wfassoc_INCLUDE_DIRS}
|
||||||
|
)
|
||||||
|
|
||||||
|
# Set import library location
|
||||||
|
set_target_properties(wfassoc::wfassoc PROPERTIES
|
||||||
|
IMPORTED_IMPLIB "${wfassoc_LIBRARIES}"
|
||||||
|
IMPORTED_LOCATION "${wfassoc_DLL}"
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Optional: Print status message
|
||||||
|
if(wfassoc_FOUND)
|
||||||
|
message(STATUS "Found wfassoc:")
|
||||||
|
message(STATUS " Root : ${wfassoc_ROOT}")
|
||||||
|
message(STATUS " Include : ${wfassoc_INCLUDE_DIRS}")
|
||||||
|
message(STATUS " Library : ${wfassoc_LIBRARIES}")
|
||||||
|
message(STATUS " DLL : ${wfassoc_DLL}")
|
||||||
|
endif()
|
||||||
290
wfassoc-cdylib/cbindgen/wfassoc++.h
Normal file
290
wfassoc-cdylib/cbindgen/wfassoc++.h
Normal file
@@ -0,0 +1,290 @@
|
|||||||
|
/**
|
||||||
|
* @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 <optional>
|
||||||
|
#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 std::string(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
HICON GetIcon() {
|
||||||
|
HICON icon = nullptr;
|
||||||
|
_Check(wfassoc::WFExtStatusGetIcon(_token, &icon));
|
||||||
|
return icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Token _token;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SelfExtStatus {
|
||||||
|
public:
|
||||||
|
explicit SelfExtStatus(Token token) : _token(token) {}
|
||||||
|
~SelfExtStatus() {
|
||||||
|
if (_token != _INVALID_TOKEN()) {
|
||||||
|
wfassoc::WFSelfExtStatusDestroy(_token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SelfExtStatus(const SelfExtStatus&) = delete;
|
||||||
|
SelfExtStatus& operator=(const SelfExtStatus&) = delete;
|
||||||
|
SelfExtStatus(SelfExtStatus&& other) noexcept : _token(other._token) { other._token = _INVALID_TOKEN(); }
|
||||||
|
SelfExtStatus& operator=(SelfExtStatus&& other) noexcept {
|
||||||
|
if (this != &other) {
|
||||||
|
if (_token != _INVALID_TOKEN()) {
|
||||||
|
wfassoc::WFSelfExtStatusDestroy(_token);
|
||||||
|
}
|
||||||
|
_token = other._token;
|
||||||
|
other._token = _INVALID_TOKEN();
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string GetName() {
|
||||||
|
const char* name = nullptr;
|
||||||
|
_Check(wfassoc::WFSelfExtStatusGetName(_token, &name));
|
||||||
|
return std::string(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
HICON GetIcon() {
|
||||||
|
HICON icon = nullptr;
|
||||||
|
_Check(wfassoc::WFSelfExtStatusGetIcon(_token, &icon));
|
||||||
|
return icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string GetExt() {
|
||||||
|
const char* inner = nullptr;
|
||||||
|
_Check(wfassoc::WFSelfExtStatusGetExt(_token, &inner));
|
||||||
|
return std::string(inner);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string GetDottedExt() {
|
||||||
|
const char* inner = nullptr;
|
||||||
|
_Check(wfassoc::WFSelfExtStatusGetDottedExt(_token, &inner));
|
||||||
|
return std::string(inner);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Token _token;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Program {
|
||||||
|
public:
|
||||||
|
explicit Program(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 std::string(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
IconRc ResolveIcon() {
|
||||||
|
Token token = _INVALID_TOKEN();
|
||||||
|
_Check(wfassoc::WFProgramResolveIcon(_token, &token));
|
||||||
|
return IconRc(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t ExtsLen() {
|
||||||
|
size_t len = 0;
|
||||||
|
_Check(wfassoc::WFProgramExtsLen(_token, &len));
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t FindExt(const char* body) {
|
||||||
|
size_t index = INVALID_INDEX;
|
||||||
|
_Check(wfassoc::WFProgramFindExt(_token, body, &index));
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
SelfExtStatus ResolveExt(size_t index) {
|
||||||
|
Token token = _INVALID_TOKEN();
|
||||||
|
_Check(wfassoc::WFProgramResolveExt(_token, index, &token));
|
||||||
|
return SelfExtStatus(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
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::optional<ExtStatus> QueryExt(View view, size_t index) {
|
||||||
|
Token token = _INVALID_TOKEN();
|
||||||
|
_Check(wfassoc::WFProgramQueryExt(_token, view, index, &token));
|
||||||
|
if (token == _INVALID_TOKEN()) {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
return ExtStatus(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Token _token;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace wfassocpp
|
||||||
|
|
||||||
|
#endif // WFASSOCPP_H_
|
||||||
@@ -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>
|
||||||
@@ -135,6 +135,9 @@ bool WFShutdown(void);
|
|||||||
* Once they fail, you can call this function to get a human-readable error message.
|
* 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 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.
|
* @return Null-terminated UTF-8 string containing the error message.
|
||||||
* If no error has occurred, the string is empty.
|
* If no error has occurred, the string is empty.
|
||||||
@@ -314,6 +317,9 @@ bool WFSchemaAddExt(Token in_schema,
|
|||||||
* Please note that this function will consume the Schema object.
|
* Please note that this function will consume the Schema object.
|
||||||
* It means that the Schema object cannot be used after this call.
|
* 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.
|
* 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[in] in_schema Schema token (will be consumed)
|
||||||
* @param[out] out_program Pointer to receive the Program token.
|
* @param[out] out_program Pointer to receive the Program token.
|
||||||
@@ -335,9 +341,14 @@ bool WFProgramDestroy(Token in_program);
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Resolve the provided program name of this 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[in] in_program Program token
|
||||||
* @param[out] out_name Pointer to receive the resolved name, or NULL if not found.
|
* @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.
|
* 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
|
* @return true on success, false on failure
|
||||||
*/
|
*/
|
||||||
@@ -345,9 +356,13 @@ bool WFProgramResolveName(Token in_program, CStyleString *out_name);
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Resolve the Program icon resource
|
* @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[in] in_program Program token
|
||||||
* @param[out] out_icon_rc Pointer to receive the icon resource token, or invalid token if not found.
|
* @param[out] out_icon_rc Pointer to receive the icon resource token.
|
||||||
* The caller take the ownership of created icon resource object.
|
* The caller take the ownership of created icon resource object.
|
||||||
* And it should be freed by calling WFIconRcDestroy() when it is no longer needed.
|
* And it should be freed by calling WFIconRcDestroy() when it is no longer needed.
|
||||||
* @return true on success, false on failure
|
* @return true on success, false on failure
|
||||||
@@ -363,18 +378,6 @@ bool WFProgramResolveIcon(Token in_program, Token *out_icon_rc);
|
|||||||
*/
|
*/
|
||||||
bool WFProgramExtsLen(Token in_program, size_t *out_len);
|
bool WFProgramExtsLen(Token in_program, size_t *out_len);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get a file extension by index
|
|
||||||
*
|
|
||||||
* @param[in] in_program Program token
|
|
||||||
* @param[in] in_index Index of the extension to retrieve
|
|
||||||
* @param[out] out_ext Pointer to receive the file extension token.
|
|
||||||
* The caller take the ownership of created file extension object.
|
|
||||||
* And it should be freed by calling WFExtDestroy() when it is no longer needed.
|
|
||||||
* @return true on success, false on failure
|
|
||||||
*/
|
|
||||||
bool WFProgramGetExt(Token in_program, size_t in_index, Token *out_ext);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Find a file extension by its body (extension string)
|
* @brief Find a file extension by its body (extension string)
|
||||||
*
|
*
|
||||||
@@ -385,6 +388,18 @@ bool WFProgramGetExt(Token in_program, size_t in_index, Token *out_ext);
|
|||||||
*/
|
*/
|
||||||
bool WFProgramFindExt(Token in_program, CStyleString in_body, size_t *out_index);
|
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
|
* @brief Register the Program in the specified scope
|
||||||
*
|
*
|
||||||
@@ -457,7 +472,10 @@ bool WFProgramQueryExt(Token in_program, View in_view, size_t in_index, Token *o
|
|||||||
bool WFExtStatusDestroy(Token in_ext_status);
|
bool WFExtStatusDestroy(Token in_ext_status);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get the name from an extension status object
|
* @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[in] in_ext_status Extension status token
|
||||||
* @param[out] out_name Pointer to receive the name.
|
* @param[out] out_name Pointer to receive the name.
|
||||||
@@ -471,14 +489,75 @@ bool WFExtStatusGetName(Token in_ext_status, CStyleString *out_name);
|
|||||||
/**
|
/**
|
||||||
* @brief Get the icon from an extension status object
|
* @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[in] in_ext_status Extension status token
|
||||||
* @param[out] out_icon Pointer to receive the icon handle, or INVALID_HICON if not available.
|
* @param[out] out_icon Pointer to receive the icon handle.
|
||||||
* This icon handle will be freed once this icon resource object is destroyed.
|
* 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.
|
* Please make a copy immediately if you need to use it longer.
|
||||||
* @return true on success, false on failure
|
* @return true on success, false on failure
|
||||||
*/
|
*/
|
||||||
bool WFExtStatusGetIcon(Token in_ext_status, HICON *out_icon);
|
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
|
* @brief Destroy an icon resource object
|
||||||
*
|
*
|
||||||
@@ -499,36 +578,6 @@ bool WFIconRcDestroy(Token in_icon_rc);
|
|||||||
*/
|
*/
|
||||||
bool WFIconRcGetIcon(Token in_icon_rc, HICON *out_icon);
|
bool WFIconRcGetIcon(Token in_icon_rc, HICON *out_icon);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Destroy a file extension object
|
|
||||||
*
|
|
||||||
* @param[in] in_ext Extension token to destroy
|
|
||||||
* @return true on success, false on failure
|
|
||||||
*/
|
|
||||||
bool WFExtDestroy(Token in_ext);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get the inner extension string without dot
|
|
||||||
*
|
|
||||||
* @param[in] in_ext Extension 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 WFExtGetInner(Token in_ext, CStyleString *out_inner);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get the inner extension string with dot prefix
|
|
||||||
*
|
|
||||||
* @param[in] in_ext Extension 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 WFExtGetDottedInner(Token in_ext, CStyleString *out_inner);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
} // extern "C"
|
} // extern "C"
|
||||||
#endif // __cplusplus
|
#endif // __cplusplus
|
||||||
@@ -537,4 +586,4 @@ bool WFExtGetDottedInner(Token in_ext, CStyleString *out_inner);
|
|||||||
} // namespace wfassoc
|
} // namespace wfassoc
|
||||||
#endif // __cplusplus
|
#endif // __cplusplus
|
||||||
|
|
||||||
#endif // __WFASSOC_H__
|
#endif // WFASSOC_H_
|
||||||
@@ -24,17 +24,17 @@ enum Error {
|
|||||||
Program(#[from] wfassoc::highlevel::ProgramError),
|
Program(#[from] wfassoc::highlevel::ProgramError),
|
||||||
|
|
||||||
/// Error when manipulating with C-style string.
|
/// Error when manipulating with C-style string.
|
||||||
#[error("{0}")]
|
#[error("C-Style string FFI error:{0}")]
|
||||||
CStrFfi(#[from] cstr_ffi::Error),
|
CStrFfi(#[from] cstr_ffi::Error),
|
||||||
/// Error when manipulating with object pool.
|
/// Error when manipulating with object pool.
|
||||||
#[error("{0}")]
|
#[error("object pool error: {0}")]
|
||||||
ObjectPool(#[from] object_pool::Error),
|
ObjectPool(#[from] object_pool::Error),
|
||||||
|
|
||||||
/// Error occurs when checking enum value
|
/// Error occurs when checking enum value
|
||||||
#[error("")]
|
#[error("the enumeration value provided to FFI function is out of its range")]
|
||||||
EnumOutOfRange,
|
EnumOutOfRange,
|
||||||
/// Error when manipulating with poison RwLock
|
/// Error when manipulating with poison RwLock
|
||||||
#[error("RwLock is poisoning")]
|
#[error("concurrency error: RwLock is poisonous")]
|
||||||
PoisonRwLock,
|
PoisonRwLock,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -191,10 +191,11 @@ static PROGRAM_POOL: LazyLock<RwLock<ObjectPool<Program>>> =
|
|||||||
static EXT_STATUS_POOL: LazyLock<RwLock<ObjectPool<wfassoc::highlevel::ProgramExtStatus>>> =
|
static EXT_STATUS_POOL: LazyLock<RwLock<ObjectPool<wfassoc::highlevel::ProgramExtStatus>>> =
|
||||||
LazyLock::new(|| RwLock::new(ObjectPool::new()));
|
LazyLock::new(|| RwLock::new(ObjectPool::new()));
|
||||||
|
|
||||||
static ICON_RC_POOL: LazyLock<RwLock<ObjectPool<wfassoc::win32::concept::IconRc>>> =
|
static SELF_EXT_STATUS_POOL: LazyLock<
|
||||||
LazyLock::new(|| RwLock::new(ObjectPool::new()));
|
RwLock<ObjectPool<wfassoc::highlevel::ProgramSelfExtStatus>>,
|
||||||
|
> = LazyLock::new(|| RwLock::new(ObjectPool::new()));
|
||||||
|
|
||||||
static EXT_POOL: LazyLock<RwLock<ObjectPool<wfassoc::win32::concept::Ext>>> =
|
static ICON_RC_POOL: LazyLock<RwLock<ObjectPool<wfassoc::win32::concept::IconRc>>> =
|
||||||
LazyLock::new(|| RwLock::new(ObjectPool::new()));
|
LazyLock::new(|| RwLock::new(ObjectPool::new()));
|
||||||
|
|
||||||
// endregion
|
// endregion
|
||||||
@@ -218,8 +219,8 @@ pub extern "C" fn WFStartup() -> bool {
|
|||||||
let _pool = pull_writer!(SCHEMA_POOL)?;
|
let _pool = pull_writer!(SCHEMA_POOL)?;
|
||||||
let _pool = pull_writer!(PROGRAM_POOL)?;
|
let _pool = pull_writer!(PROGRAM_POOL)?;
|
||||||
let _pool = pull_writer!(EXT_STATUS_POOL)?;
|
let _pool = pull_writer!(EXT_STATUS_POOL)?;
|
||||||
|
let _pool = pull_writer!(SELF_EXT_STATUS_POOL)?;
|
||||||
let _pool = pull_writer!(ICON_RC_POOL)?;
|
let _pool = pull_writer!(ICON_RC_POOL)?;
|
||||||
let _pool = pull_writer!(EXT_POOL)?;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -234,9 +235,9 @@ pub extern "C" fn WFShutdown() -> bool {
|
|||||||
pool.clear();
|
pool.clear();
|
||||||
let mut pool = pull_writer!(EXT_STATUS_POOL)?;
|
let mut pool = pull_writer!(EXT_STATUS_POOL)?;
|
||||||
pool.clear();
|
pool.clear();
|
||||||
let mut pool = pull_writer!(ICON_RC_POOL)?;
|
let mut pool = pull_writer!(SELF_EXT_STATUS_POOL)?;
|
||||||
pool.clear();
|
pool.clear();
|
||||||
let mut pool = pull_writer!(EXT_POOL)?;
|
let mut pool = pull_writer!(ICON_RC_POOL)?;
|
||||||
pool.clear();
|
pool.clear();
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
@@ -491,16 +492,9 @@ pub extern "C" fn WFProgramResolveName(
|
|||||||
let mut pool = pull_writer!(PROGRAM_POOL)?;
|
let mut pool = pull_writer!(PROGRAM_POOL)?;
|
||||||
let program = pool.get_mut(in_program)?;
|
let program = pool.get_mut(in_program)?;
|
||||||
|
|
||||||
let name = match program.resolve_name()? {
|
let name = program.resolve_name()?;
|
||||||
Some(name) => {
|
cstr_ffi::set_ffi_string(&name)?;
|
||||||
cstr_ffi::set_ffi_string(&name)?;
|
Ok(cstr_ffi::get_ffi_string())
|
||||||
cstr_ffi::get_ffi_string()
|
|
||||||
},
|
|
||||||
None => {
|
|
||||||
std::ptr::null()
|
|
||||||
},
|
|
||||||
};
|
|
||||||
Ok(name)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -514,16 +508,8 @@ pub extern "C" fn WFProgramResolveIcon(
|
|||||||
let program = pool.get_mut(in_program)?;
|
let program = pool.get_mut(in_program)?;
|
||||||
|
|
||||||
let icon = program.resolve_icon()?;
|
let icon = program.resolve_icon()?;
|
||||||
let token = match icon {
|
let mut pool = pull_writer!(ICON_RC_POOL)?;
|
||||||
Some(icon) => {
|
Ok(pool.allocate(icon)?)
|
||||||
let mut pool = pull_writer!(ICON_RC_POOL)?;
|
|
||||||
pool.allocate(icon)?
|
|
||||||
},
|
|
||||||
None => {
|
|
||||||
object_pool::invalid_token()
|
|
||||||
},
|
|
||||||
};
|
|
||||||
Ok(token)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -539,23 +525,6 @@ pub extern "C" fn WFProgramExtsLen(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[unsafe(no_mangle)]
|
|
||||||
pub extern "C" fn WFProgramGetExt(
|
|
||||||
in_program: in_param_ty!(Token),
|
|
||||||
in_index: in_param_ty!(usize),
|
|
||||||
out_ext: out_param_ty!(Token),
|
|
||||||
) -> bool {
|
|
||||||
cffi_wrapper!(|in_program: Token, in_index: usize| -> (out_ext: Token) {
|
|
||||||
let mut pool = pull_writer!(PROGRAM_POOL)?;
|
|
||||||
let program = pool.get_mut(in_program)?;
|
|
||||||
|
|
||||||
let ext = program.get_ext(in_index)?;
|
|
||||||
let mut pool = pull_writer!(EXT_POOL)?;
|
|
||||||
let token = pool.allocate(ext.clone())?;
|
|
||||||
Ok(token)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[unsafe(no_mangle)]
|
#[unsafe(no_mangle)]
|
||||||
pub extern "C" fn WFProgramFindExt(
|
pub extern "C" fn WFProgramFindExt(
|
||||||
in_program: in_param_ty!(Token),
|
in_program: in_param_ty!(Token),
|
||||||
@@ -575,6 +544,23 @@ pub extern "C" fn WFProgramFindExt(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub extern "C" fn WFProgramResolveExt(
|
||||||
|
in_program: in_param_ty!(Token),
|
||||||
|
in_index: in_param_ty!(usize),
|
||||||
|
out_self_ext_status: out_param_ty!(Token),
|
||||||
|
) -> bool {
|
||||||
|
cffi_wrapper!(|in_program: Token, in_index: usize| -> (out_self_ext_status: Token) {
|
||||||
|
let mut pool = pull_writer!(PROGRAM_POOL)?;
|
||||||
|
let program = pool.get_mut(in_program)?;
|
||||||
|
|
||||||
|
let self_ext_status = program.resolve_ext(in_index)?;
|
||||||
|
let mut pool = pull_writer!(SELF_EXT_STATUS_POOL)?;
|
||||||
|
let token = pool.allocate(self_ext_status)?;
|
||||||
|
Ok(token)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
#[unsafe(no_mangle)]
|
#[unsafe(no_mangle)]
|
||||||
pub extern "C" fn WFProgramRegister(
|
pub extern "C" fn WFProgramRegister(
|
||||||
in_program: in_param_ty!(Token),
|
in_program: in_param_ty!(Token),
|
||||||
@@ -706,11 +692,76 @@ pub extern "C" fn WFExtStatusGetIcon(
|
|||||||
let pool = pull_reader!(EXT_STATUS_POOL)?;
|
let pool = pull_reader!(EXT_STATUS_POOL)?;
|
||||||
let ext_status = pool.get(in_ext_status)?;
|
let ext_status = pool.get(in_ext_status)?;
|
||||||
|
|
||||||
let icon = match ext_status.get_icon() {
|
let icon = ext_status.get_icon();
|
||||||
Some(icon) => icon.get_icon(),
|
Ok(icon.get_icon())
|
||||||
None => ffi_types::INVALID_HICON,
|
})
|
||||||
};
|
}
|
||||||
Ok(icon)
|
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
// region: Self Extension Status
|
||||||
|
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub extern "C" fn WFSelfExtStatusDestroy(in_self_ext_status: in_param_ty!(Token)) -> bool {
|
||||||
|
cffi_wrapper!(|in_self_ext_status: Token| {
|
||||||
|
let mut pool = pull_writer!(SELF_EXT_STATUS_POOL)?;
|
||||||
|
Ok(pool.free(in_self_ext_status)?)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub extern "C" fn WFSelfExtStatusGetName(
|
||||||
|
in_self_ext_status: in_param_ty!(Token),
|
||||||
|
out_name: out_param_ty!(CStyleString),
|
||||||
|
) -> bool {
|
||||||
|
cffi_wrapper!(|in_self_ext_status: Token| -> (out_name: CStyleString) {
|
||||||
|
let pool = pull_reader!(SELF_EXT_STATUS_POOL)?;
|
||||||
|
let self_ext_status = pool.get(in_self_ext_status)?;
|
||||||
|
|
||||||
|
cstr_ffi::set_ffi_string(self_ext_status.get_name())?;
|
||||||
|
Ok(cstr_ffi::get_ffi_string())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub extern "C" fn WFSelfExtStatusGetIcon(
|
||||||
|
in_self_ext_status: in_param_ty!(Token),
|
||||||
|
out_icon: out_param_ty!(HICON),
|
||||||
|
) -> bool {
|
||||||
|
cffi_wrapper!(|in_self_ext_status: Token| -> (out_icon: HICON) {
|
||||||
|
let pool = pull_reader!(SELF_EXT_STATUS_POOL)?;
|
||||||
|
let self_ext_status = pool.get(in_self_ext_status)?;
|
||||||
|
|
||||||
|
let icon = self_ext_status.get_icon();
|
||||||
|
Ok(icon.get_icon())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub extern "C" fn WFSelfExtStatusGetExt(
|
||||||
|
in_self_ext_status: in_param_ty!(Token),
|
||||||
|
out_inner: out_param_ty!(CStyleString),
|
||||||
|
) -> bool {
|
||||||
|
cffi_wrapper!(|in_self_ext_status: Token| -> (out_inner: CStyleString) {
|
||||||
|
let pool = pull_reader!(SELF_EXT_STATUS_POOL)?;
|
||||||
|
let self_ext_status = pool.get(in_self_ext_status)?;
|
||||||
|
|
||||||
|
cstr_ffi::set_ffi_string(self_ext_status.get_ext())?;
|
||||||
|
Ok(cstr_ffi::get_ffi_string())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub extern "C" fn WFSelfExtStatusGetDottedExt(
|
||||||
|
in_self_ext_status: in_param_ty!(Token),
|
||||||
|
out_inner: out_param_ty!(CStyleString),
|
||||||
|
) -> bool {
|
||||||
|
cffi_wrapper!(|in_self_ext_status: Token| -> (out_inner: CStyleString) {
|
||||||
|
let pool = pull_reader!(SELF_EXT_STATUS_POOL)?;
|
||||||
|
let self_ext_status = pool.get(in_self_ext_status)?;
|
||||||
|
|
||||||
|
cstr_ffi::set_ffi_string(self_ext_status.get_dotted_ext().as_str())?;
|
||||||
|
Ok(cstr_ffi::get_ffi_string())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -740,41 +791,3 @@ pub extern "C" fn WFIconRcGetIcon(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// endregion
|
// endregion
|
||||||
|
|
||||||
// region: File Extension
|
|
||||||
|
|
||||||
#[unsafe(no_mangle)]
|
|
||||||
pub extern "C" fn WFExtDestroy(in_ext: in_param_ty!(Token)) -> bool {
|
|
||||||
cffi_wrapper!(|in_ext: Token| {
|
|
||||||
let mut pool = pull_writer!(EXT_POOL)?;
|
|
||||||
Ok(pool.free(in_ext)?)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[unsafe(no_mangle)]
|
|
||||||
pub extern "C" fn WFExtGetInner(
|
|
||||||
in_ext: in_param_ty!(Token),
|
|
||||||
out_inner: out_param_ty!(CStyleString),
|
|
||||||
) -> bool {
|
|
||||||
cffi_wrapper!(|in_ext: Token| -> (out_inner: CStyleString) {
|
|
||||||
let pool = pull_reader!(EXT_POOL)?;
|
|
||||||
let ext = pool.get(in_ext)?;
|
|
||||||
cstr_ffi::set_ffi_string(ext.inner())?;
|
|
||||||
Ok(cstr_ffi::get_ffi_string())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[unsafe(no_mangle)]
|
|
||||||
pub extern "C" fn WFExtGetDottedInner(
|
|
||||||
in_ext: in_param_ty!(Token),
|
|
||||||
out_inner: out_param_ty!(CStyleString),
|
|
||||||
) -> bool {
|
|
||||||
cffi_wrapper!(|in_ext: Token| -> (out_inner: CStyleString) {
|
|
||||||
let pool = pull_reader!(EXT_POOL)?;
|
|
||||||
let ext = pool.get(in_ext)?;
|
|
||||||
cstr_ffi::set_ffi_string(&ext.dotted_inner())?;
|
|
||||||
Ok(cstr_ffi::get_ffi_string())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// endregion
|
|
||||||
|
|||||||
@@ -126,7 +126,7 @@ fn run_ext_unlink(
|
|||||||
for index in exts {
|
for index in exts {
|
||||||
program.link_ext(scope, index)?;
|
program.link_ext(scope, index)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
println!("File extension now is unlinked.");
|
println!("File extension now is unlinked.");
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -139,9 +139,12 @@ fn run_ext_list(
|
|||||||
// Fetch info
|
// Fetch info
|
||||||
let mut ext_list: HashMap<String, Option<String>> = HashMap::new();
|
let mut ext_list: HashMap<String, Option<String>> = HashMap::new();
|
||||||
for index in 0..program.exts_len() {
|
for index in 0..program.exts_len() {
|
||||||
let ext = program.get_ext(index)?;
|
let self_ext_status = program.resolve_ext(index)?;
|
||||||
let status = program.query_ext(view, index)?;
|
let status = program.query_ext(view, index)?;
|
||||||
ext_list.insert(ext.dotted_inner(), status.map(|s| s.get_name().to_string()));
|
ext_list.insert(
|
||||||
|
self_ext_status.get_dotted_ext(),
|
||||||
|
status.map(|s| s.get_name().to_string()),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Output by styles
|
// Output by styles
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
# WFassoc Core
|
# WFassoc Core
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
This crate provides low level API and high level API for manipulating Windows file associations at the same time.
|
This crate provides low level API and high level API for manipulating Windows file associations at the same time.
|
||||||
For the convenient use of this project, the root module of this crate re-expose high level API.
|
For the convenient use of this project, the root module of this crate re-expose high level API.
|
||||||
So programmers can directly use them.
|
So programmers can directly use them.
|
||||||
@@ -10,3 +12,21 @@ Oppositely, for visiting low level API, please use `lowlevel` module.
|
|||||||
|
|
||||||
If you are a programmer who want to take a deep into the internal implementations,
|
If you are a programmer who want to take a deep into the internal implementations,
|
||||||
see `win32` module and its submodules for detail.
|
see `win32` module and its submodules for detail.
|
||||||
|
|
||||||
|
## Test Notes
|
||||||
|
|
||||||
|
Some tests of this crate may be dangerous because they need to manipulate Windows Registry.
|
||||||
|
So it is highlt recommend that run these tests in sandbox environment.
|
||||||
|
In detailed words, you should run `cargo test --no-run` to build all test first,
|
||||||
|
then fetch the path to executable tests according to this command shown on console.
|
||||||
|
Then execute these executable tests in your sandbox for testing this crate.
|
||||||
|
Additionally, some tests also need Administration permission for testing,
|
||||||
|
because it requires write permission in HKLM.
|
||||||
|
|
||||||
|
If you do not test it with sandbox and administrative environment,
|
||||||
|
test program will assert paniked and tell you how to resolve these issues.
|
||||||
|
|
||||||
|
The reason why do not run `cargo test` in sandbox environment directly,
|
||||||
|
is that `cargo` can not find built tests located in host machine.
|
||||||
|
It will try to fetch all dependencies again and rebuild test in sandbox entirely.
|
||||||
|
So we use this complex way for testing.
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ mod schema;
|
|||||||
mod program;
|
mod program;
|
||||||
|
|
||||||
pub use schema::{Schema, SchemaError};
|
pub use schema::{Schema, SchemaError};
|
||||||
pub use program::{Program, ParseProgramError, ProgramError, ProgramExtStatus};
|
pub use program::{Program, ParseProgramError, ProgramError, ProgramSelfExtStatus, ProgramExtStatus};
|
||||||
pub use lowlevel::{Scope, View};
|
pub use lowlevel::{Scope, View};
|
||||||
|
|
||||||
// endregion
|
// endregion
|
||||||
|
|||||||
@@ -44,6 +44,8 @@ pub enum ParseProgramError {
|
|||||||
pub enum ProgramError {
|
pub enum ProgramError {
|
||||||
#[error("{0}")]
|
#[error("{0}")]
|
||||||
Lowlevel(#[from] lowlevel::Error),
|
Lowlevel(#[from] lowlevel::Error),
|
||||||
|
#[error("{0}")]
|
||||||
|
LoadIconRc(#[from] concept::LoadIconRcError),
|
||||||
#[error("given index is invalid")]
|
#[error("given index is invalid")]
|
||||||
BadIndex,
|
BadIndex,
|
||||||
}
|
}
|
||||||
@@ -53,10 +55,12 @@ pub enum ProgramError {
|
|||||||
// region: Program
|
// region: Program
|
||||||
|
|
||||||
/// Program is a complete and immutable program representer
|
/// Program is a complete and immutable program representer
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct Program {
|
pub struct Program {
|
||||||
app_paths_key: lowlevel::AppPathsKey,
|
app_paths_key: lowlevel::AppPathsKey,
|
||||||
applications_key: lowlevel::ApplicationsKey,
|
applications_key: lowlevel::ApplicationsKey,
|
||||||
app_path: String,
|
app_path: String,
|
||||||
|
app_file_name: String,
|
||||||
app_dir_path: String,
|
app_dir_path: String,
|
||||||
name: Option<Arc<ProgramStr>>,
|
name: Option<Arc<ProgramStr>>,
|
||||||
icon: Option<Arc<ProgramIcon>>,
|
icon: Option<Arc<ProgramIcon>>,
|
||||||
@@ -254,6 +258,7 @@ impl Program {
|
|||||||
app_paths_key,
|
app_paths_key,
|
||||||
applications_key,
|
applications_key,
|
||||||
app_path,
|
app_path,
|
||||||
|
app_file_name,
|
||||||
app_dir_path,
|
app_dir_path,
|
||||||
name,
|
name,
|
||||||
icon,
|
icon,
|
||||||
@@ -268,39 +273,80 @@ impl Program {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Program {
|
impl Program {
|
||||||
pub fn resolve_name(&self) -> Result<Option<String>, ProgramError> {
|
pub fn resolve_name(&self) -> Result<String, ProgramError> {
|
||||||
Ok(self
|
// Fecch from user specified name first
|
||||||
|
let name = self
|
||||||
.name
|
.name
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|name| name.inner.extract().ok())
|
.map(|name| name.inner.extract().ok())
|
||||||
.flatten())
|
.flatten();
|
||||||
|
if let Some(name) = name {
|
||||||
|
return Ok(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO:
|
||||||
|
// Fetch it from executable manifest file.
|
||||||
|
|
||||||
|
// Finally fallback to use executable name.
|
||||||
|
Ok(self.app_file_name.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn resolve_icon(&self) -> Result<Option<concept::IconRc>, ProgramError> {
|
pub fn resolve_icon(&self) -> Result<concept::IconRc, ProgramError> {
|
||||||
Ok(self
|
// Fetch from user specified icon first
|
||||||
|
let icon = self
|
||||||
.icon
|
.icon
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|icon| icon.inner.extract(concept::IconSizeKind::Small).ok())
|
.map(|icon| icon.inner.extract(concept::IconSizeKind::Small).ok())
|
||||||
.flatten())
|
.flatten();
|
||||||
|
if let Some(icon) = icon {
|
||||||
|
return Ok(icon);
|
||||||
|
}
|
||||||
|
// Fetch from the first icon of executable instead.
|
||||||
|
let icon = concept::IconRc::new(&self.app_path, 0, concept::IconSizeKind::Small).ok();
|
||||||
|
if let Some(icon) = icon {
|
||||||
|
return Ok(icon);
|
||||||
|
}
|
||||||
|
// Finally fallback to use system default executable icon.
|
||||||
|
Ok(concept::IconRc::GENERIC_APPLICATION(
|
||||||
|
concept::IconSizeKind::Small,
|
||||||
|
)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn exts_len(&self) -> usize {
|
pub fn exts_len(&self) -> usize {
|
||||||
self.ext_keys.len()
|
self.ext_keys.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_ext(&self, index: usize) -> Result<&concept::Ext, ProgramError> {
|
|
||||||
match self.ext_keys.get(index) {
|
|
||||||
Some(program_key) => {
|
|
||||||
let ext_key = &program_key.ext_key;
|
|
||||||
Ok(ext_key.inner())
|
|
||||||
}
|
|
||||||
None => Err(ProgramError::BadIndex),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn find_ext(&self, body: &str) -> Option<usize> {
|
pub fn find_ext(&self, body: &str) -> Option<usize> {
|
||||||
self.ext_keys_map.get(body).copied()
|
self.ext_keys_map.get(body).copied()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn resolve_ext(&self, index: usize) -> Result<ProgramSelfExtStatus, ProgramError> {
|
||||||
|
// Fetch data
|
||||||
|
let progid_ext_key = self.ext_keys.get(index).ok_or(ProgramError::BadIndex)?;
|
||||||
|
|
||||||
|
// Try resolving name with string resource first,
|
||||||
|
// and fallback to ProgId verbatim.
|
||||||
|
let name = progid_ext_key
|
||||||
|
.name
|
||||||
|
.inner
|
||||||
|
.extract()
|
||||||
|
.unwrap_or(progid_ext_key.progid_key.inner().to_string());
|
||||||
|
// Try to fetch icon, and fallback to system default file icon.
|
||||||
|
let icon = progid_ext_key
|
||||||
|
.icon
|
||||||
|
.inner
|
||||||
|
.extract(concept::IconSizeKind::Small)
|
||||||
|
.unwrap_or(concept::IconRc::GENERIC_DOCUMENT(
|
||||||
|
concept::IconSizeKind::Small,
|
||||||
|
)?);
|
||||||
|
|
||||||
|
// Okey, return it
|
||||||
|
Ok(ProgramSelfExtStatus::new(
|
||||||
|
progid_ext_key.ext_key.inner().clone(),
|
||||||
|
name,
|
||||||
|
icon,
|
||||||
|
))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Program {
|
impl Program {
|
||||||
@@ -356,6 +402,7 @@ impl Program {
|
|||||||
|
|
||||||
// Add this progid to file extension "open with" list.
|
// Add this progid to file extension "open with" list.
|
||||||
let ext_key = &mut program_key.ext_key;
|
let ext_key = &mut program_key.ext_key;
|
||||||
|
ext_key.ensure(scope)?;
|
||||||
ext_key.add_into_open_with_progids(scope, progid_key.inner())?;
|
ext_key.add_into_open_with_progids(scope, progid_key.inner())?;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -392,9 +439,12 @@ impl Program {
|
|||||||
// there is no need to reset the default open way of file extension.
|
// there is no need to reset the default open way of file extension.
|
||||||
// So we simply remove it from "open with" list.
|
// So we simply remove it from "open with" list.
|
||||||
|
|
||||||
// Remove this ProgId from file extension "open with" list.
|
// Remove this ProgId from file extension "open with" list,
|
||||||
|
// if this file extension is existing
|
||||||
let ext_key = &mut program_key.ext_key;
|
let ext_key = &mut program_key.ext_key;
|
||||||
ext_key.remove_from_open_with_progids(scope, progid_key.inner())?;
|
if ext_key.is_exist(scope.into())? {
|
||||||
|
ext_key.remove_from_open_with_progids(scope, progid_key.inner())?;
|
||||||
|
}
|
||||||
|
|
||||||
// Delete ProgId subkey
|
// Delete ProgId subkey
|
||||||
progid_key.delete(scope)?;
|
progid_key.delete(scope)?;
|
||||||
@@ -535,11 +585,14 @@ impl Program {
|
|||||||
.flatten();
|
.flatten();
|
||||||
}
|
}
|
||||||
let name = name.unwrap_or(progid_key.inner().to_string());
|
let name = name.unwrap_or(progid_key.inner().to_string());
|
||||||
// Now try to fetch icon.
|
// Now try to fetch icon and fallback to system default file icon.
|
||||||
let icon = progid_key
|
let icon = progid_key
|
||||||
.get_default_icon(view)?
|
.get_default_icon(view)?
|
||||||
.map(|ico| ico.extract(concept::IconSizeKind::Small).ok())
|
.map(|ico| ico.extract(concept::IconSizeKind::Small).ok())
|
||||||
.flatten();
|
.flatten()
|
||||||
|
.unwrap_or(concept::IconRc::GENERIC_DOCUMENT(
|
||||||
|
concept::IconSizeKind::Small,
|
||||||
|
)?);
|
||||||
|
|
||||||
// Okey, return it.
|
// Okey, return it.
|
||||||
Ok(Some(ProgramExtStatus::new(name, icon)))
|
Ok(Some(ProgramExtStatus::new(name, icon)))
|
||||||
@@ -589,16 +642,58 @@ struct ProgramProgIdExtKey {
|
|||||||
|
|
||||||
// region: Exposed Stuff
|
// region: Exposed Stuff
|
||||||
|
|
||||||
|
/// Exposed struct representing this program provided method for opening specific file extension.
|
||||||
|
///
|
||||||
|
/// The data including the extension name, diaplay name and icon.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ProgramSelfExtStatus {
|
||||||
|
ext: concept::Ext,
|
||||||
|
name: String,
|
||||||
|
icon: concept::IconRc,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ProgramSelfExtStatus {
|
||||||
|
fn new(ext: concept::Ext, name: String, icon: concept::IconRc) -> Self {
|
||||||
|
Self { ext, name, icon }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the extension name without leading dot like `jpg`.
|
||||||
|
pub fn get_ext(&self) -> &str {
|
||||||
|
self.ext.inner()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the extension name with leading dot like `.jpg`.
|
||||||
|
pub fn get_dotted_ext(&self) -> String {
|
||||||
|
self.ext.dotted_inner()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the display name of this program.
|
||||||
|
///
|
||||||
|
/// The program provided display name will be used firstly.
|
||||||
|
/// If this program has no display name, the stringified ProgId will be used instead.
|
||||||
|
pub fn get_name(&self) -> &str {
|
||||||
|
self.name.as_str()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the icon of this program.
|
||||||
|
///
|
||||||
|
/// Due to the icon is optional, if there is no icon, return None.
|
||||||
|
pub fn get_icon(&self) -> &concept::IconRc {
|
||||||
|
&self.icon
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Exposed struct representing the default associated program of specific file extension.
|
/// Exposed struct representing the default associated program of specific file extension.
|
||||||
///
|
///
|
||||||
/// The data including the diaplay name and icon.
|
/// The data including the diaplay name and icon.
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct ProgramExtStatus {
|
pub struct ProgramExtStatus {
|
||||||
name: String,
|
name: String,
|
||||||
icon: Option<concept::IconRc>,
|
icon: concept::IconRc,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ProgramExtStatus {
|
impl ProgramExtStatus {
|
||||||
fn new(name: String, icon: Option<concept::IconRc>) -> Self {
|
fn new(name: String, icon: concept::IconRc) -> Self {
|
||||||
Self { name, icon }
|
Self { name, icon }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -613,8 +708,8 @@ impl ProgramExtStatus {
|
|||||||
/// Get the icon of this program.
|
/// Get the icon of this program.
|
||||||
///
|
///
|
||||||
/// Due to the icon is optional, if there is no icon, return None.
|
/// Due to the icon is optional, if there is no icon, return None.
|
||||||
pub fn get_icon(&self) -> Option<&concept::IconRc> {
|
pub fn get_icon(&self) -> &concept::IconRc {
|
||||||
self.icon.as_ref()
|
&self.icon
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -156,7 +156,7 @@ impl Schema {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn get_behavior(&self) -> Option<&str> {
|
pub(super) fn get_behavior(&self) -> Option<&str> {
|
||||||
self.icon.as_ref().map(|v| v.as_str())
|
self.behavior.as_ref().map(|v| v.as_str())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn get_strs(&self) -> &HashMap<String, String> {
|
pub(super) fn get_strs(&self) -> &HashMap<String, String> {
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ impl AppPathsKey {
|
|||||||
Ok(key.is_some())
|
Ok(key.is_some())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Ensure this application key is presented in App Paths.
|
/// Ensure this application key is presented in App Paths key.
|
||||||
///
|
///
|
||||||
/// Return true if we newly create this key,
|
/// Return true if we newly create this key,
|
||||||
/// otherwise false indicating there already is an existing key.
|
/// otherwise false indicating there already is an existing key.
|
||||||
@@ -79,15 +79,16 @@ impl AppPathsKey {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Delete this application key from App Paths.
|
/// Delete this application key from App Paths key.
|
||||||
///
|
///
|
||||||
/// If there is no such key in App Paths,
|
/// Return true if we successfully delete this key,
|
||||||
/// this function does nothing.
|
/// otherwise false indicating there is no such key (already deleted).
|
||||||
pub fn delete(&mut self, scope: Scope) -> Result<()> {
|
pub fn delete(&mut self, scope: Scope) -> Result<bool> {
|
||||||
let key = self.open_scope_for_write(scope)?;
|
let key = self.open_scope_for_write(scope)?;
|
||||||
key.parent_key
|
Ok(regext::arbitrarily_delete_subkey_all(
|
||||||
.delete_subkey_all(regext::blank_path_guard(self.key_name.inner())?)?;
|
&key.parent_key,
|
||||||
Ok(())
|
regext::blank_path_guard(self.key_name.inner())?,
|
||||||
|
)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn open_scope_for_getter(&self, scope: Scope) -> Result<RegKey> {
|
fn open_scope_for_getter(&self, scope: Scope) -> Result<RegKey> {
|
||||||
|
|||||||
@@ -68,6 +68,10 @@ impl ApplicationsKey {
|
|||||||
Ok(key.is_some())
|
Ok(key.is_some())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Ensure this application key is presented in Applications key.
|
||||||
|
///
|
||||||
|
/// Return true if we newly create this key,
|
||||||
|
/// otherwise false indicating there already is an existing key.
|
||||||
pub fn ensure(&mut self, scope: Scope) -> Result<bool> {
|
pub fn ensure(&mut self, scope: Scope) -> Result<bool> {
|
||||||
let key = self.open_scope_for_write(scope)?;
|
let key = self.open_scope_for_write(scope)?;
|
||||||
if let None = key.this_key {
|
if let None = key.this_key {
|
||||||
@@ -81,11 +85,16 @@ impl ApplicationsKey {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn delete(&mut self, scope: Scope) -> Result<()> {
|
/// Delete this application key from Applications key.
|
||||||
|
///
|
||||||
|
/// Return true if we successfully delete this key,
|
||||||
|
/// otherwise false indicating there is no such key (already deleted).
|
||||||
|
pub fn delete(&mut self, scope: Scope) -> Result<bool> {
|
||||||
let key = self.open_scope_for_write(scope)?;
|
let key = self.open_scope_for_write(scope)?;
|
||||||
key.parent_key
|
Ok(regext::arbitrarily_delete_subkey_all(
|
||||||
.delete_subkey_all(regext::blank_path_guard(self.key_name.inner())?)?;
|
&key.parent_key,
|
||||||
Ok(())
|
regext::blank_path_guard(self.key_name.inner())?,
|
||||||
|
)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
// YYC MARK:
|
// YYC MARK:
|
||||||
@@ -176,7 +185,7 @@ impl ApplicationsKey {
|
|||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
// Delete shell and its all subkey.
|
// Delete shell and its all subkey.
|
||||||
key.delete_subkey_all(Self::NAMEOF_SHELL_VERB_PART1)?;
|
regext::arbitrarily_delete_subkey_all(&key, Self::NAMEOF_SHELL_VERB_PART1)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -217,7 +226,7 @@ impl ApplicationsKey {
|
|||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
// Delete shell and its all subkey.
|
// Delete shell and its all subkey.
|
||||||
key.delete_subkey_all(Self::NAMEOF_DEFAULT_ICON_PART1)?;
|
regext::arbitrarily_delete_subkey_all(&key, Self::NAMEOF_DEFAULT_ICON_PART1)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -248,7 +257,7 @@ impl ApplicationsKey {
|
|||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
// Delete this key
|
// Delete this key
|
||||||
key.delete_value(Self::NAMEOF_FRIENDLY_APP_NAME)?;
|
regext::arbitrarily_delete_value(&key, Self::NAMEOF_FRIENDLY_APP_NAME)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -298,7 +307,7 @@ impl ApplicationsKey {
|
|||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
// Delete this subkey.
|
// Delete this subkey.
|
||||||
key.delete_subkey_all(Self::NAMEOF_SUPPORTED_TYPES)?;
|
regext::arbitrarily_delete_subkey_all(&key, Self::NAMEOF_SUPPORTED_TYPES)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -320,7 +329,7 @@ impl ApplicationsKey {
|
|||||||
if flag {
|
if flag {
|
||||||
key.set_value(Self::NAMEOF_NO_OPEN_WITH, &"")?;
|
key.set_value(Self::NAMEOF_NO_OPEN_WITH, &"")?;
|
||||||
} else {
|
} else {
|
||||||
key.delete_value(Self::NAMEOF_NO_OPEN_WITH)?;
|
regext::arbitrarily_delete_value(&key, Self::NAMEOF_NO_OPEN_WITH)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,6 +66,10 @@ impl ExtKey {
|
|||||||
Ok(key.is_some())
|
Ok(key.is_some())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Ensure this file extension key is presented in Classes key.
|
||||||
|
///
|
||||||
|
/// Return true if we newly create this key,
|
||||||
|
/// otherwise false indicating there already is an existing key.
|
||||||
pub fn ensure(&mut self, scope: Scope) -> Result<bool> {
|
pub fn ensure(&mut self, scope: Scope) -> Result<bool> {
|
||||||
let key = self.open_scope_for_write(scope)?;
|
let key = self.open_scope_for_write(scope)?;
|
||||||
if let None = key.this_key {
|
if let None = key.this_key {
|
||||||
@@ -79,16 +83,21 @@ impl ExtKey {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn delete(&mut self, scope: Scope) -> Result<()> {
|
/// Delete this file extension key from Classes key.
|
||||||
|
///
|
||||||
|
/// Return true if we successfully delete this key,
|
||||||
|
/// otherwise false indicating there is no such key (already deleted).
|
||||||
|
pub fn delete(&mut self, scope: Scope) -> Result<bool> {
|
||||||
let key = self.open_scope_for_write(scope)?;
|
let key = self.open_scope_for_write(scope)?;
|
||||||
key.parent_key
|
Ok(regext::arbitrarily_delete_subkey_all(
|
||||||
.delete_subkey_all(regext::blank_path_guard(self.ext.dotted_inner())?)?;
|
&key.parent_key,
|
||||||
Ok(())
|
regext::blank_path_guard(self.ext.dotted_inner())?,
|
||||||
|
)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
// YYC MARK:
|
// YYC MARK:
|
||||||
// Reference: https://learn.microsoft.com/en-us/windows/win32/shell/fa-file-types#setting-optional-subkeys-and-file-type-extension-attributes
|
// Reference: https://learn.microsoft.com/en-us/windows/win32/shell/fa-file-types#setting-optional-subkeys-and-file-type-extension-attributes
|
||||||
|
|
||||||
// TODO:
|
// TODO:
|
||||||
// We do not support "Content Type" and "PerceivedType"
|
// We do not support "Content Type" and "PerceivedType"
|
||||||
// because current interface are enough to use,
|
// because current interface are enough to use,
|
||||||
@@ -127,7 +136,7 @@ impl ExtKey {
|
|||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
// Delete this key
|
// Delete this key
|
||||||
key.delete_value(Self::NAMEOF_DEFAULT)?;
|
regext::arbitrarily_delete_value(&key, Self::NAMEOF_DEFAULT)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -205,7 +214,10 @@ impl ExtKey {
|
|||||||
None => return Ok(()),
|
None => return Ok(()),
|
||||||
};
|
};
|
||||||
// Remove given key
|
// Remove given key
|
||||||
open_with_progids_key.delete_value(pid.to_string())?;
|
regext::arbitrarily_delete_value(
|
||||||
|
&open_with_progids_key,
|
||||||
|
regext::blank_path_guard(pid.to_string())?,
|
||||||
|
)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,6 +66,10 @@ impl ProgIdKey {
|
|||||||
Ok(key.is_some())
|
Ok(key.is_some())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Ensure this ProgId key is presented in Classes key.
|
||||||
|
///
|
||||||
|
/// Return true if we newly create this key,
|
||||||
|
/// otherwise false indicating there already is an existing key.
|
||||||
pub fn ensure(&mut self, scope: Scope) -> Result<bool> {
|
pub fn ensure(&mut self, scope: Scope) -> Result<bool> {
|
||||||
let key = self.open_scope_for_write(scope)?;
|
let key = self.open_scope_for_write(scope)?;
|
||||||
if let None = key.this_key {
|
if let None = key.this_key {
|
||||||
@@ -79,18 +83,23 @@ impl ProgIdKey {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn delete(&mut self, scope: Scope) -> Result<()> {
|
/// Delete this ProgId key from Classes key.
|
||||||
|
///
|
||||||
|
/// Return true if we successfully delete this key,
|
||||||
|
/// otherwise false indicating there is no such key (already deleted).
|
||||||
|
pub fn delete(&mut self, scope: Scope) -> Result<bool> {
|
||||||
let key = self.open_scope_for_write(scope)?;
|
let key = self.open_scope_for_write(scope)?;
|
||||||
key.parent_key
|
Ok(regext::arbitrarily_delete_subkey_all(
|
||||||
.delete_subkey_all(regext::blank_path_guard(self.progid.to_string())?)?;
|
&key.parent_key,
|
||||||
Ok(())
|
regext::blank_path_guard(self.progid.to_string())?,
|
||||||
|
)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
// YYC MARK:
|
// YYC MARK:
|
||||||
// Reference: https://learn.microsoft.com/en-us/windows/win32/shell/fa-progids#programmatic-identifier-elements-used-by-file-associations
|
// Reference: https://learn.microsoft.com/en-us/windows/win32/shell/fa-progids#programmatic-identifier-elements-used-by-file-associations
|
||||||
|
|
||||||
// TODO:
|
// TODO:
|
||||||
// Currently we only support (Default), FriendlyTypeName and DefaultIcon
|
// Currently we only support (Default), FriendlyTypeName and DefaultIcon
|
||||||
// to just cover the basic usage.
|
// to just cover the basic usage.
|
||||||
// We may expand these in future.
|
// We may expand these in future.
|
||||||
|
|
||||||
@@ -129,7 +138,7 @@ impl ProgIdKey {
|
|||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
// Delete this key
|
// Delete this key
|
||||||
key.delete_value(Self::NAMEOF_DEFAULT)?;
|
regext::arbitrarily_delete_value(&key, Self::NAMEOF_DEFAULT)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -209,7 +218,7 @@ impl ProgIdKey {
|
|||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
// Delete shell and its all subkey.
|
// Delete shell and its all subkey.
|
||||||
key.delete_subkey_all(Self::NAMEOF_SHELL_VERB_PART1)?;
|
regext::arbitrarily_delete_subkey_all(&key, Self::NAMEOF_SHELL_VERB_PART1)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -243,7 +252,7 @@ impl ProgIdKey {
|
|||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
// Delete this key
|
// Delete this key
|
||||||
key.delete_value(Self::NAMEOF_FRIENDLY_TYPE_NAME)?;
|
regext::arbitrarily_delete_value(&key, Self::NAMEOF_FRIENDLY_TYPE_NAME)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -284,7 +293,7 @@ impl ProgIdKey {
|
|||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
// Delete shell and its all subkey.
|
// Delete shell and its all subkey.
|
||||||
key.delete_subkey_all(Self::NAMEOF_DEFAULT_ICON_PART1)?;
|
regext::arbitrarily_delete_subkey_all(&key, Self::NAMEOF_DEFAULT_ICON_PART1)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -510,6 +510,20 @@ pub struct IconRc {
|
|||||||
icon: HICON,
|
icon: HICON,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl IconRc {
|
||||||
|
/// Get the icon preset representing generic document.
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
pub fn GENERIC_DOCUMENT(kind: IconSizeKind) -> Result<Self, LoadIconRcError> {
|
||||||
|
Self::new("shell32.dll", 0, kind)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the icon preset representing generic executable.
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
pub fn GENERIC_APPLICATION(kind: IconSizeKind) -> Result<Self, LoadIconRcError> {
|
||||||
|
Self::new("imageres.dll", 0, kind)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl IconRc {
|
impl IconRc {
|
||||||
/// Load icon from executable or `.ico` file.
|
/// Load icon from executable or `.ico` file.
|
||||||
///
|
///
|
||||||
@@ -867,16 +881,19 @@ pub struct Verb {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Verb {
|
impl Verb {
|
||||||
|
/// Get the verb representing Open.
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
pub fn OPEN() -> Self {
|
pub fn OPEN() -> Self {
|
||||||
Verb::new("open").expect("unexpected bad verb")
|
Verb::new("open").expect("unexpected bad verb")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the verb representing Edit.
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
pub fn EDIT() -> Self {
|
pub fn EDIT() -> Self {
|
||||||
Verb::new("edit").expect("unexpected bad verb")
|
Verb::new("edit").expect("unexpected bad verb")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the verb representing Play.
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
pub fn PLAY() -> Self {
|
pub fn PLAY() -> Self {
|
||||||
Verb::new("play").expect("unexpected bad verb")
|
Verb::new("play").expect("unexpected bad verb")
|
||||||
|
|||||||
@@ -61,12 +61,60 @@ pub fn try_get_value<T: FromRegValue, N: AsRef<OsStr>>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Delete all tree of given path of given key anyway.
|
||||||
|
///
|
||||||
|
/// This function was invented to fix the shortcoming of [RegKey::delete_subkey_all].
|
||||||
|
/// This function always delete given path of given key no matter it is existing.
|
||||||
|
/// Oppositely, [RegKey::delete_subkey_all] will return error if there is no such path.
|
||||||
|
///
|
||||||
|
/// Return true if we successfully delete this key,
|
||||||
|
/// otherwise false indicating there is no such key (already deleted).
|
||||||
|
pub fn arbitrarily_delete_subkey_all<P: AsRef<OsStr>>(
|
||||||
|
regkey: &RegKey,
|
||||||
|
path: P,
|
||||||
|
) -> std::io::Result<bool> {
|
||||||
|
match regkey.delete_subkey_all(path) {
|
||||||
|
Ok(()) => Ok(true),
|
||||||
|
Err(e) => match e.raw_os_error() {
|
||||||
|
Some(errno) => match errno as u32 {
|
||||||
|
ERROR_FILE_NOT_FOUND => Ok(false),
|
||||||
|
_ => Err(e),
|
||||||
|
},
|
||||||
|
_ => Err(e),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Delete given value key of given key anyway.
|
||||||
|
///
|
||||||
|
/// This function was invented to fix the shortcoming of [RegKey::delete_value].
|
||||||
|
/// This function always delete given value key of given key no matter it is existing.
|
||||||
|
/// Oppositely, [RegKey::delete_value] will return error if there is no such value key.
|
||||||
|
///
|
||||||
|
/// Return true if we successfully delete this value key,
|
||||||
|
/// otherwise false indicating there is no such value key (already deleted).
|
||||||
|
pub fn arbitrarily_delete_value<N: AsRef<OsStr>>(
|
||||||
|
regkey: &RegKey,
|
||||||
|
name: N,
|
||||||
|
) -> std::io::Result<bool> {
|
||||||
|
match regkey.delete_value(name) {
|
||||||
|
Ok(()) => Ok(true),
|
||||||
|
Err(e) => match e.raw_os_error() {
|
||||||
|
Some(errno) => match errno as u32 {
|
||||||
|
ERROR_FILE_NOT_FOUND => Ok(false),
|
||||||
|
_ => Err(e),
|
||||||
|
},
|
||||||
|
_ => Err(e),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Get the name of only subkey in given key.
|
/// Get the name of only subkey in given key.
|
||||||
///
|
///
|
||||||
/// If there is only one subkey in given key, the return value is its name.
|
/// If there is only one subkey in given key, the return value is its name.
|
||||||
/// If there is no any subkey, or has multiple subkeys, return None instead.
|
/// If there is no any subkey, or has multiple subkeys, return None instead.
|
||||||
/// If error occurs when fetching data, return Err(_).
|
/// If error occurs when fetching data, return Err(_).
|
||||||
///
|
///
|
||||||
/// This is usually used for ShellVerb fetching.
|
/// This is usually used for ShellVerb fetching.
|
||||||
pub fn get_sole_subkey_name(regkey: &RegKey) -> std::io::Result<Option<String>> {
|
pub fn get_sole_subkey_name(regkey: &RegKey) -> std::io::Result<Option<String>> {
|
||||||
let mut subkey_enumerator = regkey.enum_keys();
|
let mut subkey_enumerator = regkey.enum_keys();
|
||||||
@@ -85,7 +133,7 @@ pub fn get_sole_subkey_name(regkey: &RegKey) -> std::io::Result<Option<String>>
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Get the name list of all "string" subkeys in given key.
|
/// Get the name list of all "string" subkeys in given key.
|
||||||
///
|
///
|
||||||
/// This is usually used for "OpenWithProgIds" subkey.
|
/// This is usually used for "OpenWithProgIds" subkey.
|
||||||
pub fn get_all_string_subkey_names(regkey: &RegKey) -> std::io::Result<Vec<String>> {
|
pub fn get_all_string_subkey_names(regkey: &RegKey) -> std::io::Result<Vec<String>> {
|
||||||
regkey
|
regkey
|
||||||
@@ -105,11 +153,14 @@ pub fn get_all_string_subkey_names(regkey: &RegKey) -> std::io::Result<Vec<Strin
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Delete all contents, including values and subkeys of given key.
|
/// Delete all contents, including values and subkeys of given key.
|
||||||
///
|
///
|
||||||
/// Deleting all contents of given key rely on giving a special parameter to [RegKey::delete_subkey_all].
|
/// Deleting all contents of given key rely on giving a special parameter to [RegKey::delete_subkey_all].
|
||||||
/// This is very dangerous and may be used by accident.
|
/// This is very dangerous and may be used by accident.
|
||||||
/// So I create this to explicitly indicate this behavior and avoid any mis-type in code.
|
/// So I create this to explicitly indicate this behavior and avoid any mis-type in code.
|
||||||
pub fn clean_all_contents(regkey: &RegKey) -> std::io::Result<()> {
|
pub fn clean_all_contents(regkey: &RegKey) -> std::io::Result<()> {
|
||||||
|
// There is no possibility that this key do not existing,
|
||||||
|
// because what we are cleaning is self content.
|
||||||
|
// So directly use delete_subkey_all is okey.
|
||||||
regkey.delete_subkey_all("")
|
regkey.delete_subkey_all("")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -135,7 +186,7 @@ impl BlankPathError {
|
|||||||
/// Because it will cause unexpected behavior that returning key self, rather than subkey.
|
/// Because it will cause unexpected behavior that returning key self, rather than subkey.
|
||||||
/// This is VERY dangerous especially for those registry delete functions.
|
/// This is VERY dangerous especially for those registry delete functions.
|
||||||
/// So I create this function to prevent any harmful blank path was passed into registry function.
|
/// So I create this function to prevent any harmful blank path was passed into registry function.
|
||||||
///
|
///
|
||||||
/// This function MUST be used for the value, whose content can not be confirmed at compile time,
|
/// This function MUST be used for the value, whose content can not be confirmed at compile time,
|
||||||
/// and it will be passed to get/set value, or create/delete key functions.
|
/// and it will be passed to get/set value, or create/delete key functions.
|
||||||
pub fn blank_path_guard<P: AsRef<OsStr>>(path: P) -> std::result::Result<P, BlankPathError> {
|
pub fn blank_path_guard<P: AsRef<OsStr>>(path: P) -> std::result::Result<P, BlankPathError> {
|
||||||
|
|||||||
27
wfassoc/tests/common.rs
Normal file
27
wfassoc/tests/common.rs
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
/// Check whether we are in sandbox.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panic if we are not in sandbox (we can not perform dangerous test).
|
||||||
|
/// Return if we are in sandbox environment.
|
||||||
|
pub fn check_sandbox() {
|
||||||
|
assert!(
|
||||||
|
std::env::var("SANDBOXIE").is_ok(),
|
||||||
|
concat!(
|
||||||
|
"Non-sandbox environment detected. ",
|
||||||
|
"Executing these tests in non-sandbox environment is VERY dangerous. ",
|
||||||
|
"Please set \"SANDBOXIE\" environment variable to explicitly indicate you are running these tests in sandbox environment."
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn check_privilege() {
|
||||||
|
assert!(
|
||||||
|
wfassoc::win32::utilities::has_privilege(),
|
||||||
|
concat!(
|
||||||
|
"You are running test without privilege. ",
|
||||||
|
"These tests must be run with some privilege because it need to manipulate Windows Registry. ",
|
||||||
|
"Please give it privilege in your sandbox environment."
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -186,6 +186,9 @@ fn test_str_ref_str() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_icon_rc() {
|
fn test_icon_rc() {
|
||||||
|
fn const_tester(icon: Result<IconRc, LoadIconRcError>) {
|
||||||
|
assert!(icon.is_ok())
|
||||||
|
}
|
||||||
fn ok_tester(file: &str, index: u32) {
|
fn ok_tester(file: &str, index: u32) {
|
||||||
let icon = IconRc::new(file, index, IconSizeKind::Small);
|
let icon = IconRc::new(file, index, IconSizeKind::Small);
|
||||||
assert!(icon.is_ok())
|
assert!(icon.is_ok())
|
||||||
@@ -195,6 +198,9 @@ fn test_icon_rc() {
|
|||||||
assert!(icon.is_err())
|
assert!(icon.is_err())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test 2 const value
|
||||||
|
const_tester(IconRc::GENERIC_APPLICATION(IconSizeKind::Small));
|
||||||
|
const_tester(IconRc::GENERIC_APPLICATION(IconSizeKind::Small));
|
||||||
// We pick it from "jpegfile" ProgId
|
// We pick it from "jpegfile" ProgId
|
||||||
ok_tester("imageres.dll", 72);
|
ok_tester("imageres.dll", 72);
|
||||||
ok_tester("notepad.exe", 0);
|
ok_tester("notepad.exe", 0);
|
||||||
|
|||||||
169
wfassoc/tests/highlevel.rs
Normal file
169
wfassoc/tests/highlevel.rs
Normal file
@@ -0,0 +1,169 @@
|
|||||||
|
use wfassoc::highlevel::*;
|
||||||
|
mod common;
|
||||||
|
|
||||||
|
static IDENTIFIER: &str = "Passoc";
|
||||||
|
static APP_PATH: &str = r"C:\Passoc\passoc.exe";
|
||||||
|
static CLSID: &str = "{59031a47-3f72-44a7-89c5-5595fe6b30ee}";
|
||||||
|
static EXT_BODY: &str = "pacfg";
|
||||||
|
|
||||||
|
fn make_valid_schema() -> Schema {
|
||||||
|
let mut schema = Schema::new();
|
||||||
|
schema.set_identifier(IDENTIFIER);
|
||||||
|
schema.set_path(APP_PATH);
|
||||||
|
schema.set_clsid(CLSID);
|
||||||
|
schema.add_str("main_name", "Passoc Application").unwrap();
|
||||||
|
schema.add_str("ext_name", "Pacfg File").unwrap();
|
||||||
|
schema.add_icon("main_icon", "notepad.exe,0").unwrap();
|
||||||
|
schema.add_icon("ext_icon", "notepad.exe,0").unwrap();
|
||||||
|
schema.add_behavior("main_behavior", "notepad.exe %1").unwrap();
|
||||||
|
schema.add_behavior("ext_behavior", "notepad.exe %1").unwrap();
|
||||||
|
schema.set_name(Some("main_name"));
|
||||||
|
schema.set_icon(Some("main_icon"));
|
||||||
|
schema.set_behavior(Some("main_behavior"));
|
||||||
|
schema.add_ext(EXT_BODY, "ext_name", "ext_icon", "ext_behavior").unwrap();
|
||||||
|
schema
|
||||||
|
}
|
||||||
|
|
||||||
|
// region: Schema
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_schema() {
|
||||||
|
common::check_sandbox();
|
||||||
|
common::check_privilege();
|
||||||
|
|
||||||
|
// valid schema -> valid program
|
||||||
|
let schema = make_valid_schema();
|
||||||
|
let rv = schema.into_program();
|
||||||
|
assert!(rv.is_ok());
|
||||||
|
|
||||||
|
// missing essential parts (schema, path and etc)
|
||||||
|
let schema = Schema::new();
|
||||||
|
let rv = schema.into_program();
|
||||||
|
assert!(rv.is_err());
|
||||||
|
|
||||||
|
// invalid path
|
||||||
|
let mut schema = make_valid_schema();
|
||||||
|
schema.set_path(r"C:\");
|
||||||
|
let rv = schema.into_program();
|
||||||
|
assert!(rv.is_err());
|
||||||
|
|
||||||
|
// prepare a schema for following test
|
||||||
|
let mut schema = Schema::new();
|
||||||
|
schema.set_identifier(IDENTIFIER);
|
||||||
|
schema.set_path(APP_PATH);
|
||||||
|
|
||||||
|
// duplicate detection on add_str
|
||||||
|
let rv = schema.add_str("k", "v1");
|
||||||
|
assert!(rv.is_ok());
|
||||||
|
let rv = schema.add_str("k", "v2");
|
||||||
|
assert!(rv.is_err());
|
||||||
|
|
||||||
|
// duplicate detection on add_icon
|
||||||
|
let rv = schema.add_icon("k", "v1");
|
||||||
|
assert!(rv.is_ok());
|
||||||
|
let rv = schema.add_icon("k", "v2");
|
||||||
|
assert!(rv.is_err());
|
||||||
|
|
||||||
|
// duplicate detection on add_behavior
|
||||||
|
let rv = schema.add_behavior("k", "v1");
|
||||||
|
assert!(rv.is_ok());
|
||||||
|
let rv = schema.add_behavior("k", "v2");
|
||||||
|
assert!(rv.is_err());
|
||||||
|
|
||||||
|
// duplicate detection on add_ext
|
||||||
|
let rv = schema.add_ext(EXT_BODY, "k", "k", "k");
|
||||||
|
assert!(rv.is_ok());
|
||||||
|
let rv = schema.add_ext(EXT_BODY, "k", "k", "k");
|
||||||
|
assert!(rv.is_err());
|
||||||
|
|
||||||
|
// ext referencing non-existent map entry
|
||||||
|
schema.add_str("k2", "v").unwrap();
|
||||||
|
schema.add_icon("k2", "v").unwrap();
|
||||||
|
let rv = schema.add_ext("pacfg2", "nonexistent", "k2", "k2");
|
||||||
|
assert!(rv.is_ok());
|
||||||
|
let rv = schema.into_program();
|
||||||
|
assert!(rv.is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
// region: Program
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_program() {
|
||||||
|
common::check_sandbox();
|
||||||
|
common::check_privilege();
|
||||||
|
|
||||||
|
fn tester(scope: Scope, view: View) {
|
||||||
|
// build program
|
||||||
|
let schema = make_valid_schema();
|
||||||
|
let rv = schema.into_program();
|
||||||
|
assert!(rv.is_ok());
|
||||||
|
let mut program = rv.unwrap();
|
||||||
|
|
||||||
|
// cleanup before test
|
||||||
|
let rv = program.unregister(scope);
|
||||||
|
assert!(rv.is_ok());
|
||||||
|
|
||||||
|
// initially not registered
|
||||||
|
let rv = program.is_registered(scope);
|
||||||
|
assert!(rv.is_ok());
|
||||||
|
assert_eq!(rv.unwrap(), false);
|
||||||
|
|
||||||
|
// register
|
||||||
|
let rv = program.register(scope);
|
||||||
|
assert!(rv.is_ok());
|
||||||
|
|
||||||
|
// should be registered now
|
||||||
|
let rv = program.is_registered(scope);
|
||||||
|
assert!(rv.is_ok());
|
||||||
|
assert_eq!(rv.unwrap(), true);
|
||||||
|
|
||||||
|
// link_ext first so ext key exists before register
|
||||||
|
let rv = program.link_ext(scope, 0);
|
||||||
|
assert!(rv.is_ok());
|
||||||
|
|
||||||
|
// query_ext after link + register
|
||||||
|
let rv = program.query_ext(view, 0);
|
||||||
|
assert!(rv.is_ok());
|
||||||
|
assert!(rv.unwrap().is_some());
|
||||||
|
|
||||||
|
// unlink_ext
|
||||||
|
let rv = program.unlink_ext(scope, 0);
|
||||||
|
assert!(rv.is_ok());
|
||||||
|
|
||||||
|
// query_ext should return None
|
||||||
|
let rv = program.query_ext(view, 0);
|
||||||
|
assert!(rv.is_ok());
|
||||||
|
assert!(rv.unwrap().is_none());
|
||||||
|
|
||||||
|
// resolve_name, resolve_icon
|
||||||
|
let rv = program.resolve_name();
|
||||||
|
assert!(rv.is_ok());
|
||||||
|
let rv = program.resolve_icon();
|
||||||
|
assert!(rv.is_ok());
|
||||||
|
|
||||||
|
// exts_len, find_ext, resolve_ext
|
||||||
|
assert_eq!(program.exts_len(), 1);
|
||||||
|
assert_eq!(program.find_ext(EXT_BODY), Some(0));
|
||||||
|
let rv = program.resolve_ext(0);
|
||||||
|
assert!(rv.is_ok());
|
||||||
|
|
||||||
|
// bad index
|
||||||
|
let rv = program.resolve_ext(1);
|
||||||
|
assert!(rv.is_err());
|
||||||
|
let rv = program.link_ext(scope, 1);
|
||||||
|
assert!(rv.is_err());
|
||||||
|
let rv = program.query_ext(view, 1);
|
||||||
|
assert!(rv.is_err());
|
||||||
|
|
||||||
|
// cleanup
|
||||||
|
let rv = program.unregister(scope);
|
||||||
|
assert!(rv.is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
tester(Scope::User, View::User);
|
||||||
|
tester(Scope::System, View::System);
|
||||||
|
}
|
||||||
|
|
||||||
|
// endregion
|
||||||
334
wfassoc/tests/lowlevel.rs
Normal file
334
wfassoc/tests/lowlevel.rs
Normal file
@@ -0,0 +1,334 @@
|
|||||||
|
use std::ops::Deref;
|
||||||
|
use std::str::FromStr;
|
||||||
|
use std::sync::LazyLock;
|
||||||
|
use wfassoc::lowlevel::*;
|
||||||
|
use wfassoc::win32::concept;
|
||||||
|
mod common;
|
||||||
|
|
||||||
|
static EXT: LazyLock<concept::Ext> = LazyLock::new(|| concept::Ext::from_str(".pacfg").unwrap());
|
||||||
|
static APP_FILE: LazyLock<concept::FileName> =
|
||||||
|
LazyLock::new(|| concept::FileName::from_str("passoc.exe").unwrap());
|
||||||
|
static PROG_ID: LazyLock<LosseProgId> = LazyLock::new(|| "Passoc.Pacfg".into());
|
||||||
|
static ICON: LazyLock<IconResVariant> =
|
||||||
|
LazyLock::new(|| r"%SystemRoot%\System32\imageres.dll,-72".into());
|
||||||
|
static VERB: LazyLock<ShellVerb> = LazyLock::new(|| {
|
||||||
|
let verb = concept::Verb::from_str("open").unwrap();
|
||||||
|
let cmdline = concept::CmdLine::from_str("notepad.exe %1").unwrap();
|
||||||
|
ShellVerb::new(verb, cmdline)
|
||||||
|
});
|
||||||
|
|
||||||
|
// region: AppPathsKey
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_app_paths_key() {
|
||||||
|
common::check_sandbox();
|
||||||
|
common::check_privilege();
|
||||||
|
|
||||||
|
static APP_PATH: &str = r"C:\Program Files\Passoc\passoc.exe";
|
||||||
|
static APP_DIR: &str = r"C:\Program Files\Passoc";
|
||||||
|
|
||||||
|
fn tester(scope: Scope) {
|
||||||
|
let mut key = AppPathsKey::new(APP_FILE.clone());
|
||||||
|
|
||||||
|
// delete and ensure
|
||||||
|
let rv = key.delete(scope);
|
||||||
|
assert!(rv.is_ok());
|
||||||
|
let rv = key.is_exist(scope);
|
||||||
|
assert!(rv.is_ok());
|
||||||
|
assert_eq!(rv.unwrap(), false);
|
||||||
|
let rv = key.ensure(scope);
|
||||||
|
assert!(rv.is_ok());
|
||||||
|
assert_eq!(rv.unwrap(), true);
|
||||||
|
let rv = key.is_exist(scope);
|
||||||
|
assert!(rv.is_ok());
|
||||||
|
assert_eq!(rv.unwrap(), true);
|
||||||
|
let rv = key.ensure(scope);
|
||||||
|
assert!(rv.is_ok());
|
||||||
|
assert_eq!(rv.unwrap(), false);
|
||||||
|
|
||||||
|
// get/set default
|
||||||
|
let rv = key.set_default(scope, APP_PATH);
|
||||||
|
assert!(rv.is_ok());
|
||||||
|
let rv = key.get_default(scope);
|
||||||
|
assert!(rv.is_ok());
|
||||||
|
assert_eq!(rv.unwrap(), APP_PATH);
|
||||||
|
|
||||||
|
// get/set path
|
||||||
|
let rv = key.set_path(scope, APP_DIR);
|
||||||
|
assert!(rv.is_ok());
|
||||||
|
let rv = key.get_path(scope);
|
||||||
|
assert!(rv.is_ok());
|
||||||
|
assert_eq!(rv.unwrap(), APP_DIR);
|
||||||
|
|
||||||
|
// clean up
|
||||||
|
let rv = key.delete(scope);
|
||||||
|
assert!(rv.is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
tester(Scope::User);
|
||||||
|
tester(Scope::System);
|
||||||
|
}
|
||||||
|
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
// region: ApplicationsKey
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_applications_key() {
|
||||||
|
common::check_sandbox();
|
||||||
|
common::check_privilege();
|
||||||
|
|
||||||
|
static FRIENDLY_APP_NAME: LazyLock<StrResVariant> =
|
||||||
|
LazyLock::new(|| "Passoc Application".into());
|
||||||
|
|
||||||
|
fn tester(scope: Scope, view: View) {
|
||||||
|
let mut key = ApplicationsKey::new(APP_FILE.clone());
|
||||||
|
|
||||||
|
// delete and ensure
|
||||||
|
let rv = key.delete(scope);
|
||||||
|
assert!(rv.is_ok());
|
||||||
|
let rv = key.is_exist(view);
|
||||||
|
assert!(rv.is_ok());
|
||||||
|
assert_eq!(rv.unwrap(), false);
|
||||||
|
let rv = key.ensure(scope);
|
||||||
|
assert!(rv.is_ok());
|
||||||
|
assert_eq!(rv.unwrap(), true);
|
||||||
|
let rv = key.is_exist(view);
|
||||||
|
assert!(rv.is_ok());
|
||||||
|
assert_eq!(rv.unwrap(), true);
|
||||||
|
let rv = key.ensure(scope);
|
||||||
|
assert!(rv.is_ok());
|
||||||
|
assert_eq!(rv.unwrap(), false);
|
||||||
|
|
||||||
|
// get/set shell verb
|
||||||
|
let rv = key.set_shell_verb(scope, Some(&VERB));
|
||||||
|
assert!(rv.is_ok());
|
||||||
|
let rv = key.get_shell_verb(view);
|
||||||
|
assert!(rv.is_ok());
|
||||||
|
assert_eq!(rv.unwrap(), Some(VERB.clone()));
|
||||||
|
let rv = key.set_shell_verb(scope, None);
|
||||||
|
assert!(rv.is_ok());
|
||||||
|
let rv = key.get_shell_verb(view);
|
||||||
|
assert!(rv.is_ok());
|
||||||
|
assert_eq!(rv.unwrap(), None);
|
||||||
|
|
||||||
|
// get/set default icon
|
||||||
|
let rv = key.set_default_icon(scope, Some(&ICON));
|
||||||
|
assert!(rv.is_ok());
|
||||||
|
let rv = key.get_default_icon(view);
|
||||||
|
assert!(rv.is_ok());
|
||||||
|
assert_eq!(rv.unwrap(), Some(ICON.clone()));
|
||||||
|
let rv = key.set_default_icon(scope, None);
|
||||||
|
assert!(rv.is_ok());
|
||||||
|
let rv = key.get_default_icon(view);
|
||||||
|
assert!(rv.is_ok());
|
||||||
|
assert_eq!(rv.unwrap(), None);
|
||||||
|
|
||||||
|
// get/set friendly app name
|
||||||
|
let rv = key.set_friendly_app_name(scope, Some(&FRIENDLY_APP_NAME));
|
||||||
|
assert!(rv.is_ok());
|
||||||
|
let rv = key.get_friendly_app_name(view);
|
||||||
|
assert!(rv.is_ok());
|
||||||
|
assert_eq!(rv.unwrap(), Some(FRIENDLY_APP_NAME.clone()));
|
||||||
|
let rv = key.set_friendly_app_name(scope, None);
|
||||||
|
assert!(rv.is_ok());
|
||||||
|
let rv = key.get_friendly_app_name(view);
|
||||||
|
assert!(rv.is_ok());
|
||||||
|
assert_eq!(rv.unwrap(), None);
|
||||||
|
|
||||||
|
// get/set supported types
|
||||||
|
let rv = key.set_supported_types(scope, Some(&vec![EXT.deref()]));
|
||||||
|
assert!(rv.is_ok());
|
||||||
|
let rv = key.get_supported_types(view);
|
||||||
|
assert!(rv.is_ok());
|
||||||
|
assert_eq!(rv.unwrap(), Some(vec![EXT.clone()]));
|
||||||
|
let rv = key.set_supported_types(scope, None);
|
||||||
|
assert!(rv.is_ok());
|
||||||
|
let rv = key.get_supported_types(view);
|
||||||
|
assert!(rv.is_ok());
|
||||||
|
assert_eq!(rv.unwrap(), None);
|
||||||
|
|
||||||
|
// get/set no open with
|
||||||
|
let rv = key.get_no_open_with(view);
|
||||||
|
assert!(rv.is_ok());
|
||||||
|
assert_eq!(rv.unwrap(), false);
|
||||||
|
let rv = key.set_no_open_with(scope, true);
|
||||||
|
assert!(rv.is_ok());
|
||||||
|
let rv = key.get_no_open_with(view);
|
||||||
|
assert!(rv.is_ok());
|
||||||
|
assert_eq!(rv.unwrap(), true);
|
||||||
|
let rv = key.set_no_open_with(scope, false);
|
||||||
|
assert!(rv.is_ok());
|
||||||
|
let rv = key.get_no_open_with(view);
|
||||||
|
assert!(rv.is_ok());
|
||||||
|
assert_eq!(rv.unwrap(), false);
|
||||||
|
|
||||||
|
// clean up
|
||||||
|
let rv = key.delete(scope);
|
||||||
|
assert!(rv.is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
tester(Scope::User, View::User);
|
||||||
|
tester(Scope::System, View::System);
|
||||||
|
}
|
||||||
|
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
// region: ExtKey
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ext_key() {
|
||||||
|
common::check_sandbox();
|
||||||
|
common::check_privilege();
|
||||||
|
|
||||||
|
fn tester(scope: Scope, view: View) {
|
||||||
|
let mut key = ExtKey::new(EXT.clone());
|
||||||
|
|
||||||
|
// delete and ensure
|
||||||
|
let rv = key.delete(scope);
|
||||||
|
assert!(rv.is_ok());
|
||||||
|
let rv = key.is_exist(view);
|
||||||
|
assert!(rv.is_ok());
|
||||||
|
assert_eq!(rv.unwrap(), false);
|
||||||
|
let rv = key.ensure(scope);
|
||||||
|
assert!(rv.is_ok());
|
||||||
|
assert_eq!(rv.unwrap(), true);
|
||||||
|
let rv = key.is_exist(view);
|
||||||
|
assert!(rv.is_ok());
|
||||||
|
assert_eq!(rv.unwrap(), true);
|
||||||
|
let rv = key.ensure(scope);
|
||||||
|
assert!(rv.is_ok());
|
||||||
|
assert_eq!(rv.unwrap(), false);
|
||||||
|
|
||||||
|
// get/set default
|
||||||
|
let rv = key.set_default(scope, Some(&PROG_ID));
|
||||||
|
assert!(rv.is_ok());
|
||||||
|
let rv = key.get_default(view);
|
||||||
|
assert!(rv.is_ok());
|
||||||
|
assert_eq!(rv.unwrap(), Some(PROG_ID.clone()));
|
||||||
|
let rv = key.set_default(scope, None);
|
||||||
|
assert!(rv.is_ok());
|
||||||
|
let rv = key.get_default(view);
|
||||||
|
assert!(rv.is_ok());
|
||||||
|
assert_eq!(rv.unwrap(), None);
|
||||||
|
|
||||||
|
// test open with progids
|
||||||
|
let rv = key.is_in_open_with_progids(view, &PROG_ID);
|
||||||
|
assert!(rv.is_ok());
|
||||||
|
assert_eq!(rv.unwrap(), false);
|
||||||
|
|
||||||
|
let rv = key.add_into_open_with_progids(scope, &PROG_ID);
|
||||||
|
assert!(rv.is_ok());
|
||||||
|
let rv = key.is_in_open_with_progids(view, &PROG_ID);
|
||||||
|
assert!(rv.is_ok());
|
||||||
|
assert_eq!(rv.unwrap(), true);
|
||||||
|
|
||||||
|
let rv = key.get_open_with_progids(view);
|
||||||
|
assert!(rv.is_ok());
|
||||||
|
assert_eq!(rv.unwrap(), Some(vec![PROG_ID.clone()]));
|
||||||
|
|
||||||
|
let rv = key.remove_from_open_with_progids(scope, &PROG_ID);
|
||||||
|
assert!(rv.is_ok());
|
||||||
|
let rv = key.is_in_open_with_progids(view, &PROG_ID);
|
||||||
|
assert!(rv.is_ok());
|
||||||
|
assert_eq!(rv.unwrap(), false);
|
||||||
|
|
||||||
|
// clean up
|
||||||
|
let rv = key.delete(scope);
|
||||||
|
assert!(rv.is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
tester(Scope::User, View::User);
|
||||||
|
tester(Scope::System, View::System);
|
||||||
|
}
|
||||||
|
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
// region: ProgIdKey
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_prog_id_key() {
|
||||||
|
common::check_sandbox();
|
||||||
|
common::check_privilege();
|
||||||
|
|
||||||
|
static LEGACY_NAME: LazyLock<StrResVariant> = LazyLock::new(|| "Passoc Pacfg File".into());
|
||||||
|
static FRIENDLY_TYPE_NAME: LazyLock<StrResVariant> =
|
||||||
|
LazyLock::new(|| "Passoc Pacfg File Type".into());
|
||||||
|
|
||||||
|
fn tester(scope: Scope, view: View) {
|
||||||
|
let mut key = ProgIdKey::new(PROG_ID.clone());
|
||||||
|
|
||||||
|
// delete and ensure
|
||||||
|
let rv = key.delete(scope);
|
||||||
|
assert!(rv.is_ok());
|
||||||
|
let rv = key.is_exist(view);
|
||||||
|
assert!(rv.is_ok());
|
||||||
|
assert_eq!(rv.unwrap(), false);
|
||||||
|
let rv = key.ensure(scope);
|
||||||
|
assert!(rv.is_ok());
|
||||||
|
assert_eq!(rv.unwrap(), true);
|
||||||
|
let rv = key.is_exist(view);
|
||||||
|
assert!(rv.is_ok());
|
||||||
|
assert_eq!(rv.unwrap(), true);
|
||||||
|
let rv = key.ensure(scope);
|
||||||
|
assert!(rv.is_ok());
|
||||||
|
assert_eq!(rv.unwrap(), false);
|
||||||
|
|
||||||
|
// get/set default (friendly name, legacy)
|
||||||
|
let rv = key.set_default(scope, Some(&LEGACY_NAME));
|
||||||
|
assert!(rv.is_ok());
|
||||||
|
let rv = key.get_default(view);
|
||||||
|
assert!(rv.is_ok());
|
||||||
|
assert_eq!(rv.unwrap(), Some(LEGACY_NAME.clone()));
|
||||||
|
let rv = key.set_default(scope, None);
|
||||||
|
assert!(rv.is_ok());
|
||||||
|
let rv = key.get_default(view);
|
||||||
|
assert!(rv.is_ok());
|
||||||
|
assert_eq!(rv.unwrap(), None);
|
||||||
|
|
||||||
|
// get/set friendly type name
|
||||||
|
let rv = key.set_friendly_type_name(scope, Some(&FRIENDLY_TYPE_NAME));
|
||||||
|
assert!(rv.is_ok());
|
||||||
|
let rv = key.get_friendly_type_name(view);
|
||||||
|
assert!(rv.is_ok());
|
||||||
|
assert_eq!(rv.unwrap(), Some(FRIENDLY_TYPE_NAME.clone()));
|
||||||
|
let rv = key.set_friendly_type_name(scope, None);
|
||||||
|
assert!(rv.is_ok());
|
||||||
|
let rv = key.get_friendly_type_name(view);
|
||||||
|
assert!(rv.is_ok());
|
||||||
|
assert_eq!(rv.unwrap(), None);
|
||||||
|
|
||||||
|
// get/set shell verb
|
||||||
|
let rv = key.set_shell_verb(scope, Some(&VERB));
|
||||||
|
assert!(rv.is_ok());
|
||||||
|
let rv = key.get_shell_verb(view);
|
||||||
|
assert!(rv.is_ok());
|
||||||
|
assert_eq!(rv.unwrap(), Some(VERB.clone()));
|
||||||
|
let rv = key.set_shell_verb(scope, None);
|
||||||
|
assert!(rv.is_ok());
|
||||||
|
let rv = key.get_shell_verb(view);
|
||||||
|
assert!(rv.is_ok());
|
||||||
|
assert_eq!(rv.unwrap(), None);
|
||||||
|
|
||||||
|
// get/set default icon
|
||||||
|
let rv = key.set_default_icon(scope, Some(&ICON));
|
||||||
|
assert!(rv.is_ok());
|
||||||
|
let rv = key.get_default_icon(view);
|
||||||
|
assert!(rv.is_ok());
|
||||||
|
assert_eq!(rv.unwrap(), Some(ICON.clone()));
|
||||||
|
let rv = key.set_default_icon(scope, None);
|
||||||
|
assert!(rv.is_ok());
|
||||||
|
let rv = key.get_default_icon(view);
|
||||||
|
assert!(rv.is_ok());
|
||||||
|
assert_eq!(rv.unwrap(), None);
|
||||||
|
|
||||||
|
// clean up
|
||||||
|
let rv = key.delete(scope);
|
||||||
|
assert!(rv.is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
tester(Scope::User, View::User);
|
||||||
|
tester(Scope::System, View::System);
|
||||||
|
}
|
||||||
|
|
||||||
|
// endregion
|
||||||
Reference in New Issue
Block a user