ExcelHome技术论坛

 找回密码
 免费注册

QQ登录

只需一步,快速开始

快捷登录

搜索
EH技术汇-专业的职场技能充电站 妙哉!函数段子手趣味讲函数 Excel服务器-会Excel,做管理系统 效率神器,一键搞定繁琐工作
HR薪酬管理数字化实战 Excel 2021函数公式学习大典 Excel数据透视表实战秘技 打造核心竞争力的职场宝典
让更多数据处理,一键完成 数据工作者的案头书 免费直播课集锦 ExcelHome出品 - VBA代码宝免费下载
用ChatGPT与VBA一键搞定Excel WPS表格从入门到精通 Excel VBA经典代码实践指南
查看: 1624|回复: 8

[分享] 遍历目录和文件--使用管道和cmd交互的应用

[复制链接]

TA的精华主题

TA的得分主题

发表于 2017-1-5 23:37 | 显示全部楼层 |阅读模式
[广告] VBA代码宝 - VBA编程加强工具 · VBA代码随查随用  · 内置多项VBA编程加强工具       ★ 免费下载 ★      ★使用手册
本帖最后由 loquat 于 2017-1-5 23:40 编辑

先推荐一篇帖子,裙子小姐的遍历知识普及贴:
http://club.excelhome.net/forum.php?mod=viewthread&tid=1185089


遍历文件有很多种方式:
1.使用Dir函数,配合递归
   遍历文件是一个旧话题,这里有一篇帖子
   http://club.excelhome.net/thread-1319867-1-1.html
   思路很新颖,给了递归一次脱胎换骨
2.使用fso
   fso配合递归,fso配合字典或数组
3.使用cmd dir
  这里是我之前的帖子,请直接跳到10#
  http://club.excelhome.net/thread-1063384-1-1.html
  目前调用有两种方式,方法一:直接弹窗cmd窗口,执行完命名直接读取结果
                                     方法二:后台执行cmd命令,执行完保存到临时文本文件,然后从临时文件读取
  本帖要讲的是另一种方式,建立Pipe(管道),后台执行cmd命令,将执行结果直接写入内存,然后从内存读取
  综合解决了方法一和方法二各自的弊端

不知道论坛封锁了什么特殊字符,不让上代码,直接上传附件吧。
文件搜索2-管道.rar (24.33 KB, 下载次数: 49)

TA的精华主题

TA的得分主题

 楼主| 发表于 2017-1-5 23:41 | 显示全部楼层
代码是在wzsy2_mrf的附件的基础上做的改动。

TA的精华主题

TA的得分主题

 楼主| 发表于 2017-1-5 23:41 | 显示全部楼层
[广告] VBA代码宝 - VBA编程加强工具 · VBA代码随查随用  · 内置多项VBA编程加强工具       ★ 免费下载 ★      ★使用手册
  1. Sub 遍历_管道()
  2. Dim t: t = Timer
  3. Dim a As New DosCMD
  4. Dim arr
  5. a.DosInput Environ$("comspec") & " /c dir " & Chr(34) & "c:\*.doc" & Chr(34) & " /s /b /a:-d"
  6. arr = a.DosOutPutEx        '默认等待时间120s
  7. arr = Split(arr, vbCrLf)   '分割成数组
  8. arr = Filter(arr, ".doc", True, vbTextCompare)
  9. arr = Filter(arr, "*", False, vbTextCompare)
  10. arr = Filter(arr, "$", False, vbTextCompare)
  11. Debug.Print Timer - t
  12. End Sub
复制代码

TA的精华主题

