基于HTML Help Workshop生成chm格式数据库说明文档
解决的问题现有的工具都存在一些问题:比如smart Sql,无法一次完成多个库文档生成到一个文件中,且使用过程中存在表结构过多时生成失败问题,像视图、存储过程部分生成也存在一定局限性(无DDL信息,一般没有存储过程);故想研究一下自行代码实现。
基本思路
[*]基于ado.net读取元数据,需要啥读啥,目前只包含了表、视图、存储过程,数据库基于oracle;
[*]生成对应的html、hhp、hhc文件;
[*]调起HTML Help Workshop工具生成chm格式文件。
具体源码
[*]读取元数据
public class OracleMetadataService : IDisposable
{
private readonly OracleConnection _connection;
public OracleMetadataService(string connectionString)
{
_connection = new OracleConnection(connectionString);
_connection.Open();
}
public List<TableInfo> GetTables()
{
var tables = new List<TableInfo>();
// 优化:批量获取表基本信息和列信息
using (var cmd = new OracleCommand(
@"SELECT table_name, comments
FROM user_tab_comments
WHERE table_type = 'TABLE'
ORDER BY table_name", _connection))
{
using (var reader = cmd.ExecuteReader())
{
while (reader.Read())
{
var table = new TableInfo
{
TableName = reader["table_name"].ToString(),
Comments = reader["comments"].ToString()
};
table.Columns = GetColumns(table.TableName);
SetPrimaryKeys(table);
SetForeignKeys(table);
tables.Add(table);
}
}
}
Console.WriteLine("表信息获取完成");
return tables;
}
private List<ColumnInfo> GetColumns(string tableName)
{
var columns = new List<ColumnInfo>();
using (var cmd = new OracleCommand(
@"SELECT
tc.column_name, data_type, data_length, data_precision, data_scale,
nullable, data_default, comments
FROM user_tab_columns tc
LEFT JOIN user_col_comments cc ON tc.table_name = cc.table_name AND tc.column_name = cc.column_name
WHERE tc.table_name = :tableName
ORDER BY column_id", _connection))
{
cmd.Parameters.Add("tableName", OracleDbType.Varchar2).Value = tableName;
using (var reader = cmd.ExecuteReader())
{
while (reader.Read())
{
columns.Add(new ColumnInfo
{
ColumnName = reader["column_name"].ToString(),
DataType = reader["data_type"].ToString(),
DataLength = reader["data_length"] as int?,
DataPrecision = reader["data_precision"] as int?,
DataScale = reader["data_scale"] as int?,
Nullable = reader["nullable"].ToString(),
DefaultValue = reader["data_default"]?.ToString(),
Comments = reader["comments"]?.ToString()
});
}
}
}
return columns;
}
private void SetPrimaryKeys(TableInfo table)
{
using (var cmd = new OracleCommand(
@"SELECT cols.column_name
FROM user_constraints cons, user_cons_columns cols
WHERE cons.constraint_type = 'P'
AND cons.constraint_name = cols.constraint_name
AND cons.table_name = :tableName", _connection))
{
cmd.Parameters.Add("tableName", OracleDbType.Varchar2).Value = table.TableName;
using (var reader = cmd.ExecuteReader())
{
while (reader.Read())
{
var columnName = reader["column_name"].ToString();
var column = table.Columns.Find(c => c.ColumnName == columnName);
if (column != null)
{
column.IsPrimaryKey = true;
}
}
}
}
}
private void SetForeignKeys(TableInfo table)
{
using (var cmd = new OracleCommand(
@"SELECT cols.column_name
FROM user_constraints cons, user_cons_columns cols
WHERE cons.constraint_type = 'R'
AND cons.constraint_name = cols.constraint_name
AND cons.table_name = :tableName", _connection))
{
cmd.Parameters.Add("tableName", OracleDbType.Varchar2).Value = table.TableName;
using (var reader = cmd.ExecuteReader())
{
while (reader.Read())
{
var columnName = reader["column_name"].ToString();
var column = table.Columns.Find(c => c.ColumnName == columnName);
if (column != null)
{
column.IsForeignKey = true;
}
}
}
}
}
public List<ViewInfo> GetViews(string owner)
{
var views = new List<ViewInfo>();
// 获取视图基本信息
using (var cmd = new OracleCommand(
@"SELECT view_name, text, comments
FROM user_views v
LEFT JOIN user_tab_comments tc ON v.view_name = tc.table_name
ORDER BY view_name", _connection))
{
using (var reader = cmd.ExecuteReader())
{
while (reader.Read())
{
views.Add(new ViewInfo
{
ViewName = reader["view_name"].ToString(),
//Definition = reader.GetOracleClob(0)?.Value,
Comments = reader["comments"]?.ToString()
});
}
}
}
Console.WriteLine($"视图基本信息获取完成");
// 获取每张视图的列信息
foreach (var view in views)
{
view.Definition = GetViewDDL(_connection, view.ViewName, owner);
view.Columns = GetColumns(view.ViewName);
Console.WriteLine($"视图[{view.ViewName}]列信息获取完成");
}
return views;
}
static string GetViewDDL(OracleConnection connection, string viewName, string schema)
{
string sql = @"
SELECT DBMS_METADATA.GET_DDL('VIEW', :viewName, :schema)
FROM DUAL";
using (OracleCommand command = new OracleCommand(sql, connection))
{
command.Parameters.Add(":viewName", viewName);
command.Parameters.Add(":schema", schema);
command.InitialLOBFetchSize = 1000000;
object result = command.ExecuteScalar();
return result != null ? result.ToString() : string.Empty;
}
}
public List<ProcedureInfo> GetProcedures()
{
var procedures = new List<ProcedureInfo>();
// 获取存储过程和函数基本信息
using (var cmd = new OracleCommand(
@"SELECT object_name, object_type, status, created, last_ddl_time
FROM user_objects
WHERE object_type IN ('PROCEDURE', 'FUNCTION')
ORDER BY object_type, object_name", _connection))
{
using (var reader = cmd.ExecuteReader())
{
while (reader.Read())
{
procedures.Add(new ProcedureInfo
{
ProcedureName = reader["object_name"].ToString(),
ProcedureType = reader["object_type"].ToString()
});
}
}
}
Console.WriteLine($"存储过程基本信息获取完成");
// 获取每个对象的详细信息和参数
foreach (var proc in procedures)
{
// 获取源代码
using (var cmd = new OracleCommand(
@"SELECT text
FROM user_source
WHERE name = :procName
AND type = :procType
ORDER BY line", _connection))
{
cmd.Parameters.Add("procName", OracleDbType.Varchar2).Value = proc.ProcedureName;
cmd.Parameters.Add("procType", OracleDbType.Varchar2).Value = proc.ProcedureType;
var source = new System.Text.StringBuilder();
using (var reader = cmd.ExecuteReader())
{
while (reader.Read())
{
source.AppendLine(reader["text"].ToString());
}
}
proc.SourceCode = source.ToString();
}
// 获取参数信息
proc.Parameters = GetProcedureParameters(proc.ProcedureName);
// 如果是函数,获取返回类型
if (proc.ProcedureType == "FUNCTION")
{
using (var cmd = new OracleCommand(
@"SELECT data_type
FROM user_arguments
WHERE object_name = :procName
AND argument_name IS NULL", _connection))
{
cmd.Parameters.Add("procName", OracleDbType.Varchar2).Value = proc.ProcedureName;
proc.ReturnType = cmd.ExecuteScalar()?.ToString();
}
}
//// 获取注释
//using (var cmd = new OracleCommand(
// @"SELECT comments
//FROM user_procedures
//WHERE object_name = :procName
//AND object_type = :procType", _connection))
//{
// cmd.Parameters.Add("procName", OracleDbType.Varchar2).Value = proc.ProcedureName;
// cmd.Parameters.Add("procType", OracleDbType.Varchar2).Value = proc.ProcedureType;
// proc.Comments = cmd.ExecuteScalar()?.ToString();
//}
Console.WriteLine($"存储过程[{proc.ProcedureName}]详细信息获取完成");
}
return procedures;
}
private List<ParameterInfo> GetProcedureParameters(string procedureName)
{
var parameters = new List<ParameterInfo>();
using (var cmd = new OracleCommand(
@"SELECT argument_name, data_type, in_out, position
FROM user_arguments
WHERE object_name = :procName
AND argument_name IS NOT NULL
ORDER BY position", _connection))
{
cmd.Parameters.Add("procName", OracleDbType.Varchar2).Value = procedureName;
using (var reader = cmd.ExecuteReader())
{
while (reader.Read())
{
parameters.Add(new ParameterInfo
{
ParameterName = reader["argument_name"].ToString(),
DataType = reader["data_type"].ToString(),
InOut = reader["in_out"].ToString(),
Position = reader["position"] as int?
});
}
}
}
return parameters;
}
public void Dispose()
{
_connection?.Close();
_connection?.Dispose();
}
}
[*]生成html、hhp、hhc文件
public class HtmlGeneratorService
{
private readonly string _outputDirectory;
public HtmlGeneratorService(string outputDirectory)
{
_outputDirectory = outputDirectory;
Directory.CreateDirectory(_outputDirectory);
}
public void GenerateHtmlFiles(Dictionary<string, Tuple<List<TableInfo>, List<ViewInfo>, List<ProcedureInfo>>> dic)
{
GenerateIndexFile(dic);
foreach (var item in dic)
{
GenerateTablesFiles(item.Key, item.Value.Item1);
GenerateViewsFiles(item.Key, item.Value.Item2);
GenerateProceduresFiles(item.Key, item.Value.Item3);
}
GenerateStyleSheet();
}
private void GenerateIndexFile(Dictionary<string, Tuple<List<TableInfo>, List<ViewInfo>, List<ProcedureInfo>>> dic)
{
var sb = new StringBuilder();
sb.AppendLine("<!DOCTYPE html>");
sb.AppendLine("<html>");
sb.AppendLine("<head>");
sb.AppendLine($"<title>数据库说明文档</title>");
sb.AppendLine("<link rel=\"stylesheet\" href=\"styles.css\">");
sb.AppendLine("</head>");
sb.AppendLine("<body>");
sb.AppendLine($"<h1>数据库说明文档</h1>");
foreach (var item in dic)
{
sb.AppendLine($"<h2>{item.Key}</h2>");
// 表目录
sb.AppendLine("<h3>Tables</h3>");
sb.AppendLine("<ul>");
foreach (var table in item.Value.Item1)
{
sb.AppendLine($"<li>{table.TableName} - {table.Comments}</li>");
}
sb.AppendLine("</ul>");
// 视图目录
sb.AppendLine("<h3>Views</h3>");
sb.AppendLine("<ul>");
foreach (var view in item.Value.Item2)
{
sb.AppendLine($"<li>{view.ViewName} - {view.Comments}</li>");
}
sb.AppendLine("</ul>");
// 存储过程和函数目录
sb.AppendLine("<h3>Procedures and Functions</h3>");
sb.AppendLine("<ul>");
foreach (var proc in item.Value.Item3)
{
sb.AppendLine($"<li>{proc.ProcedureName} ({proc.ProcedureType})</li>");
}
sb.AppendLine("</ul>");
}
sb.AppendLine("</body>");
sb.AppendLine("</html>");
File.WriteAllText(Path.Combine(_outputDirectory, "index.html"), sb.ToString(), Encoding.UTF8);
}
private void GenerateTablesFiles(string owner, List<TableInfo> tables)
{
var tablesDir = Path.Combine(_outputDirectory, owner, "tables");
Directory.CreateDirectory(tablesDir);
foreach (var table in tables)
{
var sb = new StringBuilder();
sb.AppendLine("<!DOCTYPE html>");
sb.AppendLine("<html>");
sb.AppendLine("<head>");
sb.AppendLine($"<title>Table: {table.TableName}</title>");
sb.AppendLine("<link rel=\"stylesheet\" href=\"../../styles.css\">");
sb.AppendLine("</head>");
sb.AppendLine("<body>");
sb.AppendLine($"<h1>Table: {table.TableName}</h1>");
sb.AppendLine($"<p>{table.Comments}</p>");
sb.AppendLine("<h2>Columns</h2>");
sb.AppendLine("<table class=\"gridtable\">");
sb.AppendLine("<tr>");
sb.AppendLine("<th>Name</th>");
sb.AppendLine("<th>Type</th>");
sb.AppendLine("<th>Length</th>");
sb.AppendLine("<th>Precision</th>");
sb.AppendLine("<th>Scale</th>");
sb.AppendLine("<th>Nullable</th>");
sb.AppendLine("<th>Default</th>");
sb.AppendLine("<th>PK</th>");
sb.AppendLine("<th>FK</th>");
sb.AppendLine("<th>Description</th>");
sb.AppendLine("</tr>");
foreach (var column in table.Columns)
{
sb.AppendLine("<tr>");
sb.AppendLine($"<td>{column.ColumnName}</td>");
sb.AppendLine($"<td>{column.DataType}</td>");
sb.AppendLine($"<td>{column.DataLength}</td>");
sb.AppendLine($"<td>{column.DataPrecision}</td>");
sb.AppendLine($"<td>{column.DataScale}</td>");
sb.AppendLine($"<td>{column.Nullable}</td>");
sb.AppendLine($"<td>{column.DefaultValue}</td>");
sb.AppendLine($"<td>{(column.IsPrimaryKey ? "✓" : "")}</td>");
sb.AppendLine($"<td>{(column.IsForeignKey ? "✓" : "")}</td>");
sb.AppendLine($"<td>{column.Comments}</td>");
sb.AppendLine("</tr>");
}
sb.AppendLine("</table>");
sb.AppendLine("<p>Back to Index</p>");
sb.AppendLine("</body>");
sb.AppendLine("</html>");
File.WriteAllText(Path.Combine(tablesDir, $"{table.TableName}.html"), sb.ToString(), Encoding.UTF8);
}
}
private void GenerateViewsFiles(string owner, List<ViewInfo> views)
{
var viewsDir = Path.Combine(_outputDirectory, owner, "views");
Directory.CreateDirectory(viewsDir);
foreach (var view in views)
{
var sb = new StringBuilder();
sb.AppendLine("<!DOCTYPE html>");
sb.AppendLine("<html>");
sb.AppendLine("<head>");
sb.AppendLine($"<title>View: {view.ViewName}</title>");
sb.AppendLine("<link rel=\"stylesheet\" href=\"../../styles.css\">");
sb.AppendLine("</head>");
sb.AppendLine("<body>");
sb.AppendLine($"<h1>View: {view.ViewName}</h1>");
sb.AppendLine($"<p>{view.Comments}</p>");
sb.AppendLine("<h2>Columns</h2>");
sb.AppendLine("<table class=\"gridtable\">");
sb.AppendLine("<tr>");
sb.AppendLine("<th>Name</th>");
sb.AppendLine("<th>Type</th>");
sb.AppendLine("<th>Length</th>");
sb.AppendLine("<th>Precision</th>");
sb.AppendLine("<th>Scale</th>");
sb.AppendLine("<th>Nullable</th>");
sb.AppendLine("<th>Description</th>");
sb.AppendLine("</tr>");
foreach (var column in view.Columns)
{
sb.AppendLine("<tr>");
sb.AppendLine($"<td>{column.ColumnName}</td>");
sb.AppendLine($"<td>{column.DataType}</td>");
sb.AppendLine($"<td>{column.DataLength}</td>");
sb.AppendLine($"<td>{column.DataPrecision}</td>");
sb.AppendLine($"<td>{column.DataScale}</td>");
sb.AppendLine($"<td>{column.Nullable}</td>");
sb.AppendLine($"<td>{column.Comments}</td>");
sb.AppendLine("</tr>");
}
sb.AppendLine("</table>");
sb.AppendLine("<h2>Definition</h2>");
sb.AppendLine("<pre>");
sb.AppendLine(view.Definition);
sb.AppendLine("</pre>");
sb.AppendLine("<p>Back to Index</p>");
sb.AppendLine("</body>");
sb.AppendLine("</html>");
File.WriteAllText(Path.Combine(viewsDir, $"{view.ViewName}.html"), sb.ToString(), Encoding.UTF8);
}
}
private void GenerateProceduresFiles(string owner, List<ProcedureInfo> procedures)
{
var procsDir = Path.Combine(_outputDirectory, owner, "procedures");
Directory.CreateDirectory(procsDir);
foreach (var proc in procedures)
{
var sb = new StringBuilder();
sb.AppendLine("<!DOCTYPE html>");
sb.AppendLine("<html>");
sb.AppendLine("<head>");
sb.AppendLine($"<title>{proc.ProcedureType}: {proc.ProcedureName}</title>");
sb.AppendLine("<link rel=\"stylesheet\" href=\"../../styles.css\">");
sb.AppendLine("</head>");
sb.AppendLine("<body>");
sb.AppendLine($"<h1>{proc.ProcedureType}: {proc.ProcedureName}</h1>");
sb.AppendLine($"<p>{proc.Comments}</p>");
if (proc.ProcedureType == "FUNCTION" && !string.IsNullOrEmpty(proc.ReturnType))
{
sb.AppendLine($"<p>Return Type: {proc.ReturnType}</p>");
}
if (proc.Parameters.Count > 0)
{
sb.AppendLine("<h2>Parameters</h2>");
sb.AppendLine("<table class=\"gridtable\">");
sb.AppendLine("<tr>");
sb.AppendLine("<th>Name</th>");
sb.AppendLine("<th>Type</th>");
sb.AppendLine("<th>In/Out</th>");
sb.AppendLine("<th>Position</th>");
sb.AppendLine("</tr>");
foreach (var param in proc.Parameters)
{
sb.AppendLine("<tr>");
sb.AppendLine($"<td>{param.ParameterName}</td>");
sb.AppendLine($"<td>{param.DataType}</td>");
sb.AppendLine($"<td>{param.InOut}</td>");
sb.AppendLine($"<td>{param.Position}</td>");
sb.AppendLine("</tr>");
}
sb.AppendLine("</table>");
}
sb.AppendLine("<h2>Source Code</h2>");
sb.AppendLine("<pre>");
sb.AppendLine(proc.SourceCode);
sb.AppendLine("</pre>");
sb.AppendLine("<p>Back to Index</p>");
sb.AppendLine("</body>");
sb.AppendLine("</html>");
File.WriteAllText(Path.Combine(procsDir, $"{proc.ProcedureName}.html"), sb.ToString(), Encoding.UTF8);
}
}
private void GenerateStyleSheet()
{
var css = @"
body {
font-family: Consolas, monospace;
line -height: 1.6;
margin: 20px;
color: #808080;
}
h1, h2, h3 {
color: #0066cc;
}
table.gridtable {
width: 100%;
border-collapse: collapse;
margin: 15px 0;
}
table.gridtable th {
background-color: #006400;
color: white;
text-align: center;
padding: 3px;
}
table.gridtable td {
border:#ddd;
padding: 3px;
}
table.gridtable tr:nth-child(even) {
background-color: #f2f2f2;
}
pre {
background-color: #f5f5f5;
padding: 10px;
border-radius: 5px;
overflow-x: auto;
}
a {
color: #A52A2A;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
";
File.WriteAllText(Path.Combine(_outputDirectory, "styles.css"), css);
}
}
[*]生成chm文件
public class ChmCompiler
{
private readonly string _htmlHelpCompilerPath;
public ChmCompiler(string htmlHelpCompilerPath)
{
_htmlHelpCompilerPath = htmlHelpCompilerPath;
}
public bool Compile(string projectFilePath, string outputChmPath)
{
if (!File.Exists(_htmlHelpCompilerPath))
{
throw new FileNotFoundException("HTML Help Compiler (hhc.exe) not found at the specified path.");
}
if (!File.Exists(projectFilePath))
{
throw new FileNotFoundException("HHP project file not found.");
}
try
{
var process = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = _htmlHelpCompilerPath,
Arguments = $"\"{projectFilePath}\"",
UseShellExecute = false,
CreateNoWindow = true,
RedirectStandardOutput = true,
RedirectStandardError = true
}
};
process.Start();
process.WaitForExit(60 * 1000);
//if (process.ExitCode != 0)
//{
// var error = process.StandardError.ReadToEnd();
// throw new Exception($"CHM compilation failed with error: {error}");
//}
// 检查生成的CHM文件是否存在
return File.Exists(outputChmPath);
}
catch (Exception ex)
{
throw new Exception("Error during CHM compilation: " + ex.Message, ex);
}
}
public void CreateHhpFile(string outputDirectory, string projectName)
{
var hhpContent = $@"
Compatibility=1.1 or later
Compiled file={projectName}.chm
Contents file={projectName}.hhc
Default topic=index.html
Display compile progress=No
Full-text search=Yes
Language=0x409 English (United States)
Title={projectName} Database Documentation
";
// 添加所有HTML文件
var htmlFiles = Directory.GetFiles(outputDirectory, "*.html", SearchOption.AllDirectories);
foreach (var file in htmlFiles)
{
var relativePath = file.Substring(outputDirectory.Length + 1);
hhpContent += relativePath + "\n";
}
// 添加CSS文件
hhpContent += "styles.css\n";
File.WriteAllText(Path.Combine(outputDirectory, $"{projectName}.hhp"), hhpContent);
// 创建目录文件 (.hhc)
CreateHhcFile(outputDirectory, projectName);
}
private void CreateHhcFile(string outputDirectory, string projectName)
{
var hhcContent = new StringBuilder(@"<!DOCTYPE HTML PUBLIC ""-//IETF//DTD HTML//EN"">
<HTML>
<HEAD>
<meta name=""GENERATOR"" content=""Microsoft® HTML Help Workshop 4.1"">
</HEAD>
<BODY>
<OBJECT type=""text/site properties"">
<param name=""Window Styles"" value=""0x800025"">
</OBJECT>
<UL>
<LI> <OBJECT type=""text/sitemap"">
<param name=""Name"" value=""Database Documentation"">
<param name=""Local"" value=""index.html"">
</OBJECT>
<UL>");
// 获取所有Owner
var owners = GetAllOwners(outputDirectory);
foreach (var owner in owners)
{
hhcContent.AppendLine($@"
<LI> <OBJECT type=""text/sitemap"">
<param name=""Name"" value=""{owner}"">
</OBJECT>
<UL>");
// 添加Tables分组
hhcContent.AppendLine($@"
<LI> <OBJECT type=""text/sitemap"">
<param name=""Name"" value=""Tables"">
</OBJECT>
<UL>");
AddFilesToContent(hhcContent, Path.Combine(outputDirectory, owner, "tables"), outputDirectory);
hhcContent.AppendLine($@"
</UL>");
// 添加Views分组
hhcContent.AppendLine($@"
<LI> <OBJECT type=""text/sitemap"">
<param name=""Name"" value=""Views"">
</OBJECT>
<UL>");
AddFilesToContent(hhcContent, Path.Combine(outputDirectory, owner, "views"), outputDirectory);
hhcContent.AppendLine($@"
</UL>");
// 添加Procedures分组
hhcContent.AppendLine($@"
<LI> <OBJECT type=""text/sitemap"">
<param name=""Name"" value=""Procedures and Functions"">
</OBJECT>
<UL>");
AddFilesToContent(hhcContent, Path.Combine(outputDirectory, owner, "procedures"), outputDirectory);
hhcContent.AppendLine($@"
</UL>");
hhcContent.AppendLine($@"
</UL>");
}
hhcContent.AppendLine($@"
</UL>
</UL>
</BODY>
</HTML>");
File.WriteAllText(Path.Combine(outputDirectory, $"{projectName}.hhc"), hhcContent.ToString());
}
// 获取所有Owner的辅助方法
private List<string> GetAllOwners(string outputDirectory)
{
// 方法1:从目录结构中获取
return Directory.GetDirectories(outputDirectory)
.Select(d => new DirectoryInfo(d).Name)
.ToList();
// 方法2:从数据库中获取(需要实现)
// return GetOwnersFromDatabase();
}
// 之前定义的辅助方法
private void AddFilesToContent(StringBuilder content, string directory, string outputDirectory)
{
if (!Directory.Exists(directory))
{
return;
}
foreach (var file in Directory.GetFiles(directory, "*.html"))
{
var itemName = Path.GetFileNameWithoutExtension(file);
var relativePath = file.Substring(outputDirectory.Length + 1);
content.AppendLine($@"
<LI> <OBJECT type=""text/sitemap"">
<param name=""Name"" value=""{itemName}"">
<param name=""Local"" value=""{relativePath}"">
</OBJECT>");
}
}
}
[*]调用
internal class Program
{
// 查找HHC编译器
private static string FindHhcCompiler()
{
// 检查常见位置
string[] possiblePaths = new[]
{
@"C:\Program Files (x86)\HTML Help Workshop\hhc.exe",
@"C:\Program Files\HTML Help Workshop\hhc.exe"
};
foreach (string path in possiblePaths)
{
if (File.Exists(path))
{
return path;
}
}
// 检查PATH环境变量
string pathEnv = Environment.GetEnvironmentVariable("PATH");
if (!string.IsNullOrEmpty(pathEnv))
{
string[] paths = pathEnv.Split(Path.PathSeparator);
foreach (string path in paths)
{
string fullPath = Path.Combine(path.Trim(), "hhc.exe");
if (File.Exists(fullPath))
{
return fullPath;
}
}
}
return null;
}
private static void Main(string[] args)
{
Console.WriteLine("Oracle Database CHM Documentation Generator");
Console.WriteLine("==========================================");
try
{
//数据库列表,自行添加
List<string> schemaList = new List<string>
{
};
// 配置参数
var baseDir = Path.Combine(Directory.GetCurrentDirectory(), "OracleDocs");
var hhcPath = FindHhcCompiler();
var outputDir = Path.Combine(baseDir, "OracleDocs");
var dic = new Dictionary<string, Tuple<List<TableInfo>, List<ViewInfo>, List<ProcedureInfo>>>();
foreach (var item in schemaList)
{
var projectName = $"TH_{item}";
var connectionString = $"";//自行添加
// 创建服务实例
using (var metadataService = new OracleMetadataService(connectionString))
{
Console.WriteLine("Fetching database metadata...");
// 获取元数据
var tables = metadataService.GetTables();
var views = metadataService.GetViews(projectName);
var procedures = metadataService.GetProcedures();
dic.Add(projectName, Tuple.Create(tables, views, procedures));
Console.WriteLine($"Found {tables.Count} tables, {views.Count} views, {procedures.Count} procedures/functions");
}
}
// 生成HTML文档
var htmlGenerator = new HtmlGeneratorService(outputDir);
Console.WriteLine("Generating HTML files...");
htmlGenerator.GenerateHtmlFiles(dic);
// 编译CHM
var chmCompiler = new ChmCompiler(hhcPath);
Console.WriteLine("Creating CHM project files...");
chmCompiler.CreateHhpFile(outputDir, "OracleDocs");
Console.WriteLine("Compiling CHM file...");
var hhpFilePath = Path.Combine(outputDir, $"TH.hhp");
var chmFilePath = Path.Combine(outputDir, $"TH.chm");
//var result = Task.Run(async () => await chmCompiler.RunProcessAsync(hhpFilePath)).Result;
if (chmCompiler.Compile(hhpFilePath, chmFilePath))
//if (result == 0)
{
Console.WriteLine($"Successfully generated CHM file: {chmFilePath}");
}
else
{
Console.WriteLine("CHM compilation failed.");
}
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex.Message}");
if (ex.InnerException != null)
{
Console.WriteLine($"Inner exception: {ex.InnerException.Message}");
}
}
Console.WriteLine("Press any key to exit...");
Console.ReadKey();
}
}
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
页:
[1]