發佈日期:

網路斷線自動排查 支援多目標 IP 與品質評估的 PowerShell 腳本

Views: 0

在日常的企業 IT 運維中,網路資訊的穩定度往往決定了企業內部系統與日常辦公的運作效率。身為專業的資訊服務夥伴,忠碁科技經常收到客戶詢問:當網路出現間歇性斷線或異常卡頓時,該如何第一時間精準抓出是哪一台設備或哪條外網線路出了問題?

為了協助客戶能夠自主且快速地進行網路健檢與故障排障,展現資訊管理的專業度,忠碁特別分享一款企業級的 PowerShell 即時網路品質監測腳本。這款腳本能同時監控多個重要節點(如外部 DNS、內部網閘道器、核心交換機等),不僅能在畫面上以高規格工整排版即時輸出數據,還能自動將斷線狀況精簡記錄成文字檔,非常適合作為企業長期掛網排查或 IT 品質稽核的工具。

為什麼用 PowerShell 網路監測腳本?

相較於傳統 CMD 檔案(Batch)容易受到 Windows 系統語系干擾,且無法精準獲取網路數據,忠碁科技工程師推薦改用微軟核心的 PowerShell(.ps1)環境,並呼叫底層的 .NET 網路元件進行 PING 檢測。這款優化後的腳本具備以下五大核心優勢:

  • 多目標 IP 同時輪詢:可自行設定多個測試目標,各 IP 擁有獨立計數與狀態,互不干擾。
  • 高強迫症工整排版:畫面採用欄位固定寬度格式化對齊,毫秒數個十百位垂直成直線,閱讀極度舒適。
  • 防誤判緩衝機制:必須連續 2 次 PING 失敗才觸發斷線紀錄,完美排除網卡剛啟動或偶爾掉 1 包的誤報。
  • 精簡 Log 自動換檔:每天自動依「月-日_IP.txt」換檔。持續斷線時若原因相同會省略重複寫入,保護硬碟空間。
  • 微秒級別精準轉換:針對內網極速回應環境,自動將 0ms 轉換顯示為更能客觀呈現品質的 <1ms。

PowerShell 網路即時監測腳本程式碼

以下為完整的技術教學程式碼。貴公司的 IT 人員或管理工程師可以直接複製下方內容,並將其另存為 ping_monitor.ps1 檔案即可投入使用。若在部署過程中遇到任何網路規劃或資訊架構的疑問,歡迎隨時聯絡忠碁科技提供技術支援。

# ==================== 自訂設定區 ====================
# 填入您想要測試的多個 IP(用逗號隔開)
$TargetIPs = @("168.95.1.1", "192.168.6.1", "192.168.6.250") 
$IntervalSeconds = 2
# ====================================================

# 初始化每個 IP 的狀態與計數
$IpStates = @{}
$FailCounts = @{}
$LastErrorMessages = @{}

foreach ($ip in $TargetIPs) {
    $IpStates[$ip] = "online"
    $FailCounts[$ip] = 0
    $LastErrorMessages[$ip] = ""
}

# 建立 .NET Ping 物件
$PingProvider = New-Object System.Net.NetworkInformation.Ping

cls
Write-Host "=========================================================================" -ForegroundColor Cyan
Write-Host "【多目標群組 - 網路即時監測與連線品質評估】" -ForegroundColor Cyan
Write-Host "測試目標清單: `$($TargetIPs -join ', ')" -ForegroundColor Cyan
Write-Host "檢測間隔    : 每 `$IntervalSeconds 秒輪詢一次 | 超時設定: 1500ms" -ForegroundColor Cyan
Write-Host "=========================================================================" -ForegroundColor Cyan