TA的得分主题

 楼主| 发表于 2017-1-5 23:42 | 显示全部楼层
  1. 'DosCMD类第1部分
  2. Option Explicit
  3. '延时函数部分
  4. Private Type FILETIME
  5.     dwLowDateTime As Long
  6.     dwHighDateTime As Long
  7. End Type

  8. Private Const WAIT_ABANDONED& = &H80&
  9. Private Const WAIT_ABANDONED_0& = &H80&
  10. Private Const WAIT_FAILED& = -1&
  11. Private Const WAIT_IO_COMPLETION& = &HC0&
  12. Private Const WAIT_OBJECT_0& = 0
  13. Private Const WAIT_OBJECT_1& = 1
  14. Private Const WAIT_TIMEOUT& = &H102&
  15. Private Const INFINITE = &HFFFF
  16. Private Const ERROR_ALREADY_EXISTS = 183&
  17. Private Const QS_HOTKEY& = &H80
  18. Private Const QS_KEY& = &H1
  19. Private Const QS_MOUSEBUTTON& = &H4
  20. Private Const QS_MOUSEMOVE& = &H2
  21. Private Const QS_PAINT& = &H20
  22. Private Const QS_POSTMESSAGE& = &H8
  23. Private Const QS_SENDMESSAGE& = &H40
  24. Private Const QS_TIMER& = &H10
  25. Private Const QS_MOUSE& = (QS_MOUSEMOVE Or QS_MOUSEBUTTON)
  26. Private Const QS_INPUT& = (QS_MOUSE Or QS_KEY)
  27. Private Const QS_ALLEVENTS& = (QS_INPUT Or QS_POSTMESSAGE Or QS_TIMER Or QS_PAINT Or QS_HOTKEY)
  28. Private Const QS_ALLINPUT& = (QS_SENDMESSAGE Or QS_PAINT Or QS_TIMER Or QS_POSTMESSAGE Or QS_MOUSEBUTTON Or QS_MOUSEMOVE Or QS_HOTKEY Or QS_KEY)
  29. Private Const UNITS = 4294967296#
  30. Private Const MAX_LONG = -2147483648#
  31. Private Declare Function CreateWaitableTimer Lib "kernel32" Alias "CreateWaitableTimerA" (ByVal lpSemaphoreAttributes As Long, ByVal bManualReset As Long, ByVal lpName As String) As Long
  32. Private Declare Function OpenWaitableTimer Lib "kernel32" Alias "OpenWaitableTimerA" (ByVal dwDesiredAccess As Long, ByVal bInheritHandle As Long, ByVal lpName As String) As Long
  33. Private Declare Function SetWaitableTimer Lib "kernel32" (ByVal hTimer As Long, lpDueTime As FILETIME, ByVal lPeriod As Long, ByVal pfnCompletionRoutine As Long, ByVal lpArgToCompletionRoutine As Long, ByVal fResume As Long) As Long
  34. Private Declare Function CancelWaitableTimer Lib "kernel32" (ByVal hTimer As Long)
  35. Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long
  36. Private Declare Function WaitForSingleObject Lib "kernel32" (ByVal hHandle As Long, ByVal dwMilliseconds As Long) As Long
  37. Private Declare Function MsgWaitForMultipleObjects Lib "user32" (ByVal nCount As Long, pHandles As Long, ByVal fWaitAll As Long, ByVal dwMilliseconds As Long, ByVal dwWakeMask As Long) As Long
  38. Private mlTimer As Long
  39. Private Type STARTUPINFO    '(CreateProcess)
  40.     cb As Long
  41.     lpReserved As Long
  42.     lpDesktop As Long
  43.     lpTitle As Long
  44.     dwX As Long
  45.     dwY As Long
  46.     dwXSize As Long
  47.     dwYSize As Long
  48.     dwXCountChars As Long
  49.     dwYCountChars As Long
  50.     dwFillAttribute As Long
  51.     dwFlags As Long
  52.     wShowWindow As Integer
  53.     cbReserved2 As Integer
  54.     lpReserved2 As Long
  55.     hStdInput As Long
  56.     hStdOutput As Long
  57.     hStdError As Long
  58. End Type
  59. Private Declare Function FlushFileBuffers Lib "kernel32" (ByVal hfile As Long) As Long
  60. Private Declare Function OpenProcess Lib "kernel32" (ByVal dwDesiredAccess As Long, ByVal bInheritHandle As Long, ByVal dwProcessId As Long) As Long
  61. Private Type PROCESS_INFORMATION    '(CreateProcess)
  62.     hProcess As Long
  63.     hThread As Long
  64.     dwProcessId As Long
  65.     dwThreadID As Long
  66. End Type
  67. Private Type SECURITY_ATTRIBUTES    '(CreateProcess)
  68.     nLength As Long
  69.     lpSecurityDescriptor As Long
  70.     bInheritHandle As Long
  71. End Type
  72. '常数声明
  73. Private Const NORMAL_PRIORITY_CLASS = &H20&
  74. Private Const STARTF_USESTDHANDLES = &H100&
  75. Private Const STARTF_USESHOWWINDOW = &H1
  76. Private Const PROCESS_TERMINATE = &H1
  77. Private Const PROCESS_QUERY_INFORMATION = &H400
  78. '函数声明
  79. Private Declare Function CreateProcessA Lib "kernel32" (ByVal lpApplicationName As Long, ByVal lpCommandLine As String, lpProcessAttributes As SECURITY_ATTRIBUTES, lpThreadAttributes As SECURITY_ATTRIBUTES, ByVal bInheritHandles As Long, ByVal dwCreationFlags As Long, ByVal lpEnvironment As Long, ByVal lpCurrentDirectory As Long, lpStartupInfo As STARTUPINFO, lpProcessInformation As PROCESS_INFORMATION) As Long
  80. Private Declare Function GetCurrentProcess Lib "kernel32" () As Long
  81. Private Declare Function TerminateProcess Lib "kernel32" (ByVal hProcess As Long, ByVal uExitCode As Long) As Long
  82. Private Declare Function PeekNamedPipe Lib "kernel32" (ByVal hNamedPipe As Long, ByVal lpBuffer As Long, ByVal nBufferSize As Long, ByRef lpBytesRead As Long, ByRef lpTotalBytesAvail As Long, ByRef lpBytesLeftThisMessage As Long) As Long
  83. Private Declare Function CreatePipe Lib "kernel32" (phReadPipe As Long, phWritePipe As Long, lpPipeAttributes As Any, ByVal nSize As Long) As Long
  84. Private Declare Function GetExitCodeProcess Lib "kernel32" (ByVal hProcess As Long, lpExitCode As Long) As Long
  85. Private Declare Function ReadFile Lib "kernel32" (ByVal hfile As Long, ByVal lpBuffer As Long, ByVal nNumberOfBytesToRead As Long, lpNumberOfBytesRead As Long, ByVal lpOverlapped As Any) As Long
  86. Private Declare Function GetLastError Lib "kernel32" () As Long
  87. Private Declare Function WriteFile Lib "kernel32" (ByVal hfile As Long, ByVal lpBuffer As Long, ByVal nNumberOfBytesToWrite As Long, ByRef lpNumberOfBytesWritten As Long, lpOverlapped As Any) As Long
  88. Private Declare Function DuplicateHandle Lib "kernel32" (ByVal hSourceProcessHandle As Long, ByVal hSourceHandle As Long, ByVal hTargetProcessHandle As Long, lpTargetHandle As Long, ByVal dwDesiredAccess As Long, ByVal bInheritHandle As Long, ByVal dwOptions As Long) As Long
  89. Private Const DUPLICATE_SAME_ACCESS = &H2
  90. Private PipeR4InputChannel As Long, PipeW4InputChannel As Long, hInputHandle As Long
  91. Private PipeR4OutputChannel As Long, PipeW4OutputChannel As Long, hOutputHandle As Long
  92. Private Proc As PROCESS_INFORMATION
  93. Public Enum InitResult
  94.     ERROR_OK = 0
  95.     ERROR_INIT_INPUT_HANDLE = 1
  96.     ERROR_INIT_OUTPUT_HANDLE = 2
  97.     ERROR_DUP_READ_HANDLE = 3
  98.     ERROR_DUP_WRITE_HANDLE = 4
  99.     ERROR_CREATE_CHILD_PROCESS = 5
  100. End Enum
  101. Public Enum TermResult
  102.     ERROR_OK = 0
  103. End Enum
  104. Public Enum InputResult
  105.     ERROR_OK = 0
  106.     ERROR_QUERY_WRITE_INFO_SIZE = 1
  107.     ERROR_DATA_TO_LARGE = 2
  108.     ERROR_WRITE_INFO = 3
  109.     ERROR_WRITE_UNEXPECTED = 5
  110. End Enum
  111. Public Enum OutputResult
  112.     ERROR_OK = 0
  113.     ERROR_QUERY_READ_INFO_SIZE = 1
  114.     ERROR_ZERO_INFO_SIZE = 2
  115.     ERROR_READ_INFO = 3
  116.     ERROR_UNEQUAL_INFO_SIZE = 4
  117.     ERROR_READ_UNEXPECTED = 5
  118.     ERROR_QUERY_INFO_SIZE = 6
  119. End Enum
  120. Private Declare Function timeGetTime Lib "winmm.dll" () As Long
  121. Dim OutPutData As String, ReadData As Boolean, OutData As String
  122. Private IsExit As Boolean
  123. Public Event Events()
  124. Private Sub Class_Initialize()
  125.     If InitDosIO <> 0 Then
  126.         Debug.Print "DOS通道创建失败!"
  127.     End If
  128. End Sub
  129. Private Sub Class_Terminate()
  130.     On Error Resume Next
  131.     IsExit = True
  132.     Call EndDosIo
  133.     If mlTimer <> 0 Then CloseHandle mlTimer
  134. End Sub
