5

これはかなり長い投稿になり、発言ごとの「答え」は実際にはありません. 問題を解決するための特効薬ではなく、説明を探しています。そのため、あなたが答えたいと思うあらゆる側面は非常に高く評価されます. 前もって感謝します!


ファイル システム オブジェクトの「問題」と思われる問題に遭遇しました。そのため、VBA のファイル システム オブジェクトがどのように機能するのか、「何か他のもの」と比較して、機能などについて疑問が生じます (わかりません)。私がやっていることに対して Excel で使用する代替手段がある場合) .net などで質問するのに適した場所がわかりません。また、自分で調査するために何を調べればよいかわかりません。だからここにいます!

そう!問題に。簡単に説明すると、フォルダを反復処理してファイル情報 (名前、拡張子、フル パスなど) を収集し、それをスプレッドシートに配置します。最終的にこの情報を使用して、ファイルを新しい場所にコピーします。ただし、大規模 (1,000 以上のファイル) では、これはローカルでは問題なく動作するように見えますが、ネットワーク上の場所 (職場) ではかなり遅くなります。1,500 個のファイルをかみ砕き、しばらく待ってから、さらに 1,500 個のファイルを処理します。ファイルの一覧表示中またはコピー中のいずれかです。繰り返しますが、これはローカルで実行する場合には当てはまりません。問題なく実行されるため、おそらく私のコードとは何の関係もないと推測できます。あたかもネットワークが断続的にゲートを開閉しているようです。

あるいは、エンド ユーザーの観点から他のプログラムを使用すると (作業ネットワーク上のプログラムで使用していたのと同じファイルに対して試しました)、前述の遅延なしではるかに高速です。問題があれば、代替プログラムが何らかのバージョンの.netを使用していると思います。簡単に言えば、私が直面している速度の問題について、私たちのネットワークを本質的に責めることはできないと思います.

したがって、私の質問/好奇心/問題は、いくつかの重要なポイントに要約されます。

-VBA の FSO と .Net の既定のライブラリの違いは何ですか?また、発生している問題の原因の違いはありますか? 明らかに、この種のデータは、実行中よりもはるかに高速に読み取ることができます。

- FSO はこのように使用されることを意図していませんか? (ネットワーク経由、大量のリモート データ、または...?) 時代遅れ/時代遅れですか?また、VBA で使用できる代替手段はありますか?

- 私たちのネットワークがローカル ドライブとは異なる方法で機能することを漠然としか理解していません。数テラバイトのデータなどを保存しますが、ローカルドライブとネットワーク上の場所へのアクセスの非常に深いレベルでの違いが何であるかはわかりません。おそらく診断に非常に役立つネットワークの詳細を提供していないことはわかっていますが、残念ながら情報を提供していません。FSO を一部/あらゆる種類のネットワークでそのような方法で使用することは、意図された使用方法ではないという説明が「潜在的に」あるかどうかを尋ねるだけだと思います。私がネットワークとやり取りしようとしている方法を制限するような方法でネットワークが設定されている可能性はありますか?

-これをローカルで実行する際に問題が発生したことはありませんが、コード内の何かがローカル ドライブよりもネットワークの場所に負担がかかる可能性はありますか?

あなたが提供できる洞察に感謝します。

4

4 に答える 4

0

FSO を使用する代わりに、DIR()より高速な速度が必要な場合に使用します。
ただし、フェイルセーフではないため、いくつかのテストを実行して、すべての場合に機能することを確認する必要があります.
たとえば、存在することを確認するために、個々の親フォルダーをチェックする必要がある場合があります。

とにかく、Dir()ネイティブ関数なので速いはずです。

これを解決するもう 1 つの方法は、Batch を使用するか (もちろん Widows を使用している場合)、またはコマンド ラインを使用して、あるファイルから別のファイルに単純にコピーすることです。速度が劇的に向上し、すべてのサブフォルダーの存在を確認する必要はありません。

Windowsコマンドラインを使用して必要なことを行うVBAコードをたまたま持っています。インターネットから入手しましたが、やりたいことを回避するためにいくつかのエラー確認を微調整しました。

Option Explicit
Option Base 0
Option Compare Text

Private Type SECURITY_ATTRIBUTES
    nLength As Long
    lpSecurityDescriptor As Long
    bInheritHandle As Long
End Type

Private Type PROCESS_INFORMATION
    hProcess As Long
    hThread As Long
    dwProcessId As Long
    dwThreadId As Long
End Type

