Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
F
Ftt_Common
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Administrator
Ftt_Common
Commits
8afe8a66
Commit
8afe8a66
authored
Jun 25, 2026
by
段廷瑞
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: 日志查看器优化
parent
41b668f8
Changes
8
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
558 additions
and
16 deletions
+558
-16
System.Formats.Nrbf.dll
AppHome/System.Formats.Nrbf.dll
+0
-0
System.Resources.Extensions.dll
AppHome/System.Resources.Extensions.dll
+0
-0
Tools.csproj
Tools/Tools.csproj
+3
-0
wLogPathDetector.cs
Tools/UI/wLogPathDetector.cs
+128
-0
wLogViewer.Designer.cs
Tools/UI/wLogViewer.Designer.cs
+24
-3
wLogViewer.cs
Tools/UI/wLogViewer.cs
+401
-10
wTextFormatter.Designer.cs
Tools/UI/wTextFormatter.Designer.cs
+1
-1
wExtend.cs
Tools/wExtend.cs
+1
-2
No files found.
AppHome/System.Formats.Nrbf.dll
0 → 100644
View file @
8afe8a66
File added
AppHome/System.Resources.Extensions.dll
0 → 100644
View file @
8afe8a66
File added
Tools/Tools.csproj
View file @
8afe8a66
...
@@ -317,6 +317,9 @@
...
@@ -317,6 +317,9 @@
<Compile
Include=
"UI\wLoginPwd.Designer.cs"
>
<Compile
Include=
"UI\wLoginPwd.Designer.cs"
>
<DependentUpon>
wLoginPwd.cs
</DependentUpon>
<DependentUpon>
wLoginPwd.cs
</DependentUpon>
</Compile>
</Compile>
<Compile
Include=
"UI\wLogPathDetector.cs"
>
<SubType>
Form
</SubType>
</Compile>
<Compile
Include=
"UI\wLogViewer.cs"
>
<Compile
Include=
"UI\wLogViewer.cs"
>
<SubType>
Form
</SubType>
<SubType>
Form
</SubType>
</Compile>
</Compile>
...
...
Tools/UI/wLogPathDetector.cs
0 → 100644
View file @
8afe8a66
using
System
;
using
System.IO
;
using
System.Linq
;
using
System.Windows.Forms
;
namespace
Tools.UI
{
/// <summary>
/// 日志路径探测器 — 选择搜索根目录
/// </summary>
public
partial
class
wLogPathDetector
:
Form
{
public
string
SelectedPath
{
get
;
private
set
;
}
TextBox
txtPath
;
Button
btnBrowse
;
Button
btnConfirm
;
Button
btnCancel
;
Label
lblInfo
;
public
wLogPathDetector
()
{
InitializeComponent
();
SetDefaultPath
();
}
void
SetDefaultPath
()
{
// 优先 D:\, 否则 E:\, 否则第一个可用驱动器
var
drives
=
DriveInfo
.
GetDrives
().
Where
(
d
=>
d
.
IsReady
).
ToList
();
foreach
(
var
drive
in
drives
)
{
if
(
drive
.
Name
.
StartsWith
(
"D:"
,
StringComparison
.
OrdinalIgnoreCase
))
{
txtPath
.
Text
=
drive
.
RootDirectory
.
FullName
;
return
;
}
}
foreach
(
var
drive
in
drives
)
{
if
(
drive
.
Name
.
StartsWith
(
"E:"
,
StringComparison
.
OrdinalIgnoreCase
))
{
txtPath
.
Text
=
drive
.
RootDirectory
.
FullName
;
return
;
}
}
var
first
=
drives
.
FirstOrDefault
();
if
(
first
!=
null
)
txtPath
.
Text
=
first
.
RootDirectory
.
FullName
;
}
void
InitializeComponent
()
{
this
.
Text
=
"探测日志路径"
;
this
.
Size
=
new
System
.
Drawing
.
Size
(
500
,
150
);
this
.
FormBorderStyle
=
FormBorderStyle
.
FixedDialog
;
this
.
MaximizeBox
=
false
;
this
.
MinimizeBox
=
false
;
this
.
StartPosition
=
FormStartPosition
.
CenterParent
;
lblInfo
=
new
Label
{
Text
=
"选择搜索根目录:"
,
Location
=
new
System
.
Drawing
.
Point
(
12
,
15
),
AutoSize
=
true
};
txtPath
=
new
TextBox
{
Location
=
new
System
.
Drawing
.
Point
(
12
,
40
),
Width
=
370
};
btnBrowse
=
new
Button
{
Text
=
"浏览..."
,
Location
=
new
System
.
Drawing
.
Point
(
390
,
38
),
Width
=
80
};
btnBrowse
.
Click
+=
(
s
,
e
)
=>
{
using
(
var
dlg
=
new
FolderBrowserDialog
{
Description
=
"选择搜索根目录"
,
RootFolder
=
Environment
.
SpecialFolder
.
MyComputer
,
SelectedPath
=
txtPath
.
Text
})
{
if
(
dlg
.
ShowDialog
()
==
DialogResult
.
OK
)
txtPath
.
Text
=
dlg
.
SelectedPath
;
}
};
btnConfirm
=
new
Button
{
Text
=
"开始探测"
,
Location
=
new
System
.
Drawing
.
Point
(
310
,
75
),
Width
=
80
};
btnConfirm
.
Click
+=
(
s
,
e
)
=>
{
if
(!
Directory
.
Exists
(
txtPath
.
Text
))
{
MessageBox
.
Show
(
"目录不存在,请重新选择。"
,
"提示"
,
MessageBoxButtons
.
OK
,
MessageBoxIcon
.
Warning
);
return
;
}
SelectedPath
=
txtPath
.
Text
;
this
.
DialogResult
=
DialogResult
.
OK
;
this
.
Close
();
};
btnCancel
=
new
Button
{
Text
=
"取消"
,
Location
=
new
System
.
Drawing
.
Point
(
398
,
75
),
Width
=
72
};
btnCancel
.
Click
+=
(
s
,
e
)
=>
{
this
.
DialogResult
=
DialogResult
.
Cancel
;
this
.
Close
();
};
this
.
Controls
.
AddRange
(
new
Control
[]
{
lblInfo
,
txtPath
,
btnBrowse
,
btnConfirm
,
btnCancel
});
}
}
}
Tools/UI/wLogViewer.Designer.cs
View file @
8afe8a66
...
@@ -34,6 +34,8 @@
...
@@ -34,6 +34,8 @@
this
.
toolStripSeparator1
=
new
System
.
Windows
.
Forms
.
ToolStripSeparator
();
this
.
toolStripSeparator1
=
new
System
.
Windows
.
Forms
.
ToolStripSeparator
();
this
.
toolStripLabel1
=
new
System
.
Windows
.
Forms
.
ToolStripLabel
();
this
.
toolStripLabel1
=
new
System
.
Windows
.
Forms
.
ToolStripLabel
();
this
.
tsbSelectPath
=
new
System
.
Windows
.
Forms
.
ToolStripButton
();
this
.
tsbSelectPath
=
new
System
.
Windows
.
Forms
.
ToolStripButton
();
this
.
tsbDetectPath
=
new
System
.
Windows
.
Forms
.
ToolStripButton
();
this
.
tsbRemovePath
=
new
System
.
Windows
.
Forms
.
ToolStripButton
();
this
.
listBox1
=
new
System
.
Windows
.
Forms
.
ListBox
();
this
.
listBox1
=
new
System
.
Windows
.
Forms
.
ListBox
();
this
.
textBox1
=
new
System
.
Windows
.
Forms
.
TextBox
();
this
.
textBox1
=
new
System
.
Windows
.
Forms
.
TextBox
();
this
.
splitContainer1
=
new
System
.
Windows
.
Forms
.
SplitContainer
();
this
.
splitContainer1
=
new
System
.
Windows
.
Forms
.
SplitContainer
();
...
@@ -54,7 +56,9 @@
...
@@ -54,7 +56,9 @@
this
.
toolStripSeparator1
,
this
.
toolStripSeparator1
,
this
.
toolStripLabel1
,
this
.
toolStripLabel1
,
this
.
tscbLogPath
,
this
.
tscbLogPath
,
this
.
tsbSelectPath
});
this
.
tsbSelectPath
,
this
.
tsbDetectPath
,
this
.
tsbRemovePath
});
this
.
toolStrip1
.
Location
=
new
System
.
Drawing
.
Point
(
0
,
0
);
this
.
toolStrip1
.
Location
=
new
System
.
Drawing
.
Point
(
0
,
0
);
this
.
toolStrip1
.
Name
=
"toolStrip1"
;
this
.
toolStrip1
.
Name
=
"toolStrip1"
;
this
.
toolStrip1
.
Size
=
new
System
.
Drawing
.
Size
(
695
,
25
);
this
.
toolStrip1
.
Size
=
new
System
.
Drawing
.
Size
(
695
,
25
);
...
@@ -85,13 +89,28 @@
...
@@ -85,13 +89,28 @@
this
.
toolStripLabel1
.
Text
=
"日志路径:"
;
this
.
toolStripLabel1
.
Text
=
"日志路径:"
;
//
//
// tsbSelectPath
// tsbSelectPath
//
//
this
.
tsbSelectPath
.
Image
=
global
::
Tools
.
Properties
.
Resources
.
文件夹
;
this
.
tsbSelectPath
.
Image
=
global
::
Tools
.
Properties
.
Resources
.
文件夹
;
this
.
tsbSelectPath
.
ImageTransparentColor
=
System
.
Drawing
.
Color
.
Magenta
;
this
.
tsbSelectPath
.
ImageTransparentColor
=
System
.
Drawing
.
Color
.
Magenta
;
this
.
tsbSelectPath
.
Name
=
"tsbSelectPath"
;
this
.
tsbSelectPath
.
Name
=
"tsbSelectPath"
;
this
.
tsbSelectPath
.
Size
=
new
System
.
Drawing
.
Size
(
76
,
22
);
this
.
tsbSelectPath
.
Size
=
new
System
.
Drawing
.
Size
(
76
,
22
);
this
.
tsbSelectPath
.
Text
=
"选择路径"
;
this
.
tsbSelectPath
.
Text
=
"选择路径"
;
//
//
// tsbDetectPath
//
this
.
tsbDetectPath
.
Image
=
global
::
Tools
.
Properties
.
Resources
.
文件夹
;
this
.
tsbDetectPath
.
ImageTransparentColor
=
System
.
Drawing
.
Color
.
Magenta
;
this
.
tsbDetectPath
.
Name
=
"tsbDetectPath"
;
this
.
tsbDetectPath
.
Size
=
new
System
.
Drawing
.
Size
(
76
,
22
);
this
.
tsbDetectPath
.
Text
=
"探测路径"
;
//
// tsbRemovePath
//
this
.
tsbRemovePath
.
Name
=
"tsbRemovePath"
;
this
.
tsbRemovePath
.
Size
=
new
System
.
Drawing
.
Size
(
28
,
22
);
this
.
tsbRemovePath
.
Text
=
"×"
;
this
.
tsbRemovePath
.
ToolTipText
=
"移除当前路径"
;
//
// listBox1
// listBox1
//
//
this
.
listBox1
.
Dock
=
System
.
Windows
.
Forms
.
DockStyle
.
Fill
;
this
.
listBox1
.
Dock
=
System
.
Windows
.
Forms
.
DockStyle
.
Fill
;
...
@@ -173,6 +192,8 @@
...
@@ -173,6 +192,8 @@
private
System
.
Windows
.
Forms
.
ToolStrip
toolStrip1
;
private
System
.
Windows
.
Forms
.
ToolStrip
toolStrip1
;
private
System
.
Windows
.
Forms
.
ToolStripLabel
toolStripLabel1
;
private
System
.
Windows
.
Forms
.
ToolStripLabel
toolStripLabel1
;
private
System
.
Windows
.
Forms
.
ToolStripButton
tsbSelectPath
;
private
System
.
Windows
.
Forms
.
ToolStripButton
tsbSelectPath
;
private
System
.
Windows
.
Forms
.
ToolStripButton
tsbDetectPath
;
private
System
.
Windows
.
Forms
.
ToolStripButton
tsbRemovePath
;
private
System
.
Windows
.
Forms
.
ListBox
listBox1
;
private
System
.
Windows
.
Forms
.
ListBox
listBox1
;
private
System
.
Windows
.
Forms
.
TextBox
textBox1
;
private
System
.
Windows
.
Forms
.
TextBox
textBox1
;
private
System
.
Windows
.
Forms
.
SplitContainer
splitContainer1
;
private
System
.
Windows
.
Forms
.
SplitContainer
splitContainer1
;
...
...
Tools/UI/wLogViewer.cs
View file @
8afe8a66
...
@@ -19,7 +19,28 @@ namespace Tools.UI
...
@@ -19,7 +19,28 @@ namespace Tools.UI
{
{
List
<
string
>
pathList
=
new
List
<
string
>();
List
<
string
>
pathList
=
new
List
<
string
>();
string
LogPath
{
get
=>
this
.
tscbLogPath
.
Text
;
set
=>
this
.
tscbLogPath
.
Text
=
value
;
}
string
LogPath
{
get
=>
CleanLogPath
(
this
.
tscbLogPath
.
Text
);
set
=>
this
.
tscbLogPath
.
Text
=
value
;
}
Dictionary
<
string
,
List
<
string
>>
_detectedAllPaths
=
new
Dictionary
<
string
,
List
<
string
>>();
string
_commonPathPrefix
=
null
;
// 公共路径前缀(用于"…"缩略显示)
/// <summary>
/// 去除路径中的类型前缀(如 "[PDM] "),还原"…"缩略前缀
/// </summary>
string
CleanLogPath
(
string
text
)
{
if
(
string
.
IsNullOrEmpty
(
text
))
return
text
;
var
pathPart
=
text
;
if
(
pathPart
.
StartsWith
(
"["
))
{
var
idx
=
pathPart
.
IndexOf
(
"] "
);
if
(
idx
>
0
)
pathPart
=
pathPart
.
Substring
(
idx
+
2
);
}
// 还原"…"缩略前缀
if
(
pathPart
.
StartsWith
(
"…"
)
&&
!
string
.
IsNullOrEmpty
(
_commonPathPrefix
))
return
_commonPathPrefix
+
pathPart
.
Substring
(
1
);
return
pathPart
;
}
public
wLogViewer
()
public
wLogViewer
()
{
{
...
@@ -29,21 +50,33 @@ namespace Tools.UI
...
@@ -29,21 +50,33 @@ namespace Tools.UI
//窗体:加载
//窗体:加载
private
void
wLogViewer_Load
(
object
sender
,
EventArgs
e
)
private
void
wLogViewer_Load
(
object
sender
,
EventArgs
e
)
{
{
//初始化编码选择下拉框
//初始化编码选择下拉框
(常用编码)
var
list
=
new
List
<
string
>
{
var
list
=
new
List
<
string
>
{
Encoding
.
Default
.
HeaderName
.
ToUpper
(),
Encoding
.
UTF8
.
HeaderName
.
ToUpper
(),
Encoding
.
UTF8
.
HeaderName
.
ToUpper
(),
"GBK"
,
"GB2312"
,
Encoding
.
Default
.
HeaderName
.
ToUpper
(),
Encoding
.
Unicode
.
HeaderName
.
ToUpper
(),
Encoding
.
BigEndianUnicode
.
HeaderName
.
ToUpper
(),
};
};
this
.
tscbEncoding
.
Items
.
AddRange
(
list
.
Distinct
().
ToArray
());
this
.
tscbEncoding
.
Items
.
AddRange
(
list
.
Distinct
().
ToArray
());
this
.
tscbEncoding
.
SelectedIndex
=
0
;
this
.
tscbEncoding
.
SelectedIndex
=
0
;
//初始化文件路径下拉框
//初始化文件路径下拉框
pathList
=
Utils
.
GetJsonConfig
<
List
<
string
>>();
pathList
=
Utils
.
GetJsonConfig
<
List
<
string
>>();
this
.
tscbLogPath
.
Items
.
AddRange
(
pathList
.
Distinct
().
ToArray
());
foreach
(
var
p
in
pathList
)
{
if
(!
this
.
tscbLogPath
.
Items
.
Contains
(
p
))
this
.
tscbLogPath
.
Items
.
Add
(
p
);
}
//初始化探测路径
LoadDetectedPaths
();
}
}
//窗体:显示完成
//窗体:显示完成
private
void
wLogViewer_Shown
(
object
sender
,
EventArgs
e
)
private
void
wLogViewer_Shown
(
object
sender
,
EventArgs
e
)
{
{
this
.
tsbSelectPath
.
Click
+=
TsbSelectPath_Click
;
this
.
tsbSelectPath
.
Click
+=
TsbSelectPath_Click
;
this
.
tsbDetectPath
.
Click
+=
TsbDetectPath_Click
;
this
.
tsbRemovePath
.
Click
+=
TsbRemovePath_Click
;
this
.
tscbLogPath
.
SelectedIndexChanged
+=
TscbLogPath_SelectedIndexChanged
;
this
.
tscbLogPath
.
SelectedIndexChanged
+=
TscbLogPath_SelectedIndexChanged
;
this
.
tscbEncoding
.
SelectedIndexChanged
+=
TscbEncoding_SelectedIndexChanged
;
this
.
tscbEncoding
.
SelectedIndexChanged
+=
TscbEncoding_SelectedIndexChanged
;
this
.
listBox1
.
SelectedIndexChanged
+=
ListBox1_SelectedIndexChanged
;
this
.
listBox1
.
SelectedIndexChanged
+=
ListBox1_SelectedIndexChanged
;
...
@@ -62,24 +95,28 @@ namespace Tools.UI
...
@@ -62,24 +95,28 @@ namespace Tools.UI
this
.
tscbLogPath
.
Items
.
Add
(
folder
);
this
.
tscbLogPath
.
Items
.
Add
(
folder
);
//刷新下拉框文本
//刷新下拉框文本
this
.
LogPath
=
folder
;
this
.
LogPath
=
folder
;
//保存路径记录
//保存路径记录(仅保存原始路径,不含类型前缀)
Utils
.
SaveJsonConfig
(
this
.
tscbLogPath
.
Items
.
Cast
<
string
>().
ToList
());
var
rawPaths
=
this
.
tscbLogPath
.
Items
.
Cast
<
string
>()
.
Where
(
p
=>
!
p
.
StartsWith
(
"["
))
.
ToList
();
Utils
.
SaveJsonConfig
(
rawPaths
);
}
}
}
}
bool
_suppressPathChange
=
false
;
// 路径变更抑制标志(刷新下拉框时使用)
/// <summary>
/// <summary>
/// 路径文本改变时,刷新列表框
/// 路径文本改变时,刷新列表框
/// </summary>
/// </summary>
private
void
TscbLogPath_SelectedIndexChanged
(
object
sender
,
EventArgs
e
)
private
void
TscbLogPath_SelectedIndexChanged
(
object
sender
,
EventArgs
e
)
{
{
if
(
_suppressPathChange
)
return
;
this
.
InitListBox
();
this
.
InitListBox
();
}
}
/// <summary>
/// <summary>
/// 编码切换:重新解析日志文件
/// 编码切换:重新解析日志文件
/// </summary>
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private
void
TscbEncoding_SelectedIndexChanged
(
object
sender
,
EventArgs
e
)
private
void
TscbEncoding_SelectedIndexChanged
(
object
sender
,
EventArgs
e
)
{
{
if
(
_suppressEncodingChange
)
return
;
this
.
OpenLogFile
();
this
.
OpenLogFile
();
}
}
/// <summary>
/// <summary>
...
@@ -115,8 +152,10 @@ namespace Tools.UI
...
@@ -115,8 +152,10 @@ namespace Tools.UI
}
}
}
}
int
_scanCount
=
0
;
// 探测扫描目录计数
Task
_task
=
null
;
//当前任务
Task
_task
=
null
;
//当前任务
CancellationTokenSource
_tokenSource
=
new
CancellationTokenSource
();
//取消令牌
CancellationTokenSource
_tokenSource
=
new
CancellationTokenSource
();
//取消令牌
bool
_suppressEncodingChange
=
false
;
//编码变更抑制标志(自动探测时使用)
/// <summary>
/// <summary>
/// 打开日志文件
/// 打开日志文件
/// </summary>
/// </summary>
...
@@ -125,13 +164,27 @@ namespace Tools.UI
...
@@ -125,13 +164,27 @@ namespace Tools.UI
var
fileName
=
this
.
listBox1
.
SelectedItem
?.
ToString
();
var
fileName
=
this
.
listBox1
.
SelectedItem
?.
ToString
();
if
(
string
.
IsNullOrEmpty
(
fileName
))
return
;
if
(
string
.
IsNullOrEmpty
(
fileName
))
return
;
//生成文件路径
var
logFileName
=
$@"
{
this
.
LogPath
}
\
{
fileName
}
"
;
if
(!
File
.
Exists
(
logFileName
))
return
;
//自动探测文件编码
var
detectedEnc
=
DetectFileEncoding
(
logFileName
);
var
detectedName
=
detectedEnc
.
HeaderName
.
ToUpper
();
//编码映射:将GBK/GB2312等系统编码统一为友好名称
if
(
detectedEnc
.
CodePage
==
Encoding
.
Default
.
CodePage
)
detectedName
=
Encoding
.
Default
.
HeaderName
.
ToUpper
();
_suppressEncodingChange
=
true
;
if
(!
this
.
tscbEncoding
.
Items
.
Contains
(
detectedName
))
this
.
tscbEncoding
.
Items
.
Insert
(
0
,
detectedName
);
this
.
tscbEncoding
.
SelectedItem
=
detectedName
;
_suppressEncodingChange
=
false
;
var
encode
=
Encoding
.
Default
;
var
encode
=
Encoding
.
Default
;
if
(!
string
.
IsNullOrEmpty
(
this
.
tscbEncoding
.
Text
))
if
(!
string
.
IsNullOrEmpty
(
this
.
tscbEncoding
.
Text
))
encode
=
Encoding
.
GetEncoding
(
this
.
tscbEncoding
.
Text
);
encode
=
Encoding
.
GetEncoding
(
this
.
tscbEncoding
.
Text
);
//清空信息
//清空信息
this
.
textBox1
.
Text
=
string
.
Empty
;
this
.
textBox1
.
Text
=
string
.
Empty
;
//生成文件路径
var
logFileName
=
$@"
{
this
.
LogPath
}
\
{
fileName
}
"
;
//如果上一个任务还未结束,则取消
//如果上一个任务还未结束,则取消
if
(
_task
!=
null
&&
!
_task
.
IsCompleted
)
if
(
_task
!=
null
&&
!
_task
.
IsCompleted
)
{
{
...
@@ -176,5 +229,343 @@ namespace Tools.UI
...
@@ -176,5 +229,343 @@ namespace Tools.UI
fs
.
Close
();
fs
.
Close
();
},
token
);
},
token
);
}
}
/// <summary>
/// 探测文件编码(BOM检测 + UTF-8合法性验证)
/// </summary>
Encoding
DetectFileEncoding
(
string
filePath
)
{
var
buffer
=
new
byte
[
4096
];
int
bytesRead
;
using
(
var
fs
=
new
FileStream
(
filePath
,
FileMode
.
Open
,
FileAccess
.
Read
,
FileShare
.
Read
))
{
bytesRead
=
fs
.
Read
(
buffer
,
0
,
buffer
.
Length
);
}
// 1. BOM检测
if
(
bytesRead
>=
3
&&
buffer
[
0
]
==
0xEF
&&
buffer
[
1
]
==
0xBB
&&
buffer
[
2
]
==
0xBF
)
return
new
UTF8Encoding
(
true
);
// UTF-8 BOM
if
(
bytesRead
>=
2
&&
buffer
[
0
]
==
0xFF
&&
buffer
[
1
]
==
0xFE
)
return
Encoding
.
Unicode
;
// UTF-16 LE
if
(
bytesRead
>=
2
&&
buffer
[
0
]
==
0xFE
&&
buffer
[
1
]
==
0xFF
)
return
Encoding
.
BigEndianUnicode
;
// UTF-16 BE
if
(
bytesRead
>=
4
&&
buffer
[
0
]
==
0xFF
&&
buffer
[
1
]
==
0xFE
&&
buffer
[
2
]
==
0x00
&&
buffer
[
3
]
==
0x00
)
return
Encoding
.
UTF32
;
// 2. 无BOM时,验证是否为合法UTF-8
if
(
IsValidUtf8
(
buffer
,
bytesRead
))
return
new
UTF8Encoding
(
false
);
// UTF-8 without BOM
// 3. 默认系统编码(中文Windows为GBK)
return
Encoding
.
Default
;
}
/// <summary>
/// 验证字节序列是否为合法UTF-8(无替换字符、无解码异常)
/// </summary>
bool
IsValidUtf8
(
byte
[]
buffer
,
int
length
)
{
try
{
var
utf8Strict
=
Encoding
.
GetEncoding
(
"UTF-8"
,
EncoderFallback
.
ExceptionFallback
,
DecoderFallback
.
ExceptionFallback
);
utf8Strict
.
GetString
(
buffer
,
0
,
length
);
return
true
;
}
catch
(
DecoderFallbackException
)
{
return
false
;
}
}
/// <summary>
/// 探测日志路径:弹出目录选择窗口后异步递归搜索 PDM.exe / MES.exe / U6.exe 的 temp 子文件夹
/// </summary>
private
async
void
TsbDetectPath_Click
(
object
sender
,
EventArgs
e
)
{
using
(
var
dlg
=
new
wLogPathDetector
())
{
if
(
dlg
.
ShowDialog
()
!=
DialogResult
.
OK
)
return
;
var
rootDir
=
dlg
.
SelectedPath
;
// 进入扫描状态
tsbDetectPath
.
Enabled
=
false
;
tsbDetectPath
.
Text
=
"扫描中…"
;
var
prevStyle
=
progressBar1
.
Style
;
progressBar1
.
Style
=
ProgressBarStyle
.
Marquee
;
Dictionary
<
string
,
List
<
string
>>
detected
=
null
;
try
{
var
onProgress
=
new
Progress
<
string
>(
msg
=>
{
this
.
tsbDetectPath
.
Text
=
msg
;
});
_scanCount
=
0
;
detected
=
await
Task
.
Run
(()
=>
DetectLogPaths
(
rootDir
,
onProgress
));
}
catch
(
Exception
ex
)
{
Utils
.
Tip
(
$"探测过程出错:
{
ex
.
Message
}
"
);
return
;
}
finally
{
// 恢复状态
tsbDetectPath
.
Enabled
=
true
;
tsbDetectPath
.
Text
=
"探测路径"
;
progressBar1
.
Style
=
prevStyle
;
}
if
(
detected
==
null
||
detected
.
Count
==
0
)
{
Utils
.
Tip
(
$"在
{
rootDir
}
下未探测到 PDM/MES/U6 的日志目录。"
);
return
;
}
// 合并到已有结果
foreach
(
var
kvp
in
detected
)
{
if
(!
_detectedAllPaths
.
ContainsKey
(
kvp
.
Key
))
_detectedAllPaths
[
kvp
.
Key
]
=
new
List
<
string
>();
foreach
(
var
path
in
kvp
.
Value
)
{
if
(!
_detectedAllPaths
[
kvp
.
Key
].
Contains
(
path
))
_detectedAllPaths
[
kvp
.
Key
].
Add
(
path
);
}
}
// 刷新下拉框显示
RefreshDetectedDisplay
();
// 持久化
SaveDetectedPaths
();
var
total
=
detected
.
Values
.
Sum
(
v
=>
v
.
Count
);
Utils
.
Tip
(
$"已探测到
{
total
}
个日志目录并加入列表。"
);
}
}
/// <summary>
/// 移除路径下拉框中当前选中的项
/// </summary>
private
void
TsbRemovePath_Click
(
object
sender
,
EventArgs
e
)
{
var
item
=
this
.
tscbLogPath
.
SelectedItem
?.
ToString
();
if
(
string
.
IsNullOrEmpty
(
item
))
{
Utils
.
Tip
(
"请先选择要移除的路径。"
);
return
;
}
// 探测路径:以 "[" 开头,从持久化字典中移除
if
(
item
.
StartsWith
(
"["
))
{
var
path
=
CleanLogPath
(
item
);
string
keyToRemove
=
null
;
foreach
(
var
kvp
in
_detectedAllPaths
)
{
if
(
kvp
.
Value
.
Remove
(
path
))
{
if
(
kvp
.
Value
.
Count
==
0
)
keyToRemove
=
kvp
.
Key
;
break
;
}
}
if
(
keyToRemove
!=
null
)
_detectedAllPaths
.
Remove
(
keyToRemove
);
RefreshDetectedDisplay
();
SaveDetectedPaths
();
}
else
{
// 手动添加的路径:从下拉框和持久化列表中移除
this
.
tscbLogPath
.
Items
.
Remove
(
item
);
pathList
.
Remove
(
item
);
Utils
.
SaveJsonConfig
(
pathList
);
}
// 若下拉框还有项则选中第一个
if
(
this
.
tscbLogPath
.
Items
.
Count
>
0
)
this
.
tscbLogPath
.
SelectedIndex
=
0
;
}
/// <summary>
/// 在指定根目录下递归搜索 PDM.exe / MES.exe / U6.exe,返回其 temp 目录
/// </summary>
Dictionary
<
string
,
List
<
string
>>
DetectLogPaths
(
string
rootDir
,
IProgress
<
string
>
onProgress
=
null
)
{
var
result
=
new
Dictionary
<
string
,
List
<
string
>>();
var
targets
=
new
Dictionary
<
string
,
string
>
{
{
"PDM.exe"
,
"PDM"
},
{
"MES.exe"
,
"MES"
},
{
"U6.exe"
,
"U6"
}
};
foreach
(
var
target
in
targets
)
{
var
tempDirs
=
new
List
<
string
>();
FindExeTempDirs
(
rootDir
,
target
.
Key
,
tempDirs
,
onProgress
);
if
(
tempDirs
.
Count
>
0
)
result
[
target
.
Value
]
=
tempDirs
;
}
return
result
;
}
/// <summary>
/// 递归搜索指定目录下包含目标 exe 且存在 temp 子目录的路径
/// </summary>
void
FindExeTempDirs
(
string
dir
,
string
exeName
,
List
<
string
>
results
,
IProgress
<
string
>
onProgress
=
null
)
{
try
{
// 进度报告(每100个目录报告一次)
var
count
=
Interlocked
.
Increment
(
ref
_scanCount
);
if
(
count
%
100
==
0
&&
onProgress
!=
null
)
onProgress
.
Report
(
$"扫描中…
{
count
}
个目录"
);
// 检查当前目录
var
exePath
=
Path
.
Combine
(
dir
,
exeName
);
if
(
File
.
Exists
(
exePath
))
{
var
tempDir
=
Path
.
Combine
(
dir
,
"temp"
);
if
(
Directory
.
Exists
(
tempDir
)
&&
!
results
.
Contains
(
tempDir
))
results
.
Add
(
tempDir
);
}
// 递归子目录
foreach
(
var
subDir
in
Directory
.
EnumerateDirectories
(
dir
))
{
try
{
// 跳过系统/隐藏目录以提升性能
var
attr
=
File
.
GetAttributes
(
subDir
);
if
((
attr
&
FileAttributes
.
System
)
==
FileAttributes
.
System
)
continue
;
}
catch
{
}
FindExeTempDirs
(
subDir
,
exeName
,
results
,
onProgress
);
}
}
catch
(
UnauthorizedAccessException
)
{
}
catch
(
DirectoryNotFoundException
)
{
}
}
/// <summary>
/// 计算路径列表的最长公共前缀(用于"…"缩略显示)
/// </summary>
string
ComputeCommonPrefix
(
IEnumerable
<
string
>
paths
)
{
var
list
=
paths
.
Where
(
p
=>
!
string
.
IsNullOrEmpty
(
p
)).
ToList
();
if
(
list
.
Count
<
2
)
return
null
;
var
first
=
list
[
0
];
var
prefixLen
=
0
;
for
(
int
i
=
0
;
i
<
first
.
Length
;
i
++)
{
var
c
=
first
[
i
];
if
(
list
.
Any
(
p
=>
p
.
Length
<=
i
||
p
[
i
]
!=
c
))
break
;
prefixLen
=
i
+
1
;
}
if
(
prefixLen
<=
3
)
return
null
;
// 太短不缩略(如 "D:\")
// 截断到最后一个目录分隔符,保持显示整洁
var
prefix
=
first
.
Substring
(
0
,
prefixLen
);
var
lastSep
=
prefix
.
LastIndexOf
(
Path
.
DirectorySeparatorChar
);
if
(
lastSep
>
3
)
prefix
=
prefix
.
Substring
(
0
,
lastSep
+
1
);
return
prefix
;
}
/// <summary>
/// 刷新下拉框中探测路径的显示(移除旧的,按公共前缀缩略后重新添加)
/// </summary>
void
RefreshDetectedDisplay
()
{
_suppressPathChange
=
true
;
// 移除旧的探测项
var
toRemove
=
this
.
tscbLogPath
.
Items
.
Cast
<
string
>()
.
Where
(
p
=>
p
.
StartsWith
(
"["
))
.
ToList
();
foreach
(
var
item
in
toRemove
)
this
.
tscbLogPath
.
Items
.
Remove
(
item
);
// 计算公共前缀
var
allPaths
=
_detectedAllPaths
.
Values
.
SelectMany
(
v
=>
v
).
ToList
();
_commonPathPrefix
=
ComputeCommonPrefix
(
allPaths
);
// 添加探测路径(缩略显示)
foreach
(
var
kvp
in
_detectedAllPaths
)
{
foreach
(
var
path
in
kvp
.
Value
.
OrderBy
(
p
=>
p
))
{
var
displayPath
=
FormatDetectedPath
(
kvp
.
Key
,
path
);
this
.
tscbLogPath
.
Items
.
Add
(
displayPath
);
}
}
_suppressPathChange
=
false
;
}
/// <summary>
/// 格式化探测路径:类型前缀 + 缩略路径
/// </summary>
string
FormatDetectedPath
(
string
type
,
string
fullPath
)
{
if
(!
string
.
IsNullOrEmpty
(
_commonPathPrefix
)
&&
fullPath
.
StartsWith
(
_commonPathPrefix
))
{
var
rest
=
fullPath
.
Substring
(
_commonPathPrefix
.
Length
);
return
$"[
{
type
}
] …
{
rest
}
"
;
}
return
$"[
{
type
}
]
{
fullPath
}
"
;
}
/// <summary>
/// 持久化探测到的路径
/// </summary>
void
SaveDetectedPaths
()
{
var
saved
=
Utils
.
GetJsonConfig
<
DetectedLogPaths
>();
saved
.
Paths
=
_detectedAllPaths
.
ToDictionary
(
kvp
=>
kvp
.
Key
,
kvp
=>
kvp
.
Value
.
Distinct
().
OrderBy
(
p
=>
p
).
ToList
()
);
Utils
.
SaveJsonConfig
(
saved
);
}
/// <summary>
/// 加载已持久化的探测路径到下拉框
/// </summary>
void
LoadDetectedPaths
()
{
var
saved
=
Utils
.
GetJsonConfig
<
DetectedLogPaths
>();
if
(
saved
.
Paths
==
null
||
saved
.
Paths
.
Count
==
0
)
return
;
_detectedAllPaths
=
saved
.
Paths
.
ToDictionary
(
kvp
=>
kvp
.
Key
,
kvp
=>
kvp
.
Value
.
Where
(
Directory
.
Exists
).
Distinct
().
ToList
()
);
// 清理不存在的条目
foreach
(
var
key
in
_detectedAllPaths
.
Keys
.
ToList
())
{
if
(
_detectedAllPaths
[
key
].
Count
==
0
)
_detectedAllPaths
.
Remove
(
key
);
}
if
(
_detectedAllPaths
.
Count
>
0
)
RefreshDetectedDisplay
();
}
/// <summary>
/// 探测路径持久化容器(支持同类型多实例)
/// </summary>
class
DetectedLogPaths
{
public
Dictionary
<
string
,
List
<
string
>>
Paths
{
get
;
set
;
}
=
new
Dictionary
<
string
,
List
<
string
>>();
}
}
}
}
}
Tools/UI/wTextFormatter.Designer.cs
View file @
8afe8a66
...
@@ -400,7 +400,7 @@ namespace Tools.UI
...
@@ -400,7 +400,7 @@ namespace Tools.UI
this
.
Controls
.
Add
(
this
.
tabControl
);
this
.
Controls
.
Add
(
this
.
tabControl
);
this
.
MinimumSize
=
new
System
.
Drawing
.
Size
(
640
,
420
);
this
.
MinimumSize
=
new
System
.
Drawing
.
Size
(
640
,
420
);
this
.
Name
=
"wTextFormatter"
;
this
.
Name
=
"wTextFormatter"
;
this
.
Text
=
"
文本格式化
"
;
this
.
Text
=
"
格式化工具
"
;
this
.
tabControl
.
ResumeLayout
(
false
);
this
.
tabControl
.
ResumeLayout
(
false
);
this
.
tabSql
.
ResumeLayout
(
false
);
this
.
tabSql
.
ResumeLayout
(
false
);
this
.
tlpSql
.
ResumeLayout
(
false
);
this
.
tlpSql
.
ResumeLayout
(
false
);
...
...
Tools/wExtend.cs
View file @
8afe8a66
...
@@ -45,12 +45,11 @@ namespace Tools
...
@@ -45,12 +45,11 @@ namespace Tools
this
.
CreateModule
(
Tools_uni
,
typeof
(
UI
.
wTaskBoard
),
"任务看板"
,
"tr:任务看板的简易化工具,免除翻页困扰"
),
this
.
CreateModule
(
Tools_uni
,
typeof
(
UI
.
wTaskBoard
),
"任务看板"
,
"tr:任务看板的简易化工具,免除翻页困扰"
),
this
.
CreateModule
(
Tools_uni
,
typeof
(
UI
.
wLoginPwd
),
"密码解析"
,
"tr:用于登陆密码的解密加密"
),
this
.
CreateModule
(
Tools_uni
,
typeof
(
UI
.
wLoginPwd
),
"密码解析"
,
"tr:用于登陆密码的解密加密"
),
this
.
CreateModule
(
Tools_uni
,
typeof
(
WindowsSearch
.
FrmSearch
),
"文件搜索"
,
"lxj:用于搜索文件内容"
),
this
.
CreateModule
(
Tools_uni
,
typeof
(
WindowsSearch
.
FrmSearch
),
"文件搜索"
,
"lxj:用于搜索文件内容"
),
this
.
CreateModule
(
Tools_uni
,
typeof
(
UI
.
wTextFormatter
),
"文本格式化"
,
"tr:SQL和JSON文本格式化工具"
),
//-------------------------------
this
.
CreateModule
(
Tools_imp
,
typeof
(
UpdateExecuter
.
frmMain
),
"更新执行工具"
,
"lxj:用于客户更新和执行SQL脚本"
),
this
.
CreateModule
(
Tools_imp
,
typeof
(
UpdateExecuter
.
frmMain
),
"更新执行工具"
,
"lxj:用于客户更新和执行SQL脚本"
),
this
.
CreateModule
(
Tools_imp
,
typeof
(
UI
.
wEmptyTable
),
"清空表"
,
"tr:输入主表、自动清空关联表的工具"
),
this
.
CreateModule
(
Tools_imp
,
typeof
(
UI
.
wEmptyTable
),
"清空表"
,
"tr:输入主表、自动清空关联表的工具"
),
this
.
CreateModule
(
Tools_imp
,
typeof
(
UI
.
wExcelExportSetting
),
"Excel导出设置"
,
"tr:Excel导出配置图形化版本"
),
this
.
CreateModule
(
Tools_imp
,
typeof
(
UI
.
wExcelExportSetting
),
"Excel导出设置"
,
"tr:Excel导出配置图形化版本"
),
this
.
CreateModule
(
Tools_imp
,
typeof
(
UI
.
wLogViewer
),
"日志查看器"
,
"tr:可以方便的查看系统日志"
),
this
.
CreateModule
(
Tools_imp
,
typeof
(
UI
.
wLogViewer
),
"日志查看器"
,
"tr:可以方便的查看系统日志"
),
this
.
CreateModule
(
Tools_imp
,
typeof
(
UI
.
wTextFormatter
),
"格式化工具"
,
"tr:SQL和JSON文本格式化工具"
),
this
.
CreateModule
(
Tools_imp
,
typeof
(
DotNetVersionChecker
.
FrmVersion
),
"系统检测"
,
"lxj:检测操作系统运行环境"
),
this
.
CreateModule
(
Tools_imp
,
typeof
(
DotNetVersionChecker
.
FrmVersion
),
"系统检测"
,
"lxj:检测操作系统运行环境"
),
//-------------------------------
//-------------------------------
this
.
CreateModule
(
Tools_dev
,
typeof
(
Ftt
.
PatchPack
.
FrmMain
),
"自动打包工具"
,
"lxj:用于CS后端程序自动打包成压缩包"
),
this
.
CreateModule
(
Tools_dev
,
typeof
(
Ftt
.
PatchPack
.
FrmMain
),
"自动打包工具"
,
"lxj:用于CS后端程序自动打包成压缩包"
),
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment