找回密码
 立即注册
首页 业界区 安全 基于HTML Help Workshop生成chm格式数据库说明文档 ...

基于HTML Help Workshop生成chm格式数据库说明文档

稼布欤 2025-6-28 09:38:20
解决的问题

现有的工具都存在一些问题:比如smart Sql,无法一次完成多个库文档生成到一个文件中,且使用过程中存在表结构过多时生成失败问题,像视图、存储过程部分生成也存在一定局限性(无DDL信息,一般没有存储过程);故想研究一下自行代码实现。
基本思路


  • 基于ado.net读取元数据,需要啥读啥,目前只包含了表、视图、存储过程,数据库基于oracle;
  • 生成对应的html、hhp、hhc文件;
  • 调起HTML Help Workshop工具生成chm格式文件。
具体源码


  • 读取元数据
  1.     public class OracleMetadataService : IDisposable
  2.     {
  3.         private readonly OracleConnection _connection;
  4.         public OracleMetadataService(string connectionString)
  5.         {
  6.             _connection = new OracleConnection(connectionString);
  7.             _connection.Open();
  8.         }
  9.         public List<TableInfo> GetTables()
  10.         {
  11.             var tables = new List<TableInfo>();
  12.             // 优化:批量获取表基本信息和列信息
  13.             using (var cmd = new OracleCommand(
  14.                 @"SELECT table_name, comments
  15.               FROM user_tab_comments
  16.               WHERE table_type = 'TABLE'
  17.               ORDER BY table_name", _connection))
  18.             {
  19.                 using (var reader = cmd.ExecuteReader())
  20.                 {
  21.                     while (reader.Read())
  22.                     {
  23.                         var table = new TableInfo
  24.                         {
  25.                             TableName = reader["table_name"].ToString(),
  26.                             Comments = reader["comments"].ToString()
  27.                         };
  28.                         table.Columns = GetColumns(table.TableName);
  29.                         SetPrimaryKeys(table);
  30.                         SetForeignKeys(table);
  31.                         tables.Add(table);
  32.                     }
  33.                 }
  34.             }
  35.             Console.WriteLine("表信息获取完成");
  36.             return tables;
  37.         }
  38.         private List<ColumnInfo> GetColumns(string tableName)
  39.         {
  40.             var columns = new List<ColumnInfo>();
  41.             using (var cmd = new OracleCommand(
  42.                 @"SELECT
  43.                 tc.column_name, data_type, data_length, data_precision, data_scale,
  44.                 nullable, data_default, comments
  45.               FROM user_tab_columns tc
  46.               LEFT JOIN user_col_comments cc ON tc.table_name = cc.table_name AND tc.column_name = cc.column_name
  47.               WHERE tc.table_name = :tableName
  48.               ORDER BY column_id", _connection))
  49.             {
  50.                 cmd.Parameters.Add("tableName", OracleDbType.Varchar2).Value = tableName;
  51.                 using (var reader = cmd.ExecuteReader())
  52.                 {
  53.                     while (reader.Read())
  54.                     {
  55.                         columns.Add(new ColumnInfo
  56.                         {
  57.                             ColumnName = reader["column_name"].ToString(),
  58.                             DataType = reader["data_type"].ToString(),
  59.                             DataLength = reader["data_length"] as int?,
  60.                             DataPrecision = reader["data_precision"] as int?,
  61.                             DataScale = reader["data_scale"] as int?,
  62.                             Nullable = reader["nullable"].ToString(),
  63.                             DefaultValue = reader["data_default"]?.ToString(),
  64.                             Comments = reader["comments"]?.ToString()
  65.                         });
  66.                     }
  67.                 }
  68.             }
  69.             return columns;
  70.         }
  71.         private void SetPrimaryKeys(TableInfo table)
  72.         {
  73.             using (var cmd = new OracleCommand(
  74.                 @"SELECT cols.column_name
  75.               FROM user_constraints cons, user_cons_columns cols
  76.               WHERE cons.constraint_type = 'P'
  77.               AND cons.constraint_name = cols.constraint_name
  78.               AND cons.table_name = :tableName", _connection))
  79.             {
  80.                 cmd.Parameters.Add("tableName", OracleDbType.Varchar2).Value = table.TableName;
  81.                 using (var reader = cmd.ExecuteReader())
  82.                 {
  83.                     while (reader.Read())
  84.                     {
  85.                         var columnName = reader["column_name"].ToString();
  86.                         var column = table.Columns.Find(c => c.ColumnName == columnName);
  87.                         if (column != null)
  88.                         {
  89.                             column.IsPrimaryKey = true;
  90.                         }
  91.                     }
  92.                 }
  93.             }
  94.         }
  95.         private void SetForeignKeys(TableInfo table)
  96.         {
  97.             using (var cmd = new OracleCommand(
  98.                 @"SELECT cols.column_name
  99.               FROM user_constraints cons, user_cons_columns cols
  100.               WHERE cons.constraint_type = 'R'
  101.               AND cons.constraint_name = cols.constraint_name
  102.               AND cons.table_name = :tableName", _connection))
  103.             {
  104.                 cmd.Parameters.Add("tableName", OracleDbType.Varchar2).Value = table.TableName;
  105.                 using (var reader = cmd.ExecuteReader())
  106.                 {
  107.                     while (reader.Read())
  108.                     {
  109.                         var columnName = reader["column_name"].ToString();
  110.                         var column = table.Columns.Find(c => c.ColumnName == columnName);
  111.                         if (column != null)
  112.                         {
  113.                             column.IsForeignKey = true;
  114.                         }
  115.                     }
  116.                 }
  117.             }
  118.         }
  119.         public List<ViewInfo> GetViews(string owner)
  120.         {
  121.             var views = new List<ViewInfo>();
  122.             // 获取视图基本信息
  123.             using (var cmd = new OracleCommand(
  124.                 @"SELECT view_name, text, comments
  125.               FROM user_views v
  126.               LEFT JOIN user_tab_comments tc ON v.view_name = tc.table_name
  127.               ORDER BY view_name", _connection))
  128.             {
  129.                 using (var reader = cmd.ExecuteReader())
  130.                 {
  131.                     while (reader.Read())
  132.                     {
  133.                         views.Add(new ViewInfo
  134.                         {
  135.                             ViewName = reader["view_name"].ToString(),
  136.                             //Definition = reader.GetOracleClob(0)?.Value,
  137.                             Comments = reader["comments"]?.ToString()
  138.                         });
  139.                     }
  140.                 }
  141.             }
  142.             Console.WriteLine($"视图基本信息获取完成");
  143.             // 获取每张视图的列信息
  144.             foreach (var view in views)
  145.             {
  146.                 view.Definition = GetViewDDL(_connection, view.ViewName, owner);
  147.                 view.Columns = GetColumns(view.ViewName);
  148.                 Console.WriteLine($"视图[{view.ViewName}]列信息获取完成");
  149.             }
  150.             return views;
  151.         }
  152.         static string GetViewDDL(OracleConnection connection, string viewName, string schema)
  153.         {
  154.             string sql = @"
  155.                 SELECT DBMS_METADATA.GET_DDL('VIEW', :viewName, :schema)
  156.                 FROM DUAL";
  157.             using (OracleCommand command = new OracleCommand(sql, connection))
  158.             {
  159.                 command.Parameters.Add(":viewName", viewName);
  160.                 command.Parameters.Add(":schema", schema);
  161.                 command.InitialLOBFetchSize = 1000000;
  162.                 object result = command.ExecuteScalar();
  163.                 return result != null ? result.ToString() : string.Empty;
  164.             }
  165.         }
  166.         public List<ProcedureInfo> GetProcedures()
  167.         {
  168.             var procedures = new List<ProcedureInfo>();
  169.             // 获取存储过程和函数基本信息
  170.             using (var cmd = new OracleCommand(
  171.                 @"SELECT object_name, object_type, status, created, last_ddl_time
  172.               FROM user_objects
  173.               WHERE object_type IN ('PROCEDURE', 'FUNCTION')
  174.               ORDER BY object_type, object_name", _connection))
  175.             {
  176.                 using (var reader = cmd.ExecuteReader())
  177.                 {
  178.                     while (reader.Read())
  179.                     {
  180.                         procedures.Add(new ProcedureInfo
  181.                         {
  182.                             ProcedureName = reader["object_name"].ToString(),
  183.                             ProcedureType = reader["object_type"].ToString()
  184.                         });
  185.                     }
  186.                 }
  187.             }
  188.             Console.WriteLine($"存储过程基本信息获取完成");
  189.             // 获取每个对象的详细信息和参数
  190.             foreach (var proc in procedures)
  191.             {
  192.                 // 获取源代码
  193.                 using (var cmd = new OracleCommand(
  194.                     @"SELECT text
  195.                   FROM user_source
  196.                   WHERE name = :procName
  197.                   AND type = :procType
  198.                   ORDER BY line", _connection))
  199.                 {
  200.                     cmd.Parameters.Add("procName", OracleDbType.Varchar2).Value = proc.ProcedureName;
  201.                     cmd.Parameters.Add("procType", OracleDbType.Varchar2).Value = proc.ProcedureType;
  202.                     var source = new System.Text.StringBuilder();
  203.                     using (var reader = cmd.ExecuteReader())
  204.                     {
  205.                         while (reader.Read())
  206.                         {
  207.                             source.AppendLine(reader["text"].ToString());
  208.                         }
  209.                     }
  210.                     proc.SourceCode = source.ToString();
  211.                 }
  212.                 // 获取参数信息
  213.                 proc.Parameters = GetProcedureParameters(proc.ProcedureName);
  214.                 // 如果是函数,获取返回类型
  215.                 if (proc.ProcedureType == "FUNCTION")
  216.                 {
  217.                     using (var cmd = new OracleCommand(
  218.                         @"SELECT data_type
  219.                       FROM user_arguments
  220.                       WHERE object_name = :procName
  221.                       AND argument_name IS NULL", _connection))
  222.                     {
  223.                         cmd.Parameters.Add("procName", OracleDbType.Varchar2).Value = proc.ProcedureName;
  224.                         proc.ReturnType = cmd.ExecuteScalar()?.ToString();
  225.                     }
  226.                 }
  227.                 //// 获取注释
  228.                 //using (var cmd = new OracleCommand(
  229.                 //    @"SELECT comments
  230.                 //  FROM user_procedures
  231.                 //  WHERE object_name = :procName
  232.                 //  AND object_type = :procType", _connection))
  233.                 //{
  234.                 //    cmd.Parameters.Add("procName", OracleDbType.Varchar2).Value = proc.ProcedureName;
  235.                 //    cmd.Parameters.Add("procType", OracleDbType.Varchar2).Value = proc.ProcedureType;
  236.                 //    proc.Comments = cmd.ExecuteScalar()?.ToString();
  237.                 //}
  238.                 Console.WriteLine($"存储过程[{proc.ProcedureName}]详细信息获取完成");
  239.             }
  240.             return procedures;
  241.         }
  242.         private List<ParameterInfo> GetProcedureParameters(string procedureName)
  243.         {
  244.             var parameters = new List<ParameterInfo>();
  245.             using (var cmd = new OracleCommand(
  246.                 @"SELECT argument_name, data_type, in_out, position
  247.               FROM user_arguments
  248.               WHERE object_name = :procName
  249.               AND argument_name IS NOT NULL
  250.               ORDER BY position", _connection))
  251.             {
  252.                 cmd.Parameters.Add("procName", OracleDbType.Varchar2).Value = procedureName;
  253.                 using (var reader = cmd.ExecuteReader())
  254.                 {
  255.                     while (reader.Read())
  256.                     {
  257.                         parameters.Add(new ParameterInfo
  258.                         {
  259.                             ParameterName = reader["argument_name"].ToString(),
  260.                             DataType = reader["data_type"].ToString(),
  261.                             InOut = reader["in_out"].ToString(),
  262.                             Position = reader["position"] as int?
  263.                         });
  264.                     }
  265.                 }
  266.             }
  267.             return parameters;
  268.         }
  269.         public void Dispose()
  270.         {
  271.             _connection?.Close();
  272.             _connection?.Dispose();
  273.         }
  274.     }
复制代码

  • 生成html、hhp、hhc文件
  1.     public class HtmlGeneratorService
  2.     {
  3.         private readonly string _outputDirectory;
  4.         public HtmlGeneratorService(string outputDirectory)
  5.         {
  6.             _outputDirectory = outputDirectory;
  7.             Directory.CreateDirectory(_outputDirectory);
  8.         }
  9.         public void GenerateHtmlFiles(Dictionary<string, Tuple<List<TableInfo>, List<ViewInfo>, List<ProcedureInfo>>> dic)
  10.         {
  11.             GenerateIndexFile(dic);
  12.             foreach (var item in dic)
  13.             {
  14.                 GenerateTablesFiles(item.Key, item.Value.Item1);
  15.                 GenerateViewsFiles(item.Key, item.Value.Item2);
  16.                 GenerateProceduresFiles(item.Key, item.Value.Item3);
  17.             }
  18.             GenerateStyleSheet();
  19.         }
  20.         private void GenerateIndexFile(Dictionary<string, Tuple<List<TableInfo>, List<ViewInfo>, List<ProcedureInfo>>> dic)
  21.         {
  22.             var sb = new StringBuilder();
  23.             sb.AppendLine("<!DOCTYPE html>");
  24.             sb.AppendLine("<html>");
  25.             sb.AppendLine("<head>");
  26.             sb.AppendLine($"<title>数据库说明文档</title>");
  27.             sb.AppendLine("<link rel="stylesheet" href="styles.css">");
  28.             sb.AppendLine("</head>");
  29.             sb.AppendLine("<body>");
  30.             sb.AppendLine($"<h1>数据库说明文档</h1>");
  31.             foreach (var item in dic)
  32.             {
  33.                 sb.AppendLine($"<h2>{item.Key}</h2>");
  34.                 // 表目录
  35.                 sb.AppendLine("<h3>Tables</h3>");
  36.                 sb.AppendLine("<ul>");
  37.                 foreach (var table in item.Value.Item1)
  38.                 {
  39.                     sb.AppendLine($"<li>{table.TableName} - {table.Comments}</li>");
  40.                 }
  41.                 sb.AppendLine("</ul>");
  42.                 // 视图目录
  43.                 sb.AppendLine("<h3>Views</h3>");
  44.                 sb.AppendLine("<ul>");
  45.                 foreach (var view in item.Value.Item2)
  46.                 {
  47.                     sb.AppendLine($"<li>{view.ViewName} - {view.Comments}</li>");
  48.                 }
  49.                 sb.AppendLine("</ul>");
  50.                 // 存储过程和函数目录
  51.                 sb.AppendLine("<h3>Procedures and Functions</h3>");
  52.                 sb.AppendLine("<ul>");
  53.                 foreach (var proc in item.Value.Item3)
  54.                 {
  55.                     sb.AppendLine($"<li>{proc.ProcedureName} ({proc.ProcedureType})</li>");
  56.                 }
  57.                 sb.AppendLine("</ul>");
  58.             }
  59.             sb.AppendLine("</body>");
  60.             sb.AppendLine("</html>");
  61.             File.WriteAllText(Path.Combine(_outputDirectory, "index.html"), sb.ToString(), Encoding.UTF8);
  62.         }
  63.         private void GenerateTablesFiles(string owner, List<TableInfo> tables)
  64.         {
  65.             var tablesDir = Path.Combine(_outputDirectory, owner, "tables");
  66.             Directory.CreateDirectory(tablesDir);
  67.             foreach (var table in tables)
  68.             {
  69.                 var sb = new StringBuilder();
  70.                 sb.AppendLine("<!DOCTYPE html>");
  71.                 sb.AppendLine("<html>");
  72.                 sb.AppendLine("<head>");
  73.                 sb.AppendLine($"<title>Table: {table.TableName}</title>");
  74.                 sb.AppendLine("<link rel="stylesheet" href="../../styles.css">");
  75.                 sb.AppendLine("</head>");
  76.                 sb.AppendLine("<body>");
  77.                 sb.AppendLine($"<h1>Table: {table.TableName}</h1>");
  78.                 sb.AppendLine($"<p>{table.Comments}</p>");
  79.                 sb.AppendLine("<h2>Columns</h2>");
  80.                 sb.AppendLine("<table class="gridtable">");
  81.                 sb.AppendLine("<tr>");
  82.                 sb.AppendLine("<th>Name</th>");
  83.                 sb.AppendLine("<th>Type</th>");
  84.                 sb.AppendLine("<th>Length</th>");
  85.                 sb.AppendLine("<th>Precision</th>");
  86.                 sb.AppendLine("<th>Scale</th>");
  87.                 sb.AppendLine("<th>Nullable</th>");
  88.                 sb.AppendLine("<th>Default</th>");
  89.                 sb.AppendLine("<th>PK</th>");
  90.                 sb.AppendLine("<th>FK</th>");
  91.                 sb.AppendLine("<th>Description</th>");
  92.                 sb.AppendLine("</tr>");
  93.                 foreach (var column in table.Columns)
  94.                 {
  95.                     sb.AppendLine("<tr>");
  96.                     sb.AppendLine($"<td>{column.ColumnName}</td>");
  97.                     sb.AppendLine($"<td>{column.DataType}</td>");
  98.                     sb.AppendLine($"<td>{column.DataLength}</td>");
  99.                     sb.AppendLine($"<td>{column.DataPrecision}</td>");
  100.                     sb.AppendLine($"<td>{column.DataScale}</td>");
  101.                     sb.AppendLine($"<td>{column.Nullable}</td>");
  102.                     sb.AppendLine($"<td>{column.DefaultValue}</td>");
  103.                     sb.AppendLine($"<td>{(column.IsPrimaryKey ? "✓" : "")}</td>");
  104.                     sb.AppendLine($"<td>{(column.IsForeignKey ? "✓" : "")}</td>");
  105.                     sb.AppendLine($"<td>{column.Comments}</td>");
  106.                     sb.AppendLine("</tr>");
  107.                 }
  108.                 sb.AppendLine("</table>");
  109.                 sb.AppendLine("<p>Back to Index</p>");
  110.                 sb.AppendLine("</body>");
  111.                 sb.AppendLine("</html>");
  112.                 File.WriteAllText(Path.Combine(tablesDir, $"{table.TableName}.html"), sb.ToString(), Encoding.UTF8);
  113.             }
  114.         }
  115.         private void GenerateViewsFiles(string owner, List<ViewInfo> views)
  116.         {
  117.             var viewsDir = Path.Combine(_outputDirectory, owner, "views");
  118.             Directory.CreateDirectory(viewsDir);
  119.             foreach (var view in views)
  120.             {
  121.                 var sb = new StringBuilder();
  122.                 sb.AppendLine("<!DOCTYPE html>");
  123.                 sb.AppendLine("<html>");
  124.                 sb.AppendLine("<head>");
  125.                 sb.AppendLine($"<title>View: {view.ViewName}</title>");
  126.                 sb.AppendLine("<link rel="stylesheet" href="../../styles.css">");
  127.                 sb.AppendLine("</head>");
  128.                 sb.AppendLine("<body>");
  129.                 sb.AppendLine($"<h1>View: {view.ViewName}</h1>");
  130.                 sb.AppendLine($"<p>{view.Comments}</p>");
  131.                 sb.AppendLine("<h2>Columns</h2>");
  132.                 sb.AppendLine("<table class="gridtable">");
  133.                 sb.AppendLine("<tr>");
  134.                 sb.AppendLine("<th>Name</th>");
  135.                 sb.AppendLine("<th>Type</th>");
  136.                 sb.AppendLine("<th>Length</th>");
  137.                 sb.AppendLine("<th>Precision</th>");
  138.                 sb.AppendLine("<th>Scale</th>");
  139.                 sb.AppendLine("<th>Nullable</th>");
  140.                 sb.AppendLine("<th>Description</th>");
  141.                 sb.AppendLine("</tr>");
  142.                 foreach (var column in view.Columns)
  143.                 {
  144.                     sb.AppendLine("<tr>");
  145.                     sb.AppendLine($"<td>{column.ColumnName}</td>");
  146.                     sb.AppendLine($"<td>{column.DataType}</td>");
  147.                     sb.AppendLine($"<td>{column.DataLength}</td>");
  148.                     sb.AppendLine($"<td>{column.DataPrecision}</td>");
  149.                     sb.AppendLine($"<td>{column.DataScale}</td>");
  150.                     sb.AppendLine($"<td>{column.Nullable}</td>");
  151.                     sb.AppendLine($"<td>{column.Comments}</td>");
  152.                     sb.AppendLine("</tr>");
  153.                 }
  154.                 sb.AppendLine("</table>");
  155.                 sb.AppendLine("<h2>Definition</h2>");
  156.                 sb.AppendLine("<pre>");
  157.                 sb.AppendLine(view.Definition);
  158.                 sb.AppendLine("</pre>");
  159.                 sb.AppendLine("<p>Back to Index</p>");
  160.                 sb.AppendLine("</body>");
  161.                 sb.AppendLine("</html>");
  162.                 File.WriteAllText(Path.Combine(viewsDir, $"{view.ViewName}.html"), sb.ToString(), Encoding.UTF8);
  163.             }
  164.         }
  165.         private void GenerateProceduresFiles(string owner, List<ProcedureInfo> procedures)
  166.         {
  167.             var procsDir = Path.Combine(_outputDirectory, owner, "procedures");
  168.             Directory.CreateDirectory(procsDir);
  169.             foreach (var proc in procedures)
  170.             {
  171.                 var sb = new StringBuilder();
  172.                 sb.AppendLine("<!DOCTYPE html>");
  173.                 sb.AppendLine("<html>");
  174.                 sb.AppendLine("<head>");
  175.                 sb.AppendLine($"<title>{proc.ProcedureType}: {proc.ProcedureName}</title>");
  176.                 sb.AppendLine("<link rel="stylesheet" href="../../styles.css">");
  177.                 sb.AppendLine("</head>");
  178.                 sb.AppendLine("<body>");
  179.                 sb.AppendLine($"<h1>{proc.ProcedureType}: {proc.ProcedureName}</h1>");
  180.                 sb.AppendLine($"<p>{proc.Comments}</p>");
  181.                 if (proc.ProcedureType == "FUNCTION" && !string.IsNullOrEmpty(proc.ReturnType))
  182.                 {
  183.                     sb.AppendLine($"<p>Return Type: {proc.ReturnType}</p>");
  184.                 }
  185.                 if (proc.Parameters.Count > 0)
  186.                 {
  187.                     sb.AppendLine("<h2>Parameters</h2>");
  188.                     sb.AppendLine("<table class="gridtable">");
  189.                     sb.AppendLine("<tr>");
  190.                     sb.AppendLine("<th>Name</th>");
  191.                     sb.AppendLine("<th>Type</th>");
  192.                     sb.AppendLine("<th>In/Out</th>");
  193.                     sb.AppendLine("<th>Position</th>");
  194.                     sb.AppendLine("</tr>");
  195.                     foreach (var param in proc.Parameters)
  196.                     {
  197.                         sb.AppendLine("<tr>");
  198.                         sb.AppendLine($"<td>{param.ParameterName}</td>");
  199.                         sb.AppendLine($"<td>{param.DataType}</td>");
  200.                         sb.AppendLine($"<td>{param.InOut}</td>");
  201.                         sb.AppendLine($"<td>{param.Position}</td>");
  202.                         sb.AppendLine("</tr>");
  203.                     }
  204.                     sb.AppendLine("</table>");
  205.                 }
  206.                 sb.AppendLine("<h2>Source Code</h2>");
  207.                 sb.AppendLine("<pre>");
  208.                 sb.AppendLine(proc.SourceCode);
  209.                 sb.AppendLine("</pre>");
  210.                 sb.AppendLine("<p>Back to Index</p>");
  211.                 sb.AppendLine("</body>");
  212.                 sb.AppendLine("</html>");
  213.                 File.WriteAllText(Path.Combine(procsDir, $"{proc.ProcedureName}.html"), sb.ToString(), Encoding.UTF8);
  214.             }
  215.         }
  216.         private void GenerateStyleSheet()
  217.         {
  218.             var css = @"
  219. body {
  220.     font-family: Consolas, monospace;
  221.     line -height: 1.6;
  222.     margin: 20px;
  223.     color: #808080;
  224. }
  225. h1, h2, h3 {
  226.     color: #0066cc;
  227. }
  228. table.gridtable {
  229.     width: 100%;
  230.     border-collapse: collapse;
  231.     margin: 15px 0;
  232. }
  233. table.gridtable th {
  234.     background-color: #006400;
  235.     color: white;
  236.     text-align: center;
  237.     padding: 3px;
  238. }
  239. table.gridtable td {
  240.     border:  #ddd;
  241.     padding: 3px;
  242. }
  243. table.gridtable tr:nth-child(even) {
  244.     background-color: #f2f2f2;
  245. }
  246. pre {
  247.     background-color: #f5f5f5;
  248.     padding: 10px;
  249.     border-radius: 5px;
  250.     overflow-x: auto;
  251. }
  252. a {
  253.     color: #A52A2A;
  254.     text-decoration: none;
  255. }
  256. a:hover {
  257.     text-decoration: underline;
  258. }
  259. ";
  260.             File.WriteAllText(Path.Combine(_outputDirectory, "styles.css"), css);
  261.         }
  262.     }
复制代码

  • 生成chm文件
  1.     public class ChmCompiler
  2.     {
  3.         private readonly string _htmlHelpCompilerPath;
  4.         public ChmCompiler(string htmlHelpCompilerPath)
  5.         {
  6.             _htmlHelpCompilerPath = htmlHelpCompilerPath;
  7.         }
  8.         public bool Compile(string projectFilePath, string outputChmPath)
  9.         {
  10.             if (!File.Exists(_htmlHelpCompilerPath))
  11.             {
  12.                 throw new FileNotFoundException("HTML Help Compiler (hhc.exe) not found at the specified path.");
  13.             }
  14.             if (!File.Exists(projectFilePath))
  15.             {
  16.                 throw new FileNotFoundException("HHP project file not found.");
  17.             }
  18.             try
  19.             {
  20.                 var process = new Process
  21.                 {
  22.                     StartInfo = new ProcessStartInfo
  23.                     {
  24.                         FileName = _htmlHelpCompilerPath,
  25.                         Arguments = $""{projectFilePath}"",
  26.                         UseShellExecute = false,
  27.                         CreateNoWindow = true,
  28.                         RedirectStandardOutput = true,
  29.                         RedirectStandardError = true
  30.                     }
  31.                 };
  32.                 process.Start();
  33.                 process.WaitForExit(60 * 1000);
  34.                 //if (process.ExitCode != 0)
  35.                 //{
  36.                 //    var error = process.StandardError.ReadToEnd();
  37.                 //    throw new Exception($"CHM compilation failed with error: {error}");
  38.                 //}
  39.                 // 检查生成的CHM文件是否存在
  40.                 return File.Exists(outputChmPath);
  41.             }
  42.             catch (Exception ex)
  43.             {
  44.                 throw new Exception("Error during CHM compilation: " + ex.Message, ex);
  45.             }
  46.         }
  47.         public void CreateHhpFile(string outputDirectory, string projectName)
  48.         {
  49.             var hhpContent = $@"[OPTIONS]
  50. Compatibility=1.1 or later
  51. Compiled file={projectName}.chm
  52. Contents file={projectName}.hhc
  53. Default topic=index.html
  54. Display compile progress=No
  55. Full-text search=Yes
  56. Language=0x409 English (United States)
  57. Title={projectName} Database Documentation
  58. [FILES]
  59. ";
  60.             // 添加所有HTML文件
  61.             var htmlFiles = Directory.GetFiles(outputDirectory, "*.html", SearchOption.AllDirectories);
  62.             foreach (var file in htmlFiles)
  63.             {
  64.                 var relativePath = file.Substring(outputDirectory.Length + 1);
  65.                 hhpContent += relativePath + "\n";
  66.             }
  67.             // 添加CSS文件
  68.             hhpContent += "styles.css\n";
  69.             File.WriteAllText(Path.Combine(outputDirectory, $"{projectName}.hhp"), hhpContent);
  70.             // 创建目录文件 (.hhc)
  71.             CreateHhcFile(outputDirectory, projectName);
  72.         }
  73.         private void CreateHhcFile(string outputDirectory, string projectName)
  74.         {
  75.             var hhcContent = new StringBuilder(@"<!DOCTYPE HTML PUBLIC ""-//IETF//DTD HTML//EN"">
  76. <HTML>
  77. <HEAD>
  78. <meta name=""GENERATOR"" content=""Microsoft® HTML Help Workshop 4.1"">
  79. </HEAD>
  80. <BODY>
  81. <OBJECT type=""text/site properties"">
  82.     <param name=""Window Styles"" value=""0x800025"">
  83. </OBJECT>
  84. <UL>
  85.     <LI> <OBJECT type=""text/sitemap"">
  86.         <param name=""Name"" value=""Database Documentation"">
  87.         <param name=""Local"" value=""index.html"">
  88.         </OBJECT>
  89.     <UL>");
  90.             // 获取所有Owner
  91.             var owners = GetAllOwners(outputDirectory);
  92.             foreach (var owner in owners)
  93.             {
  94.                 hhcContent.AppendLine($@"
  95.         <LI> <OBJECT type=""text/sitemap"">
  96.             <param name=""Name"" value=""{owner}"">
  97.             </OBJECT>
  98.         <UL>");
  99.                 // 添加Tables分组
  100.                 hhcContent.AppendLine($@"
  101.             <LI> <OBJECT type=""text/sitemap"">
  102.                 <param name=""Name"" value=""Tables"">
  103.                 </OBJECT>
  104.             <UL>");
  105.                 AddFilesToContent(hhcContent, Path.Combine(outputDirectory, owner, "tables"), outputDirectory);
  106.                 hhcContent.AppendLine($@"
  107.             </UL>");
  108.                 // 添加Views分组
  109.                 hhcContent.AppendLine($@"
  110.             <LI> <OBJECT type=""text/sitemap"">
  111.                 <param name=""Name"" value=""Views"">
  112.                 </OBJECT>
  113.             <UL>");
  114.                 AddFilesToContent(hhcContent, Path.Combine(outputDirectory, owner, "views"), outputDirectory);
  115.                 hhcContent.AppendLine($@"
  116.             </UL>");
  117.                 // 添加Procedures分组
  118.                 hhcContent.AppendLine($@"
  119.             <LI> <OBJECT type=""text/sitemap"">
  120.                 <param name=""Name"" value=""Procedures and Functions"">
  121.                 </OBJECT>
  122.             <UL>");
  123.                 AddFilesToContent(hhcContent, Path.Combine(outputDirectory, owner, "procedures"), outputDirectory);
  124.                 hhcContent.AppendLine($@"
  125.             </UL>");
  126.                 hhcContent.AppendLine($@"
  127.         </UL>");
  128.             }
  129.             hhcContent.AppendLine($@"
  130.     </UL>
  131. </UL>
  132. </BODY>
  133. </HTML>");
  134.             File.WriteAllText(Path.Combine(outputDirectory, $"{projectName}.hhc"), hhcContent.ToString());
  135.         }
  136.         // 获取所有Owner的辅助方法
  137.         private List<string> GetAllOwners(string outputDirectory)
  138.         {
  139.             // 方法1:从目录结构中获取
  140.             return Directory.GetDirectories(outputDirectory)
  141.                 .Select(d => new DirectoryInfo(d).Name)
  142.                 .ToList();
  143.             // 方法2:从数据库中获取(需要实现)
  144.             // return GetOwnersFromDatabase();
  145.         }
  146.         // 之前定义的辅助方法
  147.         private void AddFilesToContent(StringBuilder content, string directory, string outputDirectory)
  148.         {
  149.             if (!Directory.Exists(directory))
  150.             {
  151.                 return;
  152.             }
  153.             foreach (var file in Directory.GetFiles(directory, "*.html"))
  154.             {
  155.                 var itemName = Path.GetFileNameWithoutExtension(file);
  156.                 var relativePath = file.Substring(outputDirectory.Length + 1);
  157.                 content.AppendLine($@"
  158.                 <LI> <OBJECT type=""text/sitemap"">
  159.                     <param name=""Name"" value=""{itemName}"">
  160.                     <param name=""Local"" value=""{relativePath}"">
  161.                     </OBJECT>");
  162.             }
  163.         }
  164.     }
复制代码

  • 调用
  1. internal class Program
  2. {
  3.     // 查找HHC编译器
  4.     private static string FindHhcCompiler()
  5.     {
  6.         // 检查常见位置
  7.         string[] possiblePaths = new[]
  8.         {
  9.                 @"C:\Program Files (x86)\HTML Help Workshop\hhc.exe",
  10.                 @"C:\Program Files\HTML Help Workshop\hhc.exe"
  11.             };
  12.         foreach (string path in possiblePaths)
  13.         {
  14.             if (File.Exists(path))
  15.             {
  16.                 return path;
  17.             }
  18.         }
  19.         // 检查PATH环境变量
  20.         string pathEnv = Environment.GetEnvironmentVariable("PATH");
  21.         if (!string.IsNullOrEmpty(pathEnv))
  22.         {
  23.             string[] paths = pathEnv.Split(Path.PathSeparator);
  24.             foreach (string path in paths)
  25.             {
  26.                 string fullPath = Path.Combine(path.Trim(), "hhc.exe");
  27.                 if (File.Exists(fullPath))
  28.                 {
  29.                     return fullPath;
  30.                 }
  31.             }
  32.         }
  33.         return null;
  34.     }
  35.     private static void Main(string[] args)
  36.     {
  37.         Console.WriteLine("Oracle Database CHM Documentation Generator");
  38.         Console.WriteLine("==========================================");
  39.         try
  40.         {
  41.             //数据库列表,自行添加
  42.             List<string> schemaList = new List<string>
  43.            {
  44.            };
  45.             // 配置参数         
  46.             var baseDir = Path.Combine(Directory.GetCurrentDirectory(), "OracleDocs");
  47.             var hhcPath = FindHhcCompiler();
  48.             var outputDir = Path.Combine(baseDir, "OracleDocs");
  49.             var dic = new Dictionary<string, Tuple<List<TableInfo>, List<ViewInfo>, List<ProcedureInfo>>>();
  50.             foreach (var item in schemaList)
  51.             {
  52.                 var projectName = $"TH_{item}";
  53.                 var connectionString = $"";//自行添加
  54.                 // 创建服务实例
  55.                 using (var metadataService = new OracleMetadataService(connectionString))
  56.                 {
  57.                     Console.WriteLine("Fetching database metadata...");
  58.                     // 获取元数据
  59.                     var tables = metadataService.GetTables();
  60.                     var views = metadataService.GetViews(projectName);
  61.                     var procedures = metadataService.GetProcedures();
  62.                     dic.Add(projectName, Tuple.Create(tables, views, procedures));
  63.                     Console.WriteLine($"Found {tables.Count} tables, {views.Count} views, {procedures.Count} procedures/functions");
  64.                 }
  65.             }
  66.             // 生成HTML文档
  67.             var htmlGenerator = new HtmlGeneratorService(outputDir);
  68.             Console.WriteLine("Generating HTML files...");
  69.             htmlGenerator.GenerateHtmlFiles(dic);
  70.             // 编译CHM
  71.             var chmCompiler = new ChmCompiler(hhcPath);
  72.             Console.WriteLine("Creating CHM project files...");
  73.             chmCompiler.CreateHhpFile(outputDir, "OracleDocs");
  74.             Console.WriteLine("Compiling CHM file...");
  75.             var hhpFilePath = Path.Combine(outputDir, $"TH.hhp");
  76.             var chmFilePath = Path.Combine(outputDir, $"TH.chm");
  77.             //var result = Task.Run(async () => await chmCompiler.RunProcessAsync(hhpFilePath)).Result;
  78.             if (chmCompiler.Compile(hhpFilePath, chmFilePath))
  79.             //if (result == 0)
  80.             {
  81.                 Console.WriteLine($"Successfully generated CHM file: {chmFilePath}");
  82.             }
  83.             else
  84.             {
  85.                 Console.WriteLine("CHM compilation failed.");
  86.             }
  87.         }
  88.         catch (Exception ex)
  89.         {
  90.             Console.WriteLine($"Error: {ex.Message}");
  91.             if (ex.InnerException != null)
  92.             {
  93.                 Console.WriteLine($"Inner exception: {ex.InnerException.Message}");
  94.             }
  95.         }
  96.         Console.WriteLine("Press any key to exit...");
  97.         Console.ReadKey();
  98.     }
  99. }
复制代码
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
您需要登录后才可以回帖 登录 | 立即注册