Private Type STARTUPINFO
    cb As Long
    lpReserved As Long
    lpDesktop As Long
    lpTitle As Long
    dwX As Long
    dwY As Long
    dwXSize As Long
    dwYSize As Long
    dwXCountChars As Long
    dwYCountChars As Long
    dwFillAttribute As Long
    dwFlags As Long
    wShowWindow As Integer
    cbReserved2 As Integer
    lpReserved2 As Byte
    hStdInput As Long
    hStdOutput As Long
    hStdError As Long
End Type

Private Const WAIT_INFINITE         As Long = (-1&)
Private Const STARTF_USESHOWWINDOW  As Long = &H1
Private Const STARTF_USESTDHANDLES  As Long = &H100
Private Const SW_HIDE               As Long = 0&

Private Declare Function CreatePipe Lib "kernel32" (phReadPipe As Long, phWritePipe As Long, lpPipeAttributes As SECURITY_ATTRIBUTES, ByVal nSize As Long) As Long
Private Declare Function CreateProcess Lib "kernel32" Alias "CreateProcessA" (ByVal lpApplicationName As Long, ByVal lpCommandLine As String, lpProcessAttributes As Any, lpThreadAttributes As Any, ByVal bInheritHandles As Long, ByVal dwCreationFlags As Long, lpEnvironment As Any, ByVal lpCurrentDriectory As String, lpStartupInfo As STARTUPINFO, lpProcessInformation As PROCESS_INFORMATION) As Long
Private Declare Function ReadFile Lib "kernel32" (ByVal hFile As Long, lpBuffer As Any, ByVal nNumberOfBytesToRead As Long, lpNumberOfBytesRead As Long, lpOverlapped As Any) As Long
Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long
Private Declare Function WaitForSingleObject Lib "kernel32" (ByVal hHandle As Long, ByVal dwMilliseconds As Long) As Long
Private Declare Function GetExitCodeProcess Lib "kernel32" (ByVal hProcess As Long, lpExitCode As Long) As Long
Private Declare Sub GetStartupInfo Lib "kernel32" Alias "GetStartupInfoA" (lpStartupInfo As STARTUPINFO)
Private Declare Function GetFileSize Lib "kernel32" (ByVal hFile As Long, lpFileSizeHigh As Long) As Long

Public Function Redirect(szBinaryPath As String, szCommandLn As String) As String

Dim tSA_CreatePipe              As SECURITY_ATTRIBUTES
Dim tSA_CreateProcessPrc        As SECURITY_ATTRIBUTES
Dim tSA_CreateProcessThrd       As SECURITY_ATTRIBUTES
Dim tSA_CreateProcessPrcInfo    As PROCESS_INFORMATION
Dim tStartupInfo                As STARTUPINFO
Dim hRead                       As Long
Dim hWrite                      As Long
Dim bRead                       As Long
Dim abytBuff()                  As Byte
Dim lngResult                   As Long
Dim szFullCommand               As String
Dim lngExitCode                 As Long
Dim lngSizeOf                   As Long

tSA_CreatePipe.nLength = Len(tSA_CreatePipe)
tSA_CreatePipe.lpSecurityDescriptor = 0&
tSA_CreatePipe.bInheritHandle = True

tSA_CreateProcessPrc.nLength = Len(tSA_CreateProcessPrc)
tSA_CreateProcessThrd.nLength = Len(tSA_CreateProcessThrd)

If (CreatePipe(hRead, hWrite, tSA_CreatePipe, 0&) <> 0&) Then
    tStartupInfo.cb = Len(tStartupInfo)
    GetStartupInfo tStartupInfo

    With tStartupInfo
        .hStdOutput = hWrite
        .hStdError = hWrite
        .dwFlags = STARTF_USESHOWWINDOW Or STARTF_USESTDHANDLES
        .wShowWindow = SW_HIDE
    End With

    szFullCommand = """" & szBinaryPath & """" & " " & szCommandLn
    lngResult = CreateProcess(0&, szFullCommand, tSA_CreateProcessPrc, tSA_CreateProcessThrd, True, 0&, 0&, vbNullString, tStartupInfo, tSA_CreateProcessPrcInfo)

    If (lngResult <> 0&) Then
        lngResult = WaitForSingleObject(tSA_CreateProcessPrcInfo.hProcess, WAIT_INFINITE)
        lngSizeOf = GetFileSize(hRead, 0&)
        If (lngSizeOf > 0) Then
            ReDim abytBuff(lngSizeOf - 1)
            If ReadFile(hRead, abytBuff(0), UBound(abytBuff) + 1, bRead, ByVal 0&) Then
                Redirect = StrConv(abytBuff, vbUnicode)
            End If
        End If
        Call GetExitCodeProcess(tSA_CreateProcessPrcInfo.hProcess, lngExitCode)
        CloseHandle tSA_CreateProcessPrcInfo.hThread
        CloseHandle tSA_CreateProcessPrcInfo.hProcess

        'If (lngExitCode <> 0&) Then Err.Raise vbObject + 1235&, "GetExitCodeProcess", "Non-zero Application exist code"

        CloseHandle hWrite
        CloseHandle hRead
    Else
        Err.Raise vbObject + 1236&, "CreateProcess", "CreateProcess Failed, Code: " & Err.LastDllError
    End If