while (`$true) {
    `$DateStr = Get-Date -Format "MM-dd"
    `$TimeStr = Get-Date -Format "yyyy-MM-dd HH:mm:ss"

    foreach (`$TargetIP in `$TargetIPs) {
        `$LogFile = "`${DateStr}_`${TargetIP}.txt"

        # 如果 Log 檔案不存在,寫入標頭
        if (-not (Test-Path -Path `$LogFile)) {
            `$HeaderContent = @"
======================================================================
【網路斷線監測與品質紀錄】
======================================================================
測試目標 IP  : `$TargetIP
測試目的     : 檢驗本地端連往目標主機之穩定度與品質
品質判定標準 :
  - 極佳     : 延遲 <= 10ms
  - 良好     : 延遲 11ms - 29ms
  - 普通/稍慢 : 延遲 30ms - 50ms
  - 嚴重延遲 : 延遲 >= 51ms
  - 斷線     : 連續 2 次 PING 失敗(單次超時 1500ms)觸發紀錄
開始記錄時間 : `$TimeStr
======================================================================

"@
            `$HeaderContent | Out-File -FilePath `$LogFile -Encoding utf8
            Write-Host "[`$TimeStr] [系統提示] 已建立 `$TargetIP 專屬紀錄檔: `$LogFile" -ForegroundColor Magenta
        }

        # 執行 PING 檢測
        `$PingResult = `$null
        `$IsSuccess = `$false
        
        try {
            `$PingResult = `$PingProvider.Send(`$TargetIP, 1500)
            if (`$PingResult.Status -eq "Success") { `$IsSuccess = `$true }
        } catch {
            `$ExceptionMessage = `$_.Exception.Message
        }

        # 判斷 PING 結果與格式化排版
        if (`$IsSuccess) {
            `$RoundtripTime = `$PingResult.RoundtripTime
            `$TTL = `$PingResult.Options.Ttl
            
            # 品質評估邏輯
            if (`$RoundtripTime -le 10) { `$QualityText = "極佳" }
            elseif (`$RoundtripTime -le 29) { `$QualityText = "良好" }
            elseif (`$RoundtripTime -le 50) { `$QualityText = "普通/稍慢" }
            else { `$QualityText = "嚴重延遲" }

            # 當時間為 0ms 時,轉換文字為 <1ms,其餘保持原樣並靠右對齊
            `$TimeDisplay = if (`$RoundtripTime -eq 0) { "<1ms" } else { "`$RoundtripTime`ms" }

            # 黃金排版:成功(時間欄位固定固定 5 個字元寬度,確保 <1ms 與 100ms 完美對齊)
            `$ScreenMessage = "[{0}]  {1,-15} | 成功 | 時間={2,5} | TTL={3,3} | 品質: {4}" -f `$TimeStr, `$TargetIP, `$TimeDisplay, `$TTL, `$QualityText
            Write-Host `$ScreenMessage -ForegroundColor White

            # 狀態恢復判定
            if (`$IpStates[`$TargetIP] -eq "offline") {
                `$LogContent = @"
========================================
[恢復時間] `$TimeStr 連線已恢復正常! (當前回應時間: `$TimeDisplay)
========================================

"@
                `$LogContent | Out-File -FilePath `$LogFile -Append -Encoding utf8
                `$LogNotice = "[{0}]  {1,-15} | [LOG提示] 網路已恢復連線!已更新 {2}" -f `$TimeStr, `$TargetIP, `$LogFile
                Write-Host `$LogNotice -ForegroundColor Cyan
            }

            `$FailCounts[`$TargetIP] = 0
            `$IpStates[`$TargetIP] = "online"
            `$LastErrorMessages[`$TargetIP] = ""
        }
        else {
            `$FailCounts[`$TargetIP]++
            `$QualityText = "斷線"

            `$CurrentErrorMessage = ""
            if (`$null -ne `$PingResult) {
                `$CurrentErrorMessage = `$PingResult.Status.ToString()
            } else {
                `$CurrentErrorMessage = if (`$ExceptionMessage) { `$ExceptionMessage } else { "Unknown Network Error / Timeout" }
            }

            # 黃金排版:失敗(補齊長度,與上方成功欄位完美契合)
            `$ScreenMessage = "[{0}]  {1,-15} | 失敗 | 原因={2,-22} | 次數: {3,2} | 品質: {4}" -f `$TimeStr, `$TargetIP, `$CurrentErrorMessage, `$FailCounts[`$TargetIP], `$QualityText
            Write-Host `$ScreenMessage -ForegroundColor Red

            # 連續 2 次失敗觸發 Log 寫入
            if (`$FailCounts[`$TargetIP] -ge 2) {
                `$IpStates[`$TargetIP] = "offline"

                if (`$CurrentErrorMessage -ne `$LastErrorMessages[`$TargetIP]) {
                    `$LogContent = @"
========================================
[斷線時間] `$TimeStr (連續第 `$(`$FailCounts[`$TargetIP]) 次失敗)
[詳細錯誤訊息]
`$CurrentErrorMessage
[品質評估] 網路中斷 (斷線)
========================================

"@
                    `$LogContent | Out-File -FilePath `$LogFile -Append -Encoding utf8
                    `$LogNotice = "[{0}]  {1,-15} | [LOG提示] 失敗原因改變或首次斷線,已更新至 {2}" -f `$TimeStr, `$TargetIP, `$LogFile
                    Write-Host `$LogNotice -ForegroundColor DarkRed
                    
                    `$LastErrorMessages[`$TargetIP] = `$CurrentErrorMessage
                }
            }
        }
    }

    # 等待 2 秒重複執行
    Start-Sleep -Seconds `$IntervalSeconds
}

如何執行此指令檔教學

為了確保腳本順利運作,請遵循以下由忠碁科技提供的標準操作步驟:

  1. 在電腦桌面上點擊右鍵,新建一個純文字檔案,並將其重新命名為 ping_monitor.ps1(請務必確認副檔名從 .txt 變更為 .ps1)。
  2. 使用PS編輯器打開該檔案,將上方提供的程式碼完整複製並貼入,儲存後關閉。
  3. 由於 Windows 預設的安全性原則會阻止未簽署的腳本執行,請在 Windows 開始選單搜尋「PowerShell」,並以系統管理員身分執行開啟它。
  4. 在 PowerShell 視窗中輸入以下命令以解除權限限制,並輸入 Y 答應變更:
    Set-ExecutionPolicy RemoteSigned -Scope CurrentUser
  5. 最後,直接將檔案拖曳進 PowerShell 視窗中按 Enter,或輸入檔案路徑即可開始執行監測。

結語:專業的資訊維護,交給最具實力的團隊

透過這款工具,企業的網路管理人員能夠在不用採購昂貴監控軟體的前提下,達到精準掌握多個主機與節點連線品質的目的。

網路環境的穩定度是企業數位轉型的基石。如果您所在的企業正面臨複雜的網路架構異常、嚴重的跨區斷線、或是需要全方位的資訊委外維護與資安規劃,忠碁科技隨時準備好為您量身打造最高效、最穩健的企業級維運方案。歡迎與我們聯繫!

發佈日期:

LibreOffice Calc 智慧列高調整巨集

Views: 6

🚀 SmartHeight Pro:讓 Excel/Calc 表格排版瞬間整齊

一鍵批次微調,告別高矮不一的混亂報表!

還在手動拉動每一列的高度嗎?當表格內容過多,導致自動換行後的列高顯得雜亂無章時,此範例程式是您的最佳救星。這款專為 LibreOffice/OpenOffice 打造的智慧腳本,能精準鎖定範圍,自動將列高優化至最完美的視覺比例。