复制代码

TA的精华主题

TA的得分主题

 楼主| 发表于 2017-1-5 23:43 | 显示全部楼层
  1. 'DosCMD类第2部分
  2. Public Sub Wait(MilliSeconds As Long)    '延时函数
  3.     On Error GoTo ErrHandler
  4.     Dim ft As FILETIME
  5.     Dim lBusy As Long
  6.     Dim lRet As Long
  7.     Dim dblDelay As Double
  8.     Dim dblDelayLow As Double
  9.     mlTimer = CreateWaitableTimer(0, True, "Timer" & Format$(Now(), "NNSS"))
  10.     If Err.LastDllError <> ERROR_ALREADY_EXISTS Then
  11.         ft.dwLowDateTime = -1
  12.         ft.dwHighDateTime = -1
  13.         lRet = SetWaitableTimer(mlTimer, ft, 0, 0, 0, 0)
  14.     End If
  15.     dblDelay = CDbl(MilliSeconds) * 10000#
  16.     ft.dwHighDateTime = -CLng(dblDelay / UNITS) - 1
  17.     dblDelayLow = -UNITS * (dblDelay / UNITS - Fix(CStr(dblDelay / UNITS)))
  18.     If dblDelayLow < MAX_LONG Then dblDelayLow = UNITS + dblDelayLow
  19.     ft.dwLowDateTime = CLng(dblDelayLow)
  20.     lRet = SetWaitableTimer(mlTimer, ft, 0, 0, 0, False)
  21.     Do
  22.         lBusy = MsgWaitForMultipleObjects(1, mlTimer, False, INFINITE, QS_ALLINPUT&)
  23.         DoEvents
  24.     Loop Until lBusy = WAIT_OBJECT_0 And IsExit = False
  25.     CloseHandle mlTimer
  26.     mlTimer = 0
  27.     Exit Sub
  28. ErrHandler:
  29.     Err.Raise Err.Number, Err.Source, "[clsWaitableTimer.Wait]" & Err.Description
  30. End Sub

  31. Private Function InitDosIO() As InitResult
  32.     Dim Sa As SECURITY_ATTRIBUTES, ret As Long
  33.     With Sa
  34.         .nLength = Len(Sa)
  35.         .bInheritHandle = 1&
  36.         .lpSecurityDescriptor = 0&
  37.     End With
  38.     ret = CreatePipe(PipeR4InputChannel, PipeW4InputChannel, Sa, 1024&)
  39.     If ret = 0 Then    '建立进程输入管道
  40.         InitDosIO = ERROR_INIT_INPUT_HANDLE
  41.         Exit Function
  42.     End If
  43.     ret = CreatePipe(PipeR4OutputChannel, PipeW4OutputChannel, Sa, 4096&)    '建立输出通道,若建立失败,则关闭管道,退出
  44.     If ret = 0 Then    '建立进程的输出管道
  45.         CloseHandle PipeR4InputChannel
  46.         CloseHandle PipeW4InputChannel
  47.         InitDosIO = ERROR_INIT_OUTPUT_HANDLE
  48.         Exit Function
  49.     End If
  50.     ret = DuplicateHandle(GetCurrentProcess(), PipeW4InputChannel, GetCurrentProcess(), hInputHandle, 0, True, DUPLICATE_SAME_ACCESS)
  51.     If ret = 0 Then    '转换写句柄
  52.         CloseHandle PipeR4InputChannel
  53.         CloseHandle PipeW4InputChannel
  54.         CloseHandle PipeR4OutputChannel
  55.         CloseHandle PipeW4OutputChannel
  56.         InitDosIO = ERROR_DUP_WRITE_HANDLE
  57.         Exit Function
  58.     End If
  59.     ret = CloseHandle(PipeW4InputChannel)
  60.     If ret = 0 Then
  61.         Debug.Print "Close Handle Err"
  62.     End If
  63.     ret = DuplicateHandle(GetCurrentProcess(), PipeR4OutputChannel, GetCurrentProcess(), hOutputHandle, 0, True, DUPLICATE_SAME_ACCESS)
  64.     If ret = 0 Then    '转换读句柄
  65.         CloseHandle PipeR4InputChannel
  66.         CloseHandle PipeW4InputChannel
  67.         CloseHandle PipeR4OutputChannel
  68.         CloseHandle PipeW4OutputChannel
  69.         InitDosIO = ERROR_DUP_READ_HANDLE
  70.         Exit Function
  71.     End If
  72.     ret = CloseHandle(PipeR4OutputChannel)
  73.     If ret = 0 Then
  74.         Debug.Print "Close Handle 2 Err"
  75.     End If


  76.     Dim Start As STARTUPINFO, CmdStr As String
  77.     Start.cb = Len(Start)
  78.     Start.dwFlags = STARTF_USESTDHANDLES Or STARTF_USESHOWWINDOW
  79.     Start.hStdOutput = PipeW4OutputChannel
  80.     Start.hStdError = PipeW4OutputChannel
  81.     Start.hStdInput = PipeR4InputChannel
  82.     CmdStr = "CMD"    '需要打开的命令行外壳(CMD.EXE)
  83.     ret& = CreateProcessA(0&, CmdStr, Sa, Sa, True, NORMAL_PRIORITY_CLASS, 0&, 0&, Start, Proc)
  84.     If ret <> 1 Then    '建立控制进程
  85.         CloseHandle PipeR4InputChannel
  86.         CloseHandle PipeW4InputChannel
  87.         CloseHandle PipeR4OutputChannel
  88.         CloseHandle PipeW4OutputChannel
  89.         InitDosIO = ERROR_CREATE_CHILD_PROCESS
  90.         Exit Function
  91.     End If
  92.     ReadData = True
  93. End Function

  94. Public Function DosInput(ByVal Str As String) As InputResult
  95.     Dim Buflen As Long, BtWritten As Long, rtn As Long
  96.     Dim BtTest() As Byte
  97.     ReadData = True
  98.     BtTest = StrConv(Str + vbCrLf, vbFromUnicode)
  99.     Buflen = UBound(BtTest) + 1
  100.     rtn = WriteFile(hInputHandle, StrPtr(BtTest), Buflen, BtWritten, ByVal 0&)
  101.     If BtWritten = 0 Then
  102.         DosInput = ERROR_WRITE_INFO
  103.         Exit Function
  104.     End If
  105.     DosInput = 0
  106. End Function

  107. Public Function DosOutPutEx(Optional TimeOut As Long = 1200000) As String    '默认命令超时120秒
  108.     Dim OutPt As String, OldTime As Long, NewTime As Long, Tmp() As String
  109.     If ReadData = False Then Exit Function
  110.     OldTime = timeGetTime
  111.     OutPutData = ""
  112.     Do
  113.         DoEvents
  114.         If DosOutput(OutPt) = 0 Then
  115.             OutData = Left$(OutPt, Len(OutPt) - 1)
  116.             OutPutData = OutPutData & Left$(OutPt, Len(OutPt) - 1)
  117.             Call NowEvents
  118.             Tmp = Split(OutPt, Chr$(13))
  119.             If Len(Tmp(UBound(Tmp))) <> 0 Then
  120.                 If InStr(Tmp(UBound(Tmp)), ":") = 3 And Right(Tmp(UBound(Tmp)), 2) = ">" & Chr$(0) Then Exit Do
  121.             End If
  122.         End If
  123.         NewTime = timeGetTime
  124.         Call Wait(1)    '如需要更高实时性可以改为1
  125.         If NewTime - OldTime >= TimeOut Then Exit Do
  126.     Loop
  127.     DosOutPutEx = OutPutData
  128.     ReadData = False
  129. End Function

  130. Public Function GetOut() As String    '实时数据
  131.     GetOut = OutData
  132. End Function

  133. Friend Sub NowEvents()    '接口
  134.     RaiseEvent Events
  135. End Sub

  136. Private Function DosOutput(ByRef StrOutput As String) As OutputResult
  137.     Dim ret As Long, TmpBuf As String * 128, BtRead As Long, BtTotal As Long, BtLeft As Long
  138.     Dim rtn As Long, lngbytesread As Long
  139.     rtn = PeekNamedPipe(hOutputHandle, StrPtr(TmpBuf), 128, BtRead, BtTotal, BtLeft)
  140.     If rtn = 0 Then    '查询信息量
  141.         DosOutput = ERROR_QUERY_INFO_SIZE
  142.         Exit Function
  143.     End If
  144.     If BtTotal = 0 Then    '若信息为空,则退出
  145.         DosOutput = ERROR_ZERO_INFO_SIZE
  146.         Exit Function
  147.     End If
  148.     Dim Btbuf() As Byte, BtReaded As Long
  149.     ReDim Btbuf(BtTotal)
  150.     ret = ReadFile(hOutputHandle, VarPtr(Btbuf(0)), BtTotal, lngbytesread, 0&)
  151.     If ret = 0 Then
  152.         DosOutput = ERROR_READ_INFO
  153.         Exit Function
  154.     End If
  155.     If BtTotal <> lngbytesread Then
  156.         DosOutput = ERROR_UNEQUAL_INFO_SIZE
  157.     End If
  158.     Dim strBuf As String
  159.     strBuf = StrConv(Btbuf, vbUnicode)
  160.     StrOutput = strBuf
  161. End Function

  162. Private Function EndDosIo() As Long
  163.     Dim ret As Long
  164.     CloseHandle PipeR4InputChannel
  165.     CloseHandle PipeW4InputChannel
  166.     CloseHandle PipeR4OutputChannel
  167.     CloseHandle PipeW4OutputChannel
  168.     CloseHandle Proc.hThread
  169.     CloseHandle Proc.hProcess
  170.     If EndProcess(Proc.dwProcessId) = False Then
  171.         Debug.Print "主服务程序[CMD.EXE]没有关闭,请您手动关闭 ", vbInformation, "不好意思"
  172.     End If
  173. End Function

  174. Private Function EndProcess(ByVal ProcessID As Long) As Boolean
  175.     Dim hProcess As Long, ExitCode As Long, Rst As Long
  176.     hProcess = OpenProcess(PROCESS_TERMINATE Or PROCESS_QUERY_INFORMATION, True, ProcessID)
  177.     If hProcess <> 0 Then
  178.         GetExitCodeProcess hProcess, ExitCode
  179.         If ExitCode <> 0 Then
  180.             Rst = TerminateProcess(hProcess, 0)
  181.             CloseHandle hProcess
  182.             If Rst = 0 Then
  183.                 EndProcess = False
  184.             Else
  185.                 EndProcess = True
  186.             End If
  187.         Else
  188.             EndProcess = False
  189.         End If
  190.     Else
  191.         EndProcess = False
  192.     End If
  193. End Function
复制代码

TA的精华主题

TA的得分主题

发表于 2017-1-6 07:43 来自手机 | 显示全部楼层

TA的精华主题

TA的得分主题

发表于 2017-1-7 13:25 | 显示全部楼层
虽然看不懂,但作为楼主的老朋友,热烈祝贺!

TA的精华主题

TA的得分主题

 楼主| 发表于 2017-1-18 16:56 | 显示全部楼层

TA的精华主题

TA的得分主题

 楼主| 发表于 2017-1-18 17:00 | 显示全部楼层
代码在64位Office上可能会有问题
您需要登录后才可以回帖 登录 | 免费注册

本版积分规则

手机版|关于我们|联系我们|ExcelHome

GMT+8, 2025-1-13 11:00 , Processed in 0.030245 second(s), 11 queries , Gzip On, MemCache On.

Powered by Discuz! X3.4

© 1999-2023 Wooffice Inc.

沪公网安备 31011702000001号 沪ICP备11019229号-2

本论坛言论纯属发表者个人意见,任何违反国家相关法律的言论,本站将协助国家相关部门追究发言者责任!     本站特聘法律顾问:李志群律师

快速回复 返回顶部 返回列表