End If
End Function


resp = Redirect("cmd", strCmd)
whereはcmdwindows + R を押すのと同じstrCmdで、実行プロンプトに入力する文字列です。

ローカル ドライブとネットワーク ドライブのパフォーマンスの違いに関する質問にさらに答えるために、ネットワーク ドライブの操作は、どのような種類のコードでも常に遅くなります。ネットワーク ドライブにアクセスするときに実行されるバックグラウンド コードは複雑ですが、詳細はわかりません。

それが役に立てば幸いです、
乾杯、
kpark

于 2013-08-22T18:21:07.003 に答える
0

ネットワーク上の 1500 ファイルの場合、FSO を使用した次の実装はそれほど遅くはないと思いますが、どれくらいの速さを期待していましたか?

Sub TestBuildFileStructure()
' Call to test GetFiles function.

Const sDIRECTORYTOCHECK As String = <enter path to check from as string>

Dim varItem         As Variant
Dim wkbOutputFile   As Workbook
Dim shtOutputSheet  As Worksheet
Dim sDate           As String
Dim sPath           As String
Dim lRowNumber      As Long
Dim vSplit          As Variant

sPath = ThisWorkbook.Path

sDate = CStr(Now)
vSplit = Split(sDate, "/")
sDate = vSplit(0) & vSplit(1) & vSplit(2)
vSplit = Split(sDate, ":")
sDate = vSplit(0) & vSplit(1) & vSplit(2)

sDate = "Check " & sDate

Set wkbOutputFile = Workbooks.Add
'wkbOutputFile.Name = sDate
Set shtOutputSheet = wkbOutputFile.Sheets.Add
shtOutputSheet.Name = "Output"

lRowNumber = 1


Call BuildFileStructure(sDIRECTORYTOCHECK, shtOutputSheet, lRowNumber, True)

wkbOutputFile.SaveAs (sPath & "\" & sDate)



Cleanup:

Set shtOutputSheet = Nothing
Set wkbOutputFile = Nothing

End Sub

Function BuildFileStructure(ByVal strPath As String, _
                ByRef shtOutputSheet As Worksheet, _
                ByRef lRowNumber As Long, _
                Optional ByVal blnRecursive As Boolean) As Boolean

   ' This procedure returns all the files in a directory into
   ' an excel file. If called recursively, it also returns
   ' all files in subfolders.

    Const iNAMECOLUMN As Integer = 1

    Dim fsoSysObj       As FileSystemObject
    Dim fdrFolder       As Folder
    Dim fdrSubFolder    As Folder
    Dim filFile         As File

    ' Return new FileSystemObject.
    Set fsoSysObj = New FileSystemObject

    On Error Resume Next
    ' Get folder.
    Set fdrFolder = fsoSysObj.GetFolder(strPath)

    If Err <> 0 Then
      ' Incorrect path.
        BuildFileStructure = False
        GoTo BuildFileStructure_End
    End If
    On Error GoTo 0

    ' Loop through Files collection, adding to dictionary.
    For Each filFile In fdrFolder.Files
      shtOutputSheet.Cells(lRowNumber, iNAMECOLUMN).Value = filFile.Path
       lRowNumber = lRowNumber + 1
    Next filFile

    ' If Recursive flag is true, call recursively.
    If blnRecursive Then
        For Each fdrSubFolder In fdrFolder.SubFolders
            Call BuildFileStructure(fdrSubFolder.Path, shtOutputSheet, lRowNumber, True)
        Next fdrSubFolder
    End If

    ' Return True if no error occurred.
    BuildFileStructure = True

BuildFileStructure_End:
    Set fdrSubFolder = Nothing
    Set fdrFolder = Nothing
    Set filFile = Nothing
    Set fsoSysObj = Nothing

    Exit Function
End Function
于 2013-08-22T20:35:43.500 に答える