✨ 核心亮點

  • ⚡ 智慧自動進位:程式會自動偵測現有高度並執行「百位數進位」邏輯,確保每一列都能獲得充足的顯示空間,視覺效果更趨統一。
  • 🛡️ 專業保底高度:內建 0.8cm (800 twips) 最低高度限制,防止列高縮過頭,確保報表文字在列印或閱讀時永不被遮蓋。
  • 📍 視覺化精準定位:獨家「跳轉追蹤」功能!在輸入起始與結束列號時,畫面會自動滾動至目標位置,讓你邊看邊設,絕不出錯。
  • 🔄 記憶常用參數:具備智慧記憶功能,自動帶入上次設定的列號範圍,重複作業僅需連按 Enter 即可完成。
  • ✅ 視覺回饋提示:執行成功後按鈕將閃爍綠燈,給予最直覺的操作確認,不再疑惑程式是否有在跑。

🛠️ 強大功能

  • 批次處理:支援自定義任意範圍,無論是 10 列還是 1000 列,瞬間處理完畢。
  • 跳轉輔助:在設定過程中自動導航,解決長表格翻找列號的痛苦。
  • 零侵入性:只針對有高度的顯示列進行優化,自動避開已隱藏的列位。
  • 簡易整合:輕鬆掛載於現有工具列或按鈕,隨開隨用。

💻 程式源碼 (Basic)

請將以下代碼複製並貼上至您的 LibreOffice/OpenOffice 巨集編輯器中:

Global g_sLastStart As String
Global g_sLastEnd As String

Sub AdjustHeightByInput
    Dim oDoc As Object
    Dim oSheet As Object
    Dim oForm As Object
    Dim oButton As Object
    Dim sStart As String, sEnd As String
    Dim nStart As Long, nEnd As Long
    Dim i As Long
    Dim currH As Long, newH As Long
    
    oDoc = ThisComponent
    oSheet = oDoc.CurrentController.ActiveSheet
    
    oForm = oSheet.DrawPage.Forms.getByIndex(0)
    oButton = oForm.getByName("CommandButton 1") 

    If g_sLastStart = "" Then g_sLastStart = "11"
    JumpToRow(oDoc, oSheet, CLng(g_sLastStart))
    
    sStart = InputBox("起始列號:", "設定範圍 (1/2)", g_sLastStart)
    If sStart = "" Then Exit Sub
    g_sLastStart = sStart

    If g_sLastEnd = "" Then g_sLastEnd = "19"
    JumpToRow(oDoc, oSheet, CLng(g_sLastEnd))
    
    sEnd = InputBox("結束列號:", "設定範圍 (2/2)", g_sLastEnd)
    If sEnd = "" Then Exit Sub
    g_sLastEnd = sEnd
    
    nStart = CLng(sStart) - 1
    nEnd = CLng(sEnd) - 1
    
    For i = nStart To nEnd
        currH = oSheet.Rows.getByIndex(i).Height
        If currH > 0 Then
            If (currH Mod 100 <> 0) Then
                newH = (Int(currH / 100) + 1) * 100
            Else
                newH = currH
            End If
            If newH < 800 Then newH = 800
            oSheet.Rows.getByIndex(i).Height = newH
        End If
    Next i
    
    oButton.BackgroundColor = RGB(144, 238, 144)
    Wait 800 
    oButton.BackgroundColor = RGB(240, 240, 240)
End Sub

Sub JumpToRow(oDoc, oSheet, nRow)
    Dim oCell As Object
    On Error Resume Next
    oCell = oSheet.getCellByPosition(0, nRow - 1)
    oDoc.CurrentController.select(oCell)
End Sub

💡 適用場景

  • 財務報表:確保所有數據列高度一致,視覺更專業。
  • 產品清單:自動調整因說明文字長度不一造成的排版混亂。
  • 列印前校對:統一高度標準,避免列印出來高低落差過大。

🎯 想提升您的工作效率嗎?立即將此巨集導入您的 Calc 環境!

發佈日期:

電腦越用越慢?分享 PowerShell 腳本,一鍵偵測記憶體與句柄洩漏

Views: 69

🔍 記憶體去哪了?忠碁科技專用診斷工具,教你揪出消失的實體記憶體!

📖 故事的開頭…

最近電腦開機一段時間後,發現記憶體使用了近 90%,但打開工作管理員把所有進程(Processes)的記憶體加總,卻怎麼也湊不到那個數字。

忠碁為了提供相關資訊給其它智慧工具分析,並找出到底是哪個神祕黑洞吃掉了資源,忠碁參考了 網路技術文章 的分析邏輯,編寫了這支「Windows 記憶體耗盡診斷腳本」。它不只看表面數據,更能深入內核層級(Kernel)去偵測那些連工作管理員都抓不到的記憶體佔用。


💡 為什麼你需要這支腳本?(亮點分析)

一般的系統工具只能看到應用程式的佔用,但當你遇到核心層級的問題時,往往無能為力。忠碁科技 在處理企業級系統維護時,常遇到以下「記憶體黑洞」:

  • 驅動程式洩漏 (Driver Leak):非分頁池(Non-Paged Pool)異常飆高,通常是網卡或顯卡驅動作怪。
  • 網路驅動 NDU 衝突:Windows 內置監控導致的記憶體洩漏。
  • 句柄洩漏 (Handle Leak):程式碼沒寫好,瘋狂索取控制權而不歸還。
  • IO 瓶頸:大量修改中頁面(Modified Page)堆積。

✨ 本程式 5 大核心亮點:

  • 🚀 自動權限提升:內建管理員權限請求邏輯,確保獲取內核敏感數據。
  • 🧩 隱形佔用精確計算 ($gap):自動對比系統總量與進程總量,直接算出被核心、快取吃掉的差距(Gap)。
  • 🚨 智慧診斷與預警:
    • 非分頁池預警:自動判別是否超過 1GB/2GB 臨界點,並篩選出可能的第三方驅動。
    • 句柄監控:當單一進程 Handle 超過 10,000 個(例如特定印表機驅動異常),立即觸發警告。
  • 📊 專業級數據報告:執行完畢自動生成 Memory_Report.txt 與 Memory_Summary.txt,格式美觀,方便傳給 忠碁 的技術人員進行深度分析。
  • 🛠️ 實戰派建議:不僅發現問題,更會依據結果引導你使用 poolmon.exe 或 RAMMap 等進階工具。

