VBS分析Excel數據並生成新的Excel表格。

用到了第三方工具md5sum和sqlite3,sqlite3.exe是自己編譯的。
<?xml version="1.0" encoding="utf-8" ?>
<package xmlns="Windows Script Host">
  <description>xx analysis</description>
  <copyright>
    \file start.wsf - Windows Script Host
    \brief source file for xx project
     Project id: e97e4fcd-db53-4e88-87b4-147fb4f832f4

    This file is part of the xx project.
    $(SYNOPSIS)

    \copyright Copyright (C) 2013 xx Inc.
    All rights reserved.

    Developed by xx team.
     \authors [email protected]

    History:
     Date        Author      Description
     -------------------------------------------------------------
     2013-03-25  Perry       Initial created.

    Note:
    This source code can be used, modified, and redistributed under the
    terms of the license agreement that is included in the xx package
    By continuing to use, modify, or redistributed this code you indicate
    that you have read the license and understand and accept it fully.
  </copyright>
  <job ID="Application1">
    <runtime>
      <description>xx analysis</description>
      <named name="S" required="False" type="string" helpstring="an excel file which includes some xx data." />
      <example>Example: start.wsf /S "excel.xls"</example>
    </runtime>
    <object id="Shell" classid="clsid:72c24dd5-d70a-438b-8a42-98424b88afb8" />
    <object id="FileIO" classid="clsid:0d43fe01-f093-11cf-8940-00a0c9054228" />
    <object id="Network" classid="clsid:093ff999-1ea0-4079-9525-9614c3504b74" />
    <resource id="LAST_ERROR_INFORMATION">
      Description:
    </resource>
    <script language="VBScript" type="text/vbscript">
    <![CDATA[
      Option Explicit

      Const PROG_ID_EXCEL               = "Excel.Application"
      Const PROG_ID_XML1                = "Msxml2.DOMDocument"
      Const PROG_ID_XML2                = "Microsoft.XMLDOM"
      Const PROG_ID_DICT                = "Scripting.Dictionary"
      Const PROG_ID_STREAM              = "Adodb.Stream"

      Const DEFAULT_SCAN_FILES          = "xls,xlsx"
      Const DEFAULT_MD5_TOOL            = "md5sum.exe"
      Const DEFAULT_SQL_TOOL            = "sqlite3_h.exe"
      Const DEFAULT_SQL_DATA            = "data.db"
      Const DEFAULT_XML_FILE            = "data.xml"
      Const DEFAULT_XML_NODE            = "<?xml version=""1.0"" encoding=""utf-8""?><root></root>"

      Const ERR_NO_ERROR                = &H0000
      Const ERR_APP_INIT_FAIL           = &H0001
      Const ERR_STREAM_CREATE_FAIL      = &H0002
      Const ERR_DIC_CREATE_FAIL         = &HA000
      Const ERR_XML_CREATE_FAIL         = &HA001
      Const ERR_EXCEL_CREATE_FAIL       = &HA002
      Const ERR_SQLITE3_RUN_FAIL        = &HA003
      Const ERR_SQL_EXEC_FAIL           = &HA004
      Const ERR_NO_MORE_DATA            = &HA005
      Const ERR_APP_LOAD_FAIL           = &HB001
      Const ERR_XML_LOAD_FAIL           = &HB002
      Const ERR_UNAVIAL_FILE_TYPE       = &HB003
      Const ERR_TOOL_NOT_FOUND          = &HB004
      Const ERR_FILE_NOT_FOUND          = &HB005
      Const ERR_APP_SUMM_FAIL           = &HB006
      Const ERR_APP_COMP_FAIL           = &HB007
      Const ERR_PLATFORM_NOT_SUPPORT    = &HF005
      Const ERR_AUTOMATIC_OBJECT_FAIL   = &HF006

      Dim xap

      If CDbl(WSH.Version) < 5.6 Then
        MsgBox "This script needs WSH Version 5.6 or Later!"
        WSH.Quit -1
      End if

      Set xap = New CApp

      xap.Load
      xap.Scan
      xap.Summary
      xap.Save

      WSH.Quit xap.Quit

      Class Md5Result
        Private m_dict

        Private Sub Class_Initialize()
          On Error Resume Next

          ' 創建字典對象。
          Set m_dict = CreateObject(PROG_ID_DICT)
          If Err.Number <> 0 Then
            Set m_dict = Nothing
            Err.Clear
            Err.Raise ERR_DIC_CREATE_FAIL
          End If
        End Sub

        Private Sub Class_Terminate()
          ' 對象銷燬時清空字典中儲存的內容。
          If Not m_dict Is Nothing Then
            m_dict.RemoveAll
          End If
          Set m_dict = Nothing
        End Sub

        ' 獲得MD5對應的文件名。
        Public Property Get File(k)
          If m_dict.Exists(k) Then
            File = m_dict.Item(k)
          End If
        End Property

        ' 獲得KEY,即MD5值。
        Public Default Property Get Values
          Values = m_dict.Keys
        End Property

        ' 獲得數量。
        Public Property Get Count
          Count = m_dict.Count
        End Property

        ' 添加項目。
        Public Sub Add(k, v)
          If Not m_dict.Exists(k) Then
            m_dict.Add k, v
          End If
        End Sub

        ' 刪除項目。
        Public Sub Remove(k)
          If m_dict.Exists(k) Then
            m_dict.Remove k
          End If
        End Sub
      End Class

      Class Md5
        Private m_dict
        Private m_stream
        Private m_md5_tool

        Private Sub Class_Initialize()
          ' 設置默認的Md5工具。
          m_md5_tool = DEFAULT_MD5_TOOL

          On Error Resume Next

          ' 創建字典對象。
          Set m_dict = CreateObject(PROG_ID_DICT)
          If Err.Number <> 0 Then
            Set m_dict = Nothing
            Err.Clear
            On Error Goto 0
            Err.Raise ERR_DIC_CREATE_FAIL
          End If

          ' 創建字典對象。
          Set m_stream = CreateObject(PROG_ID_STREAM)
          If Err.Number <> 0 Then
            Set m_stream = Nothing
            Err.Clear
            On Error Goto 0
            Err.Raise ERR_STREAM_CREATE_FAIL
          End If
        End Sub

        Private Sub Class_Terminate()
          ' 對象銷燬時清空字典中儲存的內容。
          Clear
          Set m_dict = Nothing
          Set m_stream = Nothing
        End Sub

        ' 獲得工具所在的路徑。
        Public Property Get ToolPath
          ToolPath = m_md5_tool
        End Property

        ' 設置工具所在的路徑。
        Public Property Let ToolPath(v)
          m_md5_tool = v
        End Property

        ' 獲得準備計算的文件。
        Public Property Get Files
          Files = m_dict.Keys
        End Property

        ' 獲得待計算文件的數量。
        Public Property Get Count
          Count = m_dict.Count
        End Property

        ' 清除之前保存的文件。
        Public Sub Clear
          If Not m_dict Is Nothing Then
            m_dict.RemoveAll
          End If
        End Sub

        ' 添加一個文件。
        Public Sub AddFile(file)
          If Not IsEmpty(file) Then
            ' 不重複保存,相同的路徑只會記錄其中一個。
            If Not m_dict.Exists(file) Then
              m_dict.Add file, vbNullString
            End If
          End If
        End Sub

        Public Function MD5Init()

        End Function

        Public Function MD5Transform()

        End Function

        Public Sub MD5Update()

        End Sub

        Public Function MD5Final()

        End Function

        Public Function GetFileMd5(file)
          On Error Resume Next

          m_stream.Type = 1 ' adTypeBinary
          m_stream.Mode = 1 ' adModeRead
          m_stream.LoadFromFile file

          MD5Init
          Do While Not m_stream.EOS
            MD5Update m_stream.Read(1024)
          Loop
          GetFileMd5 = MD5Final

          m_strea.Close
        End Function

        ' 計算所有文件的Md5值。
        Public Function Calc
          Dim text, file, exec, timeout, regExp, match

          Set Calc = New Md5Result

          'On Error Resume Next

          If m_dict.Count > 0 Then
            text = m_md5_tool

            ' 合併參數。
            For Each file In m_dict.Keys
              If InStr(file, " ") > 0 Then
                text = text & " """ & file & """"
              Else
                text = text & " " & file
              End If
            Next

            ' 運行md5sum。
            Set exec = Shell.Exec(text)

            timeout = 0
            ' 等待md5sum運算完成。
            Do While exec.Status = 0
              WSH.Sleep 100
              timeout = timeout + 1

              ' 當md5sum未能在規定的時間內完成運算
              ' 就直接強制終止它的運行。
              If timeout > 500 Then
                exec.Terminate
                Exit Do
              End If
            Loop

            Set regExp = New RegExp

            ' 忽略大小寫。
            regExp.IgnoreCase = False
            ' 設定匹配模式。
            regExp.Pattern = "^\\([a-f0-9]{32}) \*(\w+.+)"

            Do While Not exec.StdOut.AtEndOfStream
              ' 獲得md5sum的輸出中的某行文本。
              text = exec.StdOut.ReadLine

              ' 確認輸出的每行是否是md5sum正常輸出格式。
              If regExp.Test(text) Then
                ' 計算匹配到的值,md5值在子匹配0中,文件名在子匹配1中。
                Set match = regExp.Execute(text)(0)
                ' 保存md5值與文件名,並刪除文件路徑中的多餘的\符號。
                Calc.Add match.SubMatches(0), Replace(match.SubMatches(1), "\\", "\")
              End If
            Loop

            Set regExp = Nothing
            Set exec = Nothing
          End If
        End Function
      End Class

      Class SqlCore
        Private m_sql_tool
        Private m_sql_db
        Private m_exec

        Private Sub Class_Initialize()
          ' 默認的SQLite3執行文件。
          m_sql_tool = DEFAULT_SQL_TOOL
          ' 默認的SQLite3數據庫文件名。
          m_sql_db = DEFAULT_SQL_DATA

          Set m_exec = Nothing
        End Sub

        Private Sub Class_Terminate()
          ' 對象銷燬前關閉可能在運行的SQLite3程序。
          Close
          Set m_exec = Nothing
        End Sub

        Public Sub Open
          On Error Resume Next

          ' 打開SQLite3。
          Set m_exec = Shell.Exec(m_sql_tool & " """ & m_sql_db & """")

          ' 確認調用SQLite3無錯誤產生。
          If Err.Number <> 0 Then
            Set m_exec = Nothing
            Err.Clear

            ' 拋出一個自定義錯誤。
            On Error GoTo 0
            Err.Raise ERR_SQLITE3_RUN_FAIL
          End If

          ' 等待SQLite3啓動。
          WSH.Sleep 100

          ' 確認SQLite3已經運行。
          If m_exec.Status <> 0 Then
            ' 拋出一個自定義錯誤。
            On Error GoTo 0
            Err.Raise ERR_SQLITE3_RUN_FAIL
          End If
        End Sub

        Public Function Write(cmd)
          If m_exec Is Nothing Then
            Err.Raise ERR_SQLITE3_RUN_FAIL
          End If

          If Not IsEmpty(cmd) Then
            ' 寫入標準輸入緩衝區,SQLite3將執行此命令。
            m_exec.StdIn.WriteLine cmd
            'QueryEx = m_exec.StdOut.AtEndOfLine
          End If
        End Function

        Public Sub Close
          Dim timeout

          On Error Resume Next
          If Not m_exec Is Nothing Then
            If m_exec.Status = 0 Then
              ' 命令SQLite3退出。
              m_exec.StdIn.WriteLine ".quit"

              timeout = 0
              ' 確認SQLite3是否已經退出。
              Do While m_exec.Status = 0
                WSH.Sleep 150
                timeout = timeout + 1
                ' 當SQLite3未能執行退出命令且超過1分鐘沒有反應
                ' 就直接強制終止它的運行。
                If timeout > 400 Then
                  m_exec.Terminate
                  Exit Do
                End If
              Loop
            End If
          End If
        End Sub

        Private Sub Query(sql)
          On Error Resume Next

          ' 打開SQLite3。
          Set m_exec = Shell.Exec(m_sql_tool & " """ & m_sql_db & """")

          ' 確認調用SQLite3無錯誤產生。
          If Err.Number <> 0 Then
            Set m_exec = Nothing
            Err.Clear

            ' 拋出一個自定義錯誤。
            On Error GoTo 0
            Err.Raise ERR_SQLITE3_RUN_FAIL
          End If

          ' 等待SQLite3啓動。
          WSH.Sleep 100

          ' 確認SQLite3已經運行。
          If m_exec.Status <> 0 Then
            ' 拋出一個自定義錯誤。
            On Error GoTo 0
            Err.Raise ERR_SQLITE3_RUN_FAIL
          End If

          ' 寫入標準輸入緩衝區,SQLite3將執行此命令。
          m_exec.StdIn.WriteLine sql

          ' 完成一次查詢。
          Close

          If Not m_exec.StdErr.AtEndOfLine Then
            ' 拋出一個自定義錯誤。
            On Error GoTo 0
            Err.Raise ERR_SQL_EXEC_FAIL
          End If
        End Sub

        Public Property Get ToolPath
          ' 獲得屬性。
          ToolPath = m_sql_tool
        End Property

        Public Property Let ToolPath(v)
          ' 設置屬性。
          m_sql_tool = v
        End Property

        Public Property Get DataFile
          ' 獲得屬性。
          DataFile = m_sql_db
        End Property

        Public Property Let DataFile(v)
          ' 設置屬性。
          m_sql_db = v
        End Property

        Public Sub ExecuteNonQuery(sql)
          ' 執行SQL查詢語句。
          Query sql

          Set m_exec = Nothing
        End Sub

        Public Function ExecuteScalar(sql)
          Dim text

          ' 執行SQL查詢語句。
          Query sql

          ' 等待讀取標準輸出緩衝區最後一行數據。
          Do While Not m_exec.StdOut.AtEndOfLine
            text = m_exec.StdOut.ReadLine

            If Not IsEmpty(text) Then
              ExecuteScalar = text
            End If
          Loop

          Set m_exec = Nothing
        End Function

        Public Function Execute(sql, out)
          On Error Resume Next

          ' 打開SQLite3。
          Set m_exec = Shell.Exec(m_sql_tool & " """ & m_sql_db & """")

          ' 確認調用SQLite3無錯誤產生。
          If Err.Number <> 0 Then
            Set m_exec = Nothing
            Err.Clear

            ' 拋出一個自定義錯誤。
            On Error GoTo 0
            Err.Raise ERR_SQLITE3_RUN_FAIL
          End If

          ' 等待SQLite3啓動。
          WSH.Sleep 200

          ' 確認SQLite3已經運行。
          If m_exec.Status <> 0 Then
            ' 拋出一個自定義錯誤。
            On Error GoTo 0
            Err.Raise ERR_SQLITE3_RUN_FAIL
          End If

          ' 寫入標準輸入緩衝區,SQLite3將執行此命令。
          m_exec.StdIn.Write sql & vbCrLf & ".quit" & vbCrLf

          Do While Not m_exec.StdOut.AtEndOfLine
            out.WriteRaw m_exec.StdOut.ReadLine
          Loop

          If Not m_exec.StdErr.AtEndOfLine Then
            ' 拋出一個自定義錯誤。
            On Error GoTo 0
            Err.Raise ERR_SQL_EXEC_FAIL
          End If

          Set m_exec = Nothing
        End Function

        Public Sub CreateTable(name, fields)
          ExecuteNonQuery "Create Table If Not Exists " & name & "(" & fields & ");"
        End Sub

        Public Sub DeleteTable(name)
          ExecuteNonQuery "Drop Table If Exists" & name & ";"
        End Sub
      End Class

      Class ExcelData
        Private m_data
        Private m_end

        Private Sub Class_Initialize()
          On Error Resume Next

          ' 創建字典對象。
          Set m_data = CreateObject(PROG_ID_DICT)
          If Err.Number <> 0 Then
            Set m_data = Nothing
            Err.Clear
            On Error Goto 0
            Err.Raise ERR_DIC_CREATE_FAIL
          End If

          m_end = True
        End Sub

        Private Sub Class_Terminate()
          m_data.RemoveAll
          Set m_data = Nothing
        End Sub

        ' 獲得所有項目文本信息。
        Public Property Get Text
          Dim str

          ' 列出每個項目並以符號 | 分隔。
          For Each str In m_data.Items
            If Not IsEmpty(Text) Then
              Text = Text & " | "
            End If
            Text = Text & CStr(str)
          Next
        End Property

        ' 枚舉每個項目值。
        Public Property Get Values
          Values = m_data.Items
        End Property

        ' 獲得單個項目值。
        Public Default Property Get Item(index)
          If Not m_data.Exists(index) Then
            Item = Empty
            Exit Property
          End If
          Item = m_data(index)
        End Property

        ' 獲得所有項目數量。
        Public Property Get Count
          Count = m_data.Count
        End Property

        Public Property Get AtEndOfLine
          AtEndOfLine = m_end
        End Property

        Public Property Let AtEndOfLine(v)
          m_end = v
        End Property

        ' 儲存一個或多個項目單位。
        Public Sub AddData(val)
          If IsArray(val) Then
            Dim item
            For Each item In val
              m_data.Add m_data.Count, item
            Next
          Else
            m_data.Add m_data.Count, val
          End If
        End Sub

        ' 測試當前對象是否合法數據格式。
        Public Function Check
          If Count > 5 Then
            ' 162-LGHH-12L0070000003G
            If Len(m_data(0)) > 22 Then
              If Len(m_data(2)) > 1 Then
                Check = True
              End If
            End If
          End If
        End Function
      End Class

      Class Excel
        Private m_excel
        Private m_workbook
        Private m_worksheet
        Private m_attributes
        Private m_filePath

        Private Sub Class_Initialize()
          On Error Resume Next

          Set m_workbook = Nothing
          Set m_worksheet = Nothing

          ' 創建Excel對象。
          Set m_excel = CreateObject(PROG_ID_EXCEL)
          If Err.Number <> 0 Then
            Set m_excel = Nothing
            Err.Clear

            On Error GoTo 0
            Err.Raise ERR_EXCEL_CREATE_FAIL
          End If

          ' 初始創建的Excel對象顯示窗口。
          m_excel.Visible = False

          ' 默認設置爲我的名字。
          m_excel.UserName = "Perry Peng"

          On Error Resume Next
          Set m_attributes = CreateObject(PROG_ID_DICT)
          If Err.Number <> 0 Then
            Set m_attributes = Nothing
            Err.Clear
            On Error Goto 0
            Err.Raise ERR_DIC_CREATE_FAIL
          End If
        End Sub

        ' 銷燬所有創建的對象。
        Private Sub Class_Terminate()
          If Not m_excel Is Nothing Then
            m_excel.Quit
          End If
          Set m_attributes = Nothing
          Set m_worksheet = Nothing
          Set m_workbook = Nothing
          Set m_excel = Nothing
        End Sub

        ' 獲得當前Excel的版本。
        Public Property Get Version
          If m_excel Is Nothing Then
            Exit Property
          End If

          Version = m_excel.Version
        End Property

        ' 獲得當前Excel的標題。
        Public Property Get Caption
          If m_excel Is Nothing Then
            Exit Property
          End If

          Caption = m_excel.Caption
        End Property

        ' 設置當前Excel的標題。
        Public Property Let Caption(v)
          If m_excel Is Nothing Then
            Exit Property
          End If

          m_excel.Caption = v
        End Property

        ' 獲得當前Excel的狀態欄信息。
        Public Property Get StatusBar
          If m_excel Is Nothing Then
            Exit Property
          End If

          StatusBar = m_excel.StatusBar
        End Property

        ' 設置當前Excel的狀態欄信息。
        Public Property Let StatusBar(v)
          If m_excel Is Nothing Then
            Exit Property
          End If

          m_excel.StatusBar = v
        End Property

        ' 獲得當前文件名。
        Public Property Get FileName
          FileName = m_filePath
        End Property

        ' 獲得當前表格名。
        Public Property Get CurrentWorkSheet
          If m_excel Is Nothing Then
            Exit Property
          End If

          If Not m_worksheet Is Nothing Then
            CurrentWorkSheet = m_worksheet.Name
          End If
        End Property

        ' 切換當前表格,使用表格名稱。
        Public Property Let CurrentWorkSheet(v)
          If m_excel Is Nothing Then
            Exit Property
          End If

          If m_attributes.Exists(v) Then
            Set m_worksheet = m_workbook.Worksheets.Item(v)
            m_worksheet.Select
          End If
        End Property

        ' 獲得當前表格讀寫的行號。
        Public Property Get Line
          If m_excel Is Nothing Then
            Exit Property
          End If

          If Not m_worksheet Is Nothing Then
            Line = m_attributes(m_worksheet.Name)
          End If
        End Property

        ' 設置當前表格讀寫行號。
        Public Property Let Line(v)
          If m_excel Is Nothing Then
            Exit Property
          End If

          If Not m_worksheet Is Nothing Then
            m_attributes(m_worksheet.Name) = v
          End If
        End Property

        ' 獲得Excel是否可見。
        Public Property Get Visible
          If m_excel Is Nothing Then
            Exit Property
          End If
          Visible = m_excel.Visible
        End Property

        ' 設置Excel是否可見。
        Public Property Let Visible(v)
          If m_excel Is Nothing Then
            Exit Property
          End If
          m_excel.Visible = v
        End Property

        ' 創建新的Excel表格。
        Public Sub AddNew()
          If m_excel Is Nothing Then
            Exit Sub
          End If

          ' 加入一個新的Excel文件。
          Set m_workbook = m_excel.Workbooks.Add()

          ' 記錄所有表名稱。
          For Each m_worksheet In m_workbook.Worksheets
            m_attributes.Add m_worksheet.Name, 0
          Next

          ' 將新建的第一張表作爲默認表。
          Set m_worksheet = m_workbook.Worksheets("Sheet1")
          m_worksheet.Select
        End Sub

        ' 打開一個已經存在的文件。
        Public Sub Open(file, readonly)
          ' 打開新文件前,關閉當前文件。
          Close

          ' 打開一個新的Excel文件。
          Set m_workbook = m_excel.Workbooks.Open(file, False, readonly)

          ' 記錄所有表名稱。
          For Each m_worksheet In m_workbook.Worksheets
            m_attributes.Add m_worksheet.Name, 0
          Next

          ' 設置默認表。
          If m_attributes.Count > 0 Then
            Set m_worksheet = m_workbook.ActiveSheet
            If m_worksheet Is Nothing Then
              Set m_worksheet = m_workbook.Worksheets.Item(m_attributes.Keys(1))
              m_worksheet.Select
            End If
          End If
        End Sub

        ' 寫入原始數據。
        Public Sub WriteRaw(data)
          Dim arryData

          ' 將原始數據拆分。
          arryData = Split(data, "|")

          ' 只接受數據。
          If IsArray(arryData) Then
            WriteLine arryData
          End If
        End Sub

        ' 寫入一行到Excel表格。
        Public Sub WriteLine(data)
          On Error Resume Next

          If m_excel Is Nothing Then
            Exit Sub
          End If

          Dim num, rng, cols

          ' 獲得新行的行號。
          num = Line + 1
          Line = num

          ' 取得行號指定的單元。
          Set rng = m_worksheet.Range("A" & CStr(num), Chr(65 + UBound(data)) & CStr(num))

          ' 選取指定的單元。
          rng.Select

          ' 將數據寫入指定單元。
          rng.Value = data

          If Err.Number <> 0 Then
            Err.Clear
            WSH.echo Err.Description
          End If
        End Sub

        ' 設置新名稱並且調整單元格寬度。
        Public Sub SetStyle1(name, width1, width2, width3, width4, width5, width6)
          On Error Resume Next

          If m_excel Is Nothing Then
            Exit Sub
          End If

          ' 設置新的名稱。
          If Not IsEmpty(name) Then
            ' 排除相同的名稱。
            If m_worksheet.Name <> name Then
              If Not m_attributes.Exists(name) Then
                m_attributes.Add name, 0
              End If

              ' 複製當前的行號。
              m_attributes(name) = Line
              m_worksheet.Name = name
            End If
          End If

          ' 僅寬度大於零時有效。
          If width1 > 0 Then
            m_worksheet.Columns("A:A").ColumnWidth = width1
          End If

          ' 僅寬度大於零時有效。
          If width2 > 0 Then
            m_worksheet.Columns("B:B").ColumnWidth = width2
          End If

          ' 僅寬度大於零時有效。
          If width3 > 0 Then
            m_worksheet.Columns("C:C").ColumnWidth = width3
          End If

          ' 僅寬度大於零時有效。
          If width4 > 0 Then
            m_worksheet.Columns("D:D").ColumnWidth = width4
          End If

          ' 僅寬度大於零時有效。
          If width5 > 0 Then
            m_worksheet.Columns("E:E").ColumnWidth = width5
          End If

          ' 僅寬度大於零時有效。
          If width6 > 0 Then
            m_worksheet.Columns("F:F").ColumnWidth = width6
          End If

          If Err.Number <> 0 Then
            Err.Clear
          End If
        End Sub

        Public Sub SetStyle2
          Dim i

          On Error Resume Next
          For i = 7 To 12
            With m_worksheet.UsedRange.Borders(i)
              .LineStyle = 1  ' xlContinuous
              .ColorIndex = 0
              .TintAndShade = 0
              .Weight = 2  ' xlThin
            End With
          Next

          If Err.Number <> 0 Then
            Err.Clear
          End If
        End Sub

        ' 從當前打開的Excel表格讀取一行數據。
        Public Function ReadLine
          On Error Resume Next
          Set ReadLine = New ExcelData

          If m_attributes.Count = 0 Then
            Exit Function
          End If

          Dim usage, rng, num

          ' 遞增當前行號。
          num = Line + 1
          Line = num
          Set usage = m_worksheet.UsedRange

          ' 到行尾時退出。
          If Line > usage.Rows.Count Then
            ' 數據已經全部讀取,AtEndOfLine默認True。
            Exit Function
          End If

          ' 數據未讀取完,還有後續數據。
          ReadLine.AtEndOfLine = False

          ' 取得每行的單元。
          Set rng = usage.Range(usage.Item(num, 1).Address, usage.Item(num, usage.Columns.Count).Address)

          ' 選取指定的單元。
          rng.Select

          ' 儲存Excel單元數據。
          ReadLine.AddData rng.Value

          ' 確定數據是合格,不合格數據將標記爲紅色。
          If Not ReadLine.Check Then
            rng.Interior.ColorIndex = 3 'Red
          End If

          Set rng = Nothing
          Set usage = Nothing
        End Function

        ' 關閉當前已打開的Excel文件。
        Public Sub Close
          If Not m_excel Is Nothing Then
            m_excel.StatusBar = "Ready"

            If Not m_workbook Is Nothing Then
              m_workbook.Close False
            End If
          End If

          ' 清除表附加記錄的屬性。
          m_attributes.RemoveAll

          ' 銷燬對象。
          Set m_worksheet = Nothing
          Set m_workbook = Nothing
        End Sub

        ' 保存當前的Excel文件。
        Public Sub Save
          If m_workbook Is Nothing Then
            Exit Sub
          End If

          If m_attributes.Count = 0 Then
            Exit Sub
          End If

          m_workbook.Save
        End Sub

        ' 另存當前Excel文件。
        Public Sub SaveAs(file)
          If m_workbook Is Nothing Then
            Exit Sub
          End If

          If m_attributes.Count = 0 Then
            Exit Sub
          End If

          m_workbook.SaveAs file
        End Sub
      End Class

      Class CApp
        Private m_primal_error_id
        Private m_internal_error_id
        Private m_app_path
        Private m_config_file
        Private m_data_file
        Private m_xmldoc
        Private m_md5
        Private m_excel
        Private m_database

        Private Sub Class_Initialize()
          m_primal_error_id = ERR_NO_ERROR
          m_internal_error_id = ERR_NO_ERROR

          On Error Resume Next
          m_app_path = FileIO.GetParentFolderName(WSH.ScriptFullName)
          m_config_file = FileIO.BuildPath(m_app_path, DEFAULT_XML_FILE)
          m_data_file = FileIO.BuildPath(m_app_path, DEFAULT_SQL_DATA)

          ' 創建XML對象。
          Set m_xmldoc = CreateObject(PROG_ID_XML1)
          If Err.Number <> 0 Then
            Set m_xmldoc = Nothing
            Err.Clear

            Set m_xmldoc = CreateObject(PROG_ID_XML2)
            If Err.Number <> 0 Then
              m_internal_error_id = Err.Number
              m_primal_error_id = ERR_APP_INIT_FAIL
              Set m_xmldoc = Nothing
              Err.Clear
              Exit Sub
            End If
          End If

          ' 創建MD5對象。
          Set m_md5 = New Md5
          If Err.Number <> 0 Then
            m_internal_error_id = Err.Number
            m_primal_error_id = ERR_APP_INIT_FAIL
            Set m_md5 = Nothing
            Err.Clear
            Exit Sub
          End If

          ' 設定MD5工具路徑。
          m_md5.ToolPath = FileIO.BuildPath(m_app_path, DEFAULT_MD5_TOOL)
          If Not FileIO.FileExists(m_md5.ToolPath) Then
            m_internal_error_id = ERR_TOOL_NOT_FOUND
            m_primal_error_id = ERR_APP_INIT_FAIL
            Set m_md5 = Nothing
            Exit Sub
          End If

          ' 創建SQLite3對象。
          Set m_database = New SqlCore
          If Err.Number <> 0 Then
            m_internal_error_id = Err.Number
            m_primal_error_id = ERR_APP_INIT_FAIL
            Set m_database = Nothing
            Err.Clear
            Exit Sub
          End If

          ' 設定數據庫文件路徑。
          m_database.DataFile = m_data_file
          m_database.ToolPath = FileIO.BuildPath(m_app_path, DEFAULT_SQL_TOOL)
          If Not FileIO.FileExists(m_database.ToolPath) Then
            m_internal_error_id = ERR_TOOL_NOT_FOUND
            m_primal_error_id = ERR_APP_INIT_FAIL
            Set m_database = Nothing
            Exit Sub
          End If

          ' 創建Excel對象。
          Set m_excel = New Excel
          If Err.Number <> 0 Then
            m_internal_error_id = Err.Number
            m_primal_error_id = ERR_APP_INIT_FAIL
            Set m_excel = Nothing
            Err.Clear
            Exit Sub
          End If
        End Sub

        Private Sub Class_Terminate()
          Set m_xmldoc = Nothing
          Set m_database = Nothing
          Set m_excel = Nothing
          Set m_md5 = Nothing
        End Sub

        ' 取得使用腳本的用戶名。
        Public Property Get UserName
          UserName = Network.UserName
        End Property

        ' 取得使用腳本的計算機名。
        Public Property Get ComputerName
          ComputerName = Network.ComputerName
        End Property

        ' 取得腳文件所在的目錄名。
        Public Property Get Path
          Path = m_app_path
        End Property

        ' 加入一個文件作爲資料源。
        Public Sub AddFile(file)
          If m_primal_error_id <> ERR_NO_ERROR Then
            Exit Sub
          End If

          m_md5.AddFile file
        End Sub

        Public Sub Load
          ' 確保前面的操作都OK。
          If m_primal_error_id <> ERR_NO_ERROR Then
            Exit Sub
          End If

          On Error Resume Next

          ' 決定是重建配置文件或是打開已存的文件。
          If Not FileIO.FileExists(m_config_file) Then
            m_xmldoc.loadXml DEFAULT_XML_NODE
            m_xmldoc.save m_config_file
          Else
            m_xmldoc.load m_config_file
          End If

          If Err.Number <> 0 Then
            m_internal_error_id = ERR_XML_LOAD_FAIL
            m_primal_error_id = ERR_APP_LOAD_FAIL
            Err.Clear
            Exit Sub
          End If

          ' 在數據庫中創建配置表。
          m_database.CreateTable "App_Settings", "Id Integer Primary Key Autoincrement, " & _
            "Key Text Not Null, " & _
            "Value Text, " & _
            "Modified Datetime Default(Datetime('now','localtime'))"
          If Err.Number <> 0 Then
            m_internal_error_id = Err.Number
            m_primal_error_id = ERR_APP_LOAD_FAIL
            Err.Clear
            Exit Sub
          End If

          ' 在數據庫中創建文件表。
          m_database.CreateTable "Scanned_Files", "Md5 Char(36) Primary Key Not Null, " & _
            "Path Text, " & _
            "IsScanned Integer Default(0), " & _
            "Version Text, " & _
            "UserName Text, " & _
            "ComputerName Text, " & _
            "Added Datetime Default(Datetime('now','localtime'))"
          If Err.Number <> 0 Then
            m_internal_error_id = Err.Number
            m_primal_error_id = ERR_APP_LOAD_FAIL
            Err.Clear
            Exit Sub
          End If

          ' 在數據庫中創建數據表。
          m_database.CreateTable "BOM_List", "Id Integer Primary Key Autoincrement Not Null, " & _
            "File_Md5 Char(36) Not Null, " & _
            "File_Id Integer Default(0), " & _
            "Number VarChar(24) Not Null, " & _
            "Mfr_Name Text, " & _
            "Mfr_Number Text Not Null, " & _
            "Mfr_Status Text, " & _
            "Mfr_Code Text, " & _
            "atFileNum Integer Default(0), " & _
            "atMfrNum Integer Default(0), " & _
            "Unique(File_Id, Number, Mfr_Number)"
          If Err.Number <> 0 Then
            m_internal_error_id = Err.Number
            m_primal_error_id = ERR_APP_LOAD_FAIL
            Err.Clear
            Exit Sub
          End If

          ' 在數據庫中創建數據表。
          m_database.CreateTable "BOM_Compare", "Id Integer Primary Key Autoincrement Not Null, " & _
            "Number VarChar(24) Not Null, " & _
            "Mfr_Number Text, " & _
            "atFileNum Integer Default(0), " & _
            "atMfrNum Integer Default(0)"
          If Err.Number <> 0 Then
            m_internal_error_id = Err.Number
            m_primal_error_id = ERR_APP_LOAD_FAIL
            Err.Clear
            Exit Sub
          End If
        End Sub

        ' 掃描腳本文件所在目錄所有的數據源。
        Public Sub Scan
          Dim fld
          Dim file
          Dim namex
          Dim res, md5
          Dim data, text, fileId

          ' 確保前面的操作都OK。
          If m_primal_error_id <> ERR_NO_ERROR Then
            Exit Sub
          End If

          On Error Resume Next
          Set fld = FileIO.GetFolder(Path)

          ' 列出當前目錄中所有的數據文件。
          For Each file In fld.Files
            namex = FileIO.GetExtensionName(file.Path)
            If InStr(1, DEFAULT_SCAN_FILES, namex, vbTextCompare) > 0 Then
              m_md5.AddFile FileIO.BuildPath(m_app_path, "*." & namex)
            End If
          Next

          ' 判斷待處理文件的數量。
          If m_md5.Count = 0 Then
            m_internal_error_id = ERR_NO_MORE_DATA
            m_primal_error_id = ERR_APP_SUMM_FAIL
            Exit Sub
          End If

          ' 計算出文件的MD5值。
          Set res = m_md5.Calc
          If Err.Number <> 0 Then
            m_internal_error_id = Err.Number
            m_primal_error_id = ERR_APP_SUMM_FAIL
            Err.Clear
            Exit Sub
          End If

          text = Empty
          ' 列出已經找到的文件,並過濾掉已經計算過的文件。
          For Each md5 In res.Values
            ' 確認文件是否已經分析過。
            ' 如果沒有分析過將文件內容取出並放到數據庫中。
            If CInt(m_database.ExecuteScalar("Select Count(*) From Scanned_Files Where Md5 = '" & md5 & "' And IsScanned > 0;")) > 0 Then
              ' 如果文件已經記錄且也被分析過,
              ' 刪除此文件記錄。
              res.Remove md5
            Else
              ' 剩下的就是沒有被分析過或者未分析成功。
              text = text & "Insert Or Replace Into Scanned_Files (Md5, Path, Version, UserName, ComputerName) Values ('" & _
                md5 & "', '" & _
                res.File(md5) & "', '" & _
                m_excel.Version & "', '" & _
                UserName & "', '" & _
                ComputerName & "');" & vbCrLf
                WSH.echo Err.Description
            End If
          Next

          ' 經過數據庫比較後,看是否有新的文件需要被錄入資料。
          If res.Count = 0 Then
            If Err.Number <> 0 Then
              m_internal_error_id = Err.Number
              Err.Clear
            Else
              m_internal_error_id = ERR_NO_MORE_DATA
            End If
            m_primal_error_id = ERR_APP_SUMM_FAIL
            Set res = Nothing
            Exit Sub
          End If

          ' 如果確定有文件需要分析,將顯示Excel窗口。
          m_excel.Visible = True

          For Each md5 In res.Values
            m_excel.Open res.File(md5), True

            Do
              ' 讀取一行數據。
              Set data = m_excel.ReadLine

              ' 確定數據是用。
              If data.Check Then
                Do While data.Count < 6
                  data.AddData vbNullString
                Loop

                fileId = Trim(data(5))
                If Not IsNumeric(fileId) Then
                  fileId = "0"
                End If

                m_excel.StatusBar = "正在讀取Excel數據,請匆修改當前Excel內容。"

                text = text & "Insert Or Ignore Into BOM_List " & _
                  "(File_Md5, Number, Mfr_Name, Mfr_Number, Mfr_Status, Mfr_Code, File_Id) Values ('" & _
                  md5 & "', '" & _
                  Trim(data(0)) & "', '" & _
                  Trim(data(1)) & "', '" & _
                  Trim(data(2)) & "', '" & _
                  Trim(data(3)) & "', '" & _
                  Trim(data(4)) & "', '" & _
                  fileId & "');" & vbCrLf

                If Len(text) > &H3000 Then
                  'Shell.Popup "正在寫入數據到數據庫中,請匆修改當前Excel內容。", 1, "Data Saving...", 64
                  m_excel.StatusBar = "正在寫入數據到數據庫中,請匆關閉當前Excel。"
                  m_database.ExecuteNonQuery text
                  text = Empty
                End If
              End If
            Loop While Not data.AtEndOfLine

            ' 分析過的文件將會被標記爲掃描過。
            text = text & "Update Scanned_Files Set IsScanned = 1 Where Md5 = '" & md5 & "';"

            m_excel.StatusBar = "正在寫入數據到數據庫中,請匆關閉當前Excel。"
            ' 已確定執行SQL。
            m_database.ExecuteNonQuery text

            ' 清除執行過的SQL語句。
            text = Empty

            ' 每分析完一個文件就將其存入數據庫。
            'If Not IsEmpty(text) Then
            '  If Shell.Popup("3秒鐘後將自動保存" & res.File(md5) & "的內容到數據庫。" & _
            '    vbCrLf & "保存文件需要一定的時間,請不要關閉程序或Excel。", 3, "Data Saving", 65) = 2 Then
            '    ' 當有人按下取消後將不會保存數據。
            '    Shell.Popup "你已經取消保存數據,當前文件分析過的內容被丟棄。", 0, "提醒", 64
            '  Else
            '    ' 已確定執行SQL。
            '    m_database.ExecuteNonQuery text
            '  End If
            '
            '  ' 清除執行過的SQL語句。
            '  text = Empty
            'End If

            m_excel.Close
          Next

          Set res = Nothing
        End Sub

        Public Function Summary
          Dim totalFiles, i, text

          On Error Resume Next

          If m_primal_error_id <> ERR_NO_ERROR Then
            If m_primal_error_id <> ERR_APP_SUMM_FAIL Then
              Exit Function
            End If
          End If

          ' 統計所有文件數量。
          totalFiles = CInt(m_database.ExecuteScalar("Select Count(*) From (Select Distinct File_Id From Bom_List);"))
          If totalFiles = 0 Then
            m_internal_error_id = ERR_NO_MORE_DATA
            m_primal_error_id = ERR_APP_COMP_FAIL
            Exit Function
          End If

          ' 清除標誌。
          text = "Update Bom_List Set atFileNum = 0, atMfrNum = 0;" & vbCrLf
          text = text & "Delete From BOM_Compare;" & vbCrLf

          ' 先標記在其它文件中出現過的項目。
          For i = 1 To totalFiles
            text = text & "Update Bom_List Set atFileNum = " & CStr(i) & " Where Number " & _
              "In (Select Case When Count(Number) = " & CStr(i) & " Then Number End From (Select " & _
              "Number From Bom_List Group By Number, File_Id) Group By Number);" & vbCrLf
          Next

          'text = text & "Insert Into BOM_Compare (Number, Mfr_Number, atMfrNum, atFileNum) " & _
          '  "Select Count(Number), Number, atFileNum, Mfr_Number From Bom_List Group By Number, " & _
          '  "Mfr_Number;" & vbCrLf

          ' 只出現一次的就能直接標記爲輸出。
          'text = text & "Update Bom_List Set atMfrNum = 1 Where atFileNum = 1;" & vbCrLf

          ' 統計相同項目之間未在所有出現過的文件中的子項目。
          text = text & "Update Bom_List Set atMfrNum = (Select X.atMfrNum From (Select Count(Number) As " & _
            "atMfrNum, Number, atFileNum, Mfr_Number From Bom_List Group By Number, Mfr_Number) As X Where " & _
            "Bom_List.Number=X.Number And Bom_List.Mfr_Number=X.Mfr_Number);" & vbCrLf

          ' 剔除合併後存在的重複項目。
          text = text & "Update Bom_List Set atMfrNum = atFileNum + 1 Where Id Not In (Select Id From Bom_List " & _
            "Group By Number, Mfr_Number);" & vbCrLf

          'text = text & "Update BOM_List As D Set D.atMfrNum = 1 Where D.atMfrNum = 0 " & _
          '  "And D.Number = E.Number And D.Mfr_Number = A.Mfr_Number And (Select Case When " & _
          '  "Count(A.Mfr_Number) = A.atFileNum Then 1 Else 0 End From Bom_List As A Where " & _
          '  "Number In (Select Distinct E.Number From Bom_List As E) Group By A.Mfr_Number);"
          '
          '  m_database.ExecuteNonQuery "Update Bom_List Set atFileNum = atFileNum + 1 " & _
          '    "Where Number In (Select Case When Count(Number) > 1 Then Number " & _
          '    "End From (Select Number From Where Bom_List Group By Number, File_Id) Group By Number);"

          'text = text & "Update Bom_List Set atMfrNum = 1 Where atFileNum = 1;" & vbCrLf

          m_database.ExecuteNonQuery text
        End Function

        Public Function Save
          On Error Resume Next

          If m_primal_error_id <> ERR_NO_ERROR Then
            If m_primal_error_id <> ERR_APP_SUMM_FAIL Then
              Exit Function
            End If
          End If

          ' 如果確定有文件需要分析,將顯示Excel窗口。
          m_excel.Visible = True
          m_excel.AddNew

          m_excel.StatusBar = "正在寫入數據到Excel。"
          m_excel.CurrentWorkSheet = "Sheet1"
          m_excel.SetStyle1 "Sheet1", 24, 27, 27, 7, 17, 5
          m_excel.WriteRaw "Item Number|MFR Name|MFR Part Number|Status|Manufacturer Code|File"
          m_database.Execute "Select Number, Mfr_Name, Mfr_Number, Mfr_Status, Mfr_Code, 0 From Bom_List Where atMfrNum = atFileNum;", m_excel
          m_excel.SetStyle2

          m_excel.CurrentWorkSheet = "Sheet2"
          m_excel.SetStyle1 "Removed", 24, 27, 27, 7, 17, 5
          m_excel.WriteRaw "Item Number|MFR Name|MFR Part Number|Status|Manufacturer Code|File"
          m_database.Execute "Select Number, Mfr_Name, Mfr_Number, Mfr_Status, Mfr_Code, 0 From Bom_List Where atMfrNum <> atFileNum;", m_excel
          m_excel.SetStyle2

          m_excel.CurrentWorkSheet = "Sheet3"
          m_excel.SetStyle1 "Undefine", 24, 5, 5, 0, 0, 0
          m_excel.WriteRaw "Item Number|File Id|Count"
          m_database.Execute "Select Number, File_Id, atFileNum From Bom_List Group By Number, File_Id;", m_excel
          m_excel.SetStyle2

          Dim fileName, now1, fd, outPath

          now1 = Now
          fileName = CStr(Year(now1)) & "_" & MonthName(Month(now1), True)
          fileName = fileName & "_" & CStr(Day(now1)) & "_" & CStr(Hour(now1))
          fileName = fileName & CStr(Minute(now1)) & CStr(Second(now1))

          outPath = FileIO.BuildPath(m_app_path, "Output")
          If Not FileIO.FolderExists(outPath) Then
            FileIO.CreateFolder outPath
          End If

          Set fd = FileIO.GetFolder(outPath)
          fileName = fileName & "_" & CStr(fd.Files.Count + 1)
          Set fd = Nothing

          m_excel.SaveAs FileIO.BuildPath(outPath, fileName)
          m_excel.Close
        End Function

        Public Function Quit
          If m_primal_error_id <> ERR_NO_ERROR Then
            WSH.echo "錯誤碼:" & Hex(m_primal_error_id), "細節錯誤碼:" & Hex(m_internal_error_id)
          End If
          Quit = m_primal_error_id
        End Function
      End Class

      Class UserInfo
        Private m_ArrayNames()
        Private m_ArrayIds()
        Private m_TxtData

        Private Sub Class_Initialize
          Dim aryTxt
          Dim szIdNm
          Dim aryIdNms
          Dim aryLen
          Dim i

          m_TxtData = GetResource("Names_ofID")
          m_TxtData = Replace(m_TxtData, vbTab, "")
          aryTxt = Split(m_TxtData, vbCrLf)

          aryLen = Ubound(aryTxt)
          If aryLen > 0 Then
            ReDim m_ArrayNames(aryLen)
            ReDim m_ArrayIds(aryLen)

            for i = 0 to aryLen
              szIdNm = aryTxt(i)
              aryIdNms= Split(szIdNm,",")

              If Ubound(aryIdNms)  = 1 Then
                m_ArrayIds(i) = aryIdNms(0)
                m_ArrayNames(i) = aryIdNms(1)
              End If
            Next
          Else
            m_ArrayNames = Array(0)
            m_ArrayIds = Array(0)
          End If
        End Sub

        Public Function GetUserNameByID(v)
          Dim i

          for i = 0 to Ubound(m_ArrayIds)
            If m_ArrayIds(i) = v Then
              GetUserNameByID = m_ArrayNames(i)
              Exit Function
            End If
          Next
          GetUserNameByID = m_ArrayIds(0)'"N/A"
        End Function

        Public Function GetIDByUserName(v)
          Dim i

          for i = 0 to Ubound(m_ArrayNames)
            If m_ArrayNames(i) = v Then
              GetIDByUserName = m_ArrayIds(i)
              Exit Function
            End If
          Next
          GetIDByUserName = "N/A"
        End Function
      End Class

      Class IList
        Private m_ArrayLists()

        Private Sub Class_Initialize
          ReDim m_ArrayLists(0)
          Set m_ArrayLists(0) = Nothing
        End Sub

        Private Sub Class_Terminate
          Erase m_ArrayLists
        End Sub

        Public Sub Add(v)
          Dim ArrayUBound

          ArrayUBound = Ubound(m_ArrayLists)
          If Not m_ArrayLists(0) Is Nothing Then
            ArrayUBound = ArrayUBound + 1
            ReDim Preserve m_ArrayLists(ArrayUBound)
          End If
          Set m_ArrayLists(ArrayUBound) = v
        End Sub

        Public Sub Clear()
          Erase m_ArrayLists
          Call Class_Initialize
        End Sub

        Public Property  Get Count
          If Not m_ArrayLists(0) Is Nothing Then
            Count = Ubound(m_ArrayLists) + 1
          Else
            Count = 0
          End If
        End Property

        Public Default Property Get Items(Index)
          If Index >=0 And Index <= Ubound(m_ArrayLists) Then
            Set Items  = m_ArrayLists(Index)
            Exit Property
          Else
            Err.Raise 9
          End If
          Set Items = Nothing
        End Property
      End Class
    ]]>
    </script>
  </job>
</package>

發佈了148 篇原創文章 · 獲贊 6 · 訪問量 29萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章