node之path模組
- 2020 年 12 月 24 日
- 筆記
- javascript, path
路徑概念介紹
概念
所謂路徑,就是定位一個文件所在的位置時,所必須經過 的目錄的層級結構的集合.
絕對路徑與相對路徑
-
windows 下的相對與絕對路徑
完全限定路徑(絕對路徑)與相對路徑
對於操作文件的 Windows API 函數,文件名通常可以相對於當前目錄,而一些 API 需要一個完全限定的路徑。如果文件名不是用下列內容之一開頭的,則該文件名是相對路徑(相對於當前工作目錄):
-
任何格式的通用命名約定(UNC)名稱: 總是以兩個反斜杠字元(“\”)開頭
-
帶冒號與反斜杠的磁碟指示符,例如「C:\」或「d:\」
-
一個反斜杠,例如,「\directory」或「\file.txt」,這也屬於絕對路徑
如果文件名僅以磁碟指示符開頭,後面不帶冒號加反斜杠的組合,則它將被解釋為該磁碟驅動器上當前目錄的相對路徑。
注意,當前目錄可能是根目錄,也可能不是根目錄,這取決於最近在該磁碟上進行「更改目錄(cd)」操作時將其設置為什麼。這種格式的例子如下:- 「C: tmp.txt」指的是一個在驅動器 C 上的當前目錄中,名為「tmp」的文件
- 「C: tempdir \ tmp.txt」指的是 C 驅動器上當前目錄的子目錄中的一個文件
程式碼示例:(DOS)
cd "C:\windows" node C:test.js ::執行C:\windows\test.js node D:test.js ::執行D:\test.js
如果一個路徑包含「雙點」,亦即,路徑的一個組件是兩個連續的點,它也被稱為相對路徑;。這個特殊的說明符用於表示當前目錄之上的目錄,也稱為「父目錄」。這種格式的例子如下:
-
「. . \ tmp.txt”指定一個名為 tmp.txt 的文件,該文件位於當前目錄的父目錄中。
-
「. . \ . . \ tmp.txt「指定當前目錄之上兩個目錄的文件。
-
「. . \ tempdir \ tmp.txt”指定一個名為 tmp.txt 的文件,它位於一個名為 tempdir 的目錄中,該目錄是當前目錄的對等目錄
相對路徑可以組合這兩種示例類型使用,例如「C:..\tmp.txt」 , 這是有效的,因為,儘管系統持續跟蹤記錄當前驅動器的當前目錄 . 它還跟蹤所有驅動器(如果你的系統有不止一個)中的當前目錄, 不管當前驅動器是被設置為哪一個.
DOS 舉例:
cd /d "D:\test1\test2\test3" ::系統追蹤記錄D盤及D盤當前目錄為D:\test1\test2\test3 cd /d "C:\windows\Boot" ::系統追蹤記錄C盤及C盤當前目錄為C:\windows\Boot cd /d "E:\video" ::系統追蹤記錄E盤及E盤當前目錄為E:\video node D:..\test.js ::執行D:\test1\test2\test.js node C:..\test.js ::執行C:\windows\test.js ::注意-所謂追蹤和記錄多個盤的當前目錄,指的是在同一個cmd會話之中記錄,多個cmd會話之間是不共享的
-
Windows 與 POSIX 的對比
-
可移植作業系統介面(Portable Operating System Interface,縮寫為 POSIX):
-
是 IEEE 為要在各種 UNIX 作業系統上運行軟體,而定義 API 的一系列互相關聯的標準的總稱,其正式稱呼為 IEEE Std 1003,而國際標準名稱為 ISO/IEC 9945
-
它基本上是 Portable Operating System Interface(可移植作業系統介面)的縮寫,而 X 則表明其對 Unix API 的傳承
-
Linux 基本上逐步實現了 POSIX 兼容,但並沒有參加正式的 POSIX 認證。
簡單來看,POSIX 就是 UNIX 與類 UNIX 系統.
-
-
POSIX 與 windows 上路徑的區別
-
路徑分隔符
windows 下使用的是「\」作為分隔符,而 linux 則反其道而行之使用”/”作為分隔符.所以在 windows 環境中獲取路徑常見
C:\windows\system
的形式,而 linux 常見/user/share
的形式 -
絕對路徑
- windows: 以盤符開始,如
C:\a.txt
- linux: 以根目錄
/
開始, 如/user/share
- windows: 以盤符開始,如
-
path 模組對 windows 與 POSIX 路徑的處理方案
-
windows 路徑的注意事項
因為 windows 使用反斜杠
\
作為路徑分隔符, 而 js 中的反斜杠被用來對特殊字元進行轉義. 因此,直接的 windows 文件路徑字元串是不能直接在 node 程式中使用的.舉例如下:let path = require("path"); let raw = 「C:\Windows\node\」; console.log(path.basename(rawPath)); // 輸出: // Windows // ode // 原因: \node 中的 \n 被解析為一個換行符
不過不需要擔心,我們只需要在 js 源程式碼中注意不要這麼草率地直接使用這種字面量即可.像是從另一個文件讀取內容這種操作,在讀取過程中,node 會自動對其中的反斜杠進行轉義,不需要我們來操心.如下例:
raw.txt:
C:\Windows\node
test.js
let path = require("path");
let fs = require("fs");
let raw = fs.readFileSync("./raw.txt", "utf-8"); //文件讀取時,會將C:\Windows\node自動轉義為C:\\Windows\\node
console.log(path.basename(raw)); //node
-
path 模組對 windows 與 POSIX 的解決方案
path 提供了 path.win32 與 path.posix 兩個屬性來分別解決 windows 與 posix 類型的路徑.
let path = require("path"); console.log("path:\n"); console.log(Object.keys(path).length); console.log(Object.keys(path)); console.log("path.win32:\n"); console.log(Object.keys(path.win32).length); console.log(Object.keys(path.win32)); console.log("path.posix:\n"); console.log(Object.keys(path.posix).length); console.log(Object.keys(path.posix)); // 輸出: // path: // 16 // [ // 'resolve', 'normalize', // 'isAbsolute', 'join', // 'relative', 'toNamespacedPath', // 'dirname', 'basename', // 'extname', 'format', // 'parse', 'sep', // 'delimiter', 'win32', // 'posix', '_makeLong' // ] // path.win32: // 16 // [ // 'resolve', 'normalize', // 'isAbsolute', 'join', // 'relative', 'toNamespacedPath', // 'dirname', 'basename', // 'extname', 'format', // 'parse', 'sep', // 'delimiter', 'win32', // 'posix', '_makeLong' // ] // path.posix: // 16 // [ // 'resolve', 'normalize', // 'isAbsolute', 'join', // 'relative', 'toNamespacedPath', // 'dirname', 'basename', // 'extname', 'format', // 'parse', 'sep', // 'delimiter', 'win32', // 'posix', '_makeLong' // ]
三個對象都擁有完全一樣的屬性與方法.path 中的主要方法都有對應的 win32 和 posix 版本來分別處理兩種不同的路徑風格.
路徑組件獲取
說明: 路徑組件的獲取函數(方法),都只是基於最基本的字元匹配模式的操作,不會對.
或..
進行解析和運算.
path.basename
語法
path.basename(path[, ext])
-
返回 path 的最後一部分
-
尾部的分隔符會被忽略
-
傳入可選參數 ext(擴展名),可在結果中過濾掉擴展名
-
ext 參數區分大小寫(.html 與.HTML 為不同擴展名)
-
這與 windows 無視文件擴展名大小寫的行為不同.
-
其實質可以視為:
path.basename(path).replace(ext,"")
path.basename("/目錄1/目錄2/文件.html"); // 返回: '文件.html' path.basename("/目錄1/目錄2/文件.html", ".html"); // 返回: '文件' path.basename("/目錄1/目錄2/文件.HTML", ".html"); // 返回: '文件.HTML'
-
path.dirname
語法
path.dirname(path)
- 返回 path 的目錄名
- 尾部的目錄分隔符(path.sep)會被忽略
path.dirname('/目錄1/目錄2/目錄3');
// 返回: '/目錄1/目錄2'
說明: 路徑組件的獲取函數(方法),都只是基於最基本的字元匹配模式的操作,不會對.
或..
進行解析和運算.
舉例如下:
let path = require("path");
let p1 = "c:\\windows\\node\\test\\..\\";
console.log(path.basename(p1)); //輸出 ..
let p2 = "c:\\windows\\node\\..\\test\\test.md";
console.log(path.dirname(p2)); //輸出 c:\windows\node\..\test
path.delimiter
這是一個屬性值,返回平台特定的路徑定界符
- windows: 分號
;
- posix: 冒號
:
path.extname
這是一個屬性值,返迴路徑中的擴展名
擴展名:
- path 的最後一部分中從最後一次出現 . (句點)字元直到字元串結束
- path 最後一部分中沒有
.
,或者 path 的基本名稱(basename)除了第一個字元以外沒有.
,則返回空字元
串
path.sep
這是一個屬性值,平台特定的路徑片段(組件)分隔符
- Windows: 反斜杠
\
- POSIX: 正斜杠
/
路徑處理函數
說明: 路徑處理函數,不同於路徑組件的獲取函數,會對路徑中的.
及..
進行解析,也就是基於相對路徑語法進行路徑運算.
path 模組的內容:
- 錯誤處理:
ERR_INVALID_ARG_TYPE
- 關鍵字元的 unicode 碼點常量: require 自
internal/constants
- 字元串類型驗證:
validateString
- 工具函數
- 路徑分隔符判斷:
isPathSeparato
- POSIX 分隔符判斷:
isPosixPathSeparator
- windows 盤符判斷:
isWindowsDeviceRoot
- 相對路徑解析(解析.或..):
normalizeString
- path 對象格式化為字元串:
_format
- 路徑分隔符判斷:
- windows 平台系列處理函數封裝: win32 對象
- POSIX 平台系列處理函數封裝: posix 對象
主要函數的調用關係
resolve –調用–> normalizeString
normalize –調用–> normalizeString
join –調用–> normalize
relative –調用–> resolve
toNameSpacedPath –調用–> resolve
win32 與 posix 對象
- 各自包含一整套特定於對應平台的路徑獲取與路徑處理函數
- 各自包含指向對方的引用
示例圖:
模組的導出語句
module.exports = process.platform === "win32" ? win32 : posix;
可見, 如果是windows平台,require("path")
得到的就是基於win32對象; posix平台得到的自然是posix對象.
我們可以對路徑處理函數再進行分類.
絕對路徑系列
絕對路徑驗證-path.isAbsolute(path)
絕對路徑驗證是基於前面 絕對與相對路徑 的內容
windows 下:
path.isAbsolute("//server"); // true -- UNC名稱
path.isAbsolute("\\\\server"); // true -- UNC名稱
path.isAbsolute("C:/foo/.."); // true -- 帶冒號與反斜杠的磁碟指示符
path.isAbsolute("C:\\foo\\.."); // true -- 帶冒號與反斜杠的磁碟指示符
path.isAbsolute("bar\\baz"); // false
path.isAbsolute("bar/baz"); // false
path.isAbsolute("."); // false
POSIX 下:
path.isAbsolute("/foo/bar"); // true -- 從根目錄開始的路徑
path.isAbsolute("/baz/.."); // true -- 從根目錄開始的路徑
path.isAbsolute("qux/"); // false
path.isAbsolute("."); // false
解析絕對路徑-path.resolve([…paths])
- 將路徑或路徑片段的序列解析為絕對路徑
- 給定的路徑序列會從右到左進行處理,後面的每個 path 會被追加到前面,直到構造出絕對路徑
- 注意: 構造出絕對路徑即返回,所以不一定會處理完所有參數
- 處理完所有給定的 path 片段之後還未生成絕對路徑,則會使用當前工作目錄
程式碼示例:(筆者平台為win7)
let path = require("path");
let s1 = "\\test1",
s2 = "/test2",
s3 = "test3";
console.log(path.resolve(s3,s2,s1)); // D:\test1
console.log(path.resolve(s3,s2)); // D:\test2
console.log(path.resolve(s3)); // D:\project-mindmap\CATCH_UP\node-path\test3
console.log(path.posix.resolve(s3, s2, s1)); // /test2/\test1
console.log(path.posix.resolve(s3, s2)); // /test2
console.log(path.posix.resolve(s3)); // D:\project-mindmap\CATCH_UP\node-path/test3
分析:
-
注意: 輸出字元串是沒有轉義這個概念的,
\
就是反斜杠,而不是轉義的標誌 -
筆者平台為windows,所以path.resolve是針對windows平台的win32版本的函數
-
對s1與s2的判定:
-
windows下: 均屬於絕對路徑,所以前兩個調用僅僅處理了最右側的參數
注: windows平台下, 對UNC及正斜杠開頭的絕對路徑調用resolve函數,總是將當前目錄對應的
盤符:
作為根路徑 -
posix下: 僅僅只有s2被判定為絕對路徑
注: 在windows平台調用
path.posix.resolve
- 並不會對代表當前工作目錄的字元串進行轉換(反斜杠轉正斜杠)
- 反斜杠被視為普通的字元
-
計算相對路徑差-path.relative(from, to)
-
根據調用關係: relative調用resolve函數
- 先比較from和to的原始字元串,如果相同,返回空串(表示當前目錄)
- 接著調用resolve將from和to處理為絕對路徑字元串
- windows平台下調用win32.resolve
- posix平台下調用posix.resolve
- 接著比較兩者並計算出相對路徑
-
因為調用resolve,所以又會有resolve包含的特性與問題
計算命名空間化路徑名-path.toNamespacedPath(path)
-
僅在 Windows 系統上,返回給定 path 的等效 名稱空間前綴路徑
-
僅在 Windows 系統上有意義
在 POSIX 系統上,該方法不可操作,並且始終返回 path 而不進行修改
-
windows命名空間(見附錄資料)
-
程式碼簡介:
-
字元串檢查(path非字元串則報錯)
-
調用win32.resolve將path處理為絕對路徑形式
resolvedPath
-
判斷path形式
-
path本身就為命名空間格式(以
"\\\\."
或"\\\\?"
打頭)return path;
-
path為UNC形式:
return `\\\\?\\UNC\\${resolvedPath.slice(2)}`;
-
path為windows盤符打頭的形式:
return `\\\\?\\${resolvedPath}`;
-
-
路徑規範化系列
相對路徑解析(模組內部函數)-normalizeString
語法: normalizeString(path, allowAboveRoot, separator, isPathSeparator)
原理分析
- 函數內部變數及其含義:
code
lastslash
lastSlashIndex
lastSegmentLength
-
函數逐個字元讀取path字元串,並在適當的時候進行操作(res回退或者res追加)
-
res回退操作:
res = res.slice(0, lastSlashIndex);
以上圖為例子,經回退操作後,res為
project\\mindmap
-
res追加操作:
res += `${separator}${path.slice(lastSlash + 1, i)}`; //其中: separator為特定於平台的分隔符; i為當前字元在path中的下標值
-
-
函數以變數dots的取值為中心,可以用如下的狀態圖描述:
0–>1: 點
1–>2: 點
2–>other: 點
other–>other: 點
0–>-1: 非分隔符,非點
1–>-1: 非分隔符,非點
2–>-1: 非分隔符,非點
other–>-1: 非分隔符,非點
-1–>-1: 非分隔符,非點
0–>0: /或\
1–>0: /或\
2–>0: /或\\n(res路徑回退)
other–>0: /或\\n(res路徑追加)
-1–>0: /或\
note left of 0
初始狀態
end note
狀態圖分析:
-
說明:
-
狀態圖中讀取的字元是否屬於分隔符,其判斷依據是函數的參數
isPathSeparator
,它是一個函數 -
normalizeString函數的
消費者
是posix.normalize與win32.normalize-
posix.normalize中,是這麼調用的:
path = normalizeString(path, !isAbsolute, "/", isPosixPathSeparator);
-
win32.normalize中是這麼調用的:
normalizeString( path.slice(rootEnd), !isAbsolute, "\\", isPathSeparator )
可見,:
- posix.normalize僅僅將反斜杠視為分隔符, 正斜杠視為普通字元
- win32.normalize將正反斜杠都視為分隔符
-
-
-
初始狀態為0(dots=0)
-
連續點的匹配:
- 0狀態下,如果讀取的下一個字元是點,進入狀態1(dots=1)
- 1狀態下,如果讀取的下一個字元是點,進入狀態2(dots=2)
- 0狀態下,如果讀取的下一個字元是點,進入狀態other(dots=3,4,5….)
- other狀態下,如果如果讀取的下一個字元是點,仍舊是other狀態(dots++)
-
任何狀態,如果讀取的下一個字元是分隔符(\或/),回到狀態0
- 狀態2回到狀態0的同時,執行 res回退操作
- 狀態other回到狀態0的同時,執行 res追加操作
-
任何狀態,如果讀取的下一個字元是非分隔符且非點號,回到狀態-1(dots=-1)
可以匹配的模式分析:
-
因為初始狀態是0,因此可以匹配兩種類型
-
點號打頭的模式:
.分隔符
,..分隔符
-
非點號打頭的模式:
分隔符.分隔符
,分隔符..分隔符
(分隔符由
isPathSeparator
參數判斷)
函數也分別對應兩種類型,分成了兩種情況處理
-
規範化路徑串-path.normalize
語法: path.normalize(path)
功能: 規範化給定的 path ,解析 ‘..’ 和 ‘.’ 片段(調用了內置函數normalizeString)
特點:
- 多個連續重複的路徑段分隔字元(例如 POSIX 上的 / 、Windows 上的 \ 或 / ),替換為單個平台特定的路徑段分隔符(POSIX 上的 / 、Windows 上的 \ )
- 尾部的分隔符會保留
- 零長度的字元串,則返回
.
,表示當前工作目錄
程式碼示例
path.win32.normalize('C:////temp\\\\/\\/\\/foo/bar');
// 返回: 'C:\\temp\\foo\\bar'
路徑組合-path.join
語法: path.join([…paths])
功能:
- 將所有給定的 path 片段連接到一起(使用平台特定的分隔符作為定界符)
- 規範化生成的路徑(調用normalize函數), 意味著可以進行路徑的”相對運算”
程式碼示例:
path.join('/目錄1', '目錄2', '目錄3/目錄4', '目錄5', '..');
// 返回: '/目錄1/目錄2/目錄3/目錄4'
path.join('目錄1', {}, '目錄2');
// 拋出 'TypeError: Path must be a string. Received {}'
路徑對象與字元串互轉
路徑字元串轉對象-path.parse
語法: path.parse(path)
,其中path是路徑字元串
功能:
- 解析路徑字元串
- 返回一個對象,對象各個屬性代表路徑的各種組成成分
- 未調用normalize或join,不會執行路徑中的”相對運算”
程式碼示例:
let path = require("path");
let str = "C:\\test\\test2\\..\\tmp\\tmp.txt";
console.log(path.parse(str));
// 輸出:
// {
// root: 'C:\\',
// dir: 'C:\\test\\test2\\..\\tmp',
// base: 'tmp.txt',
// ext: '.txt',
// name: 'tmp'
// }
路徑對象轉字元串-path.format
語法: path.format(pathObject)
功能:
- 提取參數pathObject中的各個代表路徑組成部分的屬性
- 將它們組合成為一個路徑字元串
- 與
path.format
的功能相反
注意事項:
當構建 pathObject 時,注意以下屬性組合,其中一些屬性優先於另一些屬性:
- 如果提供了 pathObject.dir ,則忽略 pathObject.root
- 如果 pathObject.base 存在,則忽略 pathObject.ext 和 pathObject.name
附錄:windows命名空間
Windows api中使用的命名空間主要有兩類,NT命名空間和Win32命名空間。
NT命名空間被設計為其他子系統和命名空間可以存在的最低級別命名空間,包括Win32子系統和擴展的Win32命名空間。
POSIX是Windows中構建在NT命名空間之上的子系統的另一個例子。
Windows的早期版本還為某些特殊設備定義了幾個預定義或保留名稱,如通訊(串列和並行)埠和默認的顯示控制台,兩者現在屬於被稱為NT設備命名空間的一部分,並且在Windows的當前版本中作為向後兼容方案仍然被支援。
win32文件命名空間
本節和下一節總結了Win32名稱空間前綴和約定,並描述了它們的使用方法。請注意,這些示例用於Windows API函數,並不一定都適用於Windows shell應用程式,如Windows資源管理器。由於這個原因,win32命名空間比Windows shell應用具有更廣泛的使用手段,利用這一點的Windows應用程式可以使用這些命名空間進行開發。
對於文件I/O, \\?\
路徑字元串的前綴告訴Windows api禁用所有字元串解析,並將在這之後的字元串直接發送到文件系統。例如,如果文件系統支援大路徑和文件名,那麼你可以超出被Windows api強制定義的MAX_PATH的限制。有關常規最大路徑限制的更多資訊,請參閱前一節的最大路徑長度限制.
因為它關閉了路徑字元串的自動展開, \\?\
前綴還允許在路徑名中使用”..”和”.”,如果您試圖使用這些保留的相對路徑說明符作為完全限定路徑的一部分對文件執行操作,這可能很有用。
許多但不是所有的文件I/O api支援 \\?\
前綴; 您應該查看每個API的具體說明加以確定。
請注意,應該使用Unicode api來確保 \\?\
前綴允許您超過MAX_PATH的限制.
win32設備命名空間
\\.\
前綴將訪問Win32設備命名空間,而不是Win32文件命名空間。這就是直接訪問物理磁碟和卷的方式,不需要通過文件系統(如果API支援這種類型的訪問)。您可以通過這種方式訪問除磁碟之外的許多設備(例如,通過使用CreateFile和DefineDosDevice函數)。
例如,如果你想要打開系統的串列通訊埠1,你可以在CreateFile函數的調用中使用「COM1」。這是因為COM1-COM9是NT名稱空間中保留名稱的一部分,不過使用\\.\
前綴也適用於這些設備名稱。相比之下,如果您安裝了一個100埠的串列擴展板,並且想要打開COM56,但是你不能使用「COM56」打開它,因為COM56沒有預定義的NT命名空間。您需要使用\\.\COM56
來打開它. 因為\\.\
直接轉到設備命名空間,而不是試圖定位一個預定義的別名
使用Win32設備命名空間的另一個例子是將CreateFile函數和\\.\PhysicalDiskX
(其中X是有效的整數值)或 \\.\CdRomX
搭配使用。這允許您繞過文件系統,直接訪問這些設備。這是可行的,因為這些設備名是系統在枚舉這些設備時創建的,一些驅動程式還會在系統中創建其他別名。例如,實現名稱「C:\」的設備驅動程式有自己的命名空間,這個命名空間恰好也是文件系統。
使用CreateFile函數的api通常也可以使用\\.\
前綴,因為CreateFile是用來打開文件以及設備的函數,這取決於您使用的參數。
如果你正在使用Windows API函數,你應該只使用\\.\
前綴訪問設備而不將之用於訪問文件。
大多數api不支援\\.\
;只有那些設計為使用設備名稱空間的設備才能識別它。使用API前,請務必檢查每個API的詳細說明來加以確定。
NT命名空間
也有一些api允許使用NT命名空間,但是Windows對象管理器在大多數情況下不需要這樣做。使用Windows Sysinternals WinObj工具在系統對象瀏覽器中瀏覽Windows名稱空間可以很好地幫助說明這一點。當您運行這個工具時,您看到的是以root
或\
開頭的NT命名空間。名為Global??
的子文件夾是Win32命名空間所在的地方。已命名的設備對象駐留在NT命名空間中的Device
子目錄的。在這裡,您還可以找到Serial0
和Serial1
,如果存在的話,它們是代表最初的兩個COM埠的設備對象。表示卷的設備對象類似於HarddiskVolume1
,儘管數字後綴可能會有所變化。Harddisk0
子目錄下的名稱DR0
是一個表示磁碟的設備對象的例子,等等。
為了讓這些設備對象可以被Windows應用程式訪問,設備驅動程式在Win32命名空間中創建一個符號鏈接(symlink)Global??
到它們各自的設備對象。例如,Global??
子目錄下的COM0
和COM1
是到Serial0
和Serial1
的符號鏈接,C:
是到HarddiskVolume1
的符號鏈接,Physicaldrive0
是到DR0的符號鏈接,等等。
如果沒有符號鏈接,特定的設備「Xxx」對於任何使用Win32命名空間的Windows應用程式都是不可用的,如前所述。但是,可以使用任何支援NT命名空間的api,並用\device\Xxx
的絕對路徑格式打開該設備的句柄。
隨著通過終端服務和虛擬機增加了多用戶支援,有必要進一步在Win32名稱空間內虛擬化系統範圍的根設備。這是通過將名為GLOBALROOT
的符號鏈接添加到Win32命名空間來實現的,您可以在前面討論過的WinObj瀏覽器工具的Global??
子目錄中看到它,並且可以通過路徑\\?\GLOBALROOT
訪問它。其中,\\?\
前綴確保它後面的路徑在系統對象管理器的真正根路徑中查找,而不是與會話相關的路徑。
winObj的使用
-
使用
- 下載並解壓
- 以管理員許可權運行