📝 腳本源碼分享(PowerShell)

請將以下代碼複製並另存為 Memory_Diagnosis.ps1 後執行:

# --- 1. 自動提升權限 ---
$currentPrincipal = New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent())
if (-not $currentPrincipal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
    Write-Host ">>> 權限不足,正在請求管理員權限..."
    $argList = "-NoProfile -ExecutionPolicy Bypass -File `"$PSCommandPath`""
    try { Start-Process powershell.exe -ArgumentList $argList -Verb RunAs; exit } catch { exit }
}

# --- 2. 路徑設定 ---
$reportPath = "$PSScriptRoot\Memory_Report.txt"
$summaryPath = "$PSScriptRoot\Memory_Summary.txt"
$report = New-Object System.Collections.Generic.List[string]

Write-Host ""
Write-Host "===== 記憶體耗盡診斷 (使用率分析) ====="
Write-Host ""

try {
    # ========== 1. 基礎記憶體數據 ==========
    $os = Get-CimInstance Win32_OperatingSystem
    if ($null -eq $os) { throw "無法取得 Win32_OperatingSystem 資訊" }

    $totalKB = $os.TotalVisibleMemorySize
    $freeKB = $os.FreePhysicalMemory
    $usedKB = $totalKB - $freeKB
    $totalGB = [math]::round($totalKB / 1MB, 2)
    $freeGB = [math]::round($freeKB / 1MB, 2)
    $usedGB = [math]::round($usedKB / 1MB, 2)
    $usagePercent = [math]::round(($usedKB / $totalKB) * 100, 2)
    $bootTime = $os.LastBootUpTime
    $uptime = (Get-Date) - $bootTime

    $report.Add("[1] 系統基本資訊")
    $report.Add("    開機時間  : $bootTime")
    $report.Add("    運行時間  : $($uptime.Days)天 $($uptime.Hours)時 $($uptime.Minutes)分")
    $report.Add("    總記憶體  : $totalGB GB")
    $report.Add("    已用記憶體: $usedGB GB")
    $report.Add("    可用記憶體: $freeGB GB")
    $report.Add("    總體佔用率: $usagePercent %")
    $report.Add("")

    # ========== 2. 核心層級記憶體分類 ==========
    $memPerf = Get-CimInstance Win32_PerfFormattedData_PerfOS_Memory -ErrorAction SilentlyContinue
    if ($null -eq $memPerf) {
        $report.Add("[2] 核心層級記憶體分類 - 警告: 無法讀取效能計數器")
        $nonPagedMB = 0
        $pagedMB = 0
        $cacheMB = 0
        $standbyMB = 0
        $modifiedMB = 0
    } else {
        $nonPagedMB = [math]::round($memPerf.PoolNonpagedBytes / 1MB, 2)
        $pagedMB = [math]::round($memPerf.PoolPagedBytes / 1MB, 2)
        $cacheMB = [math]::round($memPerf.CacheBytes / 1MB, 2)
        $standbyMB = [math]::round($memPerf.StandbyCacheCoreBytes / 1MB, 2)
        $modifiedMB = [math]::round($memPerf.ModifiedPageListBytes / 1MB, 2)

        $report.Add("[2] 核心層級記憶體分類 (找出隱形耗用)")
        $report.Add("    非分頁池 (NonPaged) : $nonPagedMB MB (超過 1000MB 需注意)")
        $report.Add("    分頁池 (Paged)      : $pagedMB MB")
        $report.Add("    系統快取 (Cache)    : $cacheMB MB")
        $report.Add("    待命記憶體 (Standby): $standbyMB MB")
        $report.Add("    修改中頁面 (Modified): $modifiedMB MB")
    }
    $report.Add("")

    # ========== 3. 非分頁池異常診斷 (記憶體洩漏) ==========
    if ($nonPagedMB -gt 2000) {
        $report.Add("[3a] 嚴重警告:非分頁池高達 $nonPagedMB MB (正常值 < 1GB)")
        $report.Add("    這表示驅動程式或核心模組有嚴重記憶體洩漏!")
        $report.Add("")
        
        $drivers = Get-CimInstance Win32_SystemDriver | Where-Object { $_.State -eq "Running" -and $_.PathName -notlike "*\\system32\\drivers\\*" }
        $report.Add("    正在執行的第三方驅動程式 (前20個,可能是元兇):")
        $drivers | Select-Object -First 20 | ForEach-Object {
            $report.Add("        - $($_.Name) : $($_.PathName)")
        }
        if ($drivers.Count -gt 20) {
            $report.Add("        ... 共 $($drivers.Count) 個,已省略 $($drivers.Count - 20) 個")
        }
        $report.Add("")
    }

    # ========== 4. 虛擬記憶體與分頁檔 ==========
    $pageFile = Get-CimInstance Win32_PageFileUsage
    $pageTotalGB = if ($pageFile) { [math]::round($pageFile.AllocatedBaseSize / 1024, 2) } else { 0 }
    $pageUsedGB = if ($pageFile) { [math]::round($pageFile.CurrentUsage / 1024, 2) } else { 0 }
    
    $report.Add("[4] 虛擬記憶體與分頁檔")
    $report.Add("    分頁檔總大小 : $pageTotalGB GB")
    $report.Add("    分頁檔已用   : $pageUsedGB GB")
    $report.Add("    提交總量 (Commit) : $([math]::round($os.TotalAllocatedBaseSize / 1KB / 1024, 2)) GB")
    $report.Add("    提交限制 (Limit)  : $([math]::round($os.TotalVirtualMemorySize / 1MB, 2)) GB")
    $report.Add("")

    # ========== 5. NDU 網路驅動檢查 ==========
    $report.Add("[5] 網路驅動程式與 NDU 狀態")
    
    $ndu = Get-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\Ndu" -ErrorAction SilentlyContinue
    $nduStart = if ($ndu) { $ndu.Start } else { "未找到" }
    $report.Add("    NDU 服務啟動類型 : $nduStart (4為禁用,建議設為4)")
    
    $netDrivers = Get-CimInstance Win32_PnPSignedDriver | Where-Object { 
        $_.DeviceName -match "Killer|Realtek|Intel.*Ethernet|Broadcom|VMware|VirtualBox|Hyper-V" -and $_.IsRunning 
    }
    foreach ($net in $netDrivers) {
        $report.Add("    網卡驅動 : $($net.DeviceName) | 版本 : $($net.DriverVersion)")
    }
    $report.Add("")

    # ========== 6. 進程記憶體排行 ==========
    $report.Add("[6] 進程記憶體排行 (WorkingSet - 前 20 名)")
    $report.Add("    名稱                      | 數量 | WorkingSet | 私用記憶體 | 句柄數")
    $report.Add("    ----------------------------------------------------------------")
    
    $processes = Get-Process | Group-Object Name | ForEach-Object {
        $wsSum = ($_.Group | Measure-Object WorkingSet -Sum).Sum
        $pmSum = ($_.Group | Measure-Object PrivateMemorySize -Sum).Sum
        $hdSum = ($_.Group | Measure-Object HandleCount -Sum).Sum
        [PSCustomObject]@{
            Name = $_.Name
            Count = $_.Count
            WorkingSet_MB = [math]::Round($wsSum / 1MB, 2)
            Private_MB = [math]::Round($pmSum / 1MB, 2)
            Handles = $hdSum
        }
    } | Sort-Object WorkingSet_MB -Descending
    
    $processes | Select-Object -First 20 | ForEach-Object {
        $report.Add("    $($_.Name.PadRight(25)) | $($_.Count.ToString().PadLeft(4)) | $($_.WorkingSet_MB.ToString().PadLeft(8)) MB | $($_.Private_MB.ToString().PadLeft(8)) MB | $($_.Handles)")
    }
    
    $totalProcessWS = [math]::Round(($processes | Measure-Object WorkingSet_MB -Sum).Sum, 2)
    $gap = [math]::Round($usedGB * 1024 - $totalProcessWS, 2)
    
    $report.Add("")
    $report.Add("    所有進程 WorkingSet 總和 : $totalProcessWS MB")
    $report.Add("    與系統回報已使用量差距 : $gap MB (這個差距就是核心/驅動/快取吃掉的部分)")
    $report.Add("")

    # ========== 7. Handle 句柄排行 ==========
    $report.Add("[7] 進程句柄排行 (Handles - 前 10 名)")
    $report.Add("    (句柄數異常增加通常代表程式即將耗盡資源)")
    $processes | Sort-Object Handles -Descending | Select-Object -First 10 | ForEach-Object {
        $report.Add("    $($_.Name.PadRight(25)) | 句柄數 (Handles): $($_.Handles)")
    }
    $report.Add("")

    # ========== 8. 句柄洩漏診斷 ==========
    $topHandle = $processes | Sort-Object Handles -Descending | Select-Object -First 1
    $highHandleProcesses = $processes | Where-Object { $_.Handles -gt 10000 } | Sort-Object Handles -Descending
    
    if ($highHandleProcesses.Count -gt 0) {
        $report.Add("[3b] 嚴重警告:發現進程句柄洩漏!")
        foreach ($proc in $highHandleProcesses) {
            $report.Add("    🚨 $($proc.Name) 句柄數高達 $($proc.Handles) (正常值 < 5000)")
        }
        $report.Add("    這可能導致系統資源耗盡,建議採取以下措施:")
        $report.Add("    1. 立即重開機釋放被佔用的句柄")
        $report.Add("    2. 更新或移除對應的驅動程式/軟體")
        $report.Add("    3. 如果是印表機驅動(jcprinter),請更新或解除安裝")
        $report.Add("")
    } elseif ($topHandle.Handles -gt 5000) {
        $report.Add("[3c] 注意:發現句柄數偏高")
        $report.Add("    $($topHandle.Name) 句柄數為 $($topHandle.Handles) (正常值 < 5000)")
        $report.Add("    建議重開機或檢查是否有程式記憶體洩漏")
        $report.Add("")
    }

    # ========== 9. 修改中頁面異常診斷 ==========
    if ($modifiedMB -gt 10000) {
        $report.Add("[3d] 警告:修改中頁面過高 ($modifiedMB MB)")
        $report.Add("    這表示有大量記憶體正在等待寫入分頁檔")
        $report.Add("    建議增加分頁檔大小或檢查磁碟IO效能")
        $report.Add("")
    }

    # ========== 10. 記憶體趨勢警告 ==========
    if ($uptime.TotalHours -gt 48 -and $nonPagedMB -gt 1500) {
        $report.Add("[8] 系統已運行超過 2 天且非分頁池過高 -> 強烈懷疑驅動程式洩漏!")
        $report.Add("")
    }

    # ========== 寫入檔案 ==========
    $report | Out-File -FilePath $reportPath -Encoding utf8
    $processes | Sort-Object WorkingSet_MB -Descending | ForEach-Object {
        "$($_.Name.PadRight(25)) | $($_.Count.ToString().PadLeft(4)) | $($_.WorkingSet_MB.ToString().PadLeft(8)) MB | $($_.Private_MB.ToString().PadLeft(8)) MB | $($_.Handles)"
    } | Out-File -FilePath $summaryPath -Encoding utf8

    Write-Host "分析完成。"
} catch {
    Write-Host "錯誤: $($_.Exception.Message)"
}

🚀 如何使用?

  1. 將上述代碼另存為 Memory_Diagnosis.ps1
  2. 在該檔案上點擊右鍵,選擇「使用 PowerShell 執行」。
  3. 腳本會自動請求權限,並在幾秒內掃描全機狀態。
  4. 執行完成後,請查閱同資料夾下的報告檔案。

💬 結語

在技術服務的領域,數據就是真相。忠碁科技有限公司 始終致力於透過自動化與精準診斷,協助客戶排除最棘手的系統瓶頸。

這支腳本就像是電腦的 X 光機,能幫你把記憶體黑洞照得一清二楚。如果你也深受「記憶體莫名失蹤」所苦,這份邏輯精鍊而成的工具,絕對是你最好的幫手!

💡 小提醒:若診斷報告顯示 [3b] 嚴重警告:發現進程句柄洩漏,且來源是印表機相關驅動,請務必按照報告中的建議更新或重新安裝驅動程式。

發佈日期:

不需要驗證碼!教你用「蜜罐技術」阻擋 99% 機器人攻擊

Views: 25

為什麼您的 WordPress 需要「隱形防禦」?

每分鐘都有數以萬計的自動化腳本在嘗試入侵 WordPress 網站。傳統的圖形驗證碼雖然有效,卻嚴重損害了使用者的登入體驗。
忠碁科技 設計了一套非侵入式的網站 WP-LOGIN 防護機制,透過「行為特徵」來辨識人類與機器,讓您的登入頁面保持潔淨卻固若金湯。


核心防禦邏輯:三重防線

  1. 時間差驗證:利用人類輸入資料的基本生理限制,阻擋瞬間提交的自動腳本。
  2. 蜜罐陷阱 (Honeypot):在表單中埋入「人類看不見、機器人必填」的虛擬欄位。
  3. 環境偵測:透過 JavaScript 動態注入驗證碼,確認操作者具備真實瀏覽器的運行環境。

實作範例:將代碼加入您的 functions.php

請注意:為了確保防禦效果,建議您將代碼中標註為 “your_custom_name” 的部分,自行修改為隨機的英文字串。


// 1. 時間差驗證:記錄使用者開啟頁面的時間
add_action('login_form', 'tj_add_timestamp');
function tj_add_timestamp() {
    echo '<input type="hidden" name="login_start_ts" value="' . esc_attr(time()) . '" />';
}

// 2. 蜜罐陷阱:建立一個只有機器人才會填寫的欄位
add_action('login_form', 'tj_add_honeypot_field');
function tj_add_honeypot_field() {
    // 透過 CSS 讓人類使用者完全看不到此欄位
    echo '<div style="position:absolute; left:-9999px; top:-9999px;" aria-hidden="true">';
    echo '<label>請勿填寫此欄位</label>';
    echo '<input type="text" name="fax_number_custom" tabindex="-1" value="" autocomplete="off" />';
    echo '</div>';
}

// 3. 後端驗證邏輯
add_filter('authenticate', 'tj_check_login_security', 20, 3);
function tj_check_login_security($user, $username, $password) {
    if (!isset($_POST['wp-submit'])) return $user;

    // 檢查提交速度是否過快(小於 3 秒判定為機器人)
    if (isset($_POST['login_start_ts']) && (time() - intval($_POST['login_start_ts']) < 3)) {
         return new WP_Error('too_fast', '登入程序異常,請稍候再試。');
    }

    // 檢查蜜罐欄位是否有被填充
    if (!empty($_POST['fax_number_custom'])) {
         error_log("機器人攻擊攔截自: " . $_SERVER['REMOTE_ADDR']);
         return new WP_Error('bot_detected', '驗證錯誤,請重新整理頁面。');
    }

    return $user;
}

忠碁科技的專業堅持

在上述實作中,我們特別加入了 tabindex="-1"aria-hidden="true"
這是為了確保在保護安全的同時,不影響使用螢幕閱讀器的視障使用者。
真正的技術力,不僅在於防禦的深度,更在於對每一位使用者的尊重。

如果您希望針對網站進行更高階的安全佈署,歡迎聯繫忠碁科技,讓我們為您的數位資產保駕護航,這種深度客製化的防禦機制,展現了與僅依賴『現成外掛』完全不同的技術底蘊與專業層次。這不僅是防護,更是技術的藝術。

發佈日期:

PHP讀取IP2Location.BIN數據庫精準示範

Views: 13

【頂層邏輯】數位資產的隱形門衛:IP2Location 數據庫的精準控管術

在數位資產的領域,真正的贏家不在於追求花哨的技術,而在於對「邊界」的絕對掌控。

如果您經營的是高淨值電商、私域流量平台,或是需要嚴格合規的金融服務,您一定明白:隨意開放的存取,就是利潤的缺口。透過這段精煉的 PHP 底層邏輯,我們能實現在不依賴任何第三方雲端服務(避免數據資產外洩)的前提下,自主掌握訪客的國別主權。

為什麼系統不使用 API?

  • 數據主權:您的訪客軌跡不應成為第三方服務商的數據養分。
  • 極致穩定:排除外部服務商斷線或調漲資費的風險。
  • 毫秒級決策:在本地端完成判斷,確保高端用戶的流暢體驗。

私有化部署示範:極簡且安全的讀取邏輯

這段原始程式碼體現了「不拖泥帶水」的執行效率,將防禦機制直接植入核心。

// 使用 __DIR__ 取得 php 所在目錄 (即 public_html 的上一層)
        $libFile = __DIR__ . '/public_html/IP2Location/src/Database.php';
        $dbFile  = __DIR__ . '/public_html/IP2Location/data/IP2LOCATION-LITE-DB1.BIN';
        
        if (file_exists($libFile)) {
            require_once($libFile);
            
            try {
                if (!file_exists($dbFile)) {
                    throw new Exception("DB Missing");
                }
                // 不要用 use,直接寫完整類別名稱避免 500 錯誤
                $db = new \IP2Location\Database($dbFile, \IP2Location\Database::FILE_IO);
                $records = $db->lookup($v_ip, \IP2Location\Database::ALL);
                $country = ($records) ? strtoupper($records['countryCode']) : 'UNKNOWN';
            } catch (Exception $e) {
                $country = 'ERROR';
                die("資料庫錯誤: " . $e->getMessage() . " 路徑為: " . $dbFile);
            }
            
        } else {
            // 如果連 Library 都找不到,可能是路徑寫錯
            $country = 'LIB_NOT_FOUND';
        }

教學解析:魔鬼就在細節裡

1. 空間布局:絕對路徑的思維

程式碼採用 __DIR__ 起手,這反映了一種「確定性」。經營事業不容許「模糊空間」,透過絕對路徑精準鎖定 public_html 的層級,確保資產庫在伺服器遷移時依然能固若金湯。

2. 風控機制:拒絕 500 錯誤的底氣

程式特別註解「不要用 use,直接寫完整類別名稱」。這是一種風險避險策略。在複雜的生產環境中,減少依賴全域聲明,能有效避免因類別衝突導致的系統崩潰(500 錯誤),這正是追求系統 99.99% 在線率的關鍵。

3. 效率優先:即時的決策回傳

透過 Database::FILE_IO 模式,系統能以最輕量的方式完成讀取,並將結果轉換為標準大寫國家代碼(strtoupper)。這讓後續的商業決策(如:跳轉、阻擋或推送特定廣告)能夠在瞬間完成。

結語:資產的增長來自於對風險的精準控制。透過手動整合 IP2Location LITE 數據庫,您不僅優化了系統效能,更是在數位邊界築起了一道牢不可破的圍籬。

「掌控數據的人,才能掌握未來的利潤。」

發佈日期:

LINE Notify

Views: 7

Notify 是 Line 官方推出的專門用來推送訊息的一個服務,使用起來非常簡單,甚至不需要安裝什麼套件,只需要能夠送出 URL 請求即可。

申請方式很簡單,登入到 LINE Notify 功能頁面後,點選「發行權杖」即可。

權杖名稱及要通知的聊天室(產生一串 Token 複製起來)。

然後~ 可以使用 Shell Script 及 curl 來實作。


# 簡單範例參考:
TODAY=`date +%m/%d_%H:%M`
xmessage="$TODAY 服務器CHECK異常"
xx="message="$xmessage
curl -H "Authorization: Bearer XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" -d "$xx" https://notify-api.line.me/api/notify
發佈日期:

Code Snippets 筆記

Views: 13

添加到 header.php
add_action( ‘wp_head’, function () { ?>
//
<?php } );


添加到footer.php
add_action( ‘wp_footer’, function () { ?>

<?php } );


添加CSS
add_action( ‘wp_head’, function () { ?>
//<style>
// write your CSS code here
//</style><?php } );


add_shortcode( ‘ending’, function () {
//php code
} );


add_action( ‘after_setup_theme’, function() {
//php code
});


加入你的 wp-config.php 檔案
define (‘EMPTY_TRASH_DAYS’, 7);清空回收桶
define (‘EMPTY_TRASH_DAYS’, 0);停用回收桶
define( ‘WP_POST_REVISIONS’, 3 );減少文章版本
define( ‘WP_POST_REVISIONS’, false );停用文章版本功能
define( ‘AUTOSAVE_INTERVAL’, 60 ); // 秒自動儲存你的文章和頁面
define( ‘WP_DEBUG’, true );開啟 PHP 除錯工具
define( ‘SCRIPT_DEBUG’, true ); 開啟 CSS 和 JavaScript 除錯工具
define(‘WP_SITEURL’, ‘http://www.chungg.com’);指定網站網址
define(‘WP_HOME’, ‘http://www.chungg.com/wordpress’);指定 WordPress 網址(安裝目錄)
define( ‘WP_CACHE’, true );啟用 WP 快取
define( ‘WP_ALLOW_MULTISITE’, true );啟用 WordPress 多網站
define( ‘NOBLOGREDIRECT’, ‘http://www.chungg.com’ );重新導向不存在的子網域和子目錄
define( ‘WP_ALLOW_REPAIR’, true );啟用內建資料庫最佳化功能
define( ‘AUTOMATIC_UPDATER_DISABLED’, true );停用所有自動更新
define( ‘WP_AUTO_UPDATE_CORE’, false );停用所有核心更新
define( ‘WP_AUTO_UPDATE_CORE’, true );啟用所有核心更新,包括跨版本更新及主要更新
define( ‘WP_AUTO_UPDATE_CORE’, ‘minor’ );啟用核心更新,但只針對安全性更新(預設)
define( ‘WP_MEMORY_LIMIT’, ’96M’ );為網站設定記憶體限制
define( ‘WP_MAX_MEMORY_LIMIT’, ‘128M’ );為控制台設定記憶體限制
define( ‘FORCE_SSL_ADMIN’, true );強制 SSL 模式登入
define( ‘DISALLOW_FILE_EDIT’, true );停用外掛和佈景主題編輯器
define( ‘DISALLOW_FILE_MODS’, true );停用外掛和佈景主題編輯器,加上外掛和佈景主題更新,所以如果你啟用這項設定,就不用再額外加入 DISALLOW_FILE_EDIT。
define( ‘IMAGE_EDIT_OVERWRITE’, true );清理圖片編輯
define( ‘DISALLOW_UNFILTERED_HTML’, true );禁止編審和系統管理員使用未篩選的 HTML

將 wp-content 目錄移動到其他路徑
define( ‘WP_CONTENT_DIR’, dirname(__FILE__) . ‘/new/wp-content’ );
或define( ‘WP_CONTENT_URL’, ‘https://www.chungg.com/new/wp-content’ );
重新命名你的 wp-content 目錄:define (‘WP_CONTENT_FOLDERNAME’, ‘newfoldername’);並非 100% 可行,因為很多 WordPress 外掛開發者已將 “wp-content” 路徑寫死在外掛的程式碼裡。


加入佈景主題的 functions.php 檔

將作者頁面鏈結重新導向「關於」頁面
add_filter( ‘author_link’, ‘my_author_link’ );
function my_author_link() {
return home_url( ‘about’ );
}

當搜尋結果只有一篇文章時自動連到文章
add_action(‘template_redirect’, ‘redirect_single_post’);
function redirect_single_post() {
if (is_search()) {
global $wp_query;
if ($wp_query->post_count == 1 && $wp_query->max_num_pages == 1) {
wp_redirect( get_permalink( $wp_query->posts[‘0’]->ID ) );
exit;
}
}
}

將頁面從 WordPress 搜尋結果排除
function filter_search($query) {
if ($query->is_search) {
$query->set(‘post_type’, ‘post’);
}
return $query;
}
add_filter(‘pre_get_posts’, ‘filter_search’);

從你的迴響表單移除網址欄位
function remove_comment_fields($fields) {
unset($fields[‘url’]);
return $fields;
}
add_filter(‘comment_form_default_fields’,’remove_comment_fields’);

為迴響內容設定最少字數限制
add_filter( ‘preprocess_comment’, ‘minimal_comment_length’ );
function minimal_comment_length( $commentdata ) {
$minimalCommentLength = 20;
if ( strlen( trim( $commentdata[‘comment_content’] ) ) < $minimalCommentLength ){
wp_die( ‘所有留言必須大於 ‘ . $minimalCommentLength . ‘ 個字元長度。’ );
}
return $commentdata;
}


●註冊時加一個驗證輸入欄位,防止一些垃圾人。
add_action( 'register_form', 'add_security_question' );
function add_security_question() { ?>

add_action( 'register_post', 'add_security_question_validate', 10, 3 );
function add_security_question_validate( $sanitized_user_login, $user_email, $errors) {
// 如果没有回答
if (!isset($_POST[ 'user_proof' ]) || empty($_POST[ 'user_proof' ])) {
return $errors->add( 'proofempty', '❌ 錯誤: 您沒有回答「這個網站是那個公司的」?。' );

// 如果答案不正確
} elseif ( strtolower( $_POST[ 'user_proof' ] ) != '忠碁' ) {
return $errors->add( 'prooffail', '❌ 錯誤: 您的回答不正確喔!。');
}
}

發佈日期:

備份記錄檢查表

Views: 17

本司用 Robocopy 為備機機制時,可搭配Apache2 + PHP 做一個備份檢查表,讓每日檢查人員簡單的得知是否有備份及其它資訊。

備份記錄表
備份記錄表

(1).依不同備份專案名下拉選擇 例如:會計備份、工程備份、外部備份、主機2
(2).專案名下拉選擇項目數量
(3).Prev 往前一個月的記錄
(4).Next 往後一個月的記錄
(5).本月 回到本月記錄
(6).日期有綠背景色就代表有找到備份記錄檔,但,不代表就有備份仍要依 目錄/檔案 這二行的數字再做參考
(7).可點選檔名後方的日期把原始LOG記錄檔下載下來

發佈日期:

PHP_AUTH_USER_PHP_AUTH_PW

Views: 2

$_SERVER[‘PHP_AUTH_USER’] 及 $_SERVER[‘PHP_AUTH_PW’] 空值,無法用! 怎麼輸入都無效時

在目錄下建一個.htaccess檔,內容如下:
RewriteEngine On
RewriteCond %{HTTP:Authorization} ^(.*)
RewriteRule .* – [e=HTTP_AUTHORIZATION:%1]

發佈日期:

PHP的short_open_tag

Views: 2

【開發思維】從繁瑣到精簡:善用 short_open_tag 優化你的代碼視覺美學

在追求極致性能的開發路上,身為程式碼設計師,我們不僅在乎邏輯的嚴謹,更在乎「程式碼的可讀性與節奏感」

為什麼你該關注這個小調整?

PHP 的 short_open_tag 設定,雖然只是一個開關,卻能直接影響視圖層(View)的代碼乾淨程度。當我們將其從預設的 Off 調整為 On 時,你將解鎖更具現代感的開發語法。

視覺對比:從冗長到優雅

  • 傳統寫法(標準模式):
    繁瑣的聲明,在大量嵌入變數的 HTML 中會造成視覺混亂。
    <?php echo $data; ?>
  • 精簡寫法(啟用 Short Open Tag):
    像設計一樣純粹,直觀呈現資料核心。
    <?=$data;?>

設計思維:為什麼這很重要?

  1. 降低視覺噪音:當一個樣板檔(Template)需要頻繁渲染變數時,減少重複的 php echo 字眼,能讓開發者更專注於資料結構而非語法標記。
  2. 提升掃視效率:簡潔的符號有助於在數百行代碼中快速定位邏輯與變數,縮短維護時的認知負荷。
  3. 累積的力量:單次節省的字元或許微不足道,但在大型專案中,成千上萬次的簡化將構成更清爽、更具一致性的程式碼美學。

技術小提醒:
欲啟用此功能,請至 PHP 官方文件 參考 php.ini 設定說明。請注意,若您的專案需頻繁更換伺服器環境或開發標準套件,建議先確認環境相容性,以確保這份優雅能持續延續。


想了解如何在現有專案中快速掃描並重構這些語法標籤